From 81e33b3d89bdb800a0e34644f59e97369f491fad Mon Sep 17 00:00:00 2001 From: Annemarie Porter Date: Fri, 28 Mar 2025 16:48:41 -0700 Subject: [PATCH] modules: Add pcm_cnv module and dependencies Add the pcm converter module, which can be used to convert the properties of a pcm stream. Also adds the dependencies of the pcm_cnv module, including a channel mixer module, multi-stage iir filter module, as well as iir and dynamic resamplers, which are included as binaries. Signed-off-by: Annemarie Porter --- CMakeLists.txt | 5 + arch/hexagon/configs/defconfig | 36 - arch/linux/configs/defconfig | 6 + modules/CMakeLists.txt | 24 + modules/Kconfig | 14 + .../audio/pcm_decoder/api/pcm_decoder_api.h | 100 + .../audio/pcm_decoder/build/CMakeLists.txt | 16 + modules/audio/pcm_decoder/inc/capi_pcm_dec.h | 35 + .../audio/pcm_encoder/api/pcm_encoder_api.h | 198 + .../audio/pcm_encoder/build/CMakeLists.txt | 16 + modules/audio/pcm_encoder/inc/capi_pcm_enc.h | 35 + modules/cmn/api/imcl_spm_intent_api.h | 168 + modules/cmn/build/CMakeLists.txt | 18 + modules/cmn/common/build/CMakeLists.txt | 18 + modules/cmn/common/utils/build/CMakeLists.txt | 48 + modules/cmn/common/utils/inc/AEEStdDef.h | 30 + modules/cmn/common/utils/inc/AudioComdef.h | 88 + modules/cmn/common/utils/inc/adsp_redef.h | 9 + modules/cmn/common/utils/inc/audio_basic_op.h | 1443 +++++ .../cmn/common/utils/inc/audio_basic_op_ext.h | 233 + modules/cmn/common/utils/inc/audio_clips.h | 83 + modules/cmn/common/utils/inc/audio_comdef.h | 88 + .../utils/inc/audio_common_param_calib.h | 54 + .../common/utils/inc/audio_complex_basic_op.h | 307 + .../cmn/common/utils/inc/audio_divide_qx.h | 267 + modules/cmn/common/utils/inc/audio_dsp.h | 475 ++ modules/cmn/common/utils/inc/audio_dsp32.h | 102 + modules/cmn/common/utils/inc/audio_exp10.h | 58 + .../common/utils/inc/audio_fft_basic_ops.h | 159 + modules/cmn/common/utils/inc/audio_iir_tdf2.h | 91 + modules/cmn/common/utils/inc/audio_log10.h | 58 + modules/cmn/common/utils/inc/audpp_common.h | 84 + .../cmn/common/utils/inc/audpp_converter.h | 175 + modules/cmn/common/utils/inc/audpp_mathlib.h | 100 + modules/cmn/common/utils/inc/audpp_util.h | 70 + modules/cmn/common/utils/inc/basic_math.h | 103 + .../cmn/common/utils/inc/byte_conversion.h | 75 + modules/cmn/common/utils/inc/crossfade.h | 142 + modules/cmn/common/utils/inc/crossfade_api.h | 137 + .../utils/inc/crossfade_calibration_api.h | 62 + modules/cmn/common/utils/inc/legacy_map.h | 364 ++ modules/cmn/common/utils/inc/simple_mm.h | 71 + .../cmn/common/utils/src/audio_basic_op_ext.c | 854 +++ .../utils/src/audio_basic_op_ext_island.c | 192 + modules/cmn/common/utils/src/audio_buffer.cpp | 671 ++ modules/cmn/common/utils/src/audio_buffer32.c | 342 ++ .../common/utils/src/audio_buffer32_island.c | 40 + .../common/utils/src/audio_complex_basic_op.c | 800 +++ modules/cmn/common/utils/src/audio_delay32.c | 475 ++ modules/cmn/common/utils/src/audio_panner.cpp | 609 ++ modules/cmn/common/utils/src/basic_math.c | 506 ++ modules/cmn/common/utils/src/basic_op.c | 5435 +++++++++++++++++ modules/cmn/common/utils/src/clips.c | 75 + modules/cmn/common/utils/src/crossfade.c | 677 ++ modules/cmn/common/utils/src/divide_qx.c | 546 ++ .../cmn/common/utils/src/divide_qx_island.c | 341 ++ modules/cmn/common/utils/src/exp10.c | 67 + modules/cmn/common/utils/src/iir_tdf2.c | 645 ++ modules/cmn/common/utils/src/log10.c | 64 + modules/cmn/common/utils/src/mathlib.c | 710 +++ modules/cmn/common/utils/src/simple_mm.c | 216 + modules/cmn/common/utils/src/util.c | 158 + modules/cmn/pcm_mf_cnv/build/CMakeLists.txt | 148 + modules/cmn/pcm_mf_cnv/capi/mfc/api/mfc_api.h | 352 ++ .../pcm_mf_cnv/capi/mfc/build/CMakeLists.txt | 16 + .../cmn/pcm_mf_cnv/capi/mfc/inc/capi_mfc.h | 33 + .../capi/pcm_cnv/api/pcm_converter_api.h | 101 + .../capi/pcm_cnv/build/CMakeLists.txt | 33 + .../capi/pcm_cnv/inc/capi_pcm_cnv.h | 35 + .../capi/pcm_cnv/src/capi_pcm_mf_cnv.cpp | 1716 ++++++ .../capi/pcm_cnv/src/capi_pcm_mf_cnv_i.h | 72 + .../pcm_cnv/src/capi_pcm_mf_cnv_island.cpp | 281 + .../pcm_cnv/src/capi_pcm_mf_cnv_utils.cpp | 1810 ++++++ .../capi/pcm_cnv/src/capi_pcm_mf_cnv_utils.h | 266 + .../src/capi_pcm_mf_cnv_utils_island.cpp | 35 + .../pcm_cnv/stub_src/capi_pcm_cnv_stub.cpp | 44 + modules/cmn/pcm_mf_cnv/lib/inc/pc_converter.h | 437 ++ .../cmn/pcm_mf_cnv/lib/src/pc_converter.cpp | 75 + .../lib/src/pc_converter_island.cpp | 982 +++ .../pcm_mf_cnv/lib/src/pc_float/pc_float.cpp | 278 + modules/cmn/pcm_mf_cnv/lib/src/pc_init.cpp | 1681 +++++ modules/cmn/pcm_mf_cnv/lib/src/pc_process.cpp | 101 + .../pcm_mf_cnv/lib/src/pc_process_island.cpp | 697 +++ .../lib/stub_src/pc_float/pc_float_stub.cpp | 41 + modules/cmn/pcm_mf_cnv/lib/tst/main.cpp | 239 + .../channel_mixer/api/chmixer_api.h | 108 + .../channel_mixer/api/chmixer_common_api.h | 190 + .../channel_mixer/build/CMakeLists.txt | 43 + .../channel_mixer/capi/inc/capi_chmixer.h | 54 + .../channel_mixer/capi/src/capi_chmixer.cpp | 839 +++ .../capi/src/capi_chmixer_utils.cpp | 1094 ++++ .../capi/src/capi_chmixer_utils.h | 175 + .../capi/stub_src/capi_chmixer.cpp | 38 + .../channel_mixer/lib/inc/ChannelMixerLib.h | 209 + .../channel_mixer/lib/src/ChannelMixerLib.c | 642 ++ .../lib/src/ChannelMixerLib_island.c | 189 + .../lib/src/ChannelMixerRemapRules.c | 214 + .../lib/src/ChannelMixerRemapRules.h | 65 + .../channel_mixer/lib/stub/ChannelMixerLib.c | 97 + .../filters/multi_stage_iir/api/api_msiir.h | 696 +++ .../multi_stage_iir/build/CMakeLists.txt | 44 + .../capi/inc/capi_multistageiir.h | 64 + .../capi/src/capi_multistageiir.cpp | 743 +++ .../capi/src/capi_multistageiir_utils.cpp | 2078 +++++++ .../capi/src/capi_multistageiir_utils.h | 285 + .../capi/src/capi_multistageiir_utils_v2.cpp | 1093 ++++ .../capi/stub_src/capi_multistageiir_stub.cpp | 84 + .../capi/tst/cfg/ffa_handset_nb.cfg | 121 + .../capi/tst/cfg/ffa_handset_wb.cfg | 121 + .../capi/tst/cfg/left_zero_right_hpf.cfg | 146 + .../capi/tst/cfg/msiir_3channel_32bit.cfg | 194 + .../tst/cfg/msiir_cross_fade_stereo_16bit.cfg | 126 + .../capi/tst/cfg/msiir_mono_HPF_32bit.cfg | 128 + .../capi/tst/cfg/msiir_mono_LGE_HPF_16bit.cfg | 129 + .../capi/tst/cfg/msiir_mono_LPF_32bit.cfg | 128 + .../capi/tst/cfg/msiir_mono_Stop_32bit.cfg | 141 + ...msiir_mono_numShiftFactor_stage1_32bit.cfg | 141 + ...ir_mono_numShiftFactor_stage1low_32bit.cfg | 141 + .../tst/cfg/msiir_mono_preGain_max_32bit.cfg | 94 + .../cfg/msiir_mono_preGain_minus5_32bit.cfg | 128 + .../filters/multi_stage_iir/capi/tst/main.cpp | 171 + .../multi_stage_iir/capi/tst/test_cases.json | 41 + .../capi/tst/test_cases_32bit.json | 62 + .../capi/tst/test_cases_cross_fade.json | 13 + .../multi_stage_iir/lib/inc/CMultiStageIIR.h | 150 + .../multi_stage_iir/lib/inc/msiir_api.h | 94 + .../lib/inc/msiir_calibration_api.h | 54 + .../multi_stage_iir/lib/src/CMultiStageIIR.c | 435 ++ .../filters/multi_stage_iir/lib/src/msiir.c | 319 + .../filters/multi_stage_iir/lib/src/msiir.h | 47 + .../api/dynamic_resampler_api.h | 162 + .../bin/arm/libdynamic_resampler.a | Bin 0 -> 673450 bytes .../dynamic_resampler/build/CMakeLists.txt | 33 + .../dynamic_resampler/inc/hw_rs_lib.h | 40 + .../dynamic_resampler/inc/hwsw_rs_lib.h | 263 + .../dynamic_resampler/inc/hwsw_rs_lib_hw.h | 173 + .../inc/multi_def_resampler.h | 31 + .../dynamic_resampler/inc/resampler_32b_ext.h | 143 + .../dynamic_resampler/inc/rs_common.h | 96 + .../dynamic_resampler/inc/rs_driver.h | 460 ++ .../dynamic_resampler/inc/rs_driver_ext.h | 410 ++ .../iir_resampler/api/iir_resampler_api.h | 176 + .../iir_resampler/bin/arm/libiir_resampler.a | Bin 0 -> 248210 bytes .../iir_resampler/build/CMakeLists.txt | 30 + .../iir_resampler/inc/iir_resampler.h | 429 ++ .../resamplers/iir_resampler/inc/iir_rs_lib.h | 100 + 146 files changed, 43239 insertions(+), 36 deletions(-) delete mode 100644 arch/hexagon/configs/defconfig create mode 100644 modules/audio/pcm_decoder/api/pcm_decoder_api.h create mode 100644 modules/audio/pcm_decoder/build/CMakeLists.txt create mode 100644 modules/audio/pcm_decoder/inc/capi_pcm_dec.h create mode 100644 modules/audio/pcm_encoder/api/pcm_encoder_api.h create mode 100644 modules/audio/pcm_encoder/build/CMakeLists.txt create mode 100644 modules/audio/pcm_encoder/inc/capi_pcm_enc.h create mode 100644 modules/cmn/api/imcl_spm_intent_api.h create mode 100644 modules/cmn/build/CMakeLists.txt create mode 100644 modules/cmn/common/build/CMakeLists.txt create mode 100644 modules/cmn/common/utils/build/CMakeLists.txt create mode 100644 modules/cmn/common/utils/inc/AEEStdDef.h create mode 100644 modules/cmn/common/utils/inc/AudioComdef.h create mode 100644 modules/cmn/common/utils/inc/adsp_redef.h create mode 100644 modules/cmn/common/utils/inc/audio_basic_op.h create mode 100644 modules/cmn/common/utils/inc/audio_basic_op_ext.h create mode 100644 modules/cmn/common/utils/inc/audio_clips.h create mode 100644 modules/cmn/common/utils/inc/audio_comdef.h create mode 100644 modules/cmn/common/utils/inc/audio_common_param_calib.h create mode 100644 modules/cmn/common/utils/inc/audio_complex_basic_op.h create mode 100644 modules/cmn/common/utils/inc/audio_divide_qx.h create mode 100644 modules/cmn/common/utils/inc/audio_dsp.h create mode 100644 modules/cmn/common/utils/inc/audio_dsp32.h create mode 100644 modules/cmn/common/utils/inc/audio_exp10.h create mode 100644 modules/cmn/common/utils/inc/audio_fft_basic_ops.h create mode 100644 modules/cmn/common/utils/inc/audio_iir_tdf2.h create mode 100644 modules/cmn/common/utils/inc/audio_log10.h create mode 100644 modules/cmn/common/utils/inc/audpp_common.h create mode 100644 modules/cmn/common/utils/inc/audpp_converter.h create mode 100644 modules/cmn/common/utils/inc/audpp_mathlib.h create mode 100644 modules/cmn/common/utils/inc/audpp_util.h create mode 100644 modules/cmn/common/utils/inc/basic_math.h create mode 100644 modules/cmn/common/utils/inc/byte_conversion.h create mode 100644 modules/cmn/common/utils/inc/crossfade.h create mode 100644 modules/cmn/common/utils/inc/crossfade_api.h create mode 100644 modules/cmn/common/utils/inc/crossfade_calibration_api.h create mode 100644 modules/cmn/common/utils/inc/legacy_map.h create mode 100644 modules/cmn/common/utils/inc/simple_mm.h create mode 100644 modules/cmn/common/utils/src/audio_basic_op_ext.c create mode 100644 modules/cmn/common/utils/src/audio_basic_op_ext_island.c create mode 100644 modules/cmn/common/utils/src/audio_buffer.cpp create mode 100644 modules/cmn/common/utils/src/audio_buffer32.c create mode 100644 modules/cmn/common/utils/src/audio_buffer32_island.c create mode 100644 modules/cmn/common/utils/src/audio_complex_basic_op.c create mode 100644 modules/cmn/common/utils/src/audio_delay32.c create mode 100644 modules/cmn/common/utils/src/audio_panner.cpp create mode 100644 modules/cmn/common/utils/src/basic_math.c create mode 100644 modules/cmn/common/utils/src/basic_op.c create mode 100644 modules/cmn/common/utils/src/clips.c create mode 100644 modules/cmn/common/utils/src/crossfade.c create mode 100644 modules/cmn/common/utils/src/divide_qx.c create mode 100644 modules/cmn/common/utils/src/divide_qx_island.c create mode 100644 modules/cmn/common/utils/src/exp10.c create mode 100644 modules/cmn/common/utils/src/iir_tdf2.c create mode 100644 modules/cmn/common/utils/src/log10.c create mode 100644 modules/cmn/common/utils/src/mathlib.c create mode 100644 modules/cmn/common/utils/src/simple_mm.c create mode 100644 modules/cmn/common/utils/src/util.c create mode 100644 modules/cmn/pcm_mf_cnv/build/CMakeLists.txt create mode 100644 modules/cmn/pcm_mf_cnv/capi/mfc/api/mfc_api.h create mode 100644 modules/cmn/pcm_mf_cnv/capi/mfc/build/CMakeLists.txt create mode 100644 modules/cmn/pcm_mf_cnv/capi/mfc/inc/capi_mfc.h create mode 100644 modules/cmn/pcm_mf_cnv/capi/pcm_cnv/api/pcm_converter_api.h create mode 100644 modules/cmn/pcm_mf_cnv/capi/pcm_cnv/build/CMakeLists.txt create mode 100644 modules/cmn/pcm_mf_cnv/capi/pcm_cnv/inc/capi_pcm_cnv.h create mode 100644 modules/cmn/pcm_mf_cnv/capi/pcm_cnv/src/capi_pcm_mf_cnv.cpp create mode 100644 modules/cmn/pcm_mf_cnv/capi/pcm_cnv/src/capi_pcm_mf_cnv_i.h create mode 100644 modules/cmn/pcm_mf_cnv/capi/pcm_cnv/src/capi_pcm_mf_cnv_island.cpp create mode 100644 modules/cmn/pcm_mf_cnv/capi/pcm_cnv/src/capi_pcm_mf_cnv_utils.cpp create mode 100644 modules/cmn/pcm_mf_cnv/capi/pcm_cnv/src/capi_pcm_mf_cnv_utils.h create mode 100644 modules/cmn/pcm_mf_cnv/capi/pcm_cnv/src/capi_pcm_mf_cnv_utils_island.cpp create mode 100644 modules/cmn/pcm_mf_cnv/capi/pcm_cnv/stub_src/capi_pcm_cnv_stub.cpp create mode 100644 modules/cmn/pcm_mf_cnv/lib/inc/pc_converter.h create mode 100644 modules/cmn/pcm_mf_cnv/lib/src/pc_converter.cpp create mode 100644 modules/cmn/pcm_mf_cnv/lib/src/pc_converter_island.cpp create mode 100644 modules/cmn/pcm_mf_cnv/lib/src/pc_float/pc_float.cpp create mode 100644 modules/cmn/pcm_mf_cnv/lib/src/pc_init.cpp create mode 100644 modules/cmn/pcm_mf_cnv/lib/src/pc_process.cpp create mode 100644 modules/cmn/pcm_mf_cnv/lib/src/pc_process_island.cpp create mode 100644 modules/cmn/pcm_mf_cnv/lib/stub_src/pc_float/pc_float_stub.cpp create mode 100644 modules/cmn/pcm_mf_cnv/lib/tst/main.cpp create mode 100644 modules/processing/channel_mixer/api/chmixer_api.h create mode 100644 modules/processing/channel_mixer/api/chmixer_common_api.h create mode 100644 modules/processing/channel_mixer/build/CMakeLists.txt create mode 100644 modules/processing/channel_mixer/capi/inc/capi_chmixer.h create mode 100644 modules/processing/channel_mixer/capi/src/capi_chmixer.cpp create mode 100644 modules/processing/channel_mixer/capi/src/capi_chmixer_utils.cpp create mode 100644 modules/processing/channel_mixer/capi/src/capi_chmixer_utils.h create mode 100644 modules/processing/channel_mixer/capi/stub_src/capi_chmixer.cpp create mode 100644 modules/processing/channel_mixer/lib/inc/ChannelMixerLib.h create mode 100644 modules/processing/channel_mixer/lib/src/ChannelMixerLib.c create mode 100644 modules/processing/channel_mixer/lib/src/ChannelMixerLib_island.c create mode 100644 modules/processing/channel_mixer/lib/src/ChannelMixerRemapRules.c create mode 100644 modules/processing/channel_mixer/lib/src/ChannelMixerRemapRules.h create mode 100644 modules/processing/channel_mixer/lib/stub/ChannelMixerLib.c create mode 100644 modules/processing/filters/multi_stage_iir/api/api_msiir.h create mode 100644 modules/processing/filters/multi_stage_iir/build/CMakeLists.txt create mode 100644 modules/processing/filters/multi_stage_iir/capi/inc/capi_multistageiir.h create mode 100644 modules/processing/filters/multi_stage_iir/capi/src/capi_multistageiir.cpp create mode 100644 modules/processing/filters/multi_stage_iir/capi/src/capi_multistageiir_utils.cpp create mode 100644 modules/processing/filters/multi_stage_iir/capi/src/capi_multistageiir_utils.h create mode 100644 modules/processing/filters/multi_stage_iir/capi/src/capi_multistageiir_utils_v2.cpp create mode 100644 modules/processing/filters/multi_stage_iir/capi/stub_src/capi_multistageiir_stub.cpp create mode 100644 modules/processing/filters/multi_stage_iir/capi/tst/cfg/ffa_handset_nb.cfg create mode 100644 modules/processing/filters/multi_stage_iir/capi/tst/cfg/ffa_handset_wb.cfg create mode 100644 modules/processing/filters/multi_stage_iir/capi/tst/cfg/left_zero_right_hpf.cfg create mode 100644 modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_3channel_32bit.cfg create mode 100644 modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_cross_fade_stereo_16bit.cfg create mode 100644 modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_HPF_32bit.cfg create mode 100644 modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_LGE_HPF_16bit.cfg create mode 100644 modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_LPF_32bit.cfg create mode 100644 modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_Stop_32bit.cfg create mode 100644 modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_numShiftFactor_stage1_32bit.cfg create mode 100644 modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_numShiftFactor_stage1low_32bit.cfg create mode 100644 modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_preGain_max_32bit.cfg create mode 100644 modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_preGain_minus5_32bit.cfg create mode 100644 modules/processing/filters/multi_stage_iir/capi/tst/main.cpp create mode 100644 modules/processing/filters/multi_stage_iir/capi/tst/test_cases.json create mode 100644 modules/processing/filters/multi_stage_iir/capi/tst/test_cases_32bit.json create mode 100644 modules/processing/filters/multi_stage_iir/capi/tst/test_cases_cross_fade.json create mode 100644 modules/processing/filters/multi_stage_iir/lib/inc/CMultiStageIIR.h create mode 100644 modules/processing/filters/multi_stage_iir/lib/inc/msiir_api.h create mode 100644 modules/processing/filters/multi_stage_iir/lib/inc/msiir_calibration_api.h create mode 100644 modules/processing/filters/multi_stage_iir/lib/src/CMultiStageIIR.c create mode 100644 modules/processing/filters/multi_stage_iir/lib/src/msiir.c create mode 100644 modules/processing/filters/multi_stage_iir/lib/src/msiir.h create mode 100644 modules/processing/resamplers/dynamic_resampler/api/dynamic_resampler_api.h create mode 100644 modules/processing/resamplers/dynamic_resampler/bin/arm/libdynamic_resampler.a create mode 100644 modules/processing/resamplers/dynamic_resampler/build/CMakeLists.txt create mode 100644 modules/processing/resamplers/dynamic_resampler/inc/hw_rs_lib.h create mode 100644 modules/processing/resamplers/dynamic_resampler/inc/hwsw_rs_lib.h create mode 100644 modules/processing/resamplers/dynamic_resampler/inc/hwsw_rs_lib_hw.h create mode 100644 modules/processing/resamplers/dynamic_resampler/inc/multi_def_resampler.h create mode 100644 modules/processing/resamplers/dynamic_resampler/inc/resampler_32b_ext.h create mode 100644 modules/processing/resamplers/dynamic_resampler/inc/rs_common.h create mode 100644 modules/processing/resamplers/dynamic_resampler/inc/rs_driver.h create mode 100644 modules/processing/resamplers/dynamic_resampler/inc/rs_driver_ext.h create mode 100644 modules/processing/resamplers/iir_resampler/api/iir_resampler_api.h create mode 100644 modules/processing/resamplers/iir_resampler/bin/arm/libiir_resampler.a create mode 100644 modules/processing/resamplers/iir_resampler/build/CMakeLists.txt create mode 100644 modules/processing/resamplers/iir_resampler/inc/iir_resampler.h create mode 100644 modules/processing/resamplers/iir_resampler/inc/iir_rs_lib.h diff --git a/CMakeLists.txt b/CMakeLists.txt index abd781f..d8bdc70 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,6 +76,11 @@ fwk/api/apm fwk/api/ar_utils fwk/api/modules ${PROJECT_SOURCE_DIR}/ar_osal/api +modules/processing/channel_mixer/api +modules/audio/pcm_decoder/api +modules/audio/pcm_encoder/api +modules/cmn/pcm_mf_cnv/capi/mfc/api +modules/cmn/pcm_mf_cnv/capi/pcm_cnv/api ) ### ### Some strings used for picking inc paths based on arch, tgt/sim & static/shared. diff --git a/arch/hexagon/configs/defconfig b/arch/hexagon/configs/defconfig deleted file mode 100644 index 0717a01..0000000 --- a/arch/hexagon/configs/defconfig +++ /dev/null @@ -1,36 +0,0 @@ -# Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib) -CONFIG_ARCH_HEXAGON=y -CONFIG_HEXAGON_TOOLS_ROOT="" -CONFIG_HEXAGON_SDK_ROOT="" -CONFIG_DISABLE_PLATFORM=y -# CONFIG_COMPILE_POSAL is not set - -# -# Modules -# -CONFIG_MODULES=y -CONFIG_MODULES_DEBUG=y -CONFIG_ENCODER=y -CONFIG_ECHO_CANCELLATION=y -CONFIG_GAIN=y - -# -# Signal Processing Framework -# -# CONFIG_SPF_AS_STATIC_LIB is not set -# CONFIG_SPF_DEBUG is not set - -# -# Signal Processing Framework Modules -# -CONFIG_DATA_LOGGING=y -CONFIG_LATENCY=y -CONFIG_SYNC=y -CONFIG_SPR=y -CONFIG_SPLITTER=y -CONFIG_DATA_MARKER=y -CONFIG_PRIORITY_SYNC=y -CONFIG_MUX_DEMUX=y -CONFIG_SISO_RAT=y -CONFIG_SH_MEM_PULL_MODE=y -CONFIG_SH_MEM_PUSH_MODE=y diff --git a/arch/linux/configs/defconfig b/arch/linux/configs/defconfig index 9b7b489..3a8c7e0 100644 --- a/arch/linux/configs/defconfig +++ b/arch/linux/configs/defconfig @@ -12,6 +12,12 @@ CONFIG_MODULES=y CONFIG_ENCODER=y CONFIG_ECHO_CANCELLATION=y CONFIG_GAIN=m +CONFIG_MSIIR=y +CONFIG_CHMIXER=y +CONFIG_PCM_CNV=y +CONFIG_MFC=y +CONFIG_PCM_DECODER=y +CONFIG_PCM_ENCODER=y # # Signal Processing Framework diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt index 39e6888..48a0e30 100644 --- a/modules/CMakeLists.txt +++ b/modules/CMakeLists.txt @@ -1,6 +1,21 @@ # Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. # SPDX-License-Identifier: BSD-3-Clause +#Include directories +include_directories( + cmn/common/utils/inc/ + cmn/api + cmn/common/internal_api/imcl/api + ${LIB_ROOT}/fwk/spf/utils/list/inc + ${LIB_ROOT}/fwk/spf/interfaces/module/capi_cmn/ctrl_port/inc + ${LIB_ROOT}/fwk/spf/containers/cmn/topologies/topo_utils/inc + ${LIB_ROOT}/fwk/spf/utils/lpi_pool/inc + ${LIB_ROOT}/fwk/spf/containers/cmn/graph_utils/inc + ) + +#Add the sub directories +add_subdirectory(cmn/build cmn) + if(CONFIG_ENCODER) add_subdirectory(examples/encoder) endif() @@ -10,3 +25,12 @@ endif() if(CONFIG_GAIN) add_subdirectory(examples/gain) endif() +if(CONFIG_CHMIXER) + add_subdirectory(processing/channel_mixer/build) +endif() +if(CONFIG_MSIIR) + add_subdirectory(processing/filters/multi_stage_iir/build) +endif() +if(CONFIG_PCM_CNV) + add_subdirectory(cmn/pcm_mf_cnv/build) +endif() diff --git a/modules/Kconfig b/modules/Kconfig index fcbe3fe..f16996c 100644 --- a/modules/Kconfig +++ b/modules/Kconfig @@ -21,4 +21,18 @@ config ECHO_CANCELLATION config GAIN tristate "Enable GAIN Library" default y + +config CH_MIXER + tristate "Enable CH_MIXER Library" + default y + +config MSIIR + tristate "Enable MSIIR Library" + default y + +config PCM_CNV + tristate "Enable PCM_CNV Library" + select CH_MIXER + select MSIIR + default y endmenu diff --git a/modules/audio/pcm_decoder/api/pcm_decoder_api.h b/modules/audio/pcm_decoder/api/pcm_decoder_api.h new file mode 100644 index 0000000..d794c9c --- /dev/null +++ b/modules/audio/pcm_decoder/api/pcm_decoder_api.h @@ -0,0 +1,100 @@ +#ifndef PCM_DECODER_API_H_ +#define PCM_DECODER_API_H_ +/*============================================================================== + @file pcm_decoder_api.h + @brief This file contains PCM decoder APIs + +================================================================================ +Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. +SPDX-License-Identifier: BSD-3-Clause +==============================================================================*/ + +/*------------------------------------------------------------------------------ + Include files +------------------------------------------------------------------------------*/ +#include "chmixer_common_api.h" +#include "module_cmn_api.h" +#include "common_enc_dec_api.h" + +/*# @h2xml_title1 {PCM Decoder Module API} + @h2xml_title_agile_rev {PCM Decoder Module API} + @h2xml_title_date {March 26, 2019} */ + +/*------------------------------------------------------------------------------ + Defines +------------------------------------------------------------------------------*/ + +/** @ingroup ar_spf_mod_decoder_mod + Enumerates the input port ID for the MFC (#MODULE_ID_MFC) module. */ +#define PCM_DEC_DATA_INPUT_PORT 0x2 + +/** @ingroup ar_spf_mod_decoder_mod + Enumerates the output port ID for the MFC module. */ +#define PCM_DEC_DATA_OUTPUT_PORT 0x1 + +/*------------------------------------------------------------------------------ + Module +------------------------------------------------------------------------------*/ + +/** @ingroup ar_spf_mod_decoder_mod + Identifier for the module used as the decoder for PCM use cases. + + This module can be used to convert the properties of PCM stream: + endianness, interleaving, bit width, number of channels, and so on. It + cannot be used to convert sampling rate. The module has only one input and + one output port. + + The input media format for this module is propagated from the Write Shared + Memory Endpoint module and cannot be configured. + + @subhead4{Supported media format ID} + + The module decodes data to the following format ID: + - Data format : #DATA_FORMAT_FIXED_POINT @lstsp1 + - fmt_id : #MEDIA_FMT_ID_PCM @lstsp1 + - Sample rates : 1..384 kHz @lstsp1 + - Number of channels: 1..32 @lstsp1 + - Bit width: @lstsep + - 16 (bits per sample 16 and Q15) @lstsp2 + - 24 (bits per sample 24 and Q23, bits per sample 32 and Q23, Q27, or + Q31) @lstsp2 + - 32 (bits per sample 32 and Q31) @lstsp1 + - Interleaving: @lstsep + - Interleaved @lstsp2 + - De-interleaved unpacked @lstsp2 + - De-interleaved packed @lstsp1 + - Endianness: little, big +*/ +#define MODULE_ID_PCM_DEC 0x07001005 + +/*# @h2xmlm_module {"MODULE_ID_PCM_DEC", MODULE_ID_PCM_DEC} + @h2xmlm_displayName {"PCM Decoder"} + @h2xmlm_description {ID for the PCM Decoder module. It can be used + to convert the properties of PCM stream: + endianness, interleaving, bit width, number of + channels, and so on. It cannot be used to + convert sampling rate. For more details, + see AudioReach Signal Processing Framework (SPF) API Reference.} + @h2xmlm_dataMaxInputPorts {1} + @h2xmlm_dataMaxOutputPorts {1} + @h2xmlm_dataInputPorts {IN=PCM_DEC_DATA_INPUT_PORT} + @h2xmlm_dataOutputPorts {OUT=PCM_DEC_DATA_OUTPUT_PORT} + @h2xmlm_supportedContTypes {APM_CONTAINER_TYPE_GC} + @h2xmlm_isOffloadable {true} + @h2xmlm_stackSize {4096} + + @{ <-- Start of the Module --> + @h2xml_Select {"param_id_pcm_output_format_cfg_t"} + @h2xmlm_InsertParameter + @h2xml_Select {"payload_pcm_output_format_cfg_t"} + @h2xmlm_InsertParameter + @h2xml_Select {param_id_pcm_output_format_cfg_t::data_format} + @h2xmle_rangeEnum {pcm_data_format} + @h2xml_Select {param_id_chmixer_coeff_t} + @h2xmlm_InsertParameter + @h2xml_Select {chmixer_coeff_t} + @h2xmlm_InsertStructure + @} <-- End of the Module --> */ + + +#endif // PCM_DECODER_API_H_ diff --git a/modules/audio/pcm_decoder/build/CMakeLists.txt b/modules/audio/pcm_decoder/build/CMakeLists.txt new file mode 100644 index 0000000..9370ad9 --- /dev/null +++ b/modules/audio/pcm_decoder/build/CMakeLists.txt @@ -0,0 +1,16 @@ +#[[ + @file CMakeLists.txt + + @brief + + @copyright + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause +]] +cmake_minimum_required(VERSION 3.10) + +#Include directories +set(pcm_decoder_includes + ${LIB_ROOT}/api/ + ${LIB_ROOT}/inc/ + ) diff --git a/modules/audio/pcm_decoder/inc/capi_pcm_dec.h b/modules/audio/pcm_decoder/inc/capi_pcm_dec.h new file mode 100644 index 0000000..bddbb8c --- /dev/null +++ b/modules/audio/pcm_decoder/inc/capi_pcm_dec.h @@ -0,0 +1,35 @@ +/* ======================================================================== */ +/** +@file capi_pcm_dec.h + + Header file to implement the Common Audio Post Processor Interface + for Tx/Rx Tuning PCM_DECODER block +*/ + +/* ========================================================================= + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause + * ========================================================================== */ + +/*------------------------------------------------------------------------ + * Include files + * -----------------------------------------------------------------------*/ +#ifndef CAPI_PCM_DEC_H +#define CAPI_PCM_DEC_H + +#include "capi.h" +#include "ar_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +capi_err_t capi_pcm_dec_get_static_properties(capi_proplist_t *init_set_properties, + capi_proplist_t *static_properties); +capi_err_t capi_pcm_dec_init(capi_t *_pif, capi_proplist_t *init_set_properties); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif // CAPI_PCM_DEC_H diff --git a/modules/audio/pcm_encoder/api/pcm_encoder_api.h b/modules/audio/pcm_encoder/api/pcm_encoder_api.h new file mode 100644 index 0000000..88d9747 --- /dev/null +++ b/modules/audio/pcm_encoder/api/pcm_encoder_api.h @@ -0,0 +1,198 @@ +#ifndef PCM_ENCODER_API_H_ +#define PCM_ENCODER_API_H_ +/*============================================================================== + @file pcm_encoder_api.h + @brief This file contains PCM encoder APIs + +================================================================================ +Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. +SPDX-License-Identifier: BSD-3-Clause +==============================================================================*/ + +/*------------------------------------------------------------------------ + Include files + -----------------------------------------------------------------------*/ +#include "chmixer_common_api.h" +#include "module_cmn_api.h" +#include "common_enc_dec_api.h" + +/*# @h2xml_title1 {PCM Encoder Module API} + @h2xml_title_agile_rev {PCM Encoder Module API} + @h2xml_title_date {March 26, 2019} */ + +/*------------------------------------------------------------------------------ + Defines +------------------------------------------------------------------------------*/ + +/** @ingroup ar_spf_mod_encoder_mod + Enumerates the input port ID for the MFC module (#MODULE_ID_MFC). */ +#define PCM_ENC_DATA_INPUT_PORT 0x2 + +/** @ingroup ar_spf_mod_encoder_mod + Enumerates the output port ID for the MFC module. */ +#define PCM_ENC_DATA_OUTPUT_PORT 0x1 + +/*------------------------------------------------------------------------------ + Parameter ID +------------------------------------------------------------------------------*/ + +/** @ingroup ar_spf_mod_encoder_mod + Identifier for the parameter used to set the frame size of the PCM encoder + module. + + The PCM encoder frame size is determined by the performance mode of the + container. For low latency, the frame size is 1 ms; for low power, the + frame size is 5 ms. + + This parameter ID can be used if the client is required to specify a + fixed frame size for the PCM encoder. + + @msgpayload + param_id_pcm_encoder_frame_size_t +*/ +#define PARAM_ID_PCM_ENCODER_FRAME_SIZE 0x0800136B + +/*# @h2xmlp_parameter {"PARAM_ID_PCM_ENCODER_FRAME_SIZE", + PARAM_ID_PCM_ENCODER_FRAME_SIZE} + @h2xmlp_description {ID for the parameter used to set the frame size of + the PCM encoder module. \n + The PCM encoder frame size is determined by the + performance mode of the container. For low latency, + the frame size is 1 ms; for low power, the frame size + is 5 ms. \n + This parameter ID can be used if the client is + required to specify a fixed frame size for the PCM + encoder.} + @h2xmlp_toolPolicy {Calibration} */ + +/** @ingroup ar_spf_mod_encoder_mod + Payload of the #PARAM_ID_PCM_ENCODER_FRAME_SIZE parameter. +*/ +#include "spf_begin_pack.h" +#include "spf_begin_pragma.h" +struct param_id_pcm_encoder_frame_size_t +{ + uint32_t frame_size_type; + /**< Specifies whether the frame size of the PCM encoder module is in + samples or microseconds. + + @valuesbul + - 0 -- frame_size_not_specified (Default) + - 1 -- frame_size_in_samples + - 2 -- frame_size_in_us @tablebulletend */ + + /*#< @h2xmle_description {Specifies whether the frame size of the PCM + encoder module is in samples or microseconds.} + @h2xmle_default {0} + @h2xmle_rangeList {"frame_size_not_specified"=0; + "frame_size_in_samples"=1; + "frame_size_in_us"=2} */ + + uint32_t frame_size_in_samples; + /**< Frame size of the PCM encoder is in samples. + + This value is used when frame_size_type is set to 1 and this value is + greater than zero. + + We recommend limiting the frame size to 100 ms (or 384 kHz * 100 ms = + 38400 samples). */ + + /*#< @h2xmle_description {Frame size of the PCM encoder is in samples. + This value is used when frame_size_type is set + to 1 and this value is greater than 0. \n + We recommend limiting the frame size to 100 ms + (or 384 kHz * 100 ms = 38400 samples).} + @h2xmle_default {0} + @h2xmle_range {0..38400} */ + + uint32_t frame_size_in_us; + /**< Frame size of the PCM encoder is in microseconds. + + This parameter value is used when frame_size_type is set to 2 and this + value is greater than 0. */ + + /*#< @h2xmle_description {Frame size of the PCM encoder is in microseconds. + This parameter value is used when frame_size_type + is set to 2 and this value is greater than 0.} + @h2xmle_default {0} + @h2xmle_range {0..100000} */ +} +#include "spf_end_pragma.h" +#include "spf_end_pack.h" +; +typedef struct param_id_pcm_encoder_frame_size_t param_id_pcm_encoder_frame_size_t; + +/*------------------------------------------------------------------------------ + Module +------------------------------------------------------------------------------*/ + +/** @ingroup ar_spf_mod_encoder_mod + Identifier for the module used as the encoder for PCM use cases. + + This module can be used to convert the properties of a PCM stream, such as + endianness, interleaving, bit width, number of channels, and so on. It + cannot be used to convert the sampling rate. + + The module has only one input and one output port. + + @note1hang The module does not support #PARAM_ID_ENCODER_OUTPUT_CONFIG + because PARAM_ID_PCM_OUTPUT_FORMAT_CFG covers the supported + controls. + + @subhead4{Supported parameter ID} + - #PARAM_ID_PCM_OUTPUT_FORMAT_CFG + + @subhead4{Supported input media format ID} + - Data format : #DATA_FORMAT_FIXED_POINT @lstsp1 + - fmt_id : Don't care @lstsp1 + - Sample rates : 1..384 kHz @lstsp1 + - Number of channels: 1..32 @lstsp1 + - Bit width: @lstsep + - 16 (bits per sample 16 and Q15) @lstsp2 + - 24 (bits per sample 24 and Q23, bits per sample 32 and Q23, Q27, or + Q31) @lstsp2 + - 32 (bits per sample 32 and Q31) @newpage + - Interleaving: @lstsep + - Interleaved @lstsp2 + - De-interleaved unpacked @lstsp2 + - De-interleaved packed @lstsp1 + - Endianness: little + */ +#define MODULE_ID_PCM_ENC 0x07001004 + +/*# @h2xmlm_module {"MODULE_ID_PCM_ENC", MODULE_ID_PCM_ENC} + @h2xmlm_displayName {"PCM Encoder"} + @h2xmlm_modSearchKeys {Encoder} + @h2xmlm_description {ID for the module used as the encoder for PCM + use cases. \n + This module can be used to convert the + properties of a PCM stream, such as + endianness, interleaving, bit width, number of + channels, and so on. It cannot be used to + convert the sampling rate. For more details, + see AudioReach Signal Processing Framework (SPF) API Reference.} + @h2xmlm_dataMaxInputPorts {1} + @h2xmlm_dataMaxOutputPorts {1} + @h2xmlm_dataInputPorts {IN=PCM_ENC_DATA_INPUT_PORT} + @h2xmlm_dataOutputPorts {OUT=PCM_ENC_DATA_OUTPUT_PORT} + @h2xmlm_supportedContTypes {APM_CONTAINER_TYPE_GC} + @h2xmlm_isOffloadable {false} + @h2xmlm_stackSize {4096} + + @{ <-- Start of the Module --> + @h2xml_Select {"param_id_pcm_output_format_cfg_t"} + @h2xmlm_InsertParameter + @h2xml_Select {"payload_pcm_output_format_cfg_t"} + @h2xmlm_InsertParameter + @h2xml_Select {param_id_pcm_output_format_cfg_t::data_format} + @h2xmle_rangeEnum {pcm_data_format} + @h2xml_Select {"param_id_pcm_encoder_frame_size_t"} + @h2xmlm_InsertParameter + @h2xml_Select {param_id_chmixer_coeff_t} + @h2xmlm_InsertParameter + @h2xml_Select {chmixer_coeff_t} + @h2xmlm_InsertStructure + @} <-- End of the Module --> */ + + +#endif // PCM_ENCODER_API_H_ diff --git a/modules/audio/pcm_encoder/build/CMakeLists.txt b/modules/audio/pcm_encoder/build/CMakeLists.txt new file mode 100644 index 0000000..1665546 --- /dev/null +++ b/modules/audio/pcm_encoder/build/CMakeLists.txt @@ -0,0 +1,16 @@ +#[[ + @file CMakeLists.txt + + @brief + + @copyright + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause +]] +cmake_minimum_required(VERSION 3.10) + +#Include directories +set(pcm_encoder_includes + ${LIB_ROOT}/api/ + ${LIB_ROOT}/inc/ + ) diff --git a/modules/audio/pcm_encoder/inc/capi_pcm_enc.h b/modules/audio/pcm_encoder/inc/capi_pcm_enc.h new file mode 100644 index 0000000..f9e6753 --- /dev/null +++ b/modules/audio/pcm_encoder/inc/capi_pcm_enc.h @@ -0,0 +1,35 @@ +/* ======================================================================== */ +/** +@file capi_pcm_enc.h + + Header file to implement the Common Audio Post Processor Interface + for Tx/Rx Tuning PCM_ENCODER block +*/ + +/* ========================================================================= + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause + * ========================================================================== */ + +/*------------------------------------------------------------------------ + * Include files + * -----------------------------------------------------------------------*/ +#ifndef CAPI_PCM_ENC_H +#define CAPI_PCM_ENC_H + +#include "capi.h" +#include "ar_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +capi_err_t capi_pcm_enc_get_static_properties(capi_proplist_t *init_set_properties, + capi_proplist_t *static_properties); +capi_err_t capi_pcm_enc_init(capi_t *_pif, capi_proplist_t *init_set_properties); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif // CAPI_PCM_ENC_H diff --git a/modules/cmn/api/imcl_spm_intent_api.h b/modules/cmn/api/imcl_spm_intent_api.h new file mode 100644 index 0000000..4756172 --- /dev/null +++ b/modules/cmn/api/imcl_spm_intent_api.h @@ -0,0 +1,168 @@ +/**======================================================================== + @file imcl_spm_intent_api.h + + @brief This file contains all the public intent names + the intent related structs are defined in internal header files + + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause + + ====================================================================== */ + +#ifndef _IMCL_SPM_INTENT_API_H_ +#define _IMCL_SPM_INTENT_API_H_ + +/*------------------------------------------------------------------------------ + * Header Includes + *----------------------------------------------------------------------------*/ + +#include "ar_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +/** ----------------------------------------------------------------------- + ** Type Declarations + ** ----------------------------------------------------------------------- */ + +/* clang-format off */ + +/*============================================================================== + Intent ID +==============================================================================*/ +// Intent ID for the control link between FFECNS and detection engine. +#define INTENT_ID_FFECNS_SVA_FREEZE 0x080010BA + + +/*============================================================================== + Intent ID +==============================================================================*/ +/**< +Intent ID for the control link between AVC Tx and Rx module. + +The AVC module automatically adjusts the loudness of the Rx voice in +response to the near-end noise level on the Tx path. This adjustment +improves the intelligibility of the receive path voice signal. +*/ +#define INTENT_ID_AVC 0x080010DB + +/*============================================================================== + Intent ID +==============================================================================*/ +// Intent ID for SP +#define INTENT_ID_SP 0x08001204 + +/*============================================================================== + Intent ID +==============================================================================*/ +// Intent ID for HAPTICS +#define INTENT_ID_HAPTICS 0x0800136E + +/*============================================================================== + Intent ID +==============================================================================*/ +// Intent ID for CPS +#define INTENT_ID_CPS 0x08001537 + +/*============================================================================== + Intent ID +==============================================================================*/ +/**< +Intent ID to send DRC parameters over control link. +e.g. +Control link between Rx DRC and Tx AVC modules in voice path. +*/ +#define INTENT_ID_DRC_CONFIG 0x080010F3 + +/*============================================================================== + Intent ID +==============================================================================*/ +/**< +Intent ID for the control link to send gain information. +This intent can be used by a module to send its gain to another module. + +e.g. +Soft Volume Control sends the target gain to AVC-TX module in voice path. +*/ +#define INTENT_ID_GAIN_INFO 0x080010F5 + +/*============================================================================== + Intent ID +==============================================================================*/ +// Intent ID for the control link between Soft Vol and Popless Equalizer. +#define INTENT_ID_P_EQ_VOL_HEADROOM 0x08001118 + +/*============================================================================== + Intent ID +==============================================================================*/ +/**< +Intent ID for the control link between RVE Tx and Rx module. + +The RVE module automatically adjusts the loudness of the Rx voice in +response to the near-end noise level on the Tx path. This adjustment +improves the intelligibility of the receive path voice signal. +*/ +#define INTENT_ID_RVE 0x080010DD + +/*============================================================================== + Intent ID +==============================================================================*/ +/** IMCL intent ID for sending the notification to the packetizer v1 + * to send sideband ack to BT that the primer sideband is received */ +#define INTENT_ID_PACKETIZER_V1_FEEDBACK 0x08001148 + +/*============================================================================== + Intent ID +==============================================================================*/ +/** IMCL intent ID for sending the notification to the cop v2 packetizer + * to send sideband ack to BT that the primer sideband is received and for packetizer + * to send rx output media format related information to depacketizer */ +#define INTENT_ID_V2_PACKETIZER_FEEDBACK 0x08001306 + +/*============================================================================== + Intent ID +==============================================================================*/ +/**< +Intent ID for the control link between CTM Tx and Rx module. +*/ +#define INTENT_ID_CTM_TTY 0x08001191 + +/*============================================================================== + Intent ID +==============================================================================*/ +/**< +Intent ID for the control link between TTY module(1x/CTM/LTE_TTY in Rx path) and soft volume module(in Tx path). +TTY module communicates with the soft volume module to indicate whether to +mute or unmute the stream in the Tx path. The parameter - PARAM_ID_IMCL_MUTE +is used for this communication. + +*/ +#define INTENT_ID_MUTE 0x08001195 + +/*============================================================================== + Intent ID +==============================================================================*/ +/**< +Intent ID for the control link between Mailbox RX and DTMF module. Mailbox RX uses this +control link to send tone configuration to DTMF generator module. +*/ +#define INTENT_ID_DTMF_GENERATOR_CTRL 0x08001207 + +/*============================================================================== + Intent ID +==============================================================================*/ +/**< +Intent ID for the control link between audioss aov dma driver and the voice wakeup module. +When VW_v2 gets status_detected or status_rejected, this control link is used to send commnad to stop the aov dma. +This is bkz in audioss vad, there is no -ve edge interrupt to stop the aov dma +*/ +#define INTENT_ID_AUDIOSS_AOV_DMA 0x0800130A + +/* clang-format on */ + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /* _IMCL_SPM_INTENT_API_H_ */ diff --git a/modules/cmn/build/CMakeLists.txt b/modules/cmn/build/CMakeLists.txt new file mode 100644 index 0000000..41ec851 --- /dev/null +++ b/modules/cmn/build/CMakeLists.txt @@ -0,0 +1,18 @@ +#[[ + @file CMakeLists.txt + + @brief + + @copyright + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause +]] +cmake_minimum_required(VERSION 3.10) + +#Include directories +include_directories( + ${LIB_ROOT}/api + ) + +#Add the sub directories +add_subdirectory(${LIB_ROOT}/common/build common) diff --git a/modules/cmn/common/build/CMakeLists.txt b/modules/cmn/common/build/CMakeLists.txt new file mode 100644 index 0000000..03a9c54 --- /dev/null +++ b/modules/cmn/common/build/CMakeLists.txt @@ -0,0 +1,18 @@ +#[[ + @file CMakeLists.txt + + @brief + + @copyright + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause +]] +cmake_minimum_required(VERSION 3.10) + +#Include directories +include_directories( + ../internal_api/imcl/api + ) + +#Add the sub directories +add_subdirectory(../utils/build utils) diff --git a/modules/cmn/common/utils/build/CMakeLists.txt b/modules/cmn/common/utils/build/CMakeLists.txt new file mode 100644 index 0000000..f5821c9 --- /dev/null +++ b/modules/cmn/common/utils/build/CMakeLists.txt @@ -0,0 +1,48 @@ +#[[ + @file CMakeLists.txt + + @brief + + @copyright + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause +]] +cmake_minimum_required(VERSION 3.10) + +#Include directories +set (lib_incs_list + ${LIB_ROOT}/inc + ) + +#Add the source files +set (lib_srcs_list + ${LIB_ROOT}/src/exp10.c + ${LIB_ROOT}/src/log10.c + ${LIB_ROOT}/src/basic_op.c + ${LIB_ROOT}/src/audio_basic_op_ext.c + ${LIB_ROOT}/src/crossfade.c + ${LIB_ROOT}/src/audio_buffer.cpp + ${LIB_ROOT}/src/audio_buffer32.c + ${LIB_ROOT}/src/iir_tdf2.c + ${LIB_ROOT}/src/simple_mm.c + ${LIB_ROOT}/src/divide_qx.c + ${LIB_ROOT}/src/basic_math.c + ${LIB_ROOT}/src/audio_panner.cpp + ${LIB_ROOT}/src/audio_buffer32_island.c + ${LIB_ROOT}/src/divide_qx_island.c + ${LIB_ROOT}/src/mathlib.c + ${LIB_ROOT}/src/audio_complex_basic_op.c + #${LIB_ROOT}/src/filterDesign.c + ${LIB_ROOT}/src/util.c + ${LIB_ROOT}/src/audio_delay32.c + ${LIB_ROOT}/src/clips.c + ) + +#Call spf_build_static_library to generate the static library +spf_build_static_library(mod_utils + "${lib_incs_list}" + "${lib_srcs_list}" + "${lib_defs_list}" + "${lib_flgs_list}" + "${lib_link_libs_list}" + ) diff --git a/modules/cmn/common/utils/inc/AEEStdDef.h b/modules/cmn/common/utils/inc/AEEStdDef.h new file mode 100644 index 0000000..e2b4974 --- /dev/null +++ b/modules/cmn/common/utils/inc/AEEStdDef.h @@ -0,0 +1,30 @@ +#ifndef AEESTDDEF_H +#define AEESTDDEF_H +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* +FILE: AEEStdDef.h + +DESCRIPTION: Legacy file kept for compatibility. Redirects to posal_types.h */ + +#include "posal_types.h" + +/* ------------------------------------------------------------------------ +** Constants +** ------------------------------------------------------------------------ */ +#undef TRUE +#undef FALSE + +#define TRUE (1) /* Boolean true value. */ +#define FALSE (0) /* Boolean false value. */ + +#ifndef NULL + #define NULL (0) +#endif + + + +#endif /* #ifndef AEESTDDEF_H */ \ No newline at end of file diff --git a/modules/cmn/common/utils/inc/AudioComdef.h b/modules/cmn/common/utils/inc/AudioComdef.h new file mode 100644 index 0000000..6d932b0 --- /dev/null +++ b/modules/cmn/common/utils/inc/AudioComdef.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +/** +@file AudioComdef.h + +@brief Type definitions for memory alignment attribute macros. + +This file contains ASM data commands and events structures definitions. +*/ + +/*=========================================================================== +NOTE: The @brief description and any detailed descriptions above do not appear + in the PDF. + + The elite_audio_mainpage.dox file contains all file/group descriptions + that are in the output PDF generated using Doxygen and Latex. To edit or + update any of the file/group text in the PDF, edit the + elite_audio_mainpage.dox file or contact Tech Pubs. +===========================================================================*/ + +#ifndef _AUDIOCOMDEF_H_ +#define _AUDIOCOMDEF_H_ + +#include "posal_types.h" +#include "ar_error_codes.h" + +/** @cond +*/ +/* Type definition for signed 40-bit integers. */ +typedef int64_t int40; + +/* Type definition for unsigned 40-bit integers. */ +typedef uint64_t uint40; +/** @endcond */ + + +/** @addtogroup mem_alignment_macros +@{ */ + +#if ((defined __hexagon__) || (defined __qdsp6__)) || defined(__GNUC__) || defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 220000) +#define MEM_ALIGN(t, a) t __attribute__((aligned(a))) + /**< Memory alignment macro for target compilers. The value of the MEM_ALIGN + macro depends on the following conditional settings: + + - If __qdsp6__ or __GNUC__ or __ARMCC_VERSION >= 220000 is TRUE, + MEM_ALIGN = t __attribute__((aligned(a))). + - If _MSC_VER is TRUE, MEM_ALIGN = __declspec(align(a)) t. + - If __ARMCC_VERSION is TRUE, MEM_ALIGN = __align(a) t. + - If the compiler is unknown, it returns an unknown compiler error. */ + +#elif defined(_MSC_VER) +#define MEM_ALIGN(t, a) __declspec(align(a)) t + /**< Memory alignment macro for target compilers. The value of the MEM_ALIGN + macro depends on the following conditional settings: + + - If __qdsp6__ or __GNUC__ or __ARMCC_VERSION >= 220000 is TRUE, + MEM_ALIGN = t __attribute__((aligned(a))). + - If _MSC_VER is TRUE, MEM_ALIGN = __declspec(align(a)) t. + - If __ARMCC_VERSION is TRUE, MEM_ALIGN = __align(a) t. + - If the compiler is unknown, it returns an unknown compiler error. */ + +#elif defined(__ARMCC_VERSION) +#define MEM_ALIGN(t, a) __align(a) t + /**< Memory alignment macro for target compilers. The value of the MEM_ALIGN + macro depends on the following conditional settings: + + - If __qdsp6__ or __GNUC__ or __ARMCC_VERSION >= 220000 is TRUE, + MEM_ALIGN = t __attribute__((aligned(a))). + - If _MSC_VER is TRUE, MEM_ALIGN = __declspec(align(a)) t. + - If __ARMCC_VERSION is TRUE, MEM_ALIGN = __align(a) t. + - If the compiler is unknown, it returns an unknown compiler error. */ + +#else +#error Unknown compiler +#endif + +/** @} */ /* end_addtogroup mem_alignment_macros */ +#ifndef _COMPLEX_TYPES +#define _COMPLEX_TYPES +typedef int32 cint2x16; +typedef int64 cint2x32; +#endif + +#endif /* _AUDIOCOMDEF_H_ */ + diff --git a/modules/cmn/common/utils/inc/adsp_redef.h b/modules/cmn/common/utils/inc/adsp_redef.h new file mode 100644 index 0000000..15d0830 --- /dev/null +++ b/modules/cmn/common/utils/inc/adsp_redef.h @@ -0,0 +1,9 @@ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +#define log2table adsp_log2table +#define sqRootLut adsp_sqRootLut +#define invSqRootLut adsp_invSqRootLut +#define invTable adsp_invTable \ No newline at end of file diff --git a/modules/cmn/common/utils/inc/audio_basic_op.h b/modules/cmn/common/utils/inc/audio_basic_op.h new file mode 100644 index 0000000..f89c347 --- /dev/null +++ b/modules/cmn/common/utils/inc/audio_basic_op.h @@ -0,0 +1,1443 @@ +/*-------------------------------------------------------------------*/ +/* */ +/* Conexant Systems, Inc., Qualcomm Inc., Nokia Inc,. Nortel */ +/* Networks, Lucent Technologies, and Motorola Inc. grants a free, */ +/* irrevocable license to 3GPP2 and its Organizational Partners to */ +/* incorporate text or other copyrightable material contained in the */ +/* contribution and any modifications thereof in the creation of */ +/* 3GPP2 publications; to copyright and sell in Organizational */ +/* Partner's name any Organizational Partner's standards publication */ +/* even though it may include portions of the contribution; and at */ +/* the Organizational Partner's sole discretion to permit others to */ +/* reproduce in whole or in part such contributions or the resulting */ +/* Organizational Partner's standards publication. The contributor */ +/* must also be willing to grant licenses under such contributor */ +/* copyrights to third parties on reasonable, non-discriminatory */ +/* terms and conditions, as appropriate. */ +/* */ +/*-------------------------------------------------------------------*/ +/* */ +/* Copyright 2001 Conexant Systems, Inc. This document is provided */ +/* as a discussion instrument only and is not binding on Conexant */ +/* Systems, Inc. The contributor reserves the right to add to, amend */ +/* or otherwise modify the contents thereof. */ +/* */ +/* Conexant Systems, Inc. grants a free, irrevocable license to 3GPP2*/ +/* and its Organization Partners to incorporate, for any legitimate */ +/* 3GPP2 purpose, any copyrightable material contained in this */ +/* contribution and any revisions thereof, in publications of 3GPP2. */ +/* The contributor may hold one or more patents or applications for */ +/* letters patent that cover the information contained in this */ +/* contribution. Noting contained in this copyright statement shall */ +/* be construed as conferring by implication or estoppel, or */ +/* otherwise a license or any other right under any existing or */ +/* later issuing patent, whether or not the use of information */ +/* herein necessarily employs an invention of any such patent or */ +/* later issuing patent. */ +/* */ +/*-------------------------------------------------------------------*/ +/* */ +/* Copyright (c) 2010-2023 QUALCOMM Technologies, Inc. */ +/* All rights reserved. */ +/* The information contained in this contribution is provided for */ +/* the sole purpose of promoting discussion within the 3GPP2 and its */ +/* Organization Partners and is not binding on the contributor. */ +/* The contributor reserves the right to add to, amend, or withdraw */ +/* the statements contained herein. */ +/* QUALCOMM Incorporated grants a free, irrevocable license to 3GPP2 */ +/* and its Organization Partners to incorporate text or other */ +/* copyrightable material contained in the contribution and any */ +/* modifications thereof in the creation of 3GPP2 publications; */ +/* to copyright and sell in Organizational Partner's name any */ +/* Organizational Partner's standards publication even though it may */ +/* include portions of the contribution; and at the Organization */ +/* Partner's sole discretion to permit others to reproduce in whole */ +/* or in part such contributions or the resulting Organizational */ +/* Partner's standards publication. The contributor may hold one or */ +/* more patents or copyrights that cover information contained in */ +/* this contribution. A license will be made available to applicants */ +/* under reasonable terms and conditions that are demonstrably free */ +/* of any unfair discrimination. */ +/* Nothing contained herein shall be construed as conferring by */ +/* implication, estoppel, or otherwise any license or right under */ +/* any patent, whether or not the use of information herein */ +/* necessarily employs an invention of any existing or later issued */ +/* patent, or copyright. The contributor reserves the right to use */ +/* all material submitted in this contribution for his own purposes, */ +/* including republication and distribution to others. */ +/* */ +/*-------------------------------------------------------------------*/ +/* */ +/* The proposals in this submission have been formulated by Nokia, */ +/* Inc. (Nokia), to assist the 3GPP2 Standards Committee. This */ +/* document is offered to the committee as a basis for discussion */ +/* and is not binding on Nokia. The requirements are subject to */ +/* change in form and in numerical values after more study. Nokia */ +/* specifically reserves the right to add to, or amend, the */ +/* quantitative statements made herein. Nothing contained herein */ +/* shall be construed as conferring by implication, estoppel, or */ +/* otherwise any license or right under any patent, whether or not */ +/* the use of information herein necessarily employs an invention of */ +/* any existing or later issued patent. */ +/* Copyright 2001 Nokia, Inc. All Rights Reserved. Nokia hereby */ +/* gives permission for copying this submission for the legitimate */ +/* purposes of the 3GPP2 Standards Committee, provided Nokia, Inc. */ +/* is credited on all copies. Distribution or reproduction of this */ +/* document, by any means, electronic, mechanical, or otherwise, in */ +/* its entirety, or any portion thereof, for monetary gain or any */ +/* non-3GPP2 purposes is expressly prohibited. */ +/* */ +/*-------------------------------------------------------------------*/ +/* */ +/* Nortel Networks grants a free, irrevocable license to 3GPP2 and */ +/* its Organizational Partners to incorporate text or other */ +/* copyrightable material contained in the contribution and any */ +/* modifications thereof in the creation of 3GPP2 publications; to */ +/* copyright and sell in Organizational Partner's name any */ +/* Organizational Partner's standards publication even though it may */ +/* include portions of the contribution; and at the Organizational */ +/* Partner's sole discretion to permit others to reproduce in whole */ +/* or in part such contributions or the resulting Organizational */ +/* Partner's standards publication. The contributor must also be */ +/* willing to grant licenses under such contributor copyrights to */ +/* third parties on reasonable, non-discriminatory terms and */ +/* conditions, as appropriate. */ +/* */ +/*-------------------------------------------------------------------*/ +/* */ +/* Notice: */ +/* This contribution has been prepared by the contributor to assist */ +/* 3GPP2 Technical Specifications Group C. This document is offered */ +/* to 3GPP2 Technical Specifications Group C as a basis for */ +/* discussion and should not be construed as a binding proposal on */ +/* Lucent Technologies Inc. or any other company. Specifically, */ +/* Lucent Technologies Inc. reserves the right to modify, amend, or */ +/* withdraw the statements contained herein. */ +/* */ +/* Permission is granted to 3GPP2 Technical Specifications Group C */ +/* participants to copy any portion of this document for legitimate */ +/* purposes of 3GPP2 Technical Specifications Group C. Copying for */ +/* monetary gain or other non-3GPP2 Technical Specifications Group C */ +/* purposes is prohibited. */ +/* */ +/*-------------------------------------------------------------------*/ +/* */ +/* Grant of license Motorola Inc. grants a free, irrevocable license */ +/* to 3GPP2 and its organizational partners to incorporate Motorola- */ +/* supplied text or other copyrightable material contained in the */ +/* contribution and any modifications thereof in the creation of */ +/* 3GPP2 publications, to copyright and sell in organizational */ +/* partners name any organizational partners standards publications */ +/* even though it may include portions of the contribution; and at */ +/* the organizational partners sole discretion to permit others */ +/* to reproduce in whole or in part such contributions or the */ +/* resulting organizational partners standards publication. Motorola */ +/* is also willing to grant licenses under such Motorola copyrights */ +/* to third parties on reasonable, non-discriminatory terms and */ +/* conditions, as appropriate. */ +/* */ +/* Notice: */ +/* This document has been prepared by Motorola Inc. to assist the */ +/* 3GPP2 standards committee. This document is offered to the */ +/* committee as a basis for discussion and should not be considered */ +/* as a binding proposal on Motorola Inc. or any other company. */ +/* Specifically, Motorola Inc. reserves the right to modify, amend, */ +/* or withdraw the statement contained herein. Permission is granted */ +/* to 3GPP2 and its organizational partners to copy any portion of */ +/* this document for the legitimate purposes of the 3GPP2. Copying */ +/* this document for monetary gain or other non-3GPP2 purpose is */ +/* prohibited. Motorola Inc. may hold one or more patents of */ +/* copyrights that cover information contained in this contribution, */ +/* and agrees that a license under those rights will be made */ +/* available on reasonable and non-discriminatory terms and */ +/* conditions, subject to receiving a reciprocal license in return. */ +/* Nothing contained herein shall be construed as conferring by */ +/* implication, estoppel, or otherwise any license or right under */ +/* any patent, whether or not the use of information herein */ +/* necessarily employs an invention of any existing or later issued */ +/* patent, or copyright. */ +/* */ +/* Notice */ +/* Permission is granted to 3GPP2 participants to copy any portion of*/ +/* this contribution for the legitimate purpose of the 3GPP2. */ +/* Copying this contribution for monetary gain or other non-3GPP2 */ +/* purpose is prohibited. */ +/* */ +/*-------------------------------------------------------------------*/ + +/* ======================================================================== + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause +===========================================================================*/ + +/*========================================================================*/ +/** +@file audio_basic_op.h + +@brief Macros for math basic ops. + +This file contains ASM data commands and events structures definitions. + +This file is essentially identical to mathevrc.h. It contains modified +TIA basic operations. Bit-exact simulation of a generic 32-bit accumulator +DSP chip (fractional math). This version has a latching overflow bit +(giOverflow) and non-compound MAC's (one where initial multiplication +does not saturate). +*/ + +/*=========================================================================== +NOTE: The @brief description and any detailed descriptions above do not appear + in the PDF. + + The elite_audio_mainpage.dox file contains all file/group descriptions + that are in the output PDF generated using Doxygen and Latex. To edit or + update any of the file/group text in the PDF, edit the + elite_audio_mainpage.dox file or contact Tech Pubs. +===========================================================================*/ + + +/*======================================================================== +Edit History + +$Header$ + +when who what, where, why +-------- --- ------------------------------------------------------- +08/09/10 rkc Created file. +10/20/10 sw,kli (Tech Pubs) Edited/added Doxygen comments and markup. +========================================================================== */ + + +#ifndef __AUDIO_BASICOP_H_ +#define __AUDIO_BASICOP_H_ + +#include "audio_comdef.h" + +#ifdef round +#undef round +#endif // round + +#if ((defined __hexagon__) || (defined __qdsp6__)) +#include +#include +#endif + +/** @addtogroup math_operations +@{ */ + +/** Maximum value of a signed 32-bit integer. +*/ +static const int32 MAX_32 = 0x7FFFFFFFL; + +/** Maximum value of an unsigned 32-bit integer. +*/ +static const uint32 UMAX_32 = 0xFFFFFFFFL; + +/** Minimum value of a signed 32-bit integer. +*/ +static const int32 MIN_32 = 0x80000000L; + +/** Half of the maximum value of a signed 32-bit integer. +*/ +static const int32 LONGWORD_HALF = 0x40000000L; + +/** Bitmask for the sign bit in a signed 32-bit integer. +*/ +#define LONGWORD_SIGN MIN_32 /* sign bit */ + +/** Minimum value of a signed 32-bit integer. +*/ +#define LONGWORD_MIN MIN_32 + +/** Maximum value of a signed 32-bit integer. +*/ +#define LONGWORD_MAX MAX_32 + +/** Maximum value of an unsigned 64-bit integer. +*/ +static const uint64 UMAX_64 = 0xFFFFFFFFFFFFFFFF; + +/** Maximum value of a signed 16-bit integer. +*/ +static const int16 MAX_16 = 0x7FFF; + +/** Maximum value of an unsigned 16-bit integer. +*/ +static const uint16 UMAX_16 = 0xFFFF; + +/** Minimum value of a signed 16-bit integer. +*/ +static const int16 MIN_16 = -32768; // 0x8000; // this removes truncation warning + +/** Half of the maximum value of a signed 16-bit integer. +*/ +static const int16 SHORTWORD_HALF = 0x4000; + +/** Bitmask for the sign bit in a signed 16-bit integer. +*/ +#define SHORTWORD_SIGN MIN_16 /* sign bit for Word16 type */ + +/** Minimum value of a signed 16-bit integer. +*/ +#define SHORTWORD_MIN MIN_16 /* smallest Ram */ + +/** Maximum value of a signed 16-bit integer. +*/ +#define SHORTWORD_MAX MAX_16 /* largest Ram */ + + +#define LW_SIGN (int32)0x80000000L /* sign bit */ +#define LW_MIN (int32)0x80000000L +#define LW_MAX (int32)0x7fffffffL + +/** @} */ /* end_addtogroup math_operations */ +/** @addtogroup math_operations +@{ */ + +/*_________________________________________________________________________ +| | +| Function Prototypes | +|_________________________________________________________________________| +*/ + +#ifdef __cplusplus +extern "C" { +#endif //__cplusplus + +#if ((defined __hexagon__) || (defined __qdsp6__)) + /* addition */ + /************/ +/** Adds two signed 16-bit integers and returns the signed 16-bit sum. +*/ +#define s16_add_s16_s16(var1, var2) (int16) Q6_R_add_RlRl(var1, var2) /* 1 ops */ + +/** + Adds two signed 16-bit integers and returns the signed, saturated + 16-bit sum. +*/ +#ifndef s16_add_s16_s16_sat +#define s16_add_s16_s16_sat(var1, var2) (int16) Q6_R_add_RlRl_sat(var1, var2) +#endif /* #ifndef s16_add_s16_s16_sat */ + +/** Adds two signed 32-bit integers and returns the signed 32-bit sum. + */ +#define s32_add_s32_s32(var1, var2) Q6_R_add_RR(var1, var2) /* 2 ops */ + +/** + Adds two signed 32-bit integers and returns the signed, saturated + 32-bit sum. +*/ +#define s32_add_s32_s32_sat(var1, var2) Q6_R_add_RR_sat(var1, var2) + +/** Adds two signed 32-bit integers and returns the signed 64-bit sum. +*/ +#define s64_add_s32_s32(var1, var2) Q6_P_add_PP((int64) var1, (int64) var2) + +/** + Adds a signed 32-bit integer and an unsigned 32-bit integer, and returns the + signed 64-bit sum. +*/ +#define s64_add_s32_u32(var1, var2) Q6_P_add_PP((int64) var1, (uint32) var2) + +/** + Adds a signed 64-bit integer and a signed 32-bit integer, and returns the + signed 64-bit sum. +*/ +#define s64_add_s64_s32(var1, var2) Q6_P_add_PP((int64) var1, (int64) var2) + +/** Adds two signed 64-bit integers and returns the signed 64-bit sum. +*/ +#define s64_add_s64_s64(var1, var2) Q6_P_add_PP((int64) var1, (int64) var2) + +/** + Subtracts two signed 16-bit integers and returns the signed + 16-bit difference. +*/ +#define s16_sub_s16_s16(var1, var2) (int16) Q6_R_sub_RlRl(var1, var2) + +/** + Subtracts two signed 16-bit integers and returns the signed, saturated + 16-bit difference. +*/ +#define s16_sub_s16_s16_sat(var1, var2) (int16) Q6_R_sub_RlRl_sat(var1, var2) + +/** + Subtracts two signed 32-bit integers and returns the signed 32-bit + difference. +*/ +#define s32_sub_s32_s32(var1, var2) Q6_R_sub_RR(var1, var2) + +/** + Subtracts two signed 32-bit integers and returns the signed, saturated + 32-bit difference. +*/ +#define s32_sub_s32_s32_sat(var1, var2) Q6_R_sub_RR_sat(var1,var2) + +/** + Subtracts two signed 64-bit integers and returns the signed 64-bit + difference. +*/ +#define s64_sub_s64_s64(var1, var2) Q6_P_sub_PP((int64) var1, (int64) var2) + + /* multiplication */ +/** + Multiplies two signed 16-bit integers and returns the signed 32-bit + product. +*/ +#define s32_mult_s16_s16(var1, var2) Q6_R_mpy_RlRl(var1, var2) + +/** + Multiplies two unsigned 16-bit integers and returns the unsigned 32-bit + product. +*/ +#define u32_mult_u16_u16(var1, var2) (uint32) Q6_R_mpyu_RlRl(var1, var2) + +/** + Multiplies a signed 16-bit integer and an unsigned 16-bit integer, and + returns the signed 32-bit product. +*/ +#define s32_mult_s16_u16(var1, var2) (int32) Q6_P_mpy_RR((int32)var1, Q6_R_zxth_R(var2)) + +/** + Multiplies two signed 16-bit integers, shifts left the 32-bit product to + remove the extra sign bit, saturates if necessary, and returns the + signed 32-bit result. +*/ +#define s32_mult_s16_s16_shift_sat(var1, var2) Q6_R_mpy_RlRl_s1_sat(var1, var2) + +/** + Multiplies two unsigned 16-bit integers, shifts left the 32-bit product + into a 64-bit integer, and returns the signed 64-bit result. +*/ +#define s64_mult_u16_u16_shift(var1,var2,shift) Q6_P_asl_PR(u32_mult_u16_u16(var1, var2), shift) + +/** + Multiplies a signed 16-bit integer and an unsigned 16-bit integer, shifts + left the 32-bit product by 1 into a 64-bit integer, and returns the + signed 64-bit result. +*/ +#define s64_mult_s16_u16_shift(var1,var2,shift) Q6_P_asl_PR(Q6_P_mpy_RR((int32)var1, Q6_R_zxth_R(var2)),shift) + +/** + Multiplies two signed 16-bit integers, shifts left the 32-bit product by 1 + into a 64-bit integer, and returns the signed 64-bit result. +*/ +#define s64_mult_s16_s16_shift(var1,var2,shift) Q6_P_asl_PR(Q6_R_mpy_RlRl(var1, var2), shift) + +/** + Multiplies a signed 32-bit integer and a signed 16-bit integer, rounds and + saturates the intermediate result to a signed 32-bit integer, and returns it. +*/ +#define s32_mult_s32_s16_rnd_sat(var1, var2) Q6_R_mpy_RRl_s1_rnd_sat(var1, var2) + +/** + Multiplies a signed 32-bit integer and a signed 16-bit integer, and returns + the signed 64-bit product. +*/ +#define s64_mult_s32_s16(var1,var2) Q6_P_mpy_RR(var1,(int32) var2) + +/** + Multiplies a signed 32-bit integer and an unsigned 16-bit + integer, and returns the signed 64-bit product. +*/ +#define s64_mult_s32_u16(var1,var2) Q6_P_mpy_RR(var1, var2) + +/** + Multiplies an unsigned 32-bit integer and a signed 16-bit integer, and + returns the signed 64-bit product. +*/ +#define s64_mult_u32_s16(var1,var2) s64_mult_u32_s16_shift(var1, var2, 16) + +/** + Multiplies an unsigned 32-bit integer and a signed 16-bit integer, shifts + left (if the shift value is positive) or right (if the value is negative), + and returns the signed 64-bit result. +*/ +#define s64_mult_u32_s16_shift(var1,var2,shift) ((var2 >= 0)? (Q6_P_asl_PR(Q6_P_mpyu_RR(var1,var2),(shift-16))) : \ + (Q6_P_asl_PR(Q6_P_neg_P(Q6_P_mpyu_RR(var1,-var2)),(shift-16)))) + +/** + Multiplies a signed 32-bit integer and an unsigned 16-bit integer, shifts + left (if the shift value is positive) or right (if the value is negative), + and returns the signed 64-bit result. +*/ +#define s64_mult_s32_u16_shift(var1,var2,shift) Q6_P_asl_PR(Q6_P_mpy_RR(var1, Q6_R_zxth_R(var2)),shift-16) + +/** + Multiplies a signed 32-bit integer and a signed 16-bit integer with shift, + and returns the signed 64-bit result. +*/ +#define s64_mult_s32_s16_shift(var1,var2,shift) Q6_P_asl_PR(Q6_P_mpy_RR(var1,(int32) var2), shift-16) + +/** + Multiplies a signed 32-bit integer and a signed 32-bit integer, rounds and + saturates the intermediate result to a signed 32-bit integer, and returns it. +*/ +#define s32_mult_s32_s32_rnd_sat(var1, var2) Q6_R_mpy_RR_rnd(var1, var2) + +/** + Multiplies two signed 32-bit integers and returns the signed 64-bit + result. +*/ +#define s64_mult_s32_s32(var1,var2) Q6_P_mpy_RR(var1 , var2 ) + +/** + Multiplies a signed 32-bit integer and a signed 32-bit integer with shift, + and returns the signed 64-bit result. +*/ +#define s64_mult_s32_s32_shift(var1, var2, shift) Q6_P_asl_PR(Q6_P_mpy_RR(var1,var2), shift-32) + +/** + Multiplies a signed 32-bit integer and an unsigned 32-bit integer with + shift, and returns the signed 64-bit result. +*/ +#define s64_mult_s32_u32_shift(var1, var2, shift) ((var1 >= 0) ? (Q6_P_asl_PR(Q6_P_mpyu_RR(var1,var2),(shift-32))) : \ + (Q6_P_asl_PR(Q6_P_neg_P(Q6_P_mpyu_RR(-var1,var2)),(shift-32)))) + +//There is an error with the following macro, please test locally before this macro is used. Currently no module in audio is using this +#define s64_mult_s32_u32(var1, var2) (var1 >= 0) ? Q6_P_mpyu_RR(var1,var2) : Q6_P_neg_P(Q6_P_mpyu_RR(-var1,var2)) + +/** + Performs a low-precision multiply of the two signed 32-bit input numbers + with shift, and returns the signed 64-bit result. +*/ +#define s64_mult_lp_s32_s32_shift(var1, var2, shift) (Q6_P_asl_PR(Q6_P_mpy_RR(var1,var2), shift-32)) + +/** + Performs a low-precision multiply of a signed 32-bit number and an unsigned + 32-bit number with shift, and returns the signed 64-bit result. +*/ +#define s64_mult_lp_s32_u32_shift(var1, var2, shift) s64_mult_s32_u32_shift(var1, var2, shift) + + /* arithmetic shifts */ + /*********************/ +/** + Performs a 16-bit saturation for a signed input number towards + the maximum value (positive). +*/ +#define SAT_MAX16(x) (x > MAX_16 ? MAX_16 : x) + +/** + Performs a 16-bit saturation for a signed input number towards + the minimum value (negative). +*/ +#define SAT_MIN16(x) (x < MIN_16 ? MIN_16 : x) + +/** + Performs an arithmetic shift right (or left if the shift is negative) for a + signed 16-bit input number, saturates it, and returns the signed 16-bit + result. +*/ +#define s16_shr_s16_sat(var1, shift) (int16) SAT_MIN16(SAT_MAX16(Q6_R_asr_RR_sat(var1, shift))) + +/** + Performs an arithmetic shift left (or right if the shift is + negative) for a signed 16-bit input number, saturates it, and + returns the signed 16-bit result. +*/ +#define s16_shl_s16_sat(var1, shift) (int16) SAT_MIN16(SAT_MAX16(Q6_R_asl_RR_sat(var1, shift))) + +/** + Performs an arithmetic shift right (or left if the shift is negative) + for a signed 32-bit input number, saturates it, and returns the + signed 32-bit result. +*/ +#define s32_shr_s32_sat(var1, shift) Q6_R_asr_RR_sat(var1, shift) + +/** + Performs an arithmetic shift left (or right if the shift is negative) + for a signed 32-bit input number, saturates it, and returns the signed + 32-bit result. +*/ +#define s32_shl_s32_sat(var1, shift) Q6_R_asl_RR_sat(var1, shift) +#define s32_shr_s32(var1, shift) Q6_R_asr_RR(var1, shift) //amith +#define s32_shl_s32(var1, shift) Q6_R_asl_RR(var1, shift) //amith + +/** + Performs an arithmetic shift left (or right if the shift is negative) + for a signed 32-bit input number, saturates it, rounds it, and returns + the signed 32-bit result. +*/ +#define s32_shl_s32_rnd_sat(var1,shift) ({ int32 var2 = var1; ((shift >= 0) ? \ + (s32_shl_s32_sat(var2, shift)) : \ + (s32_add_s32_s32_sat(s32_shl_s32_sat(var2, shift),s32_and_s32_s32(s32_shl_s32_sat(var2, (shift + 1)), 0x1)))); }) + +/** + Performs an arithmetic shift left (or right if the shift is negative) + for a signed 64-bit input number and returns the signed 64-bit result. +*/ +#define s64_shl_s64(var1, shift) Q6_P_asl_PR(var1, shift) + +/** + Performs an arithmetic shift left (or right if the shift is negative) + for a signed 16-bit input number and returns the signed 16-bit result. +*/ +#define s16_shl_s16(var1, shift) (int16) Q6_R_asl_RR(var1, shift) + +/** + Performs an arithmetic shift right (or left if the shift is negative) + for a signed 16-bit input number and returns the signed 16-bit result. +*/ +#define s16_shr_s16(var1, shift) (int16) Q6_R_asr_RR(var1, shift) + + /* logical operations */ + /**********************/ + +/** ANDs two signed 16-bit integers and returns the signed 16-bit result. +*/ +#define s16_and_s16_s16(var1, var2) (int16) Q6_R_and_RR((int32) var1, (int32) var2) + +/** ANDs two signed 32-bit integers and returns the signed 32-bit result. +*/ +#define s32_and_s32_s32(var1, var2) Q6_R_and_RR(var1, var2) + + /* absolute value */ + /*******************/ +/** + Computes the absolute value of the signed 16-bit input, saturates it, and + returns the unsigned 16-bit result. +*/ +#define u16_abs_s16_sat(var1) (uint16) Q6_R_asrh_R(Q6_R_abs_R_sat(Q6_R_aslh_R(var1)))//more efficient intrinsic? + +/** + Computes the absolute value of the signed 32-bit input, saturates it, and + returns the unsigned 32-bit result. +*/ +#define u32_abs_s32_sat(var1) (uint32) Q6_R_abs_R_sat(var1) + + /* multiply accumulate */ + /************************/ +/** + Multiplies two signed 16-bit numbers together with saturation, + adds that result to the signed 32-bit input with saturation, + rounds the result into a signed 16-bit number, and returns it. +*/ +#define s16_mac_s32_s16_s16_sat_rnd(var3,var1,var2) (int16) Q6_R_asrh_R(Q6_R_add_RR_sat(Q6_R_mpyacc_RlRl_sat(var3,var1,var2),0x08000)) + +/** + Multiplies two signed 16-bit numbers together with saturation, + subtracts that result from the signed 32-bit input with saturation, + rounds the result into a signed 16-bit number, and returns it. +*/ +#define s16_msu_s32_s16_s16_sat_rnd(var3,var1,var2) (int16) Q6_R_asrh_R(Q6_R_add_RR_sat(Q6_R_mpynac_RlRl_s1_sat(var3,var1,var2),0x08000)) + +/** + Multiplies two signed 16-bit numbers together with saturation, + subtracts that result from the signed 32-bit input with saturation, + and returns the signed 32-bit result. +*/ +#define s32_msu_s32_s16_s16_sat(var3,var1,var2) Q6_R_mpynac_RlRl_s1_sat(var3, var1, var2) + +/** + Multiplies two signed 16-bit numbers, shifts the result and + adds that result to the signed 64-bit input with + saturation, and returns the signed 64-bit result. +*/ +#define s64_mac_s64_s16_s16_shift_sat(var3,var1,var2,shift) Q6_R_add_RR_sat(var3, Q6_R_asl_RR_sat(Q6_R_mpy_RlRl(var1,var2), shift)) + +/** + Multiplies two signed 16-bit numbers, shifts the result, adds that result + to the signed 64-bit input, and returns the signed 64-bit result. +*/ +#define s64_mac_s64_s16_s16_shift s64_mac_s64_s16_s16_shift_sat + +/** + Multiplies two signed 16-bit numbers, shifts the result, adds that result + to the signed 64-bit input, and returns the signed 64-bit result without saturation. +*/ +#define s64_mac_s64_s16_s16_shift_nosat(var3,var1,var2,shift) Q6_P_add_PP(var3, Q6_P_asl_PR(Q6_R_mpy_RlRl(var1,var2), shift)) + +/** + Multiplies two signed 16-bit integers and accumulates the result in a + signed 64-bit integer. +*/ +#define s64_mac_s64_s16_s16_s1(var3, var1, var2) Q6_P_mpyacc_RlRl_s1(var3, var1, var2) + +#define s32_mac_s32_s16_s16_sat(var3, var1, var2) Q6_R_mpyacc_RlRl_sat(var3, var1, var2) + /*Additional operators*/ + /**********************/ + + /* negation */ + /*************/ +/** + Negates a signed 16-bit input number, saturates it, and returns the signed + 16-bit result. +*/ +#define s16_neg_s16_sat(var1) (int16) Q6_R_sath_R(Q6_R_neg_R( Q6_R_sxth_R(var1))) + +/** + Negates a signed 32-bit input number, saturates it, and returns the signed + 32-bit result. +*/ +#define s32_neg_s32_sat(var1) Q6_R_neg_R_sat(var1) + + /* ALU operations */ + /******************/ +/** + Finds the one's complement of a signed 16-bit number and returns + the signed 16-bit result. +*/ +#define s16_complement_s16(var1) (int16) Q6_R_not_R(var1) + +/** + Finds the maximum of two signed 16-bit input numbers + and returns the signed 16-bit result. +*/ +#define s16_max_s16_s16(var1, var2) (int16) Q6_R_max_RR(var1,var2) + +/** + Finds the minimum of two signed 16-bit input numbers and returns + the signed 16-bit result. +*/ +#define s16_min_s16_s16(var1, var2) (int16) Q6_R_min_RR(var1,var2) + +/** + Finds the minimum of two signed 32-bit input numbers + and returns the signed 32-bit result. +*/ +#define s32_min_s32_s32(var1, var2) Q6_R_min_RR(var1,var2) + +/** + Finds the maximum of two signed 32-bit input numbers + and returns the signed 32-bit result. +*/ +#define s32_max_s32_s32(var1, var2) Q6_R_max_RR(var1,var2) + + /* Accumulator manipulation */ + /****************************/ +/** + Puts the signed 16-bit input into the 16 LSBs of the output 32-bit and + returns the signed 32-bit result. +*/ +#define s32_deposit_s16_l(var1) Q6_R_sxth_R(var1) + +/** + Puts the signed 16-bit input into the 16 MSBs of the output 32-bit and + returns the signed 32-bit result. +*/ +#define s32_deposit_s16_h(var1) Q6_R_aslh_R(var1) + +/** + Extracts the 16 LSBs of a signed 32-bit input integer + and returns the signed 16-bit result. +*/ +#define s16_extract_s32_l(var1) (int16) Q6_R_sxth_R(var1) + +/** + Extracts the 16 LSBs of a signed 32-bit input integer + and returns the unsigned 16-bit result. +*/ +#define u16_extract_s32_l(var1) (uint16) Q6_R_zxth_R(var1) + +/** + Extracts the 16 MSBs of a signed 32-bit input integer + and returns the signed 16-bit result. +*/ +#define s16_extract_s32_h(var1) (int16) Q6_R_asrh_R(var1) + +/** + Extracts the 16 MSBs of a signed 64-bit input integer + and return a signed 16-bit result. +*/ +#define s16_extract_s64_h(var1) (int16)(Q6_P_asr_PR(var1, 16)) + +/** + Extracts the 16 MSBs of a signed 64-bit input integer + and return a signed 16-bit result. +*/ +#define s16_extract_s64_h_rnd(var1) (int16)(Q6_P_asr_PI_rnd(var1, 15)) + +/** + Extracts the 16 LSBs of a signed 64-bit input integer + and returns the signed 16-bit result. +*/ +#define s16_extract_s64_l(var1) (int16)(var1) + +/** + Saturates a signed 64-bit input number to a signed 32-bit value, extracts + the higher 16 bits, and returns the output. +*/ +#define s16_extract_s64_h_sat(var1) (int16)Q6_R_asrh_R(Q6_R_sat_P(var1)) + +/** + Extracts the 16 LSBs of a signed 64-bit input integer + and returns the signed 32-bit result. +*/ +#define s32_extract_s64_l(var1) (int32)(var1) + +/** + Extracts the 16 LSBs of a signed 64-bit input integer + and returns the unsigned 32-bit result. +*/ +#define u32_extract_s64_l(var1) (uint32)(var1) + + /* Round */ + /*********/ +/** + Rounds a signed 32-bit input number into a signed 16-bit number, + saturates it, and returns the signed 16-bit result. +*/ +#define s16_round_s32_sat(var1) (int16) Q6_R_asrh_R(Q6_R_add_RR_sat(var1,0x00008000L)) + + /* Normalization */ + /*****************/ +/** + Calculates the shift required to normalize a signed 64-bit input + number and returns the signed 16-bit shift value. +*/ +#define s16_norm_s64(var1) (int16) ((var1 == 0) ? (0) : (Q6_R_sub_RR(Q6_R_clb_P(var1), 33))) + +/** + Calculates the shift required to normalize a unsigned 32-bit + input number and returns the signed 16-bit shift value. +*/ +#define s16_norm_s32(var1) (int16) ((var1 == 0) ? (0) : (Q6_R_sub_RR(Q6_R_clb_R(var1),1))) + +/** + Calculates the shift required to normalize a unsigned 16-bit + input number and returns the signed 16-bit shift value. +*/ +#define s16_norm_s16(var1) s16_norm_s32(s32_deposit_s16_h(var1)) + +/** + Takes the signed 32-bit number input (possibly unnormalized) and returns the + number of consecutive 1s, starting with the most significant bit. +*/ +#define s32_cl1_s32(var1) (Q6_R_cl1_R(var1)) + + /* Saturation manipulation routines */ + /************************************/ +/** + Saturates the input signed 64-bit input number (possibly exceeding the + 32-bit dynamic range) to the signed 32-bit number and returns the result. +*/ +#ifndef s32_saturate_s64 +#define s32_saturate_s64(var1) Q6_R_sat_P(var1) +#endif /* #ifndef s32_saturate_s64 */ + +/** + Saturates the input signed 32-bit input number (possibly exceeding the + 16-bit dynamic range) to the signed 16-bit number and returns the result. +*/ +#ifndef s16_saturate_s32 +#define s16_saturate_s32(var1) (int16) Q6_R_sath_R(var1) +#endif /* #ifndef s16_saturate_s32 */ + + + +#else // #if !defined(__qdsp6__) + + /* Normalization */ + int16 norm_l(int32 L_var1); + + /* addition */ + /************/ + /** Adds two signed 16-bit integers and returns the signed 16-bit sum. + */ + int16 s16_add_s16_s16(int16 var1, int16 var2); /* 1 ops */ + + /** + Adds two signed 16-bit integers and returns the signed, saturated + 16-bit sum. + */ +#if !defined(uses_s16_add_s16_s16_sat) + int16 s16_add_s16_s16_sat(int16 var1, int16 var2); +#define uses_s16_add_s16_s16_sat +#endif /* uses_s16_add_s16_s16_sat */ + /** Adds two signed 32-bit integers and return the signed 32-bit sum. + */ + int32 s32_add_s32_s32(int32 var1, int32 var2); /* 2 ops */ + + /** + Adds two signed 32-bit integers and returns the signed, saturated + 32-bit sum. + */ + int32 s32_add_s32_s32_sat(int32 var1, int32 var2); + + /** Adds two signed 64-bit integers and returns the signed 64-bit sum. + */ + int64 s64_add_s64_s64(int64 var1, int64 var2); + + /** + Adds a signed 64-bit integer and a signed 32-bit integer, and + returns the signed 64-bit sum. + */ + int64 s64_add_s64_s32(int64 var1, int32 var2); + + /** Adds two signed 32-bit integers and returns the signed 64-bit sum. + */ + int64 s64_add_s32_s32(int32 var1, int32 var2); + + /** + Adds a signed 32-bit integer and an unsigned 32-bit integer + and returns the signed 64-bit sum. + */ + int64 s64_add_s32_u32(int32 var1, uint32 var2); + + /** + Performs subtraction between two signed 16-bit integers and returns the + signed 16-bit difference. + */ + int16 s16_sub_s16_s16(int16 var1, int16 var2); /* 1 ops */ + + /** + Performs subtraction between two signed 16-bit integers and returns the + signed, saturated 16-bit difference. + */ + int16 s16_sub_s16_s16_sat(int16 var1, int16 var2); + + /** + Performs subtraction between two signed 32-bit integers and returns + the signed 32-bit difference. + */ + int32 s32_sub_s32_s32(int32 var1, int32 var2); + + /** + Performs subtraction between two signed 32-bit integers and returns + the signed, saturated 32-bit difference. + */ + int32 s32_sub_s32_s32_sat(int32 var1, int32 var2); /* 2 ops */ + + /** + Subtracts two signed 64-bit integers and returns the signed 64-bit + difference. + */ + int64 s64_sub_s64_s64(int64 var1, int64 var2); + + /* multiplication */ + /******************/ + /** + Multiplies two signed 16-bit integers and returns the signed 32-bit + product. + */ + int32 s32_mult_s16_s16(int16 var1, int16 var2); + + /** + Multiplies a signed 16-bit integer and an unsigned 16-bit + integer, and returns the signed 32-bit product. + */ + int32 s32_mult_s16_u16(int16 var1, uint16 var2); + + /** + Multiplies a signed 32-bit integer and a signed 32-bit + integer, rounds and saturates the intermediate result to a + signed 32-bit integer, and returns it. + */ + int32 s32_mult_s32_s32_rnd_sat(int32 var1, int32 var2); + + /** + Multiplies two unsigned 16-bit integers and returns the + unsigned 32-bit product. + */ + uint32 u32_mult_u16_u16(uint16 var1, uint16 var2); + + /** + Multiplies a signed 32-bit integer and a signed 16-bit + integer, and returns the signed 64-bit product. + */ + int64 s64_mult_s32_s16(int32 var1, int16 var2); + + /** + Multiplies an unsigned 32-bit integer and a signed 16-bit + integer, and returns the signed 64-bit product. + */ + int64 s64_mult_u32_s16(uint32 var1, int16 var2); + + /** + Multiplies a signed 32-bit integer and an unsigned 16-bit integer, and + returns the signed 64-bit result. + */ + int64 s64_mult_s32_u16(int32 var1, uint16 var2); + + /** + Multiplies two signed 32-bit integers and returns the signed 64-bit + result. + */ + int64 s64_mult_s32_s32(int32 var1, int32 var2); + + /** + Multiplies two signed 16-bit integers, shifts left the 32-bit product + by 1 into a 64-bit integer, and returns the signed 64-bit result. + */ + int64 s64_mult_s16_s16_shift(int16 var1, int16 var2, int16 shift); + + /** + Multiplies an unsigned 16-bit integer and a signed 16-bit + integer, shifts left the 32-bit product by 1 into a 64-bit + integer, and returns the signed 64-bit result. + */ + int64 s64_mult_s16_u16_shift(int16 var1, uint16 var2, int16 shift); + + /** + Multiplies two unsigned 16-bit integers, shifts left the 32-bit product + into a 64-bit integer, and returns the signed 64-bit result. + */ + int64 s64_mult_u16_u16_shift(uint16 var1, uint16 var2, int16 shift); + + /** + Multiplies an unsigned 32-bit integer and a signed 16-bit integer, + shifts left (if the shift value is positive) or right (if the value + is negative) and returns the signed 64-bit result. + */ + int64 s64_mult_u32_s16_shift(uint32 var1, int16 var2, int16 shift); + + /** + Multiplies a signed 32-bit integer and an unsigned 16-bit integer, + shifts left (if the shift value is positive) or right (if the value + is negative) and returns the signed 64-bit result. + */ + int64 s64_mult_s32_u16_shift(int32 var1, uint16 var2, int16 shift); + + /** + Multiplies a signed 32-bit integer and a signed 16-bit + integer with shift, and returns the signed 64-bit result. + */ + int64 s64_mult_s32_s16_shift(int32 var1, int16 var2, int16 shift); + + /** + Multiplies a signed 32-bit integer and a signed 32-bit + integer with shift, and returns the signed 64-bit result. + */ + int64 s64_mult_s32_s32_shift(int32 var1, int32 var2, int16 shift); + + /** + Multiplies a signed 32-bit integer and an unsigned 32-bit + integer with shift, and returns the signed 64-bit result. + */ + int64 s64_mult_s32_u32_shift(int32 var1, uint32 var2, int16 shift); + + /** + Performs a low precision multiply of the two signed 32-bit + input numbers with shift, and returns the signed 64-bit result. + */ + int64 s64_mult_lp_s32_s32_shift(int32 var1, int32 var2, int16 shift); + + /** + Performs a low precision multiply of a signed 32-bit number and an + unsigned 32-bit number with shift, and returns the signed 64-bit result. + */ + int64 s64_mult_lp_s32_u32_shift(int32 var1, uint32 var2, int16 shift); + + /** + Multiplies two signed 16-bit integers, shifts left the 32-bit + product to remove the extra sign bit, saturates if + necessary, and returns the signed 32-bit result. + */ + int32 s32_mult_s16_s16_shift_sat(int16 var1, int16 var2); + + /** + Multiplies a signed 32-bit integer and a signed 16-bit + integer, rounds and saturates the intermediate result to a + signed 32-bit integer, and returns it. + */ + int32 s32_mult_s32_s16_rnd_sat(int32 var1, int16 var2); + + int64 s64_mult_s32_u32(int32 var1, uint32 var2); + + /* arithmetic shifts with saturation */ + /*********************/ + /** + Performs an arithmetic shift right (or left if the shift is negative) + for a signed 16-bit input number, saturates it, and returns the + signed 16-bit result. + */ + int16 s16_shr_s16_sat(int16 var1, int16 shift); /* 1 ops */ + + /** + Performs an arithmetic shift left (or right if the shift is negative) + for a signed 16-bit input number, saturates it, and returns the + signed 16-bit result. + */ + int16 s16_shl_s16_sat(int16 var1, int16 shift); /* 1 ops */ + + /** + Performs an arithmetic shift left (or right if the shift is negative) + for a signed 16-bit input number, saturates it, rounds it, and returns + the signed 16-bit result. + */ + int16 s16_shl_s16_sat_rnd(int16 var, int16 var2); /* 2 ops */ + +#if !defined(uses_s32_shr_s32_sat) + /** + Performs an arithmetic shift right (or left if the shift is negative) + for a signed 32-bit input number, saturates it, and returns the + signed 32-bit result. + */ + int32 s32_shr_s32_sat(int32 var1, int16 shift); /* 2 ops */ +#define uses_s32_shr_s32_sat +#endif /* uses_s32_shr_s32_sat */ + +#if !defined(uses_s32_shl_s32_sat) + /** + Performs an arithmetic shift left (or right if the shift is negative) + for a signed 32-bit input number, saturates it, and returns the + signed 32-bit result. + */ + int32 s32_shl_s32_sat(int32 var1, int16 shift); /* 2 ops */ +#define uses_s32_shl_s32_sat +#endif /* uses_s32_shl_s32_sat */ + + /** + Performs an arithmetic shift left (or right if the shift is negative) + for a signed 32-bit input number, saturates it, rounds it, and returns + the signed 32-bit result. + */ + int32 s32_shl_s32_rnd_sat(int32 L_var, int16 shift); /* 3 ops */ + + /* arithmetic shifts without saturation */ + int16 s16_shr_s16(int16 var1, int16 shift); /* 1 ops */ + int16 s16_shl_s16(int16 var1, int16 shift); /* 1 ops */ + int32 s32_shr_s32(int32 var1, int16 shift); /* 2 ops */ + int32 s32_shl_s32(int32 var1, int16 shift); /* 2 ops */ + + /** + Performs an arithmetic shift left (or right, if the shift is negative) + for a signed 64-bit input number and returns the signed 64-bit result. + */ + int64 s64_shl_s64(int64 var1, int16 shift); + + /* logical operations */ + /**********************/ + /** ANDs two signed 16-bit integers and returns the signed 16-bit result. + */ + int16 s16_and_s16_s16(int16 var1, int16 var2); + + /** ANDs two signed 32-bit integers and returns the signed 32-bit result. + */ + int32 s32_and_s32_s32(int32 var1, int32 var2); + + /* absolute value */ + /*******************/ + /** + Computes the absolute value of the signed 16-bit input, + saturates it, and returns the unsigned 16-bit result. + */ + uint16 u16_abs_s16_sat(int16 var1); /* 1 ops */ + + /** + Computes the absolute value of the signed 32-bit input, + saturates it, and returns the unsigned 32-bit result. + */ + uint32 u32_abs_s32_sat(int32 var1); /* 3 ops */ + + /* multiply accumulate */ + /************************/ + /** + Multiplies two signed 16-bit numbers together with saturation, + adds that result to the signed 32-bit input with saturation, + rounds the result into a signed 16-bit number, and returns the result. + */ + int16 s16_mac_s32_s16_s16_sat_rnd(int32 var3, + int16 var1, int16 var2); /* 2 op */ + + /** + Multiplies two signed 16-bit numbers together with saturation, + subtracts that result from the signed 32-bit input with saturation, + and returns the signed 32-bit result. + */ + int32 s32_msu_s32_s16_s16_sat(int32 var3, + int16 var1, int16 var2); /* 1 op */ + + /** + Multiplies two signed 16-bit numbers together with saturation, subtracts + that result from the signed 32-bit input with saturation, rounds the + result into a unsigned 16-bit number, and returns the result. + */ + int16 s16_msu_s32_s16_s16_sat_rnd(int32 var3, + int16 var1, int16 var2); /* 2 op */ + + /** + Multiplies two signed 16-bit numbers, shifts the product, adds it to the + signed 64-bit input, and returns the signed 64-bit result. + */ + int64 s64_mac_s64_s16_s16_shift(int64 var3, + int16 var1, int16 var2, int16 shift); + + /** + Multiplies two signed 16-bit numbers, shifts the result, adds that result + to the signed 64-bit input, and returns the signed 64-bit result without saturation. + */ + int64 s64_mac_s64_s16_s16_shift_nosat(int64 var3, + int16 var1, int16 var2, int16 shift); + + /** + Multiplies two signed 16-bit numbers, shifts the product, adds it to the + signed 64-bit input with saturation, and returns the signed 64-bit + result. + */ + int64 s64_mac_s64_s16_s16_shift_sat(int64 var3, + int16 var1, int16 var2, int16 shift); + + /** + Multiplies two signed 16-bit integers and accumulates the result in a + signed 64-bit integer. + */ + int64 s64_mac_s64_s16_s16_s1(int64 var3, int16 var1, int16 var2); + + /** + Multiplies two signed 16-bit integers and accumulates the result in a + signed 32-bit integer with saturation. + */ + int32 s32_mac_s32_s16_s16_sat(int32 var3, int16 var1, int16 var2); + + /* + Additional operators + */ + /** + Performs a special case of multiplication of a unsigned 32-bit + number with a signed 16-bit number, and returns the signed 32-bit result. + */ + int32 s32_mls_s32_s16_sat(int32 var1, int16 var2); + + /* negation */ + /*************/ + /** + Negates a signed 16-bit input number, saturates it, and returns + the signed 16-bit result. + */ + int16 s16_neg_s16_sat(int16 var1); /* 1 ops */ + + /** + Negates the signed 32-bit input number, saturates it, and returns + the signed 32-bit result. + */ + int32 s32_neg_s32_sat(int32 var1); /* 2 ops */ + + /* ALU operations */ + /******************/ + /** + Computes the one's complement of a signed 16-bit number and returns + the signed 16-bit result. + */ + int16 s16_complement_s16(int16 var1); + + /** + Finds the maximum of two signed 16-bit input numbers + and returns the signed 16-bit result. + */ + int16 s16_max_s16_s16(int16 var1, int16 var2); + + /** + Finds the minimum of two signed 16-bit input numbers + and returns the signed 16-bit result. + */ + int16 s16_min_s16_s16(int16 var1, int16 var2); + + /** + Normalizes a signed 64-bit input number and returns the signed 16-bit + result. + */ + int16 s16_norm_s64(int64 var1); + + /** + Finds the minimum of two signed 32-bit input numbers + and returns the signed 32-bit result. + */ + int32 s32_min_s32_s32(int32 var1, int32 var2); + + /** + Finds the maximum of two signed 32-bit input numbers + and returns the signed 32-bit result. + */ + int32 s32_max_s32_s32(int32 var1, int32 var2); + + /* Accumulator manipulation */ + /****************************/ + /** + Extracts the 16 LSBs of a signed 32-bit input + integer and return the signed 16-bit result. + */ + int16 s16_extract_s32_l(int32 var1); /* 1 ops */ + + /** + Extracts the 16 LSBs of a signed 32-bit input integer + and returns the unsigned 16-bit result. + */ + uint16 u16_extract_s32_l(int32 var1); + + /** + Extracts the 16 MSBs of a signed 32-bit input integer and returns the + signed 16-bit number. + */ + int16 s16_extract_s32_h(int32 var1); /* 1 ops */ + + /** + Extracts the 16 LSBs of a signed 64-bit input integer + and returns the signed 16-bit result. + */ + int16 s16_extract_s64_l(int64 var1); + + /** + Extracts the 16 MSBs of a signed 64-bit input integer and returns the + signed 16-bit result. + */ + int16 s16_extract_s64_h(int64 var1); + + /** + Extracts the 16 MSBs of a signed 64-bit input integer and returns the + signed 16-bit result. + */ + int16 s16_extract_s64_h_rnd(int64 var1); + + /** + Saturates a signed 64-bit input number to a signed 32-bit value, extracts + the higher 16 bits, and returns the output. + */ + int16 s16_extract_s64_h_sat(int64 var1); + + /** + Extracts the 16 LSBs of a signed 64-bit input integer + and returns the signed 32-bit result. + */ + int32 s32_extract_s64_l(int64 var1); + + /** + Extracts the 16 LSBs of a signed 64-bit input integer + and returns the unsigned 32-bit result. + */ + uint32 u32_extract_s64_l(int64 var1); + + /** + Puts the signed 16-bit input into the 16 LSBs of the output + 32-bit and returns the signed 32-bit result. + */ + int32 L_deposit_l(int16 var1); /* 1 ops */ + + /** + Puts the signed 16-bit input into the 16 MSBs of the output 32-bit + and returns the signed 32-bit result. + */ + int32 s32_deposit_s16_h(int16 var1); /* 1 ops */ + + /* Round */ + /*********/ + /** + Rounds the signed 32-bit input number into a signed 16-bit number, + saturates it, and returns the signed 16-bit result. + */ + int16 s16_round_s32_sat(int32 var1); /* 1 ops */ + + /* Normalization */ + /*****************/ + /** + Calculates the shift required to normalize a unsigned 32-bit + input number and returns the signed 16-bit shift value. + */ + int16 s16_norm_s32(int32 var1); /* 30 ops */ + + /** + Calculates the shift required to normalize a unsigned 16-bit + input number and returns the signed 16-bit shift value. + */ + int16 s16_norm_s16(int16 var1); /* 15 ops */ + +#if !defined(uses_s32_cl1_s32) + /** + Takes a signed 32-bit number as input (possibly unnormalized) and returns + the number of consecutive 1s starting with the most significant bit. + */ + int32 s32_cl1_s32(int32 var1); +#define uses_s32_cl1_s32 +#endif /* uses_s32_cl1_s32 */ + + /* Saturation manipulation routines */ + /************************************/ + /** Clears the overflow flag. + */ + int clearOverflow(void); + + /** Checks to see if an overflow/saturation/limiting has occurred. + */ + int isOverflow(void); + + /** + Pulls the old overflow state from the stack and replaces the current + overflow status with its predecessor. + */ + int popOverflow(void); + + /** Sets the overflow flag. + */ + int setOverflow(void); + +#if !defined(uses_s16_saturate_s32) + /** + Saturates the input signed 32-bit input number (possibly exceeding the + 16-bit dynamic range) to the signed 16-bit number and returns that + result. + */ + int16 s16_saturate_s32(int32 var1); +#define uses_s16_saturate_s32 +#endif /* uses_s16_saturate_s32 */ + +#if !defined(uses_s32_saturate_s64) + /** + Saturates the input signed 64-bit input number (possibly exceeding the + 32-bit dynamic range) to the signed 32-bit number and returns that + result. + */ + int32 s32_saturate_s64(int64 var1); +#define uses_s32_saturate_s64 +#endif /* uses_s32_saturate_s64 */ + + /* Non-saturating instructions */ + /*******************************/ + /** + Adds two signed 32-bit input numbers with carry and returns the signed + 32-bit result. + */ + int32 L_add_c(int32 var1, int32 var2); /* 2 ops */ + + /** + Performs subtraction between two signed 32-bit input numbers with carry + and returns the signed 32-bit result. + */ + int32 L_sub_c(int32 var1, int32 var2); /* 2 ops */ + + /** + Multiplies two signed 16-bit numbers, adds the result to the signed + 32-bit input, and returns the signed 32-bit result. + */ + int32 L_macNs(int32 var3, + int16 var1, int16 var2); /* 1 ops */ + + /** + Multiplies two signed 16-bit numbers, subtracts the product from the + signed 32-bit input, and returns the signed 32-bit result. + */ + int32 L_msuNs(int32 var3, + int16 var1, int16 var2); /* 1 ops */ + +#endif // #if !defined(__qdsp6__) + +/** @} */ /* end_addtogroup math_operations */ + + +/** @cond +*/ +/* Determines if two values are different. */ +boolean changed(int value1, int value2); + +/* If the new value is valid, assign it to the register. + + @param[in] originalValue Value to be changed. + @param[in] newVAlue New value, could be invalid. + @return None. + @dependencies None. +*/ +int change_if_valid +( + int originalValue, /* value to be changed */ + int newValue /* new value, could be invalid */ +); +/** @endcond */ + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +// FOR BACKWARDS COMPATIBILITY of certain modules +// the follow header file is included. +#include "legacy_map.h" +#endif /*__BASICOP_H */ diff --git a/modules/cmn/common/utils/inc/audio_basic_op_ext.h b/modules/cmn/common/utils/inc/audio_basic_op_ext.h new file mode 100644 index 0000000..2027878 --- /dev/null +++ b/modules/cmn/common/utils/inc/audio_basic_op_ext.h @@ -0,0 +1,233 @@ +#ifndef __AUDIO_BASIC_OP_EXT_H_ +#define __AUDIO_BASIC_OP_EXT_H_ + +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +/*======================================================================== + +B A S I C O P E X T + +*/ + +/** +@file audio_basic_op_ext.h + +basic_op_ext is an extension of basic_op.h, with additional intrinsics +used by linking code. +*/ + +/*=========================================================================== +NOTE: The @brief description and any detailed descriptions above do not appear + in the PDF. + + The elite_audio_mainpage.dox file contains all file/group descriptions + that are in the output PDF generated using Doxygen and Latex. To edit or + update any of the file/group text in the PDF, edit the + elite_audio_mainpage.dox file or contact Tech Pubs. +===========================================================================*/ + +/* ======================================================================= +INCLUDE FILES FOR MODULE +========================================================================== */ +#include "shared_aud_cmn_lib.h" +#include "audio_basic_op.h" + +#if ((defined __hexagon__) || (defined __qdsp6__)) +#include +#endif + + +#ifdef __cplusplus +extern "C" { +#endif //__cplusplus + +/** @addtogroup math_operations +@{ */ + +/* ----------------------------------------------------------------------- +** Macro Definitions +** ----------------------------------------------------------------------- */ + + +/* ----------------------------------------------------------------------- +** Function Declarations +** ----------------------------------------------------------------------- */ + +#if ((defined __hexagon__) || (defined __qdsp6__)) + +#define s16_imag_s32(vari) (s16_extract_s32_h(vari)) +#define s32_imag_s64(vari) ((int32_t)((vari) >> 32)) +#define s16_real_s32(varr) (s16_extract_s32_l(varr)) +#define s32_real_s64(varr) ((int32_t)varr) + +#define s32_complex_s16_s16(varr,vari) (Q6_R_combine_RlRl((vari),(varr))) +#define s64_complex_s32_s32(varr,vari) (Q6_P_combine_RR( (vari), (varr) )) + +#define s64_complex_add_s64_s64(x, y) (Q6_P_vaddw_PP_sat(x,y)) +#define s64_complex_sub_s64_s64(x, y) (Q6_P_vsubw_PP_sat(x,y)) + +#define s32_complex_mult_rnd_sat_s32_s32(var1, var2) (Q6_R_cmpy_RR_s1_rnd_sat(var1, var2)) + +/* TODO : Need to check qdsp6 v5 or 6 support this. */ +int64_t s64_complex_mult_rnd_sat_s64_s32(int64_t, int32_t); + +#define s32_complex_mult_s32_s16_sat(x, y) ((int32_t) s32_saturate_s64(s32_mult_s32_s16_rnd_sat( (x), (y) ))) + +#define s32_complex_conjugate_sat_s32(var1) ((int32_t)Q6_P_vconj_P_sat(var1)) +#define s64_complex_conjugate_sat_s64(var1) ( s64_complex_s32_s32( s32_real_s64(var1), s32_neg_s32_sat(s32_imag_s64(var1)) ) ) + +#define s64_complex_average_s64_s64(x,y) (Q6_P_vavgw_PP_crnd(x,y)) +#define s64_complex_neg_average_s64_s64_sat(x,y) (Q6_P_vnavgw_PP_crnd_sat(x,y)) + +#define s32_bitrev_s32(x,y) (Q6_R_lsr_RR(Q6_R_brev_R(x), 32-y)) + +/** + Multiplies an unsigned 32-bit integer and another unsigned 32-bit integer, + and returns the unsigned 64-bit result. +*/ +#define u64_mult_u32_u32(var1,var2) Q6_P_mpyu_RR(var1,var2) + +/** + Counts both leading ones and leading zeros starting from the MSB, and then + it selects the maximum. +*/ +#define s32_get_msb_s32(var1) Q6_R_clb_R(var1) + +/** Counts the leading zeros starting from the MSB. +*/ +#define s32_cl0_s32(var1) Q6_R_cl0_R(var1) + +/** Counts the leading ones starting from the MSB. +*/ +#define s32_ct1_s32(var1) Q6_R_ct1_R(var1) +/** + Gets the least significant bit 1 position (i.e., counts the trailing + zeros). +*/ +#define s32_get_lsb_s32(var1) Q6_R_ct0_R(var1) + +/** Sets ind (0 to 31) bit in var1 +*/ +#define s32_set_bit_s32_s32(var1, ind) Q6_R_setbit_RR(var1, ind) + +/** Clears ind (0 to 31) bit in var1 +*/ +#define s32_clr_bit_s32_s32(var1, ind) Q6_R_clrbit_RR(var1, ind) + +/** Tests ind (0 to 31) bit in var1 and result in predicate reg +*/ +#define s8_tst_bit_s32_s32(var1, ind) Q6_p_tstbit_RR(var1, ind) + + /** Wrap the var1 into the modulo range from 0 to var2 + */ +#define s32_modwrap_s32_u32(var1, var2) Q6_R_modwrap_RR(var1, var2) + +#define s64_mac_s32_s32(mac64, var1, var2) Q6_P_mpyacc_RR(mac64, var1, var2) + +#define s32_mac_s32_s32_s1_sat(mac32, var1, var2) Q6_R_mpyacc_RR_s1_sat(mac32, var1, var2) + +#define s32_accu_asr_s32_imm5(accu32, var, u5) Q6_R_asracc_RI(accu32, var, u5) + +#define s32_accu_asl_s32_imm5(accu32, var, u5) Q6_R_aslacc_RI(accu32, var, u5) + +#define s32_shr_s32_imm5_rnd(var, u5) Q6_R_asr_RI_rnd(var, u5) + +#define s64_shr_s64_imm6_rnd(var, u6) Q6_P_asr_PI_rnd(var, u6) + +/** + Multiplies two unsigned 32-bit integers and returns the upper 32 bits of the product. +*/ +#define u32_mult_u32_u32(var1, var2) Q6_R_mpyu_RR(var1, var2) + +/** + Perform an arithmetic right shift by an given amount, and then round the result +*/ + +#define s64_shr_s64_imm5_rnd(var, u5) Q6_P_asrrnd_PI(var, u5) + +/** + Multiply two signed 32-bit integers and perform left shift by 1 operation and store upper 32 bits in output. +*/ + +#define s32_mult_s32_s32_left_shift_1(var1, var2) Q6_R_mpy_RR_s1(var1, var2) + +#else //define(qdsp6) + +#define s32_complex_mult_s32_s16_sat(x, y) ((int32_t) s32_saturate_s64(s32_mult_s32_s16_rnd_sat( (x), (y) ))) + + int16 s16_extract_s64_h_rnd(int64 var1); + int16_t s16_imag_s32(int32_t vari); + int32_t s32_imag_s64(int64_t vari); + + int16_t s16_real_s32(int32_t varr); + int32_t s32_real_s64(int64_t varr); + + int32_t s32_complex_s16_s16(int16_t varr, int16_t vari); + int64_t s64_complex_s32_s32(int32_t varr, int32_t vari); + + int64_t s64_complex_add_s64_s64(int64_t, int64_t); + int64_t s64_complex_sub_s64_s64(int64_t, int64_t); + + int32_t s32_complex_mult_rnd_sat_s32_s32(int32_t var1, int32_t var2); + int64_t s64_complex_mult_rnd_sat_s64_s32(int64_t var1, int32_t var2); + + int32_t s32_complex_conjugate_sat_s32(int32_t var1); + int64_t s64_complex_conjugate_sat_s64(int64_t var1); + + int64_t s64_complex_average_s64_s64(int64_t, int64_t); + int64_t s64_complex_neg_average_s64_s64_sat(int64_t, int64_t); + int32_t s32_bitrev_s32(int32_t, int32_t); + +/** + Multiplies two unsigned 32-bit integers and returns the unsigned 64-bit + result. +*/ +uint64_t u64_mult_u32_u32(uint32_t var1,uint32_t var2); + +int32_t s32_modwrap_s32_u32(int32_t var1, uint32_t var2); + +int64_t s64_mac_s32_s32(int64_t mac64, int32_t var1, int32_t var2); + +int32_t s32_mac_s32_s32_s1_sat(int32_t mac32, int32_t var1, int32_t var2); + +int32_t s32_accu_asr_s32_imm5(int32_t accu32, int32_t var, uint32_t u5); + +int32_t s32_accu_asl_s32_imm5(int32_t accu32, int32_t var, uint32_t u5); + +#if !defined(s32_set_bit_s32_s32) +int32_t s32_set_bit_s32_s32(int32_t var1, int32_t ind); +#endif + +#if !defined(s32_clr_bit_s32_s32) +int32_t s32_clr_bit_s32_s32(int32_t var1, int32_t ind); +#endif + +#if !defined(s64_shr_s64_imm5_rnd) +int64_t s64_shr_s64_imm5_rnd(int64_t var, int16_t u5); +#endif + +#if !defined(u32_mult_u32_u32) +uint32_t u32_mult_u32_u32(uint32_t var1, uint32_t var2); +#endif + +#if !defined(s32_mult_s32_s32_left_shift_1) +int32_t s32_mult_s32_s32_left_shift_1(int32_t var1, int32_t var2); +#endif + +#endif //define(qdsp6) + +int32_t L_shift_r(int32_t L_var1, int16_t var2); + +int32_t s32_mac_s32_s32_s1_rnd_sat(int32_t mac32, int32_t var1, int32_t var2); // TODO: add intrinsic + +/** @} */ /* end_addtogroup math_operations */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /*__BASIC_OP_EXT_H_*/ diff --git a/modules/cmn/common/utils/inc/audio_clips.h b/modules/cmn/common/utils/inc/audio_clips.h new file mode 100644 index 0000000..93c5297 --- /dev/null +++ b/modules/cmn/common/utils/inc/audio_clips.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +/** +@file audio_clips.h + +This file contains declarations for clipping functions. +*/ + +/*=========================================================================== +NOTE: The @brief description and any detailed descriptions above do not appear + in the PDF. + + The elite_audio_mainpage.dox file contains all file/group descriptions + that are in the output PDF generated using Doxygen and Latex. To edit or + update any of the file/group text in the PDF, edit the + elite_audio_mainpage.dox file or contact Tech Pubs. +===========================================================================*/ + +#ifndef _CLIPS_H +#define _CLIPS_H + +#include "audio_comdef.h" + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +/** @addtogroup dsp_algorithms +@{ */ + +/** + Clips 16-bit samples. + + @param [in] input Pointer to the input buffer. + @param [in] lowerBound Lower bound for clipping; valid minimum value. + @param [out] upperBound Output buffer; data is interleaved; valid maximum + value. + + @return + bool_t -- Success or failure. + + @dependencies + None. +*/ +bool_t clip_16 +( + int16_t *input, + int16_t lowerBound, + int16_t upperBound +); + +/** + Clips 32-bit samples. + + @param [in] input Pointer to the input buffer. + @param [in] lowerBound Lower bound for clipping; valid minimum value. + @param [out] upperBound Output buffer; data is interleaved; valid maxmum + value. + + @return + bool_t -- Success or failure. + + @dependencies + None. +*/ +bool_t clip_32 +( + int32_t *input, + int32_t lowerBound, + int32_t upperBound +); + +/** @} */ /* end_addtogroup dsp_algorithms */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _CLIPS_H*/ + diff --git a/modules/cmn/common/utils/inc/audio_comdef.h b/modules/cmn/common/utils/inc/audio_comdef.h new file mode 100644 index 0000000..a21153e --- /dev/null +++ b/modules/cmn/common/utils/inc/audio_comdef.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +/** +@file AudioComdef.h + +@brief Type definitions for memory alignment attribute macros. + +This file contains ASM data commands and events structures definitions. +*/ + +/*=========================================================================== +NOTE: The @brief description and any detailed descriptions above do not appear + in the PDF. + + The elite_audio_mainpage.dox file contains all file/group descriptions + that are in the output PDF generated using Doxygen and Latex. To edit or + update any of the file/group text in the PDF, edit the + elite_audio_mainpage.dox file or contact Tech Pubs. +===========================================================================*/ + +#ifndef _AUDIOCOMDEF_H_ +#define _AUDIOCOMDEF_H_ + +#include "posal_types.h" + + +/** @cond +*/ +/* Type definition for signed 40-bit integers. */ +typedef int64_t int40; + +/* Type definition for unsigned 40-bit integers. */ +typedef uint64_t uint40; +/** @endcond */ + + +/** @addtogroup mem_alignment_macros +@{ */ + +#if ((defined __hexagon__) || (defined __qdsp6__)) || defined(__GNUC__) || defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 220000) +#define MEM_ALIGN(t, a) t __attribute__((aligned(a))) + /**< Memory alignment macro for target compilers. The value of the MEM_ALIGN + macro depends on the following conditional settings: + + - If __qdsp6__ or __GNUC__ or __ARMCC_VERSION >= 220000 is TRUE, + MEM_ALIGN = t __attribute__((aligned(a))). + - If _MSC_VER is TRUE, MEM_ALIGN = __declspec(align(a)) t. + - If __ARMCC_VERSION is TRUE, MEM_ALIGN = __align(a) t. + - If the compiler is unknown, it returns an unknown compiler error. */ + +#elif defined(_MSC_VER) +#define MEM_ALIGN(t, a) __declspec(align(a)) t + /**< Memory alignment macro for target compilers. The value of the MEM_ALIGN + macro depends on the following conditional settings: + + - If __qdsp6__ or __GNUC__ or __ARMCC_VERSION >= 220000 is TRUE, + MEM_ALIGN = t __attribute__((aligned(a))). + - If _MSC_VER is TRUE, MEM_ALIGN = __declspec(align(a)) t. + - If __ARMCC_VERSION is TRUE, MEM_ALIGN = __align(a) t. + - If the compiler is unknown, it returns an unknown compiler error. */ + +#elif defined(__ARMCC_VERSION) +#define MEM_ALIGN(t, a) __align(a) t + /**< Memory alignment macro for target compilers. The value of the MEM_ALIGN + macro depends on the following conditional settings: + + - If __qdsp6__ or __GNUC__ or __ARMCC_VERSION >= 220000 is TRUE, + MEM_ALIGN = t __attribute__((aligned(a))). + - If _MSC_VER is TRUE, MEM_ALIGN = __declspec(align(a)) t. + - If __ARMCC_VERSION is TRUE, MEM_ALIGN = __align(a) t. + - If the compiler is unknown, it returns an unknown compiler error. */ + +#else +#error Unknown compiler +#endif + +/** @} */ /* end_addtogroup mem_alignment_macros */ +#ifndef _COMPLEX_TYPES +#define _COMPLEX_TYPES +typedef int32 cint2x16; +typedef int64 cint2x32; +#endif + +#endif /* _AUDIOCOMDEF_H_ */ + diff --git a/modules/cmn/common/utils/inc/audio_common_param_calib.h b/modules/cmn/common/utils/inc/audio_common_param_calib.h new file mode 100644 index 0000000..019ce58 --- /dev/null +++ b/modules/cmn/common/utils/inc/audio_common_param_calib.h @@ -0,0 +1,54 @@ +#ifndef AUDIO_COMMON_PARAM_CALIB_H +#define AUDIO_COMMON_PARAM_CALIB_H + +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +/*============================================================================== + @file audio_common_param_calib.h + @brief This file contains common parameters +==============================================================================*/ + +#include "ar_defs.h" + +/*============================================================================== + Constants +==============================================================================*/ +/** + ID of the lib version parameter used by any audio processing module. + + This generic/common parameter is used to query the + lib version of any audio processing module. + + */ +#define AUDPROC_PARAM_ID_LIB_VERSION 0x00010937 + +/* Structure for Querying module lib version of any Audio processing modules. */ +typedef struct audproc_lib_version_t audproc_lib_version_t; +/** @h2xmlp_parameter {"AUDPROC_PARAM_ID_LIB_VERSION", AUDPROC_PARAM_ID_LIB_VERSION} + @h2xmlp_description {To query the lib version of any audio processing module.} + @h2xmlp_toolPolicy {RTC} + @h2xmlp_readOnly {true}*/ + +#include "spf_begin_pack.h" + +/* Payload of the AUDPROC_PARAM_ID_LIB_VERSION parameter used by + any Audio Processing module + */ +struct audproc_lib_version_t +{ + uint32_t lib_version_low; + /**< @h2xmle_description {Version of the module LSB.} */ + + + uint32_t lib_version_high; + /**< @h2xmle_description { Version of the module MSB} */ + +} +#include "spf_end_pack.h" +; +#endif + + diff --git a/modules/cmn/common/utils/inc/audio_complex_basic_op.h b/modules/cmn/common/utils/inc/audio_complex_basic_op.h new file mode 100644 index 0000000..7455e9b --- /dev/null +++ b/modules/cmn/common/utils/inc/audio_complex_basic_op.h @@ -0,0 +1,307 @@ +#ifndef AUDIO_COMPLEX_BASIC_OP_H +#define AUDIO_COMPLEX_BASIC_OP_H +/************************************************************************/ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause +*/ +/************************************************************************/ +/*============================================================================ + @file audio_complex_basic_op.h + + Declare methods for complex arithmetic operations. These correspond + mostly with existing QDSP6 intrinsics mappings defined in q6basic_op.h. +============================================================================*/ +/* $Header$ */ + +/*---------------------------------------------------------------------------- + * Include Files + * -------------------------------------------------------------------------*/ + +#include "AudioComdef.h" +#include "audio_basic_op.h" + +/*---------------------------------------------------------------------------- + * Preprocessor Definitions and Constants + * -------------------------------------------------------------------------*/ + +//---------------------------------------------------------------------- +// int16 real(cint2x16 x); +// get real part of a complex cint2x16 number +//---------------------------------------------------------------------- +#define s16_real_c32(x) (s16_extract_s32_l( (x) )) + +//---------------------------------------------------------------------- +// int16 s16_imag_c32(cint2x16 x); +// get imaginery part of a complex cint2x16 number +//---------------------------------------------------------------------- +#define s16_imag_c32(x) (s16_extract_s32_h( (x) )) + +//---------------------------------------------------------------------- +// int32 s32_real_c64(cint2x32 x); +// get real part of complex number +//---------------------------------------------------------------------- +#define s32_real_c64(x) ((int32)(x)) + +//---------------------------------------------------------------------- +// int32 s32_imag_c64(cint2x32 x); +// get imaginery part of a complex cint2x32 number +//---------------------------------------------------------------------- +#define s32_imag_c64(x) ((int32)((x)>>32)) + +/*---------------------------------------------------------------------------- + * Type Declarations + * -------------------------------------------------------------------------*/ +#if ((defined __hexagon__) || (defined __qdsp6__)) + +#define c64_complex_s32_s32(xr,xi) Q6_P_combine_RR(xi,xr) + +#define c32_conjugate_c32(x) (cint2x16)Q6_P_vconj_P_sat((int64)x) + +#define c64_conjugate_c64(x) Q6_P_combine_RR(Q6_R_neg_R_sat(s32_imag_c64(x)),s32_real_c64(x)) + +#define c64_add_c64_c64_sat(x,y) Q6_P_vaddw_PP_sat(x,y) + +#define c64_sub_c64_c64_sat(x,y) Q6_P_vsubw_PP_sat(x,y) + +#define c64_avrg_c64_c64_rnd(x,y) Q6_P_vavgw_PP_crnd(x,y) + +#define c64_avrg_c64_c64neg_rnd(x,y) Q6_P_vnavgw_PP_crnd_sat(x,y) + +//#define c64_mult_c64_s16_shift_sat(x64,y16,ls16) Q6_P_combine_RR(Q6_R_asl_RR_sat(s32_imag_c64(Q6_P_vmpyweh_PP_s1_rnd_sat(x64,Q6_P_combine_RR(y16,y16))),ls16),Q6_R_asl_RR_sat(s32_real_c64(Q6_P_vmpyweh_PP_s1_rnd_sat(x64,Q6_P_combine_RR(y16,y16))),ls16)) + +#define cl0(x) Q6_R_cl0_R(x) + +#else +/** + @brief Form long complex number from real and imaginary part. + + @param xr: [in] Real part + @param xi: [in] Imaginary part + @return Complex number (Q6_P_combine_RR(xi,xr)) + */ +cint2x32 c64_complex_s32_s32(int32 xr, int32 xi); + +/** + @brief Complex c32_conjugate_c32 (with saturation) + + @param x: [in] Complex input + @return Complex c32_conjugate_c32 (Q6_P_vconj_P_sat(x)) + */ +cint2x16 c32_conjugate_c32(cint2x16 x); + +/** + @brief Long complex c32_conjugate_c32 (with saturation) + + @param x: [in] Complex input + @return Complex c32_conjugate_c32 + */ +cint2x32 c64_conjugate_c64(cint2x32 x); + +/** + @brief Add two long complex numbers, with saturation. + + @param x: [in] First complex number + @param y: [in] Second complex number + @return sat(x + y) (Q6_P_vaddw_PP_sat) + */ +cint2x32 c64_add_c64_c64_sat(cint2x32 x, cint2x32 y); + +/** + @brief Subtract two long complex numbers, with saturation. + + @param x: [in] First complex number + @param y: [in] Second complex number + @return sat(x - y) (Q6_P_vsubw_PP_sat) + */ +cint2x32 c64_sub_c64_c64_sat(cint2x32 x, cint2x32 y); + +/** + @brief Average two long complex numbers with convergent rounding. + + @param x: [in] First complex number + @param y: [in] Second complex number + @return convround((x + y)>>1) (Q6_P_vavgw_PP_crnd) + */ +cint2x32 c64_avrg_c64_c64_rnd(cint2x32 x, cint2x32 y); + +/** + @brief Average a long complex number with the negation of + another, and apply convergent rounding. + + @param x: [in] First complex number + @param y: [in] Second complex number + @return convround((x - y)>>1) (Q6_P_vnavgw_PP_crnd_sat) + */ +int64 c64_avrg_c64_c64neg_rnd(int64 x, int64 y); + +/** + @brief Count leading zero bits. + + @param x: [in] Input parameter + @return Number of leading zero bits (Q6_R_cl0_R(x)) + */ +int32 cl0(int32 x); + +#endif + +/** + @brief Multiply a 64-bit complex number with 16-bit real number (fractional integer). + + @param var1: [in] Input 64-bit complex number + @param var2: [in] Input 16-bit real number + @param iqShiftL16: [in] Integer part of Qfactor of real number + @return (var1*var2) << (iqShiftL16+1) + */ +cint2x32 c64_mult_c64_s16_shift_sat( cint2x32 var1, int16 var2, int16 iqShiftL16); + +/*---------------------------------------------------------------------------- + * Function Declarations and Documentation + * -------------------------------------------------------------------------*/ + +/** + @brief Form complex number from real and imaginary part. + + @param xr: [in] Real part + @param xi: [in] Imaginary part + @return Complex number (Q6_R_combine_RlRl(xi,xr)) + */ +cint2x16 c32_complex_s16_s16(int16 xr, int16 xi); + +/** + @brief Fractionally multiply two complex numbers with + 16-bit real and imaginary parts, producing a + product with 32-bit real and imaginary parts. + + @param var1: [in] First complex number + @param var2: [in] Second complex number + @return sat((var1 * var2)<<1) (Q6_P_cmpy_RR_s1_sat(var1,var2)) + */ +cint2x32 c64_mult_c32_c32(cint2x16 var1, cint2x16 var2); + +/** + @brief Fractionally multiply two complex numbers with + 16-bit real and imaginary parts, producing a + rounded product with 16-bit real and imaginary parts. + + @param var1: [in] First complex number + @param var2: [in] Second complex number + @return s16_round_s32_sat( sat((var1 * var2)<<1) ) + (Q6_R_cmpy_RR_s1_rnd_sat(var1,var2)) + */ +cint2x16 c32_mult_c32_c32(cint2x16 var1, cint2x16 var2); + +/** + @brief Fractionally multiply one complex number with + the c32_conjugate_c32 of another. Each input has + 16-bit real and imaginary parts, and the product is + rounded with 16-bit real and imaginary parts. + + @param var1: [in] First complex number + @param var2: [in] Second complex number + @return s16_round_s32_sat( sat((var1 * conj(var2))<<1) ) + (Q6_R_cmpy_RR_conj_s1_rnd_sat(var1,var2)) + */ +cint2x16 c32_mult_c32_c32conj(cint2x16 var1, cint2x16 var2); + +/** + @brief Average two complex numbers with convergent rounding. + + @param x: [in] First complex number + @param y: [in] Second complex number + @return convround((x + y)>>1) (see Q6_P_vavgh_PP_crnd) + */ +cint2x16 c32_avrg_c32_c32_rnd(cint2x16 x, cint2x16 y); + +/** + @brief Average a complex number with the negation of another, + and apply convergent rounding. + + @param x: [in] First complex number + @param y: [in] Second complex number + @return convround((x - y)>>1) (see Q6_P_vnavgh_PP_crnd_sat) + */ +cint2x16 c32_avrg_c32_c32neg_rnd(cint2x16 x, cint2x16 y); + +/** + @brief Add two complex numbers, with saturation. + + @param var1: [in] First complex number + @param var2: [in] Second complex number + @return sat(var1 + var2) + */ +cint2x16 c32_add_c32_c32_sat(cint2x16 var1, cint2x16 var2); + +/** + @brief Subract two complex numbers, with saturation. + + @param var1: [in] First complex number + @param var2: [in] Second complex number + @return sat(var1 - var2) + */ +cint2x16 c32_sub_c32_c32_sat(cint2x16 var1, cint2x16 var2); + +/** + @brief Average a long complex number with the negation of + another, and apply convergent rounding. + + @param x: [in] First complex number + @param y: [in] Second complex number + @return convround((x - y)>>1) (Q6_P_vnavgw_PP_crnd_sat) + */ +int64 c64_avrg_c64_c64neg_rnd(int64 x, int64 y); + +// Newly added function for Quad-mic ABF processing +/** + @brief Multiply a complex number with 16-bit real number (fractional integer). + + @param var1: [in] Input complex number + @param var2: [in] Input real number + @param iqShiftL16: [in] Integer part of Qfactor of real number + @return (var1*var2) << (iqShiftL16+1) + */ +cint2x16 c32_mult_c32_s16_shift_sat( cint2x16 var1, int16 var2, int16 iqShiftL16); + +/** + @brief Multiply a complex number with 32-bit real number (fractional integer). + + @param var1: [in] Input complex number + @param var2: [in] Input real number + @param iqShiftL16: [in] Integer part of Qfactor of real number + @return (var1*var2) << (iqShiftL16+1) + */ +cint2x16 c32_mult_c32_s32_shift_sat( cint2x16 var1, int32 var2, int16 iqShiftL16); + +/** + @brief Multiply a complex number with 16-bit real number (fractional integer) + and return 32-bit complex output. + @param var1: [in] Input complex number + @param var2: [in] Input real number + @return (var1*var2) << 1 + */ +cint2x32 c64_mult_c32_s16_sat( cint2x16 var1, int16 var2 ); + +/** + @brief Fractionally multiply 64-bit complex number with + 32-bit complex number and produce a 64-bit product. + + @param var1: [in] First complex number + @param var2: [in] Second complex number + @return sat((var1 * var2)<<1) + */ +cint2x32 c64_mult_c64_c32_rnd_sat(cint2x32 var1, cint2x16 var2); + +/** + @brief Fractionally multiply 64-bit complex number with + 32-bit complex number and produce a 64-bit product. + QDSP6 bit exact version + + @param var1: [in] First complex number + @param var2: [in] Second complex number + @return sat((var1 * var2)<<1) + */ + +cint2x32 c64_mult_c64_c32_rnd_sat_q6(cint2x32 x, cint2x16 y); + + +#endif /* #ifdef COMPLEX_BASIC_OP_H */ diff --git a/modules/cmn/common/utils/inc/audio_divide_qx.h b/modules/cmn/common/utils/inc/audio_divide_qx.h new file mode 100644 index 0000000..8b0caea --- /dev/null +++ b/modules/cmn/common/utils/inc/audio_divide_qx.h @@ -0,0 +1,267 @@ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +/** +@file audio_divide_qx.h + +This file contains declarations for optimized division functions +*/ + +/*=========================================================================== +NOTE: The @brief description and any detailed descriptions above do not appear + in the PDF. + + The elite_audio_mainpage.dox file contains all file/group descriptions + that are in the output PDF generated using Doxygen and Latex. To edit or + update any of the file/group text in the PDF, edit the + elite_audio_mainpage.dox file or contact Tech Pubs. +===========================================================================*/ + +#ifndef _DIVIDE_QX +#define _DIVIDE_QX + +#include "audio_comdef.h" + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +/** @addtogroup math_operations +@{ */ + +/** + Divides a 16-bit integer by a 16-bit integer. + + @param[in] var1 Numerator; number to be divided. + @param[in] var2 Denominator; number to divide by. + + @return + int16_t quotient. + + @dependencies + None. +*/ +int16_t s16_div_s16_s16_sat(int16_t var1, + int16_t var2); + +/** + Divides a 32-bit integer by a 32-bit integer and returns + a normalized result. + + @param[in] var1 Numerator; number to be divided. + @param[in] var2 Denominator; number to divide by. + @param[out] iqOutL16Ptr Q factor for the quotient. + + @return + int32_t quotient. + + @dependencies + None. +*/ +int32_t s32_div_s32_s32_normalized(int32_t var1, + int32_t var2, + int16_t *iqOutL16Ptr); + +/** + Divides a 32-bit integer by a 32-bit integer, both of which are in same Q + format. The output quotient is in the Q format requested by the 16-bit signed + value, iqL16. + + The two inputs are normalized so they are represented with full 32-bit + precision in the range [0,1). The normalized inputs are truncated to the + upper 16 bits and divided. The normalized quotient is returned with 16-bit + precision, where the 16 bits are in the LSBs. The quotient is renormalized + back to the requested Q format. + + The input argument, iqL16, is the number of integer bits in the quotient. This + version of division only has 16 bits of precision. + + @param[in] var1 Numerator; number to be divided. + @param[in] var2 Denominator; number to divide by. + @param[in] iqL16 Q factor for the quotient. + + @return + int32_t quotient. + + @dependencies + None. +*/ +int32_t s32_div_s32_s32_sat(int32_t var1, + int32_t var2, + int16_t iqL16); + +/** + Divides a 32-bit integer by a 32-bit integer. The numerator and denominator + are to be in the same Q format, and the output quotient is in the Q + format requested by outputQL16. + + The input argument, outputQL16, is the number of fractional bits in the quotient. + This version of division has 32 bits of precision. + + @param[in] numerator Number to be divided. + @param[in] denominator Number to divide by. + @param[in] outputQL16 Q factor for output. + + @return + int32_t quotient. + + @dependencies + None. +*/ +int32_t divide_qx(int32_t numerator, + int32_t denominator, + int16_t outputQL16); + +/** + Divides a 32-bit integer by a 32-bit integer. The numerator and denominator + are to be in the same Q format, and the output quotient is in the Q factor defined + by the number of fractional bits in outputQ. The precision of + the division is 32 bits. + + @param[in] numerator Number to be divided. + @param[in] denominator Number to divide by. + @param[in] outputQ Q factor for the output. + + @return + int32_t quotient. + + @dependencies + None. +*/ +int32_t divide_int32_qx(int32_t numerator, + int32_t denominator, + int16_t outputQ); + +/** + Divides a 32-bit integer by a 32-bit integer. The numerator and denominator + are to be in the same Q format, the output quotient is in Q0, and the + division has 32 bits of precision. + + + @param[in] numeratorL32 Number to be divided. + @param[in] denominatorL32 Number to divide by. + + @return + int32_t quotient. + + @dependencies + None. +*/ +int32_t divide_int32(int32_t numeratorL32, + int32_t denominatorL32); + +/** + Divides a 40-bit integer by a 40-bit integer. + + @param[in] Lnum Number to be divided. + @param[in] Ldenom Number to divide by. + @param[in] Qadj Q factor. + + @return + int40 quotient. + + @dependencies + None. +*/ +int40 audio_divide_dp(int40 Lnum, int40 Ldenom, int16_t Qadj); + +/** + Approximates the inversion of a 32-bit positive number. If the input is in + Qn and the output in Qm, m = 30 - SF - n. For + example, if the input is 0x0F000000 in Q26 (i.e., 3.75), the return values are: + + - Result = 0x44730000 + - SF = -28 + + Therefore, m = 30-(-28)-26 = 32, i.e., inversion is (0x44730000)/2^32 = + 0.266464. + + @note1hang The approximation is performed with a linearly interpolated + lookup table. + + The cycle count of this function is 6 cycles. + + @param x Number to be inverted. + + @return + A 64-bit integer where the high 32 bits are the shift factor and the low 32 + bits are the Q-shifted inverse. + + @dependencies + None. +*/ + int64_t dsplib_approx_invert(int32_t x); + +/** + Approximates the division of a 32-bit integer by a 32-bit integer. If the + input is in Q0, the output is in Q(-SF). For example, if numer = 5297 + and denom = 13555, the return values are: + + - Result = 839214304 + - SF = -31 + + Therefore, the division value is 839214304/2^31 = 0.3907896. + + @note1hang The inversion is approximated by a linear interpolation of the + lookup table. + + The cycle count of this function is 7 cycles. + + @param[in] numer Number to be divided. + @param[in] denom Number to divide by. + + @return + int64_t quotient. + + @dependencies + None. +*/ +int64_t dsplib_approx_divide(int32_t numer, + int32_t denom); + +/** + Inverts a 32-bit positive number using Newton's method. If the input is in + Q0, the output is in Q(45-SF). For example, if the input is 14767, the + return values are: + + - Result = 18179 + - SF = 17 + + Therefore, the inversion value is 18179/2^(45-17) = 6.7722 x 10^-5. + + The cycle count of this function is 18 cycles. + + @param[in] x Value to be inverted. + + @return + int64_t inverse. + + @dependencies + None. +*/ +int64_t dsplib_newtons_invert(int32_t x); + +/** + Divides a 32-bit integer by a 32-bit integer. + + @param[in] Lnum Number to be divided. + @param[in] Ldenom Number to divide by. + @param[in] Qadj Q factor. + + @return + int40 quotient. + + @dependencies + None. +*/ +int32 audio_divide_sp(int32 Lnum, int32 Ldenom, int16 Qadj); + +/** @} */ /* end_addtogroup math_operations */ + +#ifdef __cplusplus +} +#endif /* __cplusplus*/ + +#endif /* _DIVIDE_QX */ diff --git a/modules/cmn/common/utils/inc/audio_dsp.h b/modules/cmn/common/utils/inc/audio_dsp.h new file mode 100644 index 0000000..4c6dd54 --- /dev/null +++ b/modules/cmn/common/utils/inc/audio_dsp.h @@ -0,0 +1,475 @@ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +/***************************************************************************** + * FILE NAME: audio_dsp.h TYPE: C-header file + * DESCRIPTION: Header file for audio dsp and utilities. + *****************************************************************************/ +#ifndef _AUDIO_DSP_H_ +#define _AUDIO_DSP_H_ + +#include "AudioComdef.h" +#include "audpp_mathlib.h" +#include "audpp_converter.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/*============================================================================= + Constants +=============================================================================*/ +#define MAX_BIQUAD_STAGES 4 +#define MAX_FIR_TAPS 160 + +#if ((defined __hexagon__) || (defined __qdsp6__)) +#define MAX_FIR_BLOCK_SIZE 512 +#endif + +/*============================================================================= + Typedefs +=============================================================================*/ +/*----------------------------- panner struct -------------------------------*/ + + +typedef struct _pannerStruct +{ + int16 targetGainL16Q15; /* target gain to pan toward, L16Q15 */ + int32 deltaL32Q31; /* gain increment per sample, L32Q31 */ + int32 sampleCounter; /* total sample steps of the panner */ + int32 delaySamples; /* delay of samples to start panning */ +} pannerStruct; + +/*------------------------- angle panner struct -----------------------------*/ +typedef struct _anglePannerStruct +{ + /* angle: integer part : 0~32, corresponding to 0~180 degrees */ + /* fractinoal part: UL16 */ + int32 targetAngleL32Q16; /* target angle to pan toward, L32Q16*/ + int32 deltaL32Q24; /* gain increment per sample, L32Q24 */ + int32 sampleCounter; /* total sample steps of the panner */ +} anglePannerStruct; + +/*--------------------------- one pole filter struct ------------------------*/ +typedef struct _simpleLPFilt +{ + int16 c0L16Q14; /* coefficient c0, L16Q14 */ + int16 c1MinusOneL16Q14; /* coefficient c1-1, L16Q14 */ + int32 yL32; /* feedback memory, y(n-1), L32 */ + +/* c0 */ +/* H(z) = ---------- */ +/* 1-c1*z**-1 */ +/* filter difference equation: */ +/* y(n) = c0*x(n) + c1*y(n-1) */ +/* = c0*x(n) +( c1-1)*y(n-1) + y(n-1) */ +/* set one coeff as (c1-1) and add 32 bit version of y(n-1) to */ +/* prevent accumulation of round-off errors */ +} simpleLPFilt; + +/*----------------------------- delayline struct ----------------------------*/ +typedef struct _delaylineStruct +{ + int32 delayIndex; /* delayline index */ + int32 delayLength; /* delayline length */ + int16 *delayBuf; /* pointer to delayline buffer */ +} delaylineStruct; + +/*--------------------------- variable delay struct -------------------------*/ +typedef struct _variableDelayStruct +{ + delaylineStruct delayline; /* delayline of variable delay */ + bufRateCvtrStruct converter; /* buffer rate converter */ + int32 currentDelay; /* current delay amount, <= 32767 */ +} variableDelayStruct; + +/*---------------------------- biquad filter struct -------------------------*/ + +#if ((defined __hexagon__) || (defined __qdsp6__)) + +typedef struct _biquadFilter +{ + int16 coeffsL16Q13[4 + 4]; /* a1, a2, -b1, -b2 L16Q13 */ + int16 states[4 + 4]; /* x(n-1) x(n-2) y(n-1) y(n-2)*/ + int16 coeffIndex; /* 8 byte aligned coeff index*/ + int16 memIndex; /* 8 byte aligned state index*/ + int16 coeff0; + int32 yL32; /* 32 bit version of y(n-1)*/ +} biquadFilter; + + +#else + +typedef struct _biquadFilter +{ + int16 coeffsL16Q13[5]; /* a0, -b2, -b1-1, a2, a1, L16Q13 */ + int32 yL32; /* 32 bit version of y(n-1) */ + int16 yL16[2]; /* y(n-2), y(n-1) */ + int16 xL16[2]; /* x(n-2), x(n-1) */ + +/* a0 + a1*z**-1+ a2*z**-2 */ +/* H(z) = ----------------------- */ +/* 1 + b1*z**-1 + b2*z**-2 */ +/* filter difference equation: */ +/* y(n) = a0*x(n) + a1*x(n-1) + a2*x(n-2) - b1*y(n-1) - b2*y(n-1) */ +/* = a0*x(n) + a1*x(n-1) + a2*x(n-2) */ +/* y(n-1) + (-b1-1)*y(n-1) - b2*y(n-1) */ +/* set one coeff as (-b1-1) and s16_add_s16_s16 32 bit version of y(n-1) to */ +/* prevent accumulation of round-off errors */ +} biquadFilter; +#endif +/*--------------------- biquad filter coeff spec struct ---------------------*/ +typedef int32 biquadSpec[5]; /* biquad filter coeffs L32Q23 */ + +/*--------------------- multi stage biquad filter struct --------------------*/ +typedef struct _multiStageBiquadFilter +{ + int16 stages; /* number of stages */ + biquadFilter biquads[MAX_BIQUAD_STAGES]; /* array of biquads */ +} multiStageBiquadFilter; + +/*------------------- multi stage biquad coeff spec struct ------------------*/ +typedef struct _multiStageBiquadSpec +{ + int16 stages; /* number of stages */ + const biquadSpec *coeffs; /* pointer to array of biquad specs */ +} multiStageBiquadSpec; + +/*------------------------------ FIR filter struct --------------------------*/ +typedef struct _firFilter +{ + int16 *coeffsL16; /* filter coefficients*/ +#if ((defined __hexagon__) || (defined __qdsp6__)) + int16 xL16[MAX_FIR_TAPS + MAX_FIR_BLOCK_SIZE + 4]; /* filter history (circular)*/ +#else + int16 xL16[MAX_FIR_TAPS]; +#endif + + int16 memIndex; /* current position in filter history*/ +#if ((defined __hexagon__) || (defined __qdsp6__)) + int16 outIndex; + int16 yL16[MAX_FIR_BLOCK_SIZE + 4]; +#endif + int16 taps; /* filter taps */ +} firFilter; + +/*----------------------------- FIR filter struct --------------------------*/ +typedef struct fir_filter_t +{ + int32 mem_idx; /* filter memory index */ + int32 taps; /* filter taps */ + void *history; /* filter memory (history) */ + void *coeffs; /* filter coefficients */ + +#if ((defined __hexagon__) || (defined __qdsp6__)) + int16 reverse_coeff[MAX_FIR_TAPS]; +#endif +} fir_filter_t; + +/*============================================================================= + Function Declarations +=============================================================================*/ +/*-------------------------------- buffers ----------------------------------*/ +void buffer_empty +( + int16 *buf, /* buffer to be processed */ + uint32 samples /* number of samples in this buffer */ +); + +void buffer_copy +( + int16 *destBuf, /* output buffer */ + int16 *srcBuf, /* input buffer */ + uint32 samples /* number of samples to process */ +); + +void buffer_fill +( + int16 *destBuf, /* output buffer */ + int16 *srcBuf, /* input buffer */ + int16 gainL16Q15, /* gain to the samples */ + uint32 samples /* number of samples to process */ +); +#if ((defined __hexagon__) || (defined __qdsp6__)) +void buffer_fill_asm +( + int16 *destBuf, /* output buffer */ + int16 *srcBuf, /* input buffer */ + int16 gainL16Q15, /* gain to the samples */ + uint32 samples /* number of samples to process */ +); +#endif +void buffer_mix +( + int16 *destBuf, /* output buffer */ + int16 *srcBuf, /* input buffer */ + int16 gainL16Q15, /* gain to the samples */ + uint32 samples /* number of samples to process */ +); +#if ((defined __hexagon__) || (defined __qdsp6__)) +void buffer_mix_asm +( + int16 *destBuf, /* output buffer */ + int16 *srcBuf, /* input buffer */ + int16 gainL16Q15, /* gain to the samples */ + uint32 samples /* number of samples to process */ +); +#endif +void buffer_fill_mix +( + int16 *src1, /* Input1 buffer */ + int16 *src2, /* input2 buffer */ + int16 *dst, /* Output buffer */ + int16 gainL16Q15, /* gain to the samples */ + int16 samples /* number of samples to process */ +); + +#if ((defined __hexagon__) || (defined __qdsp6__)) +void buffer_fill_mix_asm +( + int16 *src1, /* Input1 buffer */ + int16 *src2, /* input2 buffer */ + int16 *dst, /* Output buffer */ + int16 gainL16Q15, /* gain to the samples */ + int16 samples /* number of samples to process */ +); +#endif + +/*--------------------------------- delayline -------------------------------*/ +void delayline_reset +( + delaylineStruct *delayline /* delayline struct */ +); + +boolean delayline_set +( + delaylineStruct *delayLine, /* delayline struct */ + int32 delayLen /* delay length */ +); + +void buffer_delay_fill +( + int16 *destBuf, /* output buffer */ + int16 *srcBuf, /* input buffer */ + int16 gainL16Q15, /* gain to the samples */ + delaylineStruct *delayline, /* delayline struct */ + int32 delay, /* amount of delay in sample */ + int32 samples /* number of samples to process */ +); + +void buffer_delay_mix +( + int16 *destBuf, /* output buffer */ + int16 *srcBuf, /* input buffer */ + int16 gainL16Q15, /* gain to the samples */ + delaylineStruct *delayline, /* delayline struct */ + int32 delay, /* amount of delay in sample */ + int32 samples /* number of samples to process */ +); + +void delayline_update +( + delaylineStruct *delayline, /* delayline struct */ + int16 *srcBuf, /* input buffer (new samples) */ + int32 samples /* input buffer sample size */ +); + +void delayline_copy +( + delaylineStruct *dest, /* The destination delay line */ + delaylineStruct *source /* The source delay line */ +); + +/*--------------------------- variable delayline ----------------------------*/ +void variable_delay_reset +( + variableDelayStruct *variableDelay /* variable delay struct */ +); + +void variable_delay_setdelay +( + variableDelayStruct *variableDelay, /* variable delay struct */ + int16 newDelay, /* new delay in sample */ + int32 steps /* sample dur. to change to new delay*/ +); + +uint32 variable_delay_process +( + variableDelayStruct *variableDelay, /* variable delay struct */ + int16 *destBuf, /* output buffer */ + int16 *srcBuf, /* input buffer */ + int16 *dlyTmpBuf, /* scratch buffer 1 */ + int16 *dlyMixBuf, /* scratch buffer 2 */ + int16 feedbackL16Q15,/* feedback gain */ + int32 samples /* samples to process */ +); + +/*--------------------------------- panners ---------------------------------*/ +int16 panner_get_current +( + pannerStruct panner /* panner struct */ +); + +void panner_setup +( + pannerStruct *panner, /* panner struct */ + int16 newGainL16Q15, /* new target panner gain */ + int32 rampSamples, /* number of samples in the ramp */ + int32 newDelay /* delay of ramping process */ +); + +void buffer_fill_with_panner +( + int16 *destBuf, /* output buffer */ + int16 *srcBuf, /* input buffer */ + pannerStruct *panner, /* panner applied to the samples */ + int32 samples /* number of samples to process */ +); + +void buffer_mix_with_panner +( + int16 *destBuf, /* output buffer */ + int16 *srcBuf, /* input buffer */ + pannerStruct *panner, /* panner applied to the samples */ + int32 samples /* number of samples to process */ +); + + +/*--------------------------------- filters ---------------------------------*/ +void biquad_setup +( + biquadFilter *filter, /* biquad filter struct */ + const int32 coeffs[5] /* 32 bit filter coeffs */ +); + +void biquad_reset +( + biquadFilter *filter /* biquad filter struct */ +); + +void biquad_process +( + int16 *inplaceBuf, /* inplace buffer */ + biquadFilter *filter, /* biquad filter struct */ + int32 samples /* number of samples to process */ +); + +void biquad_process_io +( + int16 *destBuf, /* output buffer */ + int16 *srcBuf, /* input buffer */ + biquadFilter *filter, /* biquad filter struct */ + int32 samples /* number of sample to process */ +); + +void multiStageBiquad_setup +( + multiStageBiquadFilter *filter, /* multi stage biquad struct */ + const multiStageBiquadSpec filterSpec /* coeff struct */ +); + +void multiStageBiquad_reset +( + multiStageBiquadFilter *filter /* multi stage biquad struct */ +); + +void multiStageBiquad_process +( + int16 *inplaceBuf, /* inplace buffer */ + multiStageBiquadFilter *filter, /* multi stage biquad struct */ + int32 samples /* num of samples to process */ +); + +#if ((defined __hexagon__) || (defined __qdsp6__)) +void fir_process_c16xd16(fir_filter_t *filter, int16 *dest, int16 *src, int32 samples, int16 qx); +void fir_process_c16xd32(fir_filter_t *filter, int32 *dest, int32 *src, int32 samples, int16 qx); +void fir_process_c16xd16_rnd(fir_filter_t *filter, int16 *dest, int16 *src, int32 samples, int16 qx); +#else +void fir_process_c16xd16(fir_filter_t *filter, int16 *dest, int16 *src, int32 samples, int16 qx); +void fir_process_c16xd32(fir_filter_t *filter, int32 *dest, int32 *src, int32 samples, int16 qx); +void fir_process_c16xd16_rnd(fir_filter_t *filter, int16 *dest, int16 *src, int32 samples, int16 qx); +#endif + +uint32 fir_reset +( + firFilter *filter /* fir filter struct */ +); + +// fir filter functions for 16/32 data and coeffs +void fir_reset_v1(fir_filter_t *filter, int32 data_width); +void fir_reset1(fir_filter_t *filter, int32 data_width); + +/* the QDSP6 assembly optimized version of this filter requires the + filter tap and samples to be multiples of 4, a more general version is + added back below as fir_process_general */ +void fir_process +( + firFilter *filter, /* fir filter struct */ + int16 *destBuf, /* output buffer */ + int16 *srcBuf, /* input buffer */ + int16 samples, /* samples to process */ + int16 Qx /* Q factor of filter coeffs */ +); + +void fir_process_general +( + firFilter *filter, /* fir filter struct */ + int16 *destBuf, /* output buffer */ + int16 *srcBuf, /* input buffer */ + int16 samples, /* samples to process */ + int16 Qx /* Q factor of filter coeffs */ +); + +int32 get_cur_cir_scale +( + anglePannerStruct panner /* angle panner struct */ +); + +void cir_scale_setup +( + anglePannerStruct *panner, /* angle panner struct */ + int32 newAngleL32Q16, /* new target angle */ + int32 rampSamples /* number of samples in the ramp */ +); + + + +/*--------------------------------- filters ---------------------------------*/ +void simple_lp_reset +( + simpleLPFilt *filter /* one-pole filter struct */ +); + +void simple_lp_proc +( + int16 *inplaceBuf, /* inplace buffer */ + simpleLPFilt *filter, /* one-pole filter struct */ + int16 samples /* number of sample to process */ +); + +void inplace_delay +( + int16 *inplaceBuf, /* inplace buffer */ + delaylineStruct *delayline, /* delayline struct */ + int32 samples /* number of samples to process */ +); + +void dscale_setup +( + pannerStruct *panner, /* panner struct */ + int16 newGainL16Q15, /* new target panner gain */ + int32 rampSamples, /* number of samples in the ramp */ + int32 newDelay /* delay of ramping process */ +); + +void IIR_one_pole(int16 *xin, int32 *yL40, int32 c0, int32 c1, int16 nsamples); +void buffer_crossmix_panner(int16 *dest, int16 *src1, int16 *src2, pannerStruct *panner, int32 samples); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _AUDIO_DSP_H_ */ + diff --git a/modules/cmn/common/utils/inc/audio_dsp32.h b/modules/cmn/common/utils/inc/audio_dsp32.h new file mode 100644 index 0000000..6ae9db0 --- /dev/null +++ b/modules/cmn/common/utils/inc/audio_dsp32.h @@ -0,0 +1,102 @@ +#ifndef AUDIO_DSP32_H +#define AUDIO_DSP32_H + +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +/*============================================================================ + @file audio_dsp32.h + + Header file for 32 bit basic mem manipulations and filters + +============================================================================*/ +#include "shared_lib_api.h" +#include "audio_dsp.h" +#include "audpp_mathlib.h" +#include "audpp_converter.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/*---------------------------------------------------------------------------- + Constants +----------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------- + Typdefs +----------------------------------------------------------------------------*/ +/* circular delay line (32 bit */ +typedef struct delayline32 { + int32 idx; // index to "now" + int32 buf_size; // delay buf size + int32 *buf; // buf pointer +} delayline32_t; + +/*---------------------------------------------------------------------------- + Fcuntion Prototypes +----------------------------------------------------------------------------*/ +/* buffers */ +void buffer32_empty_v2(int32 *dest, int32 samples); + +void buffer32_copy_v2(int32 *dest, int32 *src, int32 samples); + +void buffer32_copy16(int32 *dest, int16 *src, int32 samples); + +void buffer32_fill32(int32 *dest, int32 *src, int32 gain, int16 q_factor, int32 samples); + +void buffer32_mix32(int32 *dest, int32 *src, int32 gain, int16 q_factor, int32 samples); + +void buffer32_fill16(int32 *dest, int32 *src, int16 gain, int32 samples); + +void buffer32_mix16(int32 *dest, int32 *src, int16 gain, int32 samples); + +void buffer16_fill32(int16 *dest, int16 *src, int32 gain, int16 q_factor, int32 samples); + +void buffer32_fill16src(int32 *dest, int16 *src, int16 gain, int16 q_factor, int32 samples); + +void buffer32_mix16src(int32 *dest, int16 *src, int16 gain, int16 q_factor, int32 samples); + +void buffer32_fill_panner(int32 *dest, int32 *src, pannerStruct *panner, int32 samples); + +void buffer32_mix_panner(int32 *dest, int32 *src, pannerStruct *panner, int32 samples); + +void buffer32_crossmix_panner(int32 *dest, int32 *src1, int32 *src2, pannerStruct *panner, int32 samples); + +void buffer_delay_mix32 +( + int32 *destBuf, /* output buffer */ + int32 *srcBuf, /* input buffer */ + int16 gainL16Q15, /* gain to the samples */ + delayline32_t *delayline, /* delayline struct */ + int32 delay, /* amount of delay in sample */ + int32 samples /* number of samples to process */ +); + +void buffer_delay_fill32(int32 *destBuf, int32 *srcBuf, int16 gainL16Q15, delayline32_t *delayline, int32 delay, int32 samples); + + +/* delayline */ +void delayline32_set(delayline32_t *delayline, int32 delayLen); + +void delayline32_reset(delayline32_t *delayline); + +void delayline32_update(delayline32_t *delayline, int32 *src, int32 samples); + +void delayline32_inplace_delay(int32 *inplace_buf, delayline32_t *delayline, int32 samples); + +void delayline32_read(int32 *dest, int32 *src, delayline32_t *delayline, int32 delay, int32 samples); + +void delayline32_panner_mix_out(int32 *dest, int32 *src, delayline32_t *delayline, pannerStruct *panner, int32 delay, int32 samples); + +void delayline32_panner_mix_in(delayline32_t *delayline, int32 *src, pannerStruct *panner, int32 delay, int32 samples); + +void delayline32_copy ( delayline32_t *dest, delayline32_t *source); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* AUDIO_DSP32_H */ diff --git a/modules/cmn/common/utils/inc/audio_exp10.h b/modules/cmn/common/utils/inc/audio_exp10.h new file mode 100644 index 0000000..056dcb3 --- /dev/null +++ b/modules/cmn/common/utils/inc/audio_exp10.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +/** +@file audio_exp10.h + +This file contains declaration for exponential routine. +*/ + +/*=========================================================================== +NOTE: The @brief description and any detailed descriptions above do not appear + in the PDF. + + The elite_audio_mainpage.dox file contains all file/group descriptions + that are in the output PDF generated using Doxygen and Latex. To edit or + update any of the file/group text in the PDF, edit the + elite_audio_mainpage.dox file or contact Tech Pubs. +===========================================================================*/ + +#ifndef _EXP10_H_ +#define _EXP10_H_ + +#include "audio_comdef.h" + +/*============================================================================= + Function Declarations +=============================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup math_operations +@{ */ + +/** + Exponential function. It computes 10^x for a given value of x. + + @param[in] x Exponent. + + @return + int32_t 10^x. + + @dependencies + None. +*/ +int32_t exp10_fixed(int32_t x); + +/** @} */ /* end_addtogroup math_operations */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /*_EXP10_H_*/ + diff --git a/modules/cmn/common/utils/inc/audio_fft_basic_ops.h b/modules/cmn/common/utils/inc/audio_fft_basic_ops.h new file mode 100644 index 0000000..2800905 --- /dev/null +++ b/modules/cmn/common/utils/inc/audio_fft_basic_ops.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _AUDIO_FFT_BASIC_OPS_H +#define _AUDIO_FFT_BASIC_OPS_H + +#if defined( __qdsp6__) +#include "hexagon_protos.h" +#endif + +#ifdef SPKR_PROT_UNIT_TEST + +#include "comdef.h" +#include "basic_op.h" +#include "complex_basic_op.h" +#include "basic_math.h" + +#else +/** + * for elite builds, use basic ops from AudioCmdLib instead of tst/QC_BASIC_Lib + */ + +#include "posal_types.h" +#include "audio_basic_op.h" +#include "audio_basic_op_ext.h" +#include "basic_math.h" + +#endif + +#include "audio_complex_basic_op.h" + +/** + * below wordxx declarations are not done in audCmdLib. + */ +typedef int16 Word16; +typedef uint16 UNS_Word16; +typedef int32 Word32; +typedef uint32 UWord32; +typedef double Word40; // 40-bit Long Signed integer +typedef int64 Word64; + +typedef int32 CWord2x16; +typedef int64 CWord2x32; +/** + * basic op aliases. + */ + +//#define MAX_16 +#define shr s16_shr_s16_sat +#define shl s16_shl_s16_sat +#define max_16 s16_max_s16_s16 +#define l_add s32_add_s32_s32_sat +//mult1_32_32_round -> defined below. +#define saturate s16_saturate_s32 +#define L_shr s32_shr_s32_sat +#define l_mac_sat(z,x,y) (Q6_R_mpyacc_RlRl_sat((z),(x),(y))) +#define add s16_add_s16_s16_sat +#define sub s16_sub_s16_s16_sat +//#define div_int32 +//#define sqrt_compute +//#define div_s +#define l64_shift s64_shl_s64 +#define extract_h s16_extract_s32_h +#define extract_l(x) ((Word16)(x)) +#define l_saturate s32_saturate_s64 +#define mult_r(x, y) ((Word16)Q6_R_vmpyh_RR_s1_rnd_sat( (x), (y) )) +#define add_sat s16_add_s16_s16_sat +#define S_saturate s16_saturate_s32 + +#define L_real(x) ((Word32)(x)) +#define L_imag(x) ((Word32)((x)>>32)) + +#define abs_s(x) ((Word16)Q6_R_sath_R(Q6_R_abs_R( (x) ))) + +#define L_complex(xr,xi) (Q6_P_combine_RR( (xi), (xr) )) + +//#define cmult32_real16_0 -> defined below. + +#define min_32 s32_min_s32_s32 + +#define l64_add_s64_s64 s64_add_s64_s64 +#define l_add_sat s32_add_s32_s32_sat +#define l_sub s32_sub_s32_s32_sat +#define max_32 s32_max_s32_s32 + +#define l_abs u32_abs_s32_sat + +#define negate s16_neg_s16_sat + +#define min_16 s16_min_s16_s16 + +#define norm_l(x) (Q6_R_normamt_R( (x) )) +#define L_shl s32_shl_s32_sat +#define round32_16 s16_round_s32_sat +#define Mpy_32_16_round s32_mult_s32_s16_rnd_sat + +#define L_negate s32_neg_s32_sat + +#define L_saturate(dvar1) (Q6_R_sat_P(dvar1)) +#define L_add(x, y) (Q6_R_add_RR_sat( (x), (y) )) +#define extract_l(x) ((Word16)(x)) +/** + @brief Multiply a 64-bit complex number with 16-bit real number (fractional integer). + Assumes shift is 0. + @param var1: [in] Input 64-bit complex number + @param var2: [in] Input 16-bit real number + + @return (var1*var2) << (1) + */ +static inline CWord2x32 cmult32_real16_0( CWord2x32 var1, int16 var2) +{ + return c64_mult_c64_s16_shift_sat(var1, var2, 0); +} + +static inline int32 L_shift_r_s(int32 L_var1, int16 var2) +{ + int32 L_Out, L_rnd; + + if (var2 < -31) + { + L_Out = 0; + } + else if (var2 < 0) + { + /* right shift */ + L_rnd = L_shl(L_var1, (int16)(var2 + 1)) & 0x1; + L_Out = l_add_sat(L_shl(L_var1, var2), L_rnd); + } + else + { + L_Out = L_shl(L_var1, var2); + } + + return (L_Out); +} + +static inline int32 mult1_32_32_round ( + int32 wL32, + int32 xL32, + int16 iqL16 + ) +{ + + int32 outL32; + int16 w_exp, wL16; + + w_exp = s16_norm_s32(wL32); // Compute norm of the first input + wL32 = L_shl(wL32, w_exp); // Normalize the first input + wL16 = round32_16(wL32); // Round the first input to 16 bits + + outL32 = (int32 )Mpy_32_16_round(xL32, wL16); // Perform 32 x 16 multiplication + outL32 = L_shift_r_s(outL32, iqL16-w_exp); // De-normalize the result + + return outL32; +} + +#endif //_FBSP_BASIC_OPS_H diff --git a/modules/cmn/common/utils/inc/audio_iir_tdf2.h b/modules/cmn/common/utils/inc/audio_iir_tdf2.h new file mode 100644 index 0000000..65a05cc --- /dev/null +++ b/modules/cmn/common/utils/inc/audio_iir_tdf2.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +/** +@file audio_iir_tdf2.h + +This file contains IIR TDF2 function definition. +*/ + +/*=========================================================================== +NOTE: The @brief description and any detailed descriptions above do not appear + in the PDF. + + The elite_audio_mainpage.dox file contains all file/group descriptions + that are in the output PDF generated using Doxygen and Latex. To edit or + update any of the file/group text in the PDF, edit the + elite_audio_mainpage.dox file or contact Tech Pubs. +===========================================================================*/ + +#ifndef _AUDIO_IIRTDF2_H_ +#define _AUDIO_IIRTDF2_H_ + +#include "posal_types.h" + +/*============================================================================= + Function Declarations +=============================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GUARD_BITS_16 3 + +/** @addtogroup dsp_algorithms +@{ */ + +/** + IIR Transposed Direct Form 2 (TDF2) function. + + @param[in] inp Input buffer. + @param[out] out Output buffer. + @param[in] samples Number of samples. + @param[in] numcoefs Numerator coefficients. + @param[in] dencoefs Denominator coefficients. + @param[in] mem Memory. + @param[in] shiftn Numerator shift. + @param[in] shiftd Denominator shift. + + @return + Interger value indicating success or failure. + + @dependencies + None. +*/ +int iirTDF2( int16_t *inp, + int16_t *out, + uint16_t samples, + int32_t *numcoefs, + int32_t *dencoefs, + int32_t *mem, + int16_t shiftn, + int16_t shiftd); + +/** @} */ /* end_addtogroup dsp_algorithms */ + +void iirTDF2_32(int32 *inp, + int32 *out, + int32 samples, + int32 *numcoefs, + int32 *dencoefs, + int64 *mem, + int16 shiftn, + int16 shiftd); + +void iirTDF2_16(int16 *inp, + int16 *out, + int32 samples, + int32 *numcoefs, + int32 *dencoefs, + int64 *mem, + int16 shiftn, + int16 shiftd); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _IIRTDF2_H_ */ diff --git a/modules/cmn/common/utils/inc/audio_log10.h b/modules/cmn/common/utils/inc/audio_log10.h new file mode 100644 index 0000000..d8a24aa --- /dev/null +++ b/modules/cmn/common/utils/inc/audio_log10.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +/** +@file audio_log10.h + +This file contains ASM data commands and events structures definitions. +*/ + +/*=========================================================================== +NOTE: The @brief description and any detailed descriptions above do not appear + in the PDF. + + The elite_audio_mainpage.dox file contains all file/group descriptions + that are in the output PDF generated using Doxygen and Latex. To edit or + update any of the file/group text in the PDF, edit the + elite_audio_mainpage.dox file or contact Tech Pubs. +===========================================================================*/ + +#ifndef _LOG10_H_ +#define _LOG10_H_ + +#include "audio_comdef.h" + +/*============================================================================= + Function Declarations +=============================================================================*/ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup math_operations +@{ */ + +/** + Fixed point function for log10. It calculates 10*log10(input) using a table + lookup for log2(input): 10*log10(input)=3.0103*log2(input). + + @param[in] input Input value. + + @return + Signed 32-bit value 10log10(input). + + @dependencies + None. +*/ +int32_t log10_fixed(int32_t input); /* computes 10log10 of int32_t input */ + +/** @} */ /* end_addtogroup math_operations */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif //_LOG10_H_ + diff --git a/modules/cmn/common/utils/inc/audpp_common.h b/modules/cmn/common/utils/inc/audpp_common.h new file mode 100644 index 0000000..99aba1a --- /dev/null +++ b/modules/cmn/common/utils/inc/audpp_common.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +/***************************************************************************** + * FILE NAME: audpp_common.h TYPE: C-header file + * DESCRIPTION: Common types and constants for post processor. + *****************************************************************************/ +#ifndef _COMMON_H_ +#define _COMMON_H_ + +/*============================================================================= + Constants +=============================================================================*/ +/* main */ +#define BIT_EXACT_WITH_QSOUND 0 // to match QSound or not +#define REAL_TIME 0 // simulate changing parameters on the fly +#define FIFO_SIZE (2*1920) // FIFO buffer size (16-bit words), + // set to even for stereo!! + +/* untouched interface value */ +#define EMPTY_VALUE (0xFFFFFFFF) // invalid interface value to mark init state + +/* block size */ // currently, every module uses same size +#define BLOCKSIZE 480 + +/* chorus */ +#define CHORUS_GAME_AUDIO 1 // per-object chorus for game audio +#define CHORUS_MIDI (!CHORUS_GAME_AUDIO) // global chorus for MIDI + +/* reverb */ +#define REVERB_HARDCODE_DELAYS 0 // load comb and all-pass delays from table +#define REVERB_LP_POST_BIQUAD 0 // low-power reverb post filter + +/*============================================================================= + Typedefs +=============================================================================*/ +typedef enum +{ + MONO= 1, + STEREO = 2, + SUB_SURROUND = 4 +} ChannelMode; + +typedef enum +{ + PPSUCCESS=0, + PPFAILURE, + PPERR_DELAY_NEGATIVE, + PPERR_DELAY_INVALID, + PPERR_FILTER_INVALID_TAPCOUNT, + PPERR_FILTER, + PPERR_RATECONVERTER, + PPERR_PANNERCONVERT_OVERFLOW, + PPERR_PANNERCONVERT_INVALID, + PPERR_RATECONVERTER_INVALID_INDEX, + PPERR_STEREOCONVERTER, + PPERR_IIR_OVERFLOW, + PPERR_EQ_INVALID_NUMBANDS, +} PPStatus; + +// output type +typedef enum +{ + OUTPUT_NULL = -1, + OUTPUT_SPEAKERS = 0, + OUTPUT_HEADPHONE +} outputtype; + +// geometries for speaker mode +typedef enum +{ + SPEAKER_GEOMETRY_NULL = -1, + SPEAKER_GEOMETRY_DESKTOP = 0, + SPEAKER_GEOMETRY_FRONT, + SPEAKER_GEOMETRY_SIDE +} spkgeometry; + +/*============================================================================= + Interface Parameters +=============================================================================*/ + +#endif /* _COMMON_H_ */ diff --git a/modules/cmn/common/utils/inc/audpp_converter.h b/modules/cmn/common/utils/inc/audpp_converter.h new file mode 100644 index 0000000..7467f66 --- /dev/null +++ b/modules/cmn/common/utils/inc/audpp_converter.h @@ -0,0 +1,175 @@ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ +/***************************************************************************** + * FILE NAME: audpp_converter.h TYPE: C-header file + * DESCRIPTION: Header file for rate conversion types and fns. + *****************************************************************************/ +#ifndef _CONVERTER_H_ +#define _CONVERTER_H_ + +#include "audpp_mathlib.h" +#include "audpp_common.h" +#include "audio_basic_op_ext.h" + +#define NORMAL_PLAYBACK_RATE 0x10000 +#define CIRC_MAX_SIZE 32767 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/*============================================================================= + Typedefs +=============================================================================*/ +typedef struct _rateConvertState +{ + int32 indexL32Q16; /* current sample index, Q16 */ + int32 rateL32Q16; /* current playback rate, Q16 */ + int32 accelL32Q16; /* per-sample change in rate */ +} rateConvertState; + +typedef struct _ratePanner +{ + int32 targetRateL32Q16; /* desired final playback rate */ + int32 rateChangeL32Q16; /* change from prev rate */ + int32 sampleCounter; /* samples left in rate change */ + int32 stepRateL32Q16; /* 1/steps in Q16 */ + int32 indexL32Q16; /* current index in conversion */ +} ratePanner; + +typedef struct _stereoRateConverter +{ + ratePanner panner; /* rate panner struct */ + int16 oldInputs[6]; /* 3 interleaved stereo samples */ +} stereoRateConverter; + +typedef struct _bufRateCvtrStruct +{ + rateConvertState srcState; /* rate convert state */ + int16 oldInputs[2]; /* previous inputs for interp */ +} bufRateCvtrStruct; + +/*============================================================================= + Function Declarations +=============================================================================*/ +/*----------------------------- rate convert state --------------------------*/ +int32 rateConvertState_predict_inputs +( + rateConvertState *state, /* rate convert state struct */ + int32 outputs /* output sample number */ +); + +void rateConvertState_update_index +( + rateConvertState *state, /* rate convert state struct */ + int32 *inputs /* input sample number */ +); + +/*-------------------------------- rate panner ------------------------------*/ +void ratepanner_reset +( + ratePanner *panner /* rate panner struct */ +); + +uint32 ratepanner_get_playback_rate +( + ratePanner *panner /* rate panner struct */ +); + +uint32 ratepanner_set_playback_rate +( + ratePanner *panner, /* rate panner struct */ + int32 newRateL32Q16, /* target rate */ + int32 steps /* steps to reach target rate */ +); + +void ratepanner_setup_convert +( + ratePanner *panner, /* rate panner struct */ + rateConvertState *srcState /* rate convert state struct */ +); + +uint32 ratepanner_advance +( + ratePanner *panner, /* rate panner struct */ + int32 outputs /* output sample numer */ +); + +int32 ratepanner_predict_inputs +( + ratePanner *panner, /* rate panner struct */ + int32 outputs /* output sample number */ +); + + +/*------------------------------- rate conversion ---------------------------*/ +int32 convert_linear +( + int16 *destBuf, /* output buffer */ + int32 outputs, /* number of output samples */ + int16 *srcBuf, /* input buffer */ + int32 inputs, /* number of input samples */ + rateConvertState *state /* buffer converter state */ +); + +int32 convert_rate_stereo +( + int16 *destBufL, /* output buffer L */ + int16 *destBufR, /* output buffer R */ + int32 outputs, /* number of output samples */ + int16 *srcBuf, /* input buffer */ + int32 inputs, /* number of input samples */ + rateConvertState *state /* buffer converter state */ +); + +uint32 stereo_converter_convert +( + stereoRateConverter *converter, /* stereo rate converter struct */ + int16 *destBufL, /* output buffer L */ + int16 *destBufR, /* output buffer R */ + int32 *outputs, /* output sample number */ + int16 *srcBuf, /* input buffer */ + int16 channels, /* number of channels */ + int32 *inputs, /* input sample number */ + int32 start /* offset in buffer to start */ +); + +int32 stereo_converter_switch_to_bypass +( + stereoRateConverter *converter, /* stereo rate converter struct */ + int16 *destBufL, /* output buffer L */ + int16 *destBufR, /* output buffer R */ + int16 channel, /* number of channels */ + int32 n, /* number of samples to process */ + int32 start /* offset in buffer to start */ +); + +/*-------------------------- buffer rate converter --------------------------*/ +void buf_rate_converter_reset +( + bufRateCvtrStruct *converter /* buffer rate converter struct */ +); + +void buf_rate_converter_setrate +( + bufRateCvtrStruct *converter, /* buffer rate converter struct */ + int32 playbackRateL32Q16/* playback rate in Q16 */ +); + +uint32 buf_rate_converter_convert +( + int16 *destBuf, /* output buffer */ + int32 *outputsPtr, /* -> output number of samples */ + int16 *srcBuf, /* input buffer */ + int32 *inputsPtr, /* -> input number of samples */ + bufRateCvtrStruct *converter /* buffer rate converter struct */ +); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _CONVERTER_H_ */ + diff --git a/modules/cmn/common/utils/inc/audpp_mathlib.h b/modules/cmn/common/utils/inc/audpp_mathlib.h new file mode 100644 index 0000000..65ac892 --- /dev/null +++ b/modules/cmn/common/utils/inc/audpp_mathlib.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ +/***************************************************************************** + * FILE NAME: audpp_mathlib.h TYPE: C-header file + * DESCRIPTION: Contains constants and math function decalarations for + * post processor. + *****************************************************************************/ +#ifndef _MATHLIB_H +#define _MATHLIB_H + +#ifdef __XTENSA__ +#include "../audioss/audio_basic_op.h" +#else +#include "audio_basic_op.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/*============================================================================= + Constants +=============================================================================*/ +#define Q8_ONE ((int16)256) +#define Q9_ONE ((int16)512) +#define Q13_HALF ((int16)4096) + +#define Q15_ZERO ((int16)0) +#define Q15_ONE ((int16)32767) +#define Q15_MINUSONE ((int16)(-32768)) +#define Q15_HALF ((int16)16384) +#define Q15_atten3db ((int16)23170) + +#define Q16_ONE (65536L) // (1L<<16) +#define Q16_HALF (32768L) +#define Q16_TWO ((int32)0x20000L) + +#define Q23_ONE (8388608L ) // (1L<<23) +#define Q23_MINUSONE (-8388608L) // (-1L<<23) +#define Q23_PI_2 (13176795L) +#define Q23_PI (26353589L) +#define Q23_PI3_2 (39530384L) +#define Q23_TWOPI (52707179L) +#undef LONG_MAX +#define LONG_MAX (2147483647L) /* maximum (signed) long value */ +#define Q23_MAX LONG_MAX + +#define Q31_ONE ((int32)0x7fffffffL) + +/*============================================================================= + Q15 math +=============================================================================*/ +int16 Q15_initRatio(int32 numer, int32 denom); +int16 Q15_initQ16(int32 initL32Q16); +int16 Q15_initQ23(int32 initL32Q16); +int16 Q15_mult (int16 aL16Q15, int16 bL16Q15); +void Q15_multBy(int16 *aL16Q15, int16 bL16Q15); +int16 Q15_mult3 (int16 aL16Q15, int16 bL16Q15, int16 cL16Q15); +int16 Q15_scaleByInt16(int16 scaleL16Q15, int16 xL16); +int16 Q15_negate(int16 scaleL16Q15); +int16 Q15_initMB(int16 mB); +int16 Q15_getMB(int16 scaleL16Q15); + +/*============================================================================= + Q16 math +=============================================================================*/ +int32 Q16_mult(int32 aL16Q16, int32 bL16Q16); +int32 Q16_square(int32 x); +int32 Q16_sqrt(int32 x); +int32 Q16_reciprocal(int32 denom); +int32 Q16_reciprocalU(int32 denom); +int32 Q16_divide_truncated(int32 numerL32Q16, int32 denomL32Q16); + +/*============================================================================= + Q23 math +=============================================================================*/ +int32 Q23_initMB(int16 mB); +int32 Q23_mult(int32 aL32Q23, int32 bL32Q23); +int32 Q23_square(int32 xL32Q23); +int32 Q23_sqrt(int32 xL32Q23); +int32 Q23_reciprocal(int32 divisor); +int32 Q23_reciprocalU(int32 divisor); +int32 Q23_sine(int32 radiansL32Q23); +int32 Q23_cosine(int32 radiansL32Q23); +int32 Q23_sine0(int32 radiansL32Q23); + // fast version (no normalization of argument), so 0<=radians= 0 ; i-- ) { + if ((var & ( 0x01 << i )) == 0) { + count++; + } + else { + break; + } + } + return count; +} + +#endif //#if !defined(s32_cl0_s32) + +#if !defined(s32_cl1_s64) +int32_t s32_cl1_s64(int64 var) +{ + int32_t count = 0; + int16_t i; + + for ( i = 63 ; i >= 0 ; i-- ) { + if ((var & ( 0x01LL << i )) != 0) { + count++; + } + else { + break; + } + } + return count; +} +#endif //#if !defined(s32_cl1_s64) + +#if !defined(s32_cl0_s64) +int32_t s32_cl0_s64(int64 var) +{ + int32_t count = 0; + int16_t i; + + for ( i = 63 ; i >= 0 ; i-- ) { + if ((var & ( 0x01LL << i )) == 0) { + count++; + } + else { + break; + } + } + return count; +} +#endif //#if !defined(s32_cl0_s64) + +#if !defined(s16_imag_s32) +/*************************************************************************** + * + * FUNCTION NAME: s16_imag_s32 + * + * PURPOSE: + * + * extract imagine from the complex + * + * INPUTS: + * + * var1 + * complex + * + * OUTPUTS: + * + * none + * + * RETURN VALUE: + * + * L_Out + * imagine + * + * IMPLEMENTATION: + * extract imagine s16_extract_s32_h + * + * KEYWORDS: imagine, imag, complex + * + *************************************************************************/ +int16_t s16_imag_s32(int32_t vari) +{ + return s16_extract_s32_h(vari); +} /* End of s16_imag_s32 function */ +#endif //#if !defined(s16_imag_s32) + +#if !defined(s32_imag_s64) +/*************************************************************************** + * + * FUNCTION NAME: s32_imag_s64 + * + * PURPOSE: + * + * extract imagine from the complex + * + * INPUTS: + * + * var1 + * complex + * + * OUTPUTS: + * + * none + * + * RETURN VALUE: + * + * L_Out + * imagine + * + * IMPLEMENTATION: + * extract imagine from the complex + * + * KEYWORDS: imagine, imag, complex + * + *************************************************************************/ +int32_t s32_imag_s64(int64 vari) +{ + return (int32_t)(vari >> 32); +} /* End of s32_imag_s64 function */ +#endif //#if !defined(s32_imag_s64) + +#if !defined(s16_real_s32) +/*************************************************************************** + * + * FUNCTION NAME: s16_real_s32 + * + * PURPOSE: + * + * extract real from the complex + * + * INPUTS: + * + * var1 + * complex + * + * OUTPUTS: + * + * none + * + * RETURN VALUE: + * + * L_Out + * real + * + * IMPLEMENTATION: + * extract real s16_extract_s32_l + * + * KEYWORDS: real, complex + * + *************************************************************************/ +int16_t s16_real_s32(int32_t varr) +{ + return s16_extract_s32_l(varr); +} /* End of s16_real_s32 function */ +#endif //#if !defined(s16_real_s32) + +#if !defined(s32_real_s64) +/*************************************************************************** + * + * FUNCTION NAME: s32_real_s64 + * + * PURPOSE: + * + * extract real from the complex + * + * INPUTS: + * + * var1 + * complex + * + * OUTPUTS: + * + * none + * + * RETURN VALUE: + * + * L_Out + * real + * + * IMPLEMENTATION: + * extract real s16_extract_s32_l + * + * KEYWORDS: real, complex + * + *************************************************************************/ +int32_t s32_real_s64(int64 varr) +{ + return (int32_t)(varr); +} /* End of s32_real_s64 function */ +#endif //#if !defined(s32_real_s64) + +#if !defined(s32_complex_s16_s16) +/*************************************************************************** + * + * FUNCTION NAME: s32_complex_s16_s16 + * + * PURPOSE: + * + * pack real and imagine into a complex + * + * INPUTS: + * + * var1 + * real + * var2 + * imagine + * + * OUTPUTS: + * + * none + * + * RETURN VALUE: + * + * L_Out + * complex + * + * IMPLEMENTATION: + * pack real and imagine + * + * KEYWORDS: pack, complex + * + *************************************************************************/ +int32_t s32_complex_s16_s16(int16_t varr, int16_t vari) +{ + int32_t resultL32 = 0x00; + resultL32 |= ((vari & 0x0000FFFF) << 16); + resultL32 |= (varr & 0x0000FFFF); + + return resultL32; +} /* End of s32_complex_s16_s16 function */ +#endif //#if !defined(s32_complex_s16_s16) + +#if !defined(s64_complex_s32_s32) +/*************************************************************************** + * + * FUNCTION NAME: s64_complex_s32_s32 + * + * PURPOSE: + * + * pack real and imagine into a complex + * + * INPUTS: + * + * var1 + * real + * var2 + * imagine + * + * OUTPUTS: + * + * none + * + * RETURN VALUE: + * + * L_Out + * complex + * + * IMPLEMENTATION: + * pack real and imagine + * + * KEYWORDS: pack, complex + * + *************************************************************************/ +int64 s64_complex_s32_s32(int32_t varr, int32_t vari) +{ + long long c; + long long xr64 = (long long) varr; + long long xi64 = (long long) vari; + + c = xr64 & (0xFFFFFFFFLL); + c |= (xi64 << 32); + + return( c ); +} /* End of s64_complex_s32_s32 function */ +#endif //#if !defined(s64_complex_s32_s32) + +#if !defined(s64_complex_add_s64_s64) +/*************************************************************************** + * + * FUNCTION NAME: s64_complex_add_s64_s64 + * + * PURPOSE: + * + * add complex + * + * INPUTS: + * + * var1 + * complex + * var2 + * complex + * + * OUTPUTS: + * + * none + * + * RETURN VALUE: + * + * L_Out + * complex + * + * IMPLEMENTATION: + * add complex + * + * KEYWORDS: add, complex + * + *************************************************************************/ +int64 s64_complex_add_s64_s64(int64 var1, int64 var2) +{ + int64 var1r = (int64) s32_real_s64(var1); + int64 var2r = (int64) s32_real_s64(var2); + int64 var1i = (int64) s32_imag_s64(var1); + int64 var2i = (int64) s32_imag_s64(var2); + + return s64_complex_s32_s32((int32_t)(var1r + var2r), (int32_t)(var1i + var2i)); + +} /* End of s64_complex_add_s64_s64 function */ +#endif //#if !defined(s64_complex_add_s64_s64) + +#if !defined(s64_complex_sub_s64_s64) +/*************************************************************************** + * + * FUNCTION NAME: s64_complex_sub_s64_s64 + * + * PURPOSE: + * + * sub complex + * + * INPUTS: + * + * var1 + * complex + * var2 + * complex + * + * OUTPUTS: + * + * none + * + * RETURN VALUE: + * + * L_Out + * complex + * + * IMPLEMENTATION: + * sub complex + * + * KEYWORDS: sub, complex + * + *************************************************************************/ +int64 s64_complex_sub_s64_s64(int64 var1, int64 var2) +{ + int64 var1r = (int64) s32_real_s64(var1); + int64 var2r = (int64) s32_real_s64(var2); + int64 var1i = (int64) s32_imag_s64(var1); + int64 var2i = (int64) s32_imag_s64(var2); + + return s64_complex_s32_s32((int32_t)(var1r-var2r), (int32_t)(var1i - var2i)); + +} /* End of s64_complex_sub_s64_s64 function */ +#endif //#if !defined(s64_complex_sub_s64_s64) + + +#if !defined(s32_complex_mult_rnd_sat_s32_s32) +/*************************************************************************** +* +* FUNCTION NAME: s32_complex_mult_rnd_sat_s32_s32 +* +* PURPOSE: +* +* Perform multiplication of two complex with high16 packing. +* +* INPUTS: +* +* var1 +* complex +* var2 +* complex +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* complex +* +* IMPLEMENTATION: +* Multiply two complex with high 16bit packing. +* +* KEYWORDS: multiply, mult, mpy, complex +* +*************************************************************************/ +int32_t s32_complex_mult_rnd_sat_s32_s32( int32_t var1, int32_t var2 ) +{ + int16_t tempImagL16, tempRealL16; + int32_t resultL32 = 0x00; + + tempImagL16 = s16_extract_s32_h(s32_saturate_s64( + (s64_mult_s32_s32(s16_imag_s32(var1),s16_real_s32(var2)) << 1) + + (s64_mult_s32_s32(s16_imag_s32(var2),s16_real_s32(var1)) << 1) + + 0x8000)); + tempRealL16 = s16_extract_s32_h(s32_saturate_s64( + (s64_mult_s32_s32(s16_real_s32(var1),s16_real_s32(var2)) << 1) + - (s64_mult_s32_s32(s16_imag_s32(var1),s16_imag_s32(var2)) << 1) + + 0x8000)); + + + resultL32 |= tempImagL16 << 16; + resultL32 |= (int32_t)tempRealL16 & 0xFFFF; + + return resultL32; + +} /* End of s32_complex_mult_rnd_sat_s32_s32 function*/ +#endif //#if !defined(s32_complex_mult_rnd_sat_s32_s32) + +#if !defined(s32_complex_conjugate_sat_s32) +/*************************************************************************** +* +* FUNCTION NAME: s32_complex_conjugate_sat_s32 +* +* PURPOSE: +* +* Perform conjugate of complex. +* +* INPUTS: +* +* var1 +* complex +* var2 +* complex +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* complex +* +* IMPLEMENTATION: +* conjugate complex. a + bi -> a + bi, a - bi -> a + bi +* +* KEYWORDS: conjugate, conj, complex +* +*************************************************************************/ +int32_t s32_complex_conjugate_sat_s32( int32_t var1) +{ + int32_t resultL32 = 0x00; + int16_t tempRealL16 = s16_real_s32(var1); + int16_t tempImagL16 = s16_imag_s32(var1); + + resultL32 |= s16_neg_s16_sat(tempImagL16) << 16; + resultL32 |= (int32_t)tempRealL16 & 0xFFFF; + + return resultL32; + +} /* End of s32_complex_conjugate_sat_s32 function*/ +#endif //#if !defined(s32_complex_conjugate_sat_s32) + +#if !defined(s64_complex_conjugate_sat_s64) +/*************************************************************************** +* +* FUNCTION NAME: s64_complex_conjugate_sat_s64 +* +* PURPOSE: +* +* Perform conjugate of complex. +* +* INPUTS: +* +* var1 +* complex +* var2 +* complex +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* complex +* +* IMPLEMENTATION: +* conjugate complex. a + bi -> a + bi, a - bi -> a + bi +* +* KEYWORDS: conjugate, conj, complex +* +*************************************************************************/ +int64 s64_complex_conjugate_sat_s64( int64 var1) +{ + return s64_complex_s32_s32(s32_real_s64(var1), s32_neg_s32_sat(s32_imag_s64(var1))); + +} /* End of s64_complex_conjugate_sat_s64 function*/ +#endif //#if !defined(s64_complex_conjugate_sat_s64) + +#if !defined(s64_complex_average_s64_s64) +/*************************************************************************** +* +* FUNCTION NAME: s64_complex_average_s64_s64 +* +* PURPOSE: +* +* Average two long complex numbers with convergent rounding +* +* INPUTS: +* +* var1 +* long complex +* var2 +* long complex +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* long complex +* +* IMPLEMENTATION: +* Average two long complex numbers with convergent rounding +* +* KEYWORDS: average, avg, complex +* +*************************************************************************/ +int64 s64_complex_average_s64_s64(int64 x, int64 y) +{ + int64 t64; + int32_t x32[2], y32[2]; + int32_t s32[2]; + int16_t i; + + x32[0] = s32_real_s64(x); + x32[1] = s32_imag_s64(x); + y32[0] = s32_real_s64(y); + y32[1] = s32_imag_s64(y); + + for (i=0; i < 2; i++) + { + t64 = s64_add_s32_s32(x32[i], y32[i]); + // convergent rounding + t64 = s64_add_s64_s64( t64, (int64)( ( ((int32_t)t64)>>1 ) & 1) ); + t64 = s64_shl_s64(t64, -1); + s32[i] = (int32_t) t64; + } + + return( s64_complex_s32_s32(s32[0], s32[1]) ); +} +#endif //#if !defined(s64_complex_average_s64_s64) + +#if !defined(s64_complex_neg_average_s64_s64_sat) +/*************************************************************************** +* +* FUNCTION NAME: s64_complex_neg_average_s64_s64_sat +* +* PURPOSE: +* +* Average two long complex numbers with convergent rounding +* +* INPUTS: +* +* var1 +* long complex +* var2 +* long complex +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* long complex +* +* IMPLEMENTATION: +* Average two long complex numbers with convergent rounding +* +* KEYWORDS: average, avg, complex +* +*************************************************************************/ +int64 s64_complex_neg_average_s64_s64_sat(int64 x, int64 y) +{ + int64 t64; + int32_t x32[2], y32[2]; + int32_t s32[2]; + int16_t i; + + x32[0] = s32_real_s64(x); + x32[1] = s32_imag_s64(x); + y32[0] = s32_real_s64(y); + y32[1] = s32_imag_s64(y); + + for (i=0; i < 2; i++) + { + t64 = s64_sub_s64_s64(x32[i], y32[i]); + // convergent rou nding + t64 = s64_add_s64_s64( t64, (int64)( ( ((int32_t)t64)>>1 ) & 1) ); + t64 = s64_shl_s64(t64, -1); + s32[i] = s32_saturate_s64(t64); + } + + return( s64_complex_s32_s32(s32[0], s32[1]) ); +} +#endif //#if !defined(s64_complex_neg_average_s64_s64_sat) + +#if !defined(s32_bitrev_s32) +int32_t s32_bitrev_s32(int32_t x, int32_t BITS) +{ + int i; + int32_t y = 0; + for ( i= 0; i>= 1; + } + return y; +} /*End of s32_bitrev_s32 function */ +#endif //#if !defined(s32_bitrev_s32) + +#if !defined(u64_mult_u32_u32) +/*************************************************************************** +* +* FUNCTION NAME: u64_mult_u32_u32 +* +* PURPOSE: +* +* Perform a multiplication of the two 32 bit input unsigned numbers +* Output a 64 bit unsigned number. +* +* INPUTS: +* +* var1 +* 32 bit unsigned integer (uint32_t) whose value +* falls in the range 0x0000 0000 <= var1 <= 0xffff ffff. +* var2 +* 32 bit unsigned integer (uint32_t) whose value +* falls in the range 0x0000 0000 <= var1 <= 0xffff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 64 bit long unsigned integer (uint64) whose value +* falls in the range +* 0x0000 0000 0000 0000 <= out <= 0xffff ffff ffff ffff. +* +* IMPLEMENTATION: +* +* Multiply the two 32 bit input numbers. +* +* KEYWORDS: multiply, mult, mpy +* +*************************************************************************/ +uint64 u64_mult_u32_u32(uint32_t var1,uint32_t var2) +{ + return (uint64) var1 * var2; +} +#endif //if !defined(u64_mult_u32_u32) + +#if !defined(s32_modwrap_s32_u32) +int32_t s32_modwrap_s32_u32(int32_t var1, uint32_t var2) { + if ( var1 < 0 ) { + return var1 + var2; + } + + else if ( (uint32_t)var1 >= var2 ) { + return var1 - var2; + } + + return var1; +} +#endif //#if !defined(s32_modwrap_s32_u32) +#if !defined(s64_mac_s32_s32) +int64 s64_mac_s32_s32(int64 mac64, int32_t var1, int32_t var2) +{ + int64 tmp64; + + tmp64 = (int64) var1 * var2; + + return (mac64 + tmp64); +} +#endif // #if !defined(s64_mac_s32_s32) + +#if !defined(s32_mac_s32_s32_s1_sat) +int32_t s32_mac_s32_s32_s1_sat(int32_t mac32, int32_t var1, int32_t var2) +{ + int64 tmp64; + + tmp64 = (int64) var1 * var2; + + tmp64 >>= 31; //(var1 * var2) << 1 + tmp64 += mac32; + + mac32 = s32_saturate_s64(tmp64); + + return (mac32); +} +#endif // #if !defined(s64_mac_s32_s32) + +#if !defined(s32_mac_s32_s32_s1_rnd_sat) +int32_t s32_mac_s32_s32_s1_rnd_sat(int32_t mac32, int32_t var1, int32_t var2) +{ + int64 tmp64; + tmp64 = (int64) var1 * var2; + tmp64 += (int64) (1<<30); + tmp64 >>= 31; //(var1 * var2) << 1 + tmp64 += mac32; + mac32 = s32_saturate_s64(tmp64); + return (mac32); +} +#endif // #if !defined(s32_mac_s32_s32_s1_rnd_sat) +#if !defined(s32_accu_asr_s32_imm5) +int32_t s32_accu_asr_s32_imm5(int32_t accu32, int32_t var, uint32_t u5) +{ + if (u5 < 32) + { + accu32 = accu32 + (var >> u5); + return (accu32); + } + else + { + return accu32; + } +} +#endif + +#if !defined(s32_accu_asl_s32_imm5) +int32_t s32_accu_asl_s32_imm5(int32_t accu32, int32_t var, uint32_t u5) +{ + if (u5 < 32) + { + accu32 = accu32 + (var << u5); + } + else + { + if (var > 0) + { + accu32 = s32_saturate_s64((int64) accu32 + MAX_32); + } + else + { + accu32 = s32_saturate_s64((int64) accu32 + MIN_32); + } + } + return (accu32); +} +#endif + +#if !defined(s32_set_bit_s32_s32) +int32_t s32_set_bit_s32_s32(int32_t var1, int32_t ind) +{ + return var1|(1<> 16); + // zRe = s32_saturate_s64(( + // (s64_mult_s32_s16(xRe, yRe) << 1) + // - (s64_mult_s32_s16(xIm, yIm) << 1) + // + 0x8000) >> 16); + zRe = s32_complex_mult_s32_s16_sat(xRe, yRe); + zIm = s32_complex_mult_s32_s16_sat(xIm, yRe); + + zRe = s32_add_s32_s32(zRe, s32_complex_mult_s32_s16_sat(s32_neg_s32_sat(xIm), yIm)); + + // to match Q6 definition of 32x16 MAC + if ((xRe == (int32_t)0x80000000L) && (yIm == (int16_t)0x8000)) + zIm = s32_saturate_s64((int64)zIm + 0x080000000LL); + else + zIm = s32_add_s32_s32(zIm, s32_complex_mult_s32_s16_sat(xRe, yIm)); + + return (s64_complex_s32_s32(zRe, zIm)); +} /* End of s64_complex_mult_rnd_sat_s64_s32 function*/ + +#if !defined(L_shift_r) +/*************************************************************************** + * + * FUNCTION NAME: L_shift_r + * + * PURPOSE: + * + * Shift and round. Perform a shift right. After shifting, use + * the last bit shifted out of the LSB to round the result up + * or down. Optimized for lower MIPS (11/12/10). + * + * INPUTS: + * + * L_var1 + * 32 bit long signed integer (int32_t) whose value + * falls in the range + * 0x8000 0000 <= L_var1 <= 0x7fff ffff. + * var2 + * 16 bit short signed integer (int16_t) whose value + * falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. + * + * OUTPUTS: + * + * none + * + * RETURN VALUE: + * + * L_var1 + * 32 bit long signed integer (int32_t) whose value + * falls in the range + * 0x8000 0000 <= L_var1 <= 0x7fff ffff. + * + * + * IMPLEMENTATION: + * + * Shift and round. Perform a shift right. After shifting, use + * the last bit shifted out of the LSB to round the result up + * or down. This is just like shift_r above except that the + * input/output is 32 bits as opposed to 16. + * + * if var2 is positve perform a arithmetic left shift + * with saturation (see l_shl() above). + * + * If var2 is zero simply return L_var1. + * + * If var2 is negative perform a arithmetic right shift (L_shr) + * of L_var1 by (-var2)+1. Add the LS bit of the result to + * L_var1 shifted right (L_shr) by -var2. + * + * Note that there is no constraint on var2, so if var2 is + * -0xffff 8000 then -var2 is 0x0000 8000, not 0x0000 7fff. + * This is the reason the l_shl function is used. + * + * + * KEYWORDS: + * + *************************************************************************/ +int32_t L_shift_r(int32_t L_var1, int16_t var2) +{ + int32_t L_Out, L_rnd; + int16_t swSign; + + // Simple shifting operation + L_Out = s32_shl_s32_sat(L_var1, var2); + + // Extract the sign of the shift amount + swSign = (var2 & 0x8000) >> 15; + + // Rounding operation for right shift + L_rnd = (s32_shl_s32_sat(L_var1, (int16_t)(var2+1))) & swSign; + + L_Out = s32_add_s32_s32_sat(L_Out, L_rnd); + +#ifdef WMOPS_FX + counter_fx.l_shl-=2; + counter_fx.l_add_sat--; + counter_fx.L_shift_r++; +#endif + + return (L_Out); +} +#endif //#if !defined(L_shift_r) diff --git a/modules/cmn/common/utils/src/audio_buffer.cpp b/modules/cmn/common/utils/src/audio_buffer.cpp new file mode 100644 index 0000000..9f6c83d --- /dev/null +++ b/modules/cmn/common/utils/src/audio_buffer.cpp @@ -0,0 +1,671 @@ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +/*===========================================================================*] +[* FILE NAME: audio_buffer.c *] +[* DESCRIPTION: *] +[* Functions regarding audio buffer *] +[* FUNCTION LIST : *] +[* buffer_empty: Set all samples in a buffer to zero *] +[* buffer_copy: Copy one buffer to another *] +[* buffer_fill: Apply L16Q15 gain to a buffer and store result in another *] +[* buffer_mix: Apply L16Q15 gain to a buffer and mix result into another *] +[*===========================================================================*/ +#include "audio_dsp.h" +#if defined CAPI_STANDALONE +#include "capi_util.h" +#elif defined APPI_EXAMPLE_STANDALONE +#include "appi_util.h" +#else +#include +#endif +#if ((defined __hexagon__) || (defined __qdsp6__)) +#include +#endif +/*===========================================================================*/ +/* FUNCTION : buffer_empty */ +/* */ +/* DESCRIPTION: Set all samples of a buffer to zero. */ +/* */ +/* INPUTS: buf-> buffer to be processed */ +/* samples: size of buffer */ +/* OUTPUTS: buf-> buffer to be processed */ +/* */ +/* IMPLEMENTATION NOTES: */ +/*===========================================================================*/ +void buffer_empty +( + int16 *buf, /* buffer to be processed */ + uint32 samples /* number of samples in this buffer */ +) +{ +#ifndef __qdsp6__ + uint32 i; + + for (i = 0; i < samples; i++) + { + *buf++ = 0; + } +#else + memset(buf, 0, samples<<1); +#endif +} /*---------------------- end of function buffer_empty ---------------------*/ + + +/*===========================================================================*/ +/* FUNCTION : buffer_copy */ +/* */ +/* DESCRIPTION: Copy from one buffer to another */ +/* */ +/* INPUTS: destBuf-> output buffer */ +/* srcBuf-> input buffer */ +/* samples: size of buffer */ +/* OUTPUTS: destBuf-> output buffer */ +/* */ +/* IMPLEMENTATION NOTES: */ +/*===========================================================================*/ +void buffer_copy +( + int16 *destBuf, /* output buffer */ + int16 *srcBuf, /* input buffer */ + uint32 samples /* number of samples to process */ +) +{ +#ifndef __qdsp6__ + uint32 i; + for (i = 0; i < samples; i++) + { + *destBuf++ = *srcBuf++; + } +#else + memscpy(destBuf, samples<<1, srcBuf, samples<<1); +#endif +} /*---------------------- end of function buffer_copy ----------------------*/ + +/*===========================================================================*/ +/* FUNCTION : buffer_fill */ +/* */ +/* DESCRIPTION: Apply L16Q15 gain to input and store the result to the */ +/* output buffer. */ +/* */ +/* INPUTS: destBuf-> output buffer */ +/* srcBuf-> input buffer */ +/* gainL16Q15: gain to be applied to the samples */ +/* samples: total number of samples to be processed */ +/* OUTPUTS: destBuf-> output buffer */ +/* */ +/* IMPLEMENTATION NOTES: */ +/* To match QSound code, there is no discussion of the gain values, */ +/* compared to function buffer_mix */ +/*===========================================================================*/ + +void buffer_fill +( + int16 *destBuf, /* output buffer */ + int16 *srcBuf, /* input buffer */ + int16 gainL16Q15, /* gain to the samples */ + uint32 samples /* number of samples to process */ +) +{ +#ifndef __qdsp6__ + uint32 i; + + if (gainL16Q15 == Q15_ONE) + { + for (i = 0; i < samples; i++) + { + *destBuf++ = *srcBuf++; + } + } + else if(gainL16Q15 == Q15_MINUSONE) + { + for (i = 0; i < samples; i++) + { + *destBuf++ = -*srcBuf++; + } + } + else + { + for (i = 0; i < samples; i++) + { + /* output = input * gain --*/ + *destBuf++ = s16_extract_s64_h_rnd(s40_mult_s16_s16_shift(*srcBuf++, gainL16Q15 ,1)); + } + } +#else + uint32 i, count, address; + + /************************************************************************/ + /* Following code process samples till destination buffer is 8 byte */ + /* Aligned. Destination buffer is made to take aligned address after */ + /* first three samples. This is avoid out of boundary memory access */ + /* that can happen on source buffer in assembly code. */ + /************************************************************************/ + address = (uint32)(&destBuf[0]); + //number of samples to make destination aligned. + count = 4 - ((address&6)>>1); + if (count < 3) + { + // destination is made to take next aligned address. + count = count + 4; + } + count = s32_min_s32_s32(samples, count); + + if (count) + { + if (gainL16Q15 == Q15_ONE) + { + for (i=count; i; i--) + { + *destBuf++ = *srcBuf++; + } + } + else if(gainL16Q15 == Q15_MINUSONE) + { + for (i=count; i; i--) + { + *destBuf++ = -*srcBuf++; + } + } + else + { + for (i=count; i; i--) + { + /* output = input * gain --*/ + *destBuf++ = s16_extract_s64_h_rnd(Q6_P_mpy_RlRl_s1(*srcBuf++, gainL16Q15)); + } + } + samples = samples-count; + } + + /***********************************************************************/ + /* To avoid out of boundary memory access on source buffer last 4 */ + /* samples are processed in c. In adition to these 4 samples, if sample*/ + /* count is not multiple of 4, remaining samples will also be processed*/ + /* in c code. */ + /***********************************************************************/ + /*assembly code will execute if number of samples >=12. Assembly code */ + /*processes samples in multiples of 4 and if any samples are left they */ + /*will be processed in c code. >= 12 is because of loop unrolling in */ + /* assembly to better pack pipe line. >=(12+4) condition is to account */ + /* last 4 samples that will be processed in c code */ + /***********************************************************************/ + if (samples>=(12+4)) + { + // last 4 samples will be processed in c to avoid out of boudary + // memory access on source pointers + samples = samples - 4; + + //assembly + buffer_fill_asm(destBuf, srcBuf, gainL16Q15, samples); + + /*update address pointers as they will not be updated in function */ + /* call */ + srcBuf = srcBuf + (samples&(~3)); + destBuf = destBuf + (samples&(~3)); + + // samples to be processed in c + samples = 4 + (samples&3); + } + + /*process remaining samples */ + if (samples) + { + if (gainL16Q15 == Q15_ONE) + { + for (i = samples; i; i--) + { + *destBuf++ = *srcBuf++; + } + } + else if(gainL16Q15 == Q15_MINUSONE) + { + for (i = samples; i; i--) + { + *destBuf++ = -*srcBuf++; + } + } + else + { + for (i = samples; i; i--) + { + /* output = input * gain --*/ + *destBuf++ = s16_extract_s64_h_rnd(Q6_P_mpy_RlRl_s1(*srcBuf++, gainL16Q15)); + } + } + } +#endif +} /*--------------------- end of function buffer_fill ----------------------*/ + +/*===========================================================================*/ +/* FUNCTION : buffer_mix */ +/* */ +/* DESCRIPTION: Apply L16Q15 gain to input and mix (sum) it into a running */ +/* output buffer. */ +/* */ +/* INPUTS: destBuf-> output buffer */ +/* srcBuf-> input buffer */ +/* gainL16Q15: gain to be applied to the samples */ +/* samples: total number of samples to be processed */ +/* OUTPUTS: destBuf-> output buffer */ +/* */ +/* IMPLEMENTATION NOTES: */ +/* To match QSound code, discuss gain value and divde into three cases */ +/* compared to function buffer_fill */ +/*===========================================================================*/ + +void buffer_mix +( + int16 *destBuf, /* output buffer */ + int16 *srcBuf, /* input buffer */ + int16 gainL16Q15, /* gain to the samples */ + uint32 samples /* number of samples to process */ +) +{ +#ifndef __qdsp6__ + uint32 i; + int16 tmpL16; + + /*-----------------------------------------------------------------------*/ + /*------------ case 1 : if gain is unity, directly sum ------------------*/ + if (gainL16Q15 == Q15_ONE) + { + for (i = 0; i < samples; i++) + { + /*-- output = output + input --*/ + *destBuf = s16_add_s16_s16_sat(*destBuf, *srcBuf++); + destBuf++; + } + } /* end of if (gainL16Q15 == Q15_ONE) */ + + /*------------ case 2 : if gain is minus unity, directly subtract -------*/ + else if (gainL16Q15 == Q15_MINUSONE) + { + for (i = 0; i < samples; i++) + { + /*-- output = output - input --*/ + *destBuf = s16_sub_s16_s16_sat(*destBuf, *srcBuf++); + destBuf++; + } + } /* end of else if (gainL16Q15 == Q15_MINUSONE) */ + + /*------------ case 3: normal case, with L16Q15 gain --------------------*/ + else + { + for (i = 0; i < samples; i++) + { + /*-- input * gain --*/ + tmpL16 = s16_extract_s64_h_rnd(s40_mult_s16_s16_shift(*srcBuf++, gainL16Q15 ,1)); + /*-- output = output + input * gain --*/ + *destBuf = s16_add_s16_s16_sat(*destBuf, tmpL16); + destBuf++; + } + } /* end of else */ +#else + uint32 i, count, address; + int16 tmpL16; + + /************************************************************************/ + /* Following code process samples till destination buffer is 8 byte */ + /* Aligned. Destination buffer is made to take aligned address after */ + /* first three samples. This is avoid out of boundary memory access */ + /* that can happen on source buffer in assembly code. */ + /************************************************************************/ + address = (uint32)(&destBuf[0]); + //number of samples to make destination aligned. + count = 4 - ((address&6)>>1); + if (count < 3) + { + // destination is made to take next aligned address. + count = count + 4; + } + count = s32_min_s32_s32(samples, count); + + if(count) + { + if (gainL16Q15 == Q15_ONE) + { + for (i=count; i; i--) + { + /*-- output = output + input --*/ + *destBuf = s16_add_s16_s16_sat(*destBuf, *srcBuf++); + destBuf++; + } + } /* end of if (gainL16Q15 == Q15_ONE) */ + + /*------------ case 2 : if gain is minus unity, directly subtract -------*/ + else if (gainL16Q15 == Q15_MINUSONE) + { + for (i=count; i; i--) + { + /*-- output = output - input --*/ + *destBuf = s16_sub_s16_s16_sat(*destBuf, *srcBuf++); + destBuf++; + } + } /* end of else if (gainL16Q15 == Q15_MINUSONE) */ + + /*------------ case 3: normal case, with L16Q15 gain --------------------*/ + else + { + for (i=count; i; i--) + { + /*-- input * gain --*/ + tmpL16 = s16_extract_s64_h_rnd(Q6_P_mpy_RlRl_s1(*srcBuf++, gainL16Q15)); + /*-- output = output + input * gain --*/ + *destBuf = s16_add_s16_s16_sat(*destBuf, tmpL16); + destBuf++; + } + } /* end of else */ + samples = samples-count; + } + + /***********************************************************************/ + /* To avoid out of boundary memory access on source buffer last 4 */ + /* samples are processed in c. In adition to these 4 samples, if sample*/ + /* count is not multiple of 4, remaining samples will also be processed*/ + /* in c code. */ + /***********************************************************************/ + /*assembly code will execute if number of samples >=8. Assembly code */ + /*processes samples in multiples of 4 and if any samples are left they */ + /*will be processed in c code. >= 8 is because of loop unrolling in */ + /* assembly to better pack pipe line. >=(8+4) condition is to account */ + /* last 4 samples that will be processed in c code */ + /***********************************************************************/ + if (samples>=(8+4)) + { + // last 4 samples will be processed in c to avoid out of boudary + // memory access on source pointers + samples = samples - 4; + + //assembly + buffer_mix_asm(destBuf, srcBuf, gainL16Q15, samples); + + /*update address pointers as they will not be updated in function */ + /* call */ + srcBuf = srcBuf + (samples&(~3)); + destBuf = destBuf + (samples&(~3)); + + // samples to be processed in c + samples = 4 + (samples&3); + } + + /*process remaining samples */ + if (samples) + { + if (gainL16Q15 == Q15_ONE) + { + for (i=samples; i; i--) + { + /*-- output = output + input --*/ + *destBuf = s16_add_s16_s16_sat(*destBuf, *srcBuf++); + destBuf++; + } + } /* end of if (gainL16Q15 == Q15_ONE) */ + + /*------------ case 2 : if gain is minus unity, directly subtract -------*/ + else if (gainL16Q15 == Q15_MINUSONE) + { + for (i=samples; i; i--) + { + /*-- output = output - input --*/ + *destBuf = s16_sub_s16_s16_sat(*destBuf, *srcBuf++); + destBuf++; + } + } /* end of else if (gainL16Q15 == Q15_MINUSONE) */ + + /*------------ case 3: normal case, with L16Q15 gain --------------------*/ + else + { + for (i=samples; i; i--) + { + /*-- input * gain --*/ + tmpL16 = s16_extract_s64_h_rnd(Q6_P_mpy_RlRl_s1(*srcBuf++, gainL16Q15)); + /*-- output = output + input * gain --*/ + *destBuf = s16_add_s16_s16_sat(*destBuf, tmpL16); + destBuf++; + } + } /* end of else */ + } +#endif +} /*---------------------- end of function buffer_mix ----------------------*/ + + +/*===========================================================================*/ +/* FUNCTION : buffer_fill_mix */ +/* */ +/* DESCRIPTION: Apply L16Q15 gain to input and mix (sum) it into a running */ +/* output buffer. */ +/* */ +/* INPUTS: dest-> output buffer */ +/* src1-> input buffer 1 */ +/* src1-> input buffer 2 */ +/* gainL16Q15: gain to be applied to the samples */ +/* samples: total number of samples to be processed */ +/* OUTPUTS: destBuf-> output buffer */ +/* */ +/* IMPLEMENTATION NOTES: */ +/* The function is implemented by merging the buffer_fill,buffer_mix */ +/* functions */ +/*===========================================================================*/ +void buffer_fill_mix +( + int16 *src1, /* Input1 buffer */ + int16 *src2, /* input2 buffer */ + int16 *dst, /* Output buffer */ + int16 gainL16Q15, /* gain to the samples */ + int16 samples /* number of samples to process */ +) +{ +#ifndef __qdsp6__ + int16 i, tmpL16; + /*-----------------------------------------------------------------------*/ + /*------------ case 1 : if gain is unity, directly sum ------------------*/ + if (gainL16Q15 == Q15_ONE) + { + for (i = 0; i < samples; i++) + { + /*-- output = src1 + src2 --*/ + *dst = s16_add_s16_s16_sat(*src1++, *src2++); + dst++; + } + } + /*------------ case 2 : if gain is minus unity, directly subtract -------*/ + else if (gainL16Q15 == Q15_MINUSONE) + { + for (i = 0; i < samples; i++) + { + /*-- output = src1 - src2 --*/ + *dst = s16_sub_s16_s16_sat(*src1++, *src2++); + dst++; + } + } + /*------------ case 3: normal case, with L16Q15 gain --------------------*/ + else + { + for (i = 0; i < samples; i++) + { + /*-- input * gain --*/ + tmpL16 = s16_extract_s40_h(s40_mult_s16_s16_shift(*src2++, gainL16Q15 ,1)); + /*-- output = output + input * gain --*/ + *dst = s16_add_s16_s16_sat(*src1++, tmpL16); + dst++; + } + } +#else + + int16 i, tmpL16; + uint32 count, address; + + /************************************************************************/ + /* Following code process samples till destination buffer is 8 byte */ + /* Aligned. Destination buffer is made to take aligned address after */ + /* first three samples. This is avoid out of boundary memory access */ + /* that can happen on source buffer (on left side) in assembly code. */ + /************************************************************************/ + address = (uint32)(&dst[0]); + //number of samples to make destination aligned. + count = 4 - ((address&6)>>1); + if (count < 3) + { + // destination is made to take next aligned address + count = count + 4; + } + count = s32_min_s32_s32(samples, count); + + if(count) + { + if (gainL16Q15 == Q15_ONE) + { + for (i=count; i; i--) + { + /*-- output = src1 + src2 --*/ + *dst = s16_add_s16_s16_sat(*src1++, *src2++); + dst++; + } + } + /*------------ case 2 : if gain is minus unity, directly subtract -------*/ + else if (gainL16Q15 == Q15_MINUSONE) + { + for (i=count; i; i--) + { + /*-- output = src1 - src2 --*/ + *dst = s16_sub_s16_s16_sat(*src1++, *src2++); + dst++; + } + } + /*------------ case 3: normal case, with L16Q15 gain --------------------*/ + else + { + for (i=count; i; i--) + { + /*-- input * gain --*/ + tmpL16 = s16_extract_s40_h(Q6_P_mpy_RlRl_s1(*src2++, gainL16Q15)); + /*-- output = output + input * gain --*/ + *dst = s16_add_s16_s16_sat(*src1++, tmpL16); + dst++; + } + } + samples = samples-count; + } + + /***********************************************************************/ + /* To avoid out of boundary memory access on source buffer last 4 */ + /* samples are processed in c. In adition to these 4 samples, if sample*/ + /* count is not multiple of 4, remaining samples will also be processed*/ + /* in c code. */ + /***********************************************************************/ + /*assembly code will execute if number of samples >=12. Assembly code */ + /*processes samples in multiples of 4 and if any samples are left they */ + /*will be processed in c code. >= 12 is because of loop unrolling in */ + /* assembly to better pack pipe line. >=(12+4) condition is to account */ + /* last 4 samples that will be processed in c code */ + /***********************************************************************/ + if (samples>=(12+4)) + { + // last 4 samples will be processed in c to avoid out of boudary + // memory access on source pointers + samples = samples - 4; + + //assembly + buffer_fill_mix_asm(src1, src2, dst, gainL16Q15, samples); + + //update address pointers as they will not be updated in function call + src1 = src1 + (samples&(~3)); + src2 = src2 + (samples&(~3)); + dst = dst + (samples&(~3)); + + // samples to be processed in c + samples = 4 + (samples&3); + } + + //process remaining samples + if (samples) + { + if (gainL16Q15 == Q15_ONE) + { + for (i=samples; i; i--) + { + /*-- output = src1 + src2 --*/ + *dst = s16_add_s16_s16_sat(*src1++, *src2++); + dst++; + } + } + /*------------ case 2 : if gain is minus unity, directly subtract -------*/ + else if (gainL16Q15 == Q15_MINUSONE) + { + for (i=samples; i; i--) + { + /*-- output = src1 - src2 --*/ + *dst = s16_sub_s16_s16_sat(*src1++, *src2++); + dst++; + } + } + /*------------ case 3: normal case, with L16Q15 gain --------------------*/ + else + { + for (i=samples; i; i--) + { + /*-- input * gain --*/ + tmpL16 = s16_extract_s40_h(Q6_P_mpy_RlRl_s1(*src2++, gainL16Q15)); + /*-- output = output + input * gain --*/ + *dst = s16_add_s16_s16_sat(*src1++, tmpL16); + dst++; + } + } + } +#endif +} /*---------------------- end of function buff_fill_mix--------------------*/ + +// smoothly crossfade between src1 and src2 with Q15 smooth panner, unity panner = all src2 +void buffer_crossmix_panner(int16 *dest, int16 *src1, int16 *src2, pannerStruct *panner, int32 samples) +{ + int16 target_q15 = panner->targetGainL16Q15; + int32 pan_samples = panner->sampleCounter; + int32 delta_q31 = panner->deltaL32Q31; + int16 gain_q15, tmp16; + int32 gain_q31, ramp_samples, i; + + // current implementation totally ignores delay of panner + // src1 gets applied gain, and src2 is applied 1-gain + gain_q15 = panner_get_current(*panner); + gain_q31 = s32_shl_s32_sat((int32)gain_q15, 16); + + ramp_samples = s32_min_s32_s32(pan_samples, samples); + + // process samples with dynamic gains + for (i = 0; i < ramp_samples; ++i) { + // get q15 gain + gain_q15 = s16_extract_s32_h(gain_q31); + // apply gain to src 2 and save + *dest = s16_extract_s64_h_rnd(s40_mult_s16_s16_shift(*src2++, gain_q15, 1)); + // get 1-gain and apply to src 1 and mix + gain_q15 = s16_sub_s16_s16_sat(Q15_ONE, gain_q15); + tmp16 = s16_extract_s64_h_rnd(s40_mult_s16_s16_shift(*src1++, gain_q15, 1)); + *dest = s16_add_s16_s16_sat(*dest, tmp16); + dest++; + // add delta to q31 gain + gain_q31 = s32_add_s32_s32_sat(gain_q31, delta_q31); + } + pan_samples -= ramp_samples; + + // process samples with no gain change + samples -= ramp_samples; + if (samples > 0) { + if (Q15_ONE == target_q15) { + buffer_copy(dest, src2, samples); + } + else if (0 == target_q15) { + buffer_copy(dest, src1, samples); + } + else { + gain_q15 = s16_sub_s16_s16_sat(Q15_ONE, target_q15); + buffer_fill(dest, src2, target_q15, samples); + buffer_mix(dest, src1, gain_q15, samples); + } + } + panner->sampleCounter = pan_samples; +} + + diff --git a/modules/cmn/common/utils/src/audio_buffer32.c b/modules/cmn/common/utils/src/audio_buffer32.c new file mode 100644 index 0000000..a676784 --- /dev/null +++ b/modules/cmn/common/utils/src/audio_buffer32.c @@ -0,0 +1,342 @@ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +/*============================================================================ + @file audio_buffer32.c + + Buffer related 32 bit manipulations + ============================================================================*/ +#include "audio_dsp32.h" +#include "audio_basic_op_ext.h" +#include + + +// copy 16 bit buffer into 32 bit buffer +void buffer32_copy16(int32 *dest, int16 *src, int32 samples) +{ + int32 i; + for (i = 0; i < samples; ++i) { + *dest++ = *src++; + } +} + +// apply 32 bit gain, then write to output 32 bit buffer +void buffer32_fill32(int32 *dest, int32 *src, int32 gain, int16 q_factor, int32 samples) +{ + int32 i; + int16 shift = s16_sub_s16_s16(32, q_factor); + int32 unity = 1<targetGainL16Q15; + int32 pan_samples = panner->sampleCounter; + int32 delta_q31 = panner->deltaL32Q31; + int16 gain_q15; + int32 gain_q31, ramp_samples, i; + + // assuming panner has no delay of execution + gain_q15 = panner_get_current(*panner); + gain_q31 = s32_shl_s32_sat((int32)gain_q15, 16); + + ramp_samples = s32_min_s32_s32(pan_samples, samples); + + for (i = 0; i < ramp_samples; ++i) { + // get q15 gain + gain_q15 = s16_extract_s32_h(gain_q31); + // apply gain and output + *dest++ = s32_mult_s32_s16_rnd_sat(*src, gain_q15); + src++; + // add delta to q31 gain + gain_q31 = s32_add_s32_s32_sat(gain_q31, delta_q31); + } + samples -= ramp_samples; + pan_samples -= ramp_samples; + + if (samples > 0) { + for (i = 0; i < samples; ++i) { + *dest++ = s32_mult_s32_s16_rnd_sat(*src, target_q15); + src++; + } + } + + panner->sampleCounter = pan_samples; +} + +// apply Q15 smooth panner to 32 bit input and mix into 32 bit output +void buffer32_mix_panner(int32 *dest, int32 *src, pannerStruct *panner, int32 samples) +{ + int32 target_q31; + int32 pan_samples = panner->sampleCounter; + int32 delta_q31 = panner->deltaL32Q31; + int16 gain_q15; + int32 gain_q31, ramp_samples, i; + + // assuming panner has no delay of execution + gain_q15 = panner_get_current(*panner); + gain_q31 = s32_shl_s32_sat((int32)gain_q15, 16); + target_q31 = s32_shl_s32_sat((int32)panner->targetGainL16Q15, 16); + + ramp_samples = s32_min_s32_s32(pan_samples, samples); + + for (i = 0; i < ramp_samples; ++i) { + *dest = s32_mac_s32_s32_s1_rnd_sat(*dest, *src, gain_q31); + dest++; + src++; + // add delta to q31 gain + gain_q31 = s32_add_s32_s32_sat(gain_q31, delta_q31); + } + samples -= ramp_samples; + pan_samples -= ramp_samples; + + for (i = 0; i < samples; ++i) { + *dest = s32_mac_s32_s32_s1_rnd_sat(*dest, *src, target_q31); + dest++; + src++; + } + + panner->sampleCounter = pan_samples; +} + +// smoothly crossfade src1 into src2 with Q15 smooth panner, results saved in output buf +void buffer32_crossmix_panner(int32 *dest, int32 *src1, int32 *src2, pannerStruct *panner, int32 samples) +{ + int16 target_q15 = panner->targetGainL16Q15; + int32 pan_samples = panner->sampleCounter; + int32 delta_q31 = panner->deltaL32Q31; + int16 gain_q15; + int32 gain_q31, ramp_samples, i, tmp32, temp; + + // current implementation totally ignores delay of panner + // src1 gets applied gain, and src2 is applied 1-gain + gain_q15 = panner_get_current(*panner); + gain_q31 = s32_shl_s32_sat((int32)gain_q15, 16); + + ramp_samples = s32_min_s32_s32(pan_samples, samples); + + // process samples with dynamic gains + for (i = 0; i < ramp_samples; ++i) { + // get q15 gain + gain_q15 = s16_extract_s32_h(gain_q31); + // apply gain to src 2 and save + *dest = s32_mult_s32_s16_rnd_sat(*src2, gain_q15); + src2++; + // get 1-gain and apply to src 1 and mix + gain_q15 = s16_sub_s16_s16_sat(Q15_ONE, gain_q15); + tmp32 = s32_mult_s32_s16_rnd_sat(*src1, gain_q15); + src1++; + temp = s32_add_s32_s32_sat(*dest, tmp32); + *dest++ = temp; + // add delta to q31 gain + gain_q31 = s32_add_s32_s32_sat(gain_q31, delta_q31); + } + pan_samples -= ramp_samples; + + // process samples with no gain change + samples -= ramp_samples; + if (samples > 0) { + if (Q15_ONE == target_q15) { + buffer32_copy_v2(dest, src2, samples); + } + else if (0 == target_q15) { + buffer32_copy_v2(dest, src1, samples); + } + else { + gain_q15 = s16_sub_s16_s16_sat(Q15_ONE, target_q15); + buffer32_fill16(dest, src2, target_q15, samples); + buffer32_mix16(dest, src1, gain_q15, samples); + } + } + + panner->sampleCounter = pan_samples; +} diff --git a/modules/cmn/common/utils/src/audio_buffer32_island.c b/modules/cmn/common/utils/src/audio_buffer32_island.c new file mode 100644 index 0000000..f23d9ee --- /dev/null +++ b/modules/cmn/common/utils/src/audio_buffer32_island.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +/*============================================================================ + @file audio_buffer32_island.c + + Buffer related 32 bit manipulations + ============================================================================*/ + +#include "audio_dsp32.h" +#include "audio_basic_op_ext.h" +#include + +// copy 32 bit buffers +void buffer32_copy_v2(int32 *dest, int32 *src, int32 samples) +{ +#ifndef __qdsp6__ + int32 i; + for (i = 0; i < samples; ++i) { + *dest++ = *src++; + } +#else + memscpy(dest, samples<<2, src, samples<<2); +#endif +} + +// empty a 32 bit buffer +void buffer32_empty_v2(int32 *dest, int32 samples) +{ +#ifndef __qdsp6__ + int32 i; + for (i = 0; i < samples; ++i) { + *dest++ = 0; + } +#else + memset(dest, 0, samples<<2); +#endif +} \ No newline at end of file diff --git a/modules/cmn/common/utils/src/audio_complex_basic_op.c b/modules/cmn/common/utils/src/audio_complex_basic_op.c new file mode 100644 index 0000000..8be13d0 --- /dev/null +++ b/modules/cmn/common/utils/src/audio_complex_basic_op.c @@ -0,0 +1,800 @@ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +/*============================================================================ + FILE: audio_complex_basic_op.c + + OVERVIEW: Implement operations for complex numbers, which can be + mapped to QDSP6 intrinsics. + + DEPENDENCIES: None +============================================================================*/ + +/*---------------------------------------------------------------------------- + * Include Files + * -------------------------------------------------------------------------*/ + +#include "audio_complex_basic_op.h" + +/*---------------------------------------------------------------------------- + * Preprocessor Definitions and Constants + * -------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------- + * Type Declarations + * -------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------- + * Global Data Definitions + * -------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------- + * Static Variable Definitions + * -------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------- + * Static Function Declarations and Definitions + * -------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------- + * Externalized Function Definitions + * -------------------------------------------------------------------------*/ + +#if !defined(cl0) +/*====================================================================== + + FUNCTION cl0 + + DESCRIPTION Count leading zero bits. + + DEPENDENCIES None + + PARAMETERS x: [in] Input parameter + + RETURN VALUE Number of leading zero bits (Q6_R_cl0_R(x)) + + SIDE EFFECTS None + +======================================================================*/ + +int32 cl0(int32 x) +{ + int32 l0 = 0; + if (x == 0) + { + l0 = 32; + } + else if (x > 0) + { + l0 = s16_norm_s32 ( x ) + 1; + } + return( l0 ); +} + +#endif + +/*====================================================================== + + FUNCTION complex + + DESCRIPTION Form complex number from real and imaginary part. + 16-bit LSW: real part + 16-bit MSW: imaginary part + + DEPENDENCIES None + + PARAMETERS xr: [in] Real part + xi: [in] Imaginary part + + RETURN VALUE Complex number (Q6_R_combine_RlRl(xi,xr)) + + SIDE EFFECTS None + +======================================================================*/ + +cint2x16 c32_complex_s16_s16(int16 xr, int16 xi) +{ + cint2x16 c; + c = ((int32)xr) & 0xFFFF; + c |= (((int32)xi) << 16); + return( c ); +} + +#if !defined(c32_conjugate_c32) +/*====================================================================== + + FUNCTION c32_conjugate_c32 + + DESCRIPTION Complex conjugate (with saturation) + + DEPENDENCIES None + + PARAMETERS x: [in] Complex input + + RETURN VALUE Complex conjugate (Q6_P_vconj_P_sat(x)) + + SIDE EFFECTS None + +======================================================================*/ + +cint2x16 c32_conjugate_c32(cint2x16 x) +{ + int16 xr = s16_real_c32(x); + int16 xi = s16_imag_c32(x); + return( c32_complex_s16_s16(xr, s16_neg_s16_sat(xi)) ); +} +#endif + + +/*====================================================================== + + FUNCTION c64_mult_c32_c32 + + DESCRIPTION Fractionally multiply two complex numbers with + 16-bit real and imaginary parts, producing a + product with 32-bit real and imaginary parts. + + DEPENDENCIES None + + PARAMETERS var1: [in] First complex number + var2: [in] Second complex number + + RETURN VALUE sat((var1 * var2)<<1) (Q6_P_cmpy_RR_s1_sat(var1,var2)) + + SIDE EFFECTS None + +======================================================================*/ + +cint2x32 c64_mult_c32_c32(cint2x16 var1, cint2x16 var2) +{ + int16 var1r = s16_real_c32(var1); + int16 var1i = s16_imag_c32(var1); + int16 var2r = s16_real_c32(var2); + int16 var2i = s16_imag_c32(var2); + + int32 xr = s32_shl_s32_sat( s32_sub_s32_s32_sat(s32_mult_s16_s16(var1r, var2r), s32_mult_s16_s16(var1i, var2i)), 1); + int32 xi = s32_shl_s32_sat( s32_add_s32_s32_sat (s32_mult_s16_s16(var1r, var2i), s32_mult_s16_s16(var1i, var2r)), 1); + return( c64_complex_s32_s32(xr, xi) ); +} + + +/*====================================================================== + + FUNCTION c32_mult_c32_c32 + + DESCRIPTION Fractionally multiply two complex numbers with + 16-bit real and imaginary parts, producing a + rounded product with 16-bit real and imaginary parts. + + DEPENDENCIES None + + PARAMETERS var1: [in] First complex number + var2: [in] Second complex number + + RETURN VALUE round( sat((var1 * var2)<<1) ) + (Q6_R_cmpy_RR_s1_rnd_sat(var1,var2)) + + SIDE EFFECTS None + +======================================================================*/ + +cint2x16 c32_mult_c32_c32(cint2x16 var1, cint2x16 var2) +{ + int16 var1r = s16_real_c32(var1); + int16 var1i = s16_imag_c32(var1); + int16 var2r = s16_real_c32(var2); + int16 var2i = s16_imag_c32(var2); + + int16 xr = s16_round_s32_sat(s32_shl_s32_sat( s32_sub_s32_s32_sat ( s32_mult_s16_s16(var1r, var2r), s32_mult_s16_s16(var1i, var2i) ), 1) ); + int16 xi = s16_round_s32_sat(s32_shl_s32_sat( s32_add_s32_s32_sat ( s32_mult_s16_s16(var1r, var2i), s32_mult_s16_s16(var1i, var2r) ), 1) ); + return( c32_complex_s16_s16(xr, xi) ); +} + + +/*====================================================================== + + FUNCTION c32_mult_c32_c32conj + + DESCRIPTION Fractionally multiply one complex number with + the conjugate of another. Each input has + 16-bit real and imaginary parts, and the product is + rounded with 16-bit real and imaginary parts. + + DEPENDENCIES None + + PARAMETERS var1: [in] First complex number + var2: [in] Second complex number + + RETURN VALUE round( sat((var1 * conj(var2))<<1) ) + (Q6_R_cmpy_RR_conj_s1_rnd_sat(var1,var2)) + + SIDE EFFECTS None + +======================================================================*/ + +cint2x16 c32_mult_c32_c32conj(cint2x16 var1, cint2x16 var2) +{ + return( c32_mult_c32_c32(var1, c32_conjugate_c32(var2)) ); +} + + +/*====================================================================== + + FUNCTION c32_add_c32_c32_sat + + DESCRIPTION Add two complex numbers, with saturation. + + DEPENDENCIES None + + PARAMETERS var1: [in] First complex number + var2: [in] Second complex number + + RETURN VALUE sat(var1 + var2) + + SIDE EFFECTS None + +======================================================================*/ + +cint2x16 c32_add_c32_c32_sat(cint2x16 var1, cint2x16 var2) +{ + int16 zr, zi; + zr = s16_add_s16_s16_sat( s16_real_c32(var1), s16_real_c32(var2) ); + zi = s16_add_s16_s16_sat( s16_imag_c32(var1), s16_imag_c32(var2) ); + + return( c32_complex_s16_s16((int16)zr, (int16)zi) ); +} + + +/*====================================================================== + + FUNCTION c32_sub_c32_c32_sat + + DESCRIPTION Subtract two complex numbers, with saturation. + + DEPENDENCIES None + + PARAMETERS var1: [in] First complex number + var2: [in] Second complex number + + RETURN VALUE sat(var1 - var2) + + SIDE EFFECTS None + +======================================================================*/ + +cint2x16 c32_sub_c32_c32_sat(cint2x16 var1, cint2x16 var2) +{ + int16 zr, zi; + zr = s16_sub_s16_s16_sat( s16_real_c32(var1), s16_real_c32(var2) ); + zi = s16_sub_s16_s16_sat( s16_imag_c32(var1), s16_imag_c32(var2) ); + + return( c32_complex_s16_s16((int16)zr, (int16)zi) ); +} + +#if !defined(c64_complex_s32_s32) +/*====================================================================== + + FUNCTION c64_complex_s32_s32 + + DESCRIPTION Form long complex number from real and imaginary part. + 32-bit LSW: real part + 32-bit MSW: imaginary part + + DEPENDENCIES None + + PARAMETERS xr: [in] Real part + xi: [in] Imaginary part + + RETURN VALUE Complex number (Q6_P_combine_RR(xi,xr)) + + SIDE EFFECTS None + +======================================================================*/ + +cint2x32 c64_complex_s32_s32(int32 xr, int32 xi) +{ + cint2x32 c; + c = ((int64)xr) & 0xFFFFFFFF; + c |= (((int64)xi) << 32); + return( c ); +} +#endif + +#if !defined(c64_conjugate_c64) +/*====================================================================== + + FUNCTION c64_conjugate_c64 + + DESCRIPTION Long complex conjugate (with saturation) + + DEPENDENCIES None + + PARAMETERS x: [in] Complex input + + RETURN VALUE Complex conjugate + + SIDE EFFECTS None + +======================================================================*/ + +cint2x32 c64_conjugate_c64(cint2x32 x) +{ + return( c64_complex_s32_s32( s32_real_c64(x), s32_neg_s32_sat(s32_imag_c64(x)) ) ); +} +#endif + +#if !defined(c64_add_c64_c64_sat) +/*====================================================================== + + FUNCTION c64_add_c64_c64_sat + + DESCRIPTION Add two long complex numbers, with saturation. + + DEPENDENCIES None + + PARAMETERS x: [in] First complex number + y: [in] Second complex number + + RETURN VALUE sat(x + y) (Q6_P_vaddw_PP_sat) + + SIDE EFFECTS None + +======================================================================*/ + +cint2x32 c64_add_c64_c64_sat(cint2x32 x, cint2x32 y) +{ + int32 xr, xi, yr, yi, sr, si; + + xr = s32_real_c64(x); + xi = s32_imag_c64(x); + yr = s32_real_c64(y); + yi = s32_imag_c64(y); + + sr = s32_add_s32_s32_sat(xr, yr); + si = s32_add_s32_s32_sat(xi, yi); + + return(c64_complex_s32_s32(sr, si)); +} +#endif + +#if !defined(c64_sub_c64_c64_sat) +/*====================================================================== + + FUNCTION c64_sub_c64_c64_sat + + DESCRIPTION Subtract two long complex numbers, with saturation. + + DEPENDENCIES None + + PARAMETERS x: [in] First complex number + y: [in] Second complex number + + RETURN VALUE sat(x - y) (Q6_P_vsubw_PP_sat) + + SIDE EFFECTS None + +======================================================================*/ + +cint2x32 c64_sub_c64_c64_sat(cint2x32 x, cint2x32 y) +{ + + int32 xr, xi, yr, yi, sr, si; + + xr = s32_real_c64(x); + xi = s32_imag_c64(x); + yr = s32_real_c64(y); + yi = s32_imag_c64(y); + + sr = s32_sub_s32_s32_sat(xr, yr); + si = s32_sub_s32_s32_sat(xi, yi); + + return(c64_complex_s32_s32(sr, si)); + +} +#endif + +/*====================================================================== + + FUNCTION c32_avrg_c32_c32_rnd + + DESCRIPTION Average two complex numbers with convergent rounding. + + DEPENDENCIES None + + PARAMETERS x: [in] First complex number + y: [in] Second complex number + + RETURN VALUE convround((x + y)>>1) + (see Q6_P_vavgh_PP_crnd) + + SIDE EFFECTS None + +======================================================================*/ + +cint2x16 c32_avrg_c32_c32_rnd( cint2x16 x, cint2x16 y ) { + int32 zr, zi; + zr = (int32)s16_real_c32(x) + (int32)s16_real_c32(y); + zi = (int32)s16_imag_c32(x) + (int32)s16_imag_c32(y); + + // convergent rouding + zr += (zr>>1) & 1; + zi += (zi>>1) & 1; + //zr += 1; + //zi += 1; + + zr >>= 1; + zi >>= 1; + + return( c32_complex_s16_s16((int16)zr, (int16)zi) ); +} + + +/*====================================================================== + + FUNCTION c32_avrg_c32_c32neg_rnd + + DESCRIPTION Average a complex number with the negation of another, + and apply convergent rounding. + + DEPENDENCIES None + + PARAMETERS x: [in] First complex number + y: [in] Second complex number + + RETURN VALUE convround((x - y)>>1) + (see Q6_P_vnavgh_PP_crnd_sat) + + SIDE EFFECTS None + +======================================================================*/ + +cint2x16 c32_avrg_c32_c32neg_rnd( cint2x16 x, cint2x16 y ) { + int32 zr, zi; + zr = (int32)s16_real_c32(x) - (int32)s16_real_c32(y); + zi = (int32)s16_imag_c32(x) - (int32)s16_imag_c32(y); + + // convergent rouding + zr += (zr>>1) & 1; + zi += (zi>>1) & 1; + //zr += 1; + //zi += 1; + + zr >>= 1; + zi >>= 1; + + return( c32_complex_s16_s16(s16_saturate_s32(zr), s16_saturate_s32(zi)) ); +} + +#if !defined(c64_avrg_c64_c64_rnd) +/*====================================================================== + + FUNCTION c64_avrg_c64_c64_rnd + + DESCRIPTION Average two long complex numbers with convergent + rounding. + + DEPENDENCIES None + + PARAMETERS x: [in] First complex number + y: [in] Second complex number + + RETURN VALUE convround((x + y)>>1) (Q6_P_vavgw_PP_crnd) + + SIDE EFFECTS None + +======================================================================*/ + +cint2x32 c64_avrg_c64_c64_rnd(cint2x32 x, cint2x32 y) +{ + int64 t64; + int32 xr, xi, yr, yi, sr, si; + + xr = s32_real_c64(x); + xi = s32_imag_c64(x); + yr = s32_real_c64(y); + yi = s32_imag_c64(y); + + t64 = s64_add_s32_s32(xr, yr); + // convergent rounding + t64 = s64_add_s64_s64( t64, (int64)( ( ((int32)t64)>>1 ) & 1) ); + t64 = s64_shl_s64(t64,-1); + sr = (int32) t64; + + t64 = s64_add_s32_s32(xi, yi); + // convergent rounding + t64 = s64_add_s64_s64( t64, (int64)( ( ((int32)t64)>>1 ) & 1) ); + t64 = s64_shl_s64(t64,-1); + si = (int32) t64; + + return(c64_complex_s32_s32(sr, si)); +} +#endif + +#if !defined(c64_avrg_c64_c64neg_rnd) +/*====================================================================== + + FUNCTION c64_avrg_c64_c64neg_rnd + + DESCRIPTION Average a long complex number with the negation of + another, and apply convergent rounding. + + DEPENDENCIES None + + PARAMETERS x: [in] First complex number + y: [in] Second complex number + + RETURN VALUE convround((x - y)>>1) + (Q6_P_vnavgw_PP_crnd_sat) + + SIDE EFFECTS None + +======================================================================*/ + +cint2x32 c64_avrg_c64_c64neg_rnd(cint2x32 x, cint2x32 y) +{ + int64 t64; + int32 xr, xi, yr, yi, sr, si; + + xr = (int64) s32_real_c64(x); + xi = (int64) s32_imag_c64(x); + yr = (int64) s32_real_c64(y); + yi = (int64) s32_imag_c64(y); + + t64 = s64_sub_s64_s64(xr, yr); + // convergent rounding + t64 = s64_add_s64_s64( t64, (int64)( ( ((int32)t64)>>1 ) & 1) ); + t64 = s64_shl_s64(t64,-1); + sr = s32_saturate_s64(t64); + + t64 = s64_sub_s64_s64(xi, yi); + // convergent rounding + t64 = s64_add_s64_s64( t64, (int64)( ( ((int32)t64)>>1 ) & 1) ); + t64 = s64_shl_s64(t64,-1); + si = s32_saturate_s64(t64); + + return(c64_complex_s32_s32(sr, si)); + +} +#endif + + +// NEW FUNCTION ADDED TO SUPPORT Quad mic ABF Processing +/*====================================================================== + + FUNCTION c32_mult_c32_s16_shift_sat + + DESCRIPTION Multiply a complex number with 16-bit fractional real number + and s16_round_s32_sat the result to complex number. + + DEPENDENCIES None + + PARAMETERS var1: [in] Input complex number + var2: [in] Input Real number + iqShiftL16: [in] Integer part of Qfactor of the real number + + RETURN VALUE (var1*var2) << (iqshiftL16+1) + + SIDE EFFECTS None + +======================================================================*/ + +cint2x16 c32_mult_c32_s16_shift_sat( cint2x16 var1, + int16 var2, + int16 iqShiftL16) +{ + + int32 accu32; + int16 xr, xi; + int16 var1r = s16_real_c32(var1); // Real part of the complex number + int16 var1i = s16_imag_c32(var1); // Imaginary part of the complex number + + // Multiply with the real part + accu32 = s32_shl_s32_sat(s32_mult_s16_s16_shift_sat(var1r, var2), iqShiftL16); // Multiply and shift + xr = s16_round_s32_sat(accu32); // Round down to 16 bits + + // Multiply with the imaginary part + accu32 = s32_shl_s32_sat(s32_mult_s16_s16_shift_sat(var1i, var2), iqShiftL16); // Multiply and shift + xi = s16_round_s32_sat(accu32); // Round down to 16 bits + + return( c32_complex_s16_s16(xr, xi) ); + +} + +/*====================================================================== + + FUNCTION c32_mult_c32_s32_shift_sat + + DESCRIPTION Multiply a complex number with 32-bit fractional real number + and round the result to complex number. + + DEPENDENCIES None + + PARAMETERS var1: [in] Input complex number + var2: [in] Input Real number + iqShiftL16: [in] Integer part of Qfactor of the real number + + RETURN VALUE (var1*var2) << (iqshiftL16+1) + + SIDE EFFECTS None + +======================================================================*/ + +cint2x16 c32_mult_c32_s32_shift_sat( cint2x16 var1, + int32 var2, + int16 iqShiftL16) +{ + + int32 accu32; + int16 xr, xi; + int16 var1r = s16_real_c32(var1); // Real part of the complex number + int16 var1i = s16_imag_c32(var1); // Imaginary part of the complex number + + // Multiply with the real part + accu32 = (int32)s32_mult_s32_s16_rnd_sat(var2, var1r); // Multiply a 32-bit with 16-bit and round + accu32 = s32_shl_s32_sat(accu32, iqShiftL16); // Left shift and adjust the Q-factor + xr = s16_round_s32_sat(accu32); // Round down to 16 bits + + // Multiply with the imaginary part + accu32 = (int32)s32_mult_s32_s16_rnd_sat(var2, var1i); // Multiply a 32-bit with 16-bit and round + accu32 = s32_shl_s32_sat(accu32, iqShiftL16); // Left shift and adjust the Q-factor + xi = s16_round_s32_sat(accu32); // Round down to 16 bits + + return( c32_complex_s16_s16(xr, xi) ); +} + +/*====================================================================== + + FUNCTION c64_mult_c32_s16_sat + + DESCRIPTION Multiply a 16-bit complex number with 16-bit fractional real number + and return the 32-bit complex number + + DEPENDENCIES None + + PARAMETERS var1: [in] Input complex number + var2: [in] Input Real number + + RETURN VALUE (var1*var2) << 1 + + SIDE EFFECTS None + +======================================================================*/ + +cint2x32 c64_mult_c32_s16_sat( cint2x16 var1, + int16 var2 ) +{ + + int32 xr32, xi32; + int16 var1r = s16_real_c32(var1); // Real part of the complex number + int16 var1i = s16_imag_c32(var1); // Imaginary part of the complex number + + // Multiply with the real part + xr32 = s32_mult_s16_s16_shift_sat(var1r, var2); // Multiply 16x16 and get 32-bit output + + // Multiply with the imaginary part + xi32 = s32_mult_s16_s16_shift_sat(var1i, var2); // Multiply 16x16 and get 32-bit output + + return( c64_complex_s32_s32(xr32, xi32) ); +} + +#if !defined(c64_mult_c64_s16_shift_sat) +/*====================================================================== + + FUNCTION c64_mult_c64_s16_shift_sat + + DESCRIPTION Multiply a 64-bit complex number with 16-bit fractional real number + and round the result to 64-bit complex number. + + DEPENDENCIES None + + PARAMETERS var1: [in] Input 64-bit complex number + var2: [in] Input 16-bit Real number + iqShiftL16: [in] Integer part of Qfactor of the real number + + RETURN VALUE (var1*var2) << (iqshiftL16+1) + + SIDE EFFECTS None + +======================================================================*/ + +cint2x32 c64_mult_c64_s16_shift_sat( cint2x32 var1, + int16 var2, + int16 iqShiftL16) +{ + int32 xr32, xi32; + int32 var1r = s32_real_c64(var1); // Real part of the complex number + int32 var1i = s32_imag_c64(var1); // Imaginary part of the complex number + + // Multiply with the real part + xr32 = (int32)s32_mult_s32_s16_rnd_sat(var1r, var2); // Multiply a 32-bit with 16-bit and round + xr32 = s32_shl_s32_sat(xr32, iqShiftL16); // Left shift and adjust the Q-factor + + // Multiply with the imaginary part + xi32 = (int32)s32_mult_s32_s16_rnd_sat(var1i, var2); // Multiply a 32-bit with 16-bit and round + xi32 = s32_shl_s32_sat(xi32, iqShiftL16); // Left shift and adjust the Q-factor + + return( c64_complex_s32_s32(xr32, xi32) ); +} +#endif + +/*====================================================================== + + FUNCTION c64_mult_c64_c32_rnd_sat + + DESCRIPTION Fractionally multiply 32-bit complex number with + 16-bit complex number, producing a + product with 32-bit complex number + + DEPENDENCIES None + + PARAMETERS var1: [in] First complex number (32-bit) + var2: [in] Second complex number (16-bit) + + RETURN VALUE sat((var1 * var2)<<1) + + SIDE EFFECTS None + +======================================================================*/ + +cint2x32 c64_mult_c64_c32_rnd_sat(cint2x32 var1, cint2x16 var2) +{ + int32 var1r = s32_real_c64(var1); + int32 var1i = s32_imag_c64(var1); + int16 var2r = s16_real_c32(var2); + int16 var2i = s16_imag_c32(var2); + + int32 xr = s32_sub_s32_s32_sat ( (int32 )s32_mult_s32_s16_rnd_sat(var1r, var2r), + (int32 )s32_mult_s32_s16_rnd_sat(var1i, var2i)); + + int32 xi = s32_add_s32_s32_sat ( (int32 )s32_mult_s32_s16_rnd_sat(var1r, var2i), + (int32 )s32_mult_s32_s16_rnd_sat(var1i, var2r)); + return( c64_complex_s32_s32(xr, xi) ); +} + + +/*====================================================================== + + FUNCTION c64_mult_c64_c32_rnd_sat_q6 + + DESCRIPTION Fractionally multiply 32-bit complex number with + 16-bit complex number, producing a + product with 32-bit complex number + + DEPENDENCIES None + + PARAMETERS var1: [in] First complex number (32-bit) + var2: [in] Second complex number (16-bit) + + RETURN VALUE sat((var1 * var2)<<1) + + SIDE EFFECTS None + +======================================================================*/ + +cint2x32 c64_mult_c64_c32_rnd_sat_q6( cint2x32 x, cint2x16 y) +{ + int32 xRe, xIm; + int16 yRe, yIm; + int32 zRe, zIm; + + xRe = s32_real_c64(x); xIm = s32_imag_c64(x); + yRe = s16_real_c32(y); yIm = s16_imag_c32(y); + + // the complex MPY is defined in such a way in order + // to facilitate faster ASM implementation on Q6 + zRe = s32_mult_s32_s16_rnd_sat( xRe, yRe ); + zIm = s32_mult_s32_s16_rnd_sat( xIm, yRe ); + + zRe = s32_add_s32_s32_sat( zRe, s32_mult_s32_s16_rnd_sat( s32_neg_s32_sat(xIm), yIm ) ); + + // to match Q6 definition of 32x16 MAC + if ((xRe == (int32)0x80000000L) && (yIm == -32768)) + zIm = s32_saturate_s64((int64)zIm + 0x080000000LL); + else + zIm = s32_add_s32_s32_sat( zIm, s32_mult_s32_s16_rnd_sat( xRe, yIm ) ); + + return (c64_complex_s32_s32(zRe, zIm)); +} + diff --git a/modules/cmn/common/utils/src/audio_delay32.c b/modules/cmn/common/utils/src/audio_delay32.c new file mode 100644 index 0000000..9d3c989 --- /dev/null +++ b/modules/cmn/common/utils/src/audio_delay32.c @@ -0,0 +1,475 @@ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +/*============================================================================ + @file audio_delay32.c + Delayline related 32 bit processing functions +============================================================================*/ +#include "audio_dsp32.h" +#include "audio_basic_op_ext.h" + +/* reset delayline */ +void delayline32_reset(delayline32_t *delayline) +{ + buffer32_empty_v2(delayline->buf, delayline->buf_size); + delayline->idx = 0; +} + +/* refill delayline with new samples */ +void delayline32_update(delayline32_t *delayline, int32 *src, int32 samples) +{ + int32 delay_buf_size = delayline->buf_size; + int32 delay_idx = delayline->idx; + int32 *delay_buf = delayline->buf; + int32 *src_ptr; + int32 update_length, n; + int32 samples_till_delay_buf_end = delay_buf_size - delay_idx; + + // determine number of samples to update, and where new samples begin + if (samples > delay_buf_size) { + update_length = delay_buf_size; + src_ptr = src + samples - delay_buf_size; + } else { + update_length = samples; + src_ptr = src; + } + + // update samples from current idx to the end of delayline + n = s32_min_s32_s32(samples_till_delay_buf_end, update_length); + buffer32_copy_v2(delay_buf+delay_idx, src_ptr, n); + delay_idx += n; + src_ptr += n; + + if (n == samples_till_delay_buf_end) { + update_length -= n; + // update from the beginning of delayline + buffer32_copy_v2(delay_buf, src_ptr, update_length); + delay_idx = update_length; + } + + // save delayline idx + delayline->idx = delay_idx; +} + +/* exchange buffer values between two 32 bit buffers */ +static void exchange_buffer_values(int32 *buf1, int32 *buf2, int32 samples) +{ + int32 tmp32; + int32 i; + + for (i = 0; i < samples; ++i) { + tmp32 = *buf1; + *buf1 = *buf2; + *buf2 = tmp32; + buf1++; + buf2++; + } +} + +/* inplace delay imp for 32 bit delayline and data */ +void delayline32_inplace_delay(int32 *inplace_buf, delayline32_t *delayline, int32 samples) +{ + int32 *delay_buf = delayline->buf; + int32 delay_buf_size = delayline->buf_size; + int32 delay_idx = delayline->idx; + int32 samples_to_process, n; + int32 samples_till_delay_buf_end = delay_buf_size - delay_idx; + + while (samples > 0) { + samples_to_process = s32_min_s32_s32(samples, delay_buf_size); + samples -= samples_to_process; + n = s32_min_s32_s32(samples_till_delay_buf_end, samples_to_process); + + // exchange buffer and delayed samples from current location in delay buf + exchange_buffer_values(inplace_buf, delay_buf+delay_idx, n); + inplace_buf += n; + delay_idx += n; + + if (n == samples_till_delay_buf_end) { + + samples_to_process -= n; + // exchange buffer and delayed samples from first location in delay buf + exchange_buffer_values(inplace_buf, delay_buf, samples_to_process); + inplace_buf += samples_to_process; + delay_idx = samples_to_process; + } + } + + // save delayline idx + delayline->idx = delay_idx; +} + +/* read from delayline with specific delay amount, some cases need input fresh samples */ +void delayline32_read(int32 *dest, int32 *src, delayline32_t *delayline, int32 delay, int32 samples) +{ + int32 *delay_buf = delayline->buf; + int32 delay_buf_size = delayline->buf_size; + int32 delay_idx = delayline->idx; + int32 samples_from_delay, samples_till_delay_buf_end, read_idx, n; + + // determine where to read from, assuming delay is less than delay buf size + read_idx = s32_modwrap_s32_u32(delay_idx - delay, delay_buf_size); + + // determine samples from delayline, and remaining to read from input + samples_from_delay = s32_min_s32_s32(delay, samples); + samples -= samples_from_delay; + + // read samples from delayline (read_idx till end) + samples_till_delay_buf_end = delay_buf_size - read_idx; + n = s32_min_s32_s32(samples_till_delay_buf_end, samples_from_delay); + buffer32_copy_v2(dest, delay_buf+read_idx, n); + read_idx += n; + dest += n; + + // read samples from delayline (wrapped around) + if (n == samples_till_delay_buf_end) { + samples_from_delay -= n; + // update from the beginning of delayline + buffer32_copy_v2(dest, delay_buf, samples_from_delay); + dest += samples_from_delay; + } + + // read samples from input buffer + buffer32_copy_v2(dest, src, samples); +} + +/* extract contents from delayline and mix into output buffer with panner */ +void delayline32_panner_mix_out(int32 *dest, int32 *src, delayline32_t *delayline, pannerStruct *panner, int32 delay, int32 samples) +{ + // this function implies that samples < delayline length + int32 *delay_buf = delayline->buf; + int32 delay_buf_size = delayline->buf_size; + int32 delay_idx = delayline->idx; + int32 samples_till_delay_buf_end, samples_from_delay, read_idx, n; + + // determine where to read from, assuming delay is less than delay buf size + read_idx = s32_modwrap_s32_u32(delay_idx - delay, delay_buf_size); + + // determine samples from delayline, and remaining to read from input + samples_from_delay = s32_min_s32_s32(delay, samples); + samples -= samples_from_delay; + + // process samples from read idx till end of delay buf + samples_till_delay_buf_end = delay_buf_size - read_idx; + n = s32_min_s32_s32(samples_till_delay_buf_end, samples_from_delay); + buffer32_mix_panner(dest, delay_buf+read_idx, panner, n); + read_idx += n; + dest += n; + + // read samples from delayline (wrapped around) + if (n == samples_till_delay_buf_end) { + samples_from_delay -= n; + // process samples from beginning of delay buf + buffer32_mix_panner(dest, delay_buf, panner, samples_from_delay); + dest += samples_from_delay; + } + + // process samples from input buffer + buffer32_mix_panner(dest, src, panner, samples); +} + +/* mix content into delayline with smooth panner */ +void delayline32_panner_mix_in(delayline32_t *delayline, int32 *src, pannerStruct *panner, int32 delay, int32 samples) +{ + // this function requires that samples < delayline length !!! + int32 *delay_buf = delayline->buf; + int32 delay_buf_size = delayline->buf_size; + int32 delay_idx = delayline->idx; + int32 samples_till_delay_buf_end, mix_idx, n; + + // determine where to read from, assuming delay is less than delay buf size + mix_idx = s32_modwrap_s32_u32(delay_idx - delay, delay_buf_size); + + // process samples from read idx till end of delay buf + samples_till_delay_buf_end = delay_buf_size - mix_idx; + n = s32_min_s32_s32(samples_till_delay_buf_end, samples); + buffer32_mix_panner(delay_buf+mix_idx, src, panner, n); + mix_idx += n; + src += n; + + // read samples from delayline (wrapped around) + if (n == samples_till_delay_buf_end) { + samples -= n; + // process samples from beginning of delay buf + buffer32_mix_panner(delay_buf, src, panner, samples); + } +} + +/*===========================================================================*/ +/* FUNCTION : delayline_set32 */ +/* */ +/* DESCRIPTION: Set delayline delay length. Reset delay index and */ +/* delay buffer */ +/* */ +/* INPUTS: delayline-> delayline struct */ +/* OUTPUTS: delayline-> delayline struct */ +/* */ +/* IMPLEMENTATION NOTES: */ +/*===========================================================================*/ +void delayline32_set +( + delayline32_t *delayLine, /* delayline struct */ + int32 delayLen /* delay length */ +) +{ + delayLine->buf_size = delayLen; + delayline32_reset(delayLine); + +} /*------------------ end of function delayline_set ------------------------*/ + + +/*===========================================================================*/ +/* FUNCTION : buffer_delay_mix32 */ +/* */ +/* DESCRIPTION: Mix into a running output buffer with delayed samples, */ +/* applying L16Q15 gain at the same time. The samples could be */ +/* from the delayline or the input buffer. */ +/* */ +/* INPUTS: dest-> output buffer */ +/* src-> input buffer */ +/* gainL16Q15: gain to be applied to the samples */ +/* delayLine-> delayline struct */ +/* delay: amount of delay in sample */ +/* samples: total number of samples to be processed */ +/* OUTPUTS: dest-> output buffer */ +/* */ +/* IMPLEMENTATION NOTES: */ +/* To match QSound code, discuss gain value and divde into three cases */ +/* compared to function buffer_delay_fill */ +/*===========================================================================*/ +void buffer_delay_mix32 +( + int32 *destBuf, /* output buffer */ + int32 *srcBuf, /* input buffer */ + int16 gainL16Q15, /* gain to the samples */ + delayline32_t *delayline, /* delayline struct */ + int32 delay, /* amount of delay in sample */ + int32 samples /* number of samples to process */ +) +{ + int32 *destPtr = destBuf; + int32 *srcPtr = srcBuf; + int32 *delayPtr; + int32 delayLength = delayline->buf_size; + int32 delayIndex = delayline->idx; + int32 fromDelay, i; + int32 tmpL32; //int16 tmpL16; + + /*-----------------------------------------------------------------------*/ + + /*--------- set delay pointer so it points to "delay" samples ago -------*/ + delayIndex -= delay; + if (delayIndex < 0) + { /* delay should be less than delayLength; if not, we are in trouble */ + delayIndex += delayLength; + } + delayPtr = delayline->buf + delayIndex; + + /*----------------------- process from delayline ------------------------*/ + if (delay > 0 && samples > 0) + { + fromDelay = delay; + if (fromDelay > samples) + { /* if delay amount is more than output samples, then all of them */ + /* should from delayline */ + fromDelay = samples; + } + + /*----- case 1 : if gain is unity, directly sum -----*/ + if (gainL16Q15 == Q15_ONE) + { + for (i = 0; i < fromDelay; i++) + { + /*-- output = output + delay --*/ + *destPtr = s32_add_s32_s32_sat(*destPtr, *delayPtr++); + destPtr++; + /* manage circular buffer */ + if (delayPtr == delayline->buf + delayLength) + { + delayPtr = delayline->buf; + } + } /* end of for (i = 0; i < fromDelay; i++) */ + } + + /*----- case 2 : if gain is minus unity, directly subtract -----*/ + else if (gainL16Q15 == Q15_MINUSONE) + { + for (i = 0; i < fromDelay; i++) + { + /*-- output = output - delay --*/ + *destPtr = s32_sub_s32_s32_sat(*destPtr, *delayPtr++); + destPtr++; + /* manage circular buffer */ + if (delayPtr == delayline->buf + delayLength) + { + delayPtr = delayline->buf; + } + } /* end of for (i = 0; i < fromDelay; i++) */ + } + + /*----- case 3: normal case, with L16Q15 gain -----*/ + else + { + for (i = 0; i < fromDelay; i++) + { + /*-- delay * gain --*/ + //tmpL16 = s16_extract_s40_h(s40_mult_s16_s16_shift(*delayPtr++, gainL16Q15, 1)); + tmpL32 = s32_extract_s64_l(s64_mult_s32_s16_shift(*delayPtr++, gainL16Q15, -15)); + + /*-- output = output + delay * gain --*/ + *destPtr = s32_add_s32_s32_sat(*destPtr, tmpL32); + destPtr++; + /* manage circular buffer */ + if (delayPtr == delayline->buf + delayLength) + { + delayPtr = delayline->buf; + } + } /* end of for (i = 0; i < fromDelay; i++) */ + } + /* we have so far processed fromDelay samples */ + samples -= fromDelay; + } + + /*----------------------- process from input buffer ---------------------*/ + if (samples > 0) + { + buffer32_mix32(destPtr, srcPtr, gainL16Q15, 15, samples); + } /* end of if (samples > 0) */ +} /*--------------------- end of function buffer_delay_mix -----------------*/ + +/*===========================================================================*/ +/* FUNCTION : delayline32_copy */ +/* */ +/* DESCRIPTION: Copies the contents of one delay line to another. The */ +/* contents of the destination are lost. This is for 32 bit */ +/* data. */ +/* */ +/* INPUTS: delayline src-> delayline32_t */ +/* OUTPUTS: delayline dest-> delayline32_t */ +/* */ +/* IMPLEMENTATION NOTES: */ +/*===========================================================================*/ +void delayline32_copy +( + delayline32_t *dest, /* The destination delay line */ + delayline32_t *source /* The source delay line */ +) +{ + int32 samplesToCopy = 0; + int32 *pSrcEnd = source->buf + source->buf_size; + int32 *pSrc = source->buf + source->idx; + int32 *pDst = dest->buf; + + if (dest->buf_size < source->buf_size) + { + // Some part of the source must be discarded. + int32 numSamplesToDiscard = source->buf_size - dest->buf_size; + pSrc += numSamplesToDiscard; + if (pSrc > pSrcEnd) + { + pSrc -= source->buf_size; + } + samplesToCopy = dest->buf_size; + } + else + { + samplesToCopy = source->buf_size; + } + + int32 srcDistanceToEnd = pSrcEnd - pSrc; + if (srcDistanceToEnd < samplesToCopy) + { + buffer32_copy_v2(pDst, pSrc, srcDistanceToEnd); + samplesToCopy -= srcDistanceToEnd; + pSrc = source->buf; + pDst += srcDistanceToEnd; + } + + buffer32_copy_v2(pDst, pSrc, samplesToCopy); + pDst += samplesToCopy; + + dest->idx = pDst - dest->buf; + if (dest->idx >= dest->buf_size) + { + dest->idx -= dest->buf_size; + } +} /*------------------ end of function delayline_copy ------------------------*/ + +/*===========================================================================*/ +/* FUNCTION : buffer_delay_fill32 */ +/* */ +/* DESCRIPTION: Store in output buffer with delayed samples, while applying */ +/* L16Q15 gain. The samples could be from the delayline or the */ +/* input buffer. */ +/* */ +/* INPUTS: dest-> output buffer */ +/* src-> input buffer */ +/* gainL16Q15: gain to be applied to the samples */ +/* delayLine-> delayline struct */ +/* delay: amount of delay in sample */ +/* samples: total number of samples to be processed */ +/* OUTPUTS: dest-> output buffer */ +/* */ +/* IMPLEMENTATION NOTES: */ +/*===========================================================================*/ +void buffer_delay_fill32 +( + int32 *dest, /* output buffer */ + int32 *src, /* input buffer */ + int16 gainL16Q15, /* gain to the samples */ + delayline32_t *delayline, /* delayline struct */ + int32 delay, /* amount of delay in sample */ + int32 samples /* number of samples to process */ +) +{ + int32 delay_buf_size = delayline->buf_size; + int32 delay_idx = delayline->idx; + int32 *delay_buf = delayline->buf; + int32 from_delay=0, i; + + // set delay pointer so it points to "delay" samples ago + delay_idx = s32_modwrap_s32_u32(delay_idx-delay, delay_buf_size); + + /*----------------------- process from delayline ------------------------*/ + if (delay > 0 && samples > 0) { + + // if frame length less than delayline, all samples from delayline + from_delay = delay; + if (from_delay > samples) { + from_delay = samples; + } + + // case 1: unity gain, direct copy + if (gainL16Q15 == Q15_ONE) { + for (i = 0; i < from_delay; ++i) { + dest[i] = delay_buf[delay_idx]; + delay_idx = s32_modwrap_s32_u32(delay_idx+1, delay_buf_size); + } + } + + // case 2: negative unity gain, copy negative + else if (gainL16Q15 == Q15_MINUSONE) { + for (i = 0; i < from_delay; ++i) { + dest[i] = s32_neg_s32_sat(delay_buf[delay_idx]); + delay_idx = s32_modwrap_s32_u32(delay_idx+1, delay_buf_size); + } + } + + // case 3: apply L16Q15 gain + else { + for (i = 0; i < from_delay; i++) { + dest[i] = s32_mult_s32_s16_rnd_sat(delay_buf[delay_idx], gainL16Q15); + delay_idx = s32_modwrap_s32_u32(delay_idx+1, delay_buf_size); + } + } + + samples -= from_delay; + } + + /*----------------------- process from input buffer ---------------------*/ + if (samples > 0) { + buffer32_fill16(dest+from_delay, src, gainL16Q15, samples); + } +} diff --git a/modules/cmn/common/utils/src/audio_panner.cpp b/modules/cmn/common/utils/src/audio_panner.cpp new file mode 100644 index 0000000..4170c48 --- /dev/null +++ b/modules/cmn/common/utils/src/audio_panner.cpp @@ -0,0 +1,609 @@ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +/*===========================================================================*] +[* FILE NAME: audio_panner.c *] +[* DESCRIPTION: *] +[* Functions regarding panner: linear smooth value change *] +[* FUNCTION LIST : *] +[* panner_get_current: Get current gain of a panner *] +[* panner_setup: setup panner parameters *] +[* get_cur_cir_scale: Get current angle of an angle panner *] +[* cir_scale_setup: setup angle panner parameters *] +[* buffer_scale_mix: Apply panner to a buffer and mix into another *] +[*===========================================================================*/ +#include "audio_dsp.h" +#include "audio_divide_qx.h" + +#if ((defined __hexagon__) || (defined __qdsp6__)) +#ifdef __cplusplus +extern "C" +#else +extern +#endif /* __cplusplus */ +void buffer_scale_mix_asm(int16 *srcPtr, int16 *destPtr, int32 currentGainL32Q31, + int32 deltaL32Q31, int16 rampSamples); +#endif +/*================== diagram for a linear panning process ===================*/ +/* typedef struct { */ +/* int16 targetGainL16Q15; - target gain to pan toward, L16Q15 */ +/* int32 deltaL32Q31; - gain increment per sample, L32Q31 */ +/* int32 sampleCounter; - total sample steps of the panner */ +/* int32 delaySamples; - delay of samples to start panning */ +/* } pannerStruct; */ +/*---------------------------------------------------------------------------*/ +/* */ +/* current/---------------- <- targetGainL16Q15 */ +/* time / . */ +/* call start . / . */ +/* setup panning . / . */ +/* . . v/ . */ +/* . . / . . . . . . . . . . <- current gain */ +/* . . /. . */ +/* v v / . . */ +/* --------------/. . . . . . . . . . . . <- initial gain */ +/* delaySamples ramp samples */ +/* |<---------->|<------->| */ +/* |<--->| */ +/* sampleCounter (set to ramp sample during setup) */ +/* ------------------------------------------> time */ +/* (as the panner works, sampleCounter will decrease until it becomes zero, */ +/* and at that moment, gain of the panner should reach targetGainL16Q15) */ +/* (angle panner has similar structure, can use same graph as reference) */ +/*===========================================================================*/ + + +/*===========================================================================*/ +/* FUNCTION : panner_get_current */ +/* */ +/* DESCRIPTION: Return current gain of a panner */ +/* */ +/* INPUTS: panner: panner struct */ +/* OUTPUTS: return current gain in L16Q15 */ +/* */ +/* IMPLEMENTATION NOTES: */ +/*===========================================================================*/ +int16 panner_get_current +( + pannerStruct panner /* panner struct */ +) +{ + int16 tmpL16Q15; + + if (panner.sampleCounter == 0) + { /* if counter zeroed, target gain already achieved */ + return panner.targetGainL16Q15; + } + else + { + /* calculate diff of current gain from target gain, Q15 */ + tmpL16Q15 = s16_saturate_s32( + Q16_mult(panner.deltaL32Q31, panner.sampleCounter)); + /* get current gain */ + return s16_sub_s16_s16_sat(panner.targetGainL16Q15, tmpL16Q15); + } +} /*----------------- end of function panner_get_current --------------------*/ + + +/*===========================================================================*/ +/* FUNCTION : panner_setup */ +/* */ +/* DESCRIPTION: According to new target gain and ramp sample number, setup */ +/* a panner's target gain, delta and sample counter. */ +/* */ +/* INPUTS: panner-> panner struct */ +/* newGainL16Q15: new target panner gain */ +/* rampSamples: number of samples in the ramp */ +/* newDelay: delay after setup to execute the panner, in sample */ +/* OUTPUTS: panner-> panner struct, with values set */ +/* */ +/* IMPLEMENTATION NOTES: */ +/* By inputting zero ramSamples and zero newDelay, this function can */ +/* set a static gain to the panner immediately. */ +/*===========================================================================*/ +void panner_setup +( + pannerStruct *panner, /* panner struct */ + int16 newGainL16Q15, /* new target panner gain */ + int32 rampSamples, /* number of samples in the ramp */ + int32 newDelay /* delay of ramping process */ +) +{ + int16 changeL16Q15, currentGainL16Q15; + + /* substitute panner struct values with shorter names */ + int32 sampleCounter = panner->sampleCounter; + int32 deltaL32Q31 = panner->deltaL32Q31; + int32 delaySamples = panner->delaySamples; + + /*----------------------------- if no ramp ------------------------------*/ + if (rampSamples <= 0) + { + /* set counter, delta, and delay to zero */ + sampleCounter = 0; + deltaL32Q31 = 0; + delaySamples = 0; // if no ramp, set gain immediately without delay + } /* end of if (rampSamples <= 0) */ + + /*--------------- if there is ramp, then need some work here ------------*/ + else /* (rampSamples > 0) */ + { + /*------ set new delay samples ------*/ + delaySamples = newDelay; + + /*------ get current gain ------*/ + currentGainL16Q15 = panner_get_current(*panner); + + /*------ determine change -------*/ + changeL16Q15 = s16_sub_s16_s16_sat(newGainL16Q15, currentGainL16Q15); + /* if no change, then reset counter and delta */ + if (changeL16Q15 == 0) + { + sampleCounter = 0; + deltaL32Q31 = 0; + } + /* else, set counter and calculate delta */ + else /* (changeL16Q15 != 0) */ + { + sampleCounter = rampSamples; + /* Q15/Q0, s16_add_s16_s16 Q16: delta will be in Q31 */ + deltaL32Q31 = divide_qx(changeL16Q15, sampleCounter, 16); + } + } /* end of else (rampSamples > 0) */ + + /*------------- store new values back into panner struct ----------------*/ + panner->targetGainL16Q15 = newGainL16Q15; // update target gain + panner->sampleCounter = sampleCounter; + panner->deltaL32Q31 = deltaL32Q31; + panner->delaySamples = delaySamples; +} /*------------------- end of function panner_setup ------------------------*/ + +/*===========================================================================*/ +/* FUNCTION : buffer_fill_with_panner */ +/* */ +/* DESCRIPTION: Apply L16Q15 panner to input and store it into output buffer.*/ +/* */ +/* INPUTS: destBuf-> output buffer */ +/* srcBuf-> input buffer */ +/* panner-> panner to be applied to the input buffer */ +/* samples: total number of samples to be processed */ +/* OUTPUTS: destBuf-> output buffer */ +/* panner-> panner internal variables gets updated also */ +/* */ +/* IMPLEMENTATION NOTES: */ +/* Assuming samples <= 32767 */ +/*===========================================================================*/ +void buffer_fill_with_panner +( + int16 *destBuf, /* output buffer */ + int16 *srcBuf, /* input buffer */ + pannerStruct *panner, /* panner applied to the samples */ + int32 samples /* number of samples to process */ +) +{ + int16 *srcPtr = srcBuf; + int16 *destPtr = destBuf; + int16 targetGainL16Q15 = panner->targetGainL16Q15; + int32 panSamples = panner->sampleCounter; + int32 deltaL32Q31 = panner->deltaL32Q31; + int32 pannerDelay = panner->delaySamples; + int16 currentGainL16Q15; + int32 currentGainL32Q31; + + int32 i, rampSamples; + + /*-----------------------------------------------------------------------*/ + + /*--------------------- get current panner gain value -------------------*/ + currentGainL16Q15 = panner_get_current(*panner); + + /*------ if the panner is just a static gain, switch to buffer_fill -----*/ + if (panSamples == 0 || deltaL32Q31 == 0 || pannerDelay >= samples) + { + buffer_fill(destBuf, srcBuf, currentGainL16Q15, samples); + if (pannerDelay >= samples) + { + /* if delay of ramping is greater than the current frame */ + panner->delaySamples = s32_sub_s32_s32_sat(pannerDelay, samples); + } + return; + } /* end of if panner is static gain */ + + /*--------- otherwise, panner is doing ramped mix somewhere ------------*/ + /*--- 1. if there still leftover delays in panner, mix that part ---*/ + if (pannerDelay > 0) + { + buffer_fill(destBuf,srcBuf,currentGainL16Q15,s16_extract_s32_l(pannerDelay)); + } + samples = s32_sub_s32_s32_sat(samples, pannerDelay); + + /*--- 2. now start processing ramped samples ---*/ + /* advance input / output pointers by what's processed so far */ + srcPtr += pannerDelay; + destPtr += pannerDelay; + + /* convert current gain to Q31 version */ + currentGainL32Q31 = s32_shl_s32_sat((int32)currentGainL16Q15, 16); + + /* if panner ramp is shorter than remaining sample number in the frame, */ + /* we only process this part with panner, and directly mix the rest */ + rampSamples = s16_extract_s32_l(s32_min_s32_s32(samples, panSamples)); + + /* process the ramped samples with panner */ + for (i = 0; i < rampSamples; i ++) + { + /* covert back the current gain in L16Q15 */ + currentGainL16Q15 = s16_extract_s32_h(currentGainL32Q31); +#if ((defined __hexagon__) || (defined __qdsp6__)) + + *destPtr = s16_extract_s40_h(Q6_P_mpy_RlRl_s1(*srcPtr, currentGainL16Q15)); + destPtr++; + srcPtr++; + +#else + /* apply the Q15 gain to input, and store output */ + *destPtr = s16_extract_s64_h_rnd(s40_mult_s16_s16_shift(*srcPtr, currentGainL16Q15,1)); + destPtr++; + srcPtr++; +#endif + /* update the current gain, adding delta to it, Q31 */ + currentGainL32Q31 = s32_add_s32_s32_sat(currentGainL32Q31, deltaL32Q31); + } /* end of for (i = 0; i < rampSamples; i ++) */ + samples = s32_sub_s32_s32_sat(samples, rampSamples); + + /*--- 3. if still have samples left, mix them with static gain ---*/ + if (samples > 0) + { + buffer_fill(destPtr, srcPtr, targetGainL16Q15, samples); + } + + /*------------------------ advance the panner ---------------------------*/ + if (panSamples > 0) + { + panSamples = s32_sub_s32_s32_sat(panSamples, rampSamples); + if (panSamples == 0) + { deltaL32Q31 = 0; } + } + panner->delaySamples = 0; + panner->sampleCounter = panSamples; + panner->deltaL32Q31 = deltaL32Q31; +} /*---------------- end of function buffer_fill_with_panner ---------------*/ + + +/*===========================================================================*/ +/* FUNCTION : buffer_scale_mix */ +/* */ +/* DESCRIPTION: Apply L16Q15 panner to input and mix (sum) it into a running */ +/* output buffer. */ +/* */ +/* INPUTS: destBuf-> output buffer */ +/* srcBuf-> input buffer */ +/* panner-> panner to be applied to the input buffer */ +/* samples: total number of samples to be processed */ +/* OUTPUTS: destBuf-> output buffer */ +/* panner-> panner internal variables gets updated also */ +/* */ +/* IMPLEMENTATION NOTES: */ +/* Assuming that samples <= 32767 */ +/*===========================================================================*/ +void buffer_mix_with_panner +( + int16 *destBuf, /* output buffer */ + int16 *srcBuf, /* input buffer */ + pannerStruct *panner, /* panner applied to the samples */ + int32 samples /* number of samples to process */ +) +{ + int16 *srcPtr = srcBuf; + int16 *destPtr = destBuf; + int16 targetGainL16Q15 = panner->targetGainL16Q15; + int32 panSamples = panner->sampleCounter; + int32 deltaL32Q31 = panner->deltaL32Q31; + int32 pannerDelay = panner->delaySamples; + int16 currentGainL16Q15; + int32 currentGainL32Q31; + + int32 i, rampSamples; + +#ifndef __qdsp6__ + int16 tmpL16; +#endif + + /*-----------------------------------------------------------------------*/ + + /*--------------------- get current panner gain value -------------------*/ + currentGainL16Q15 = panner_get_current(*panner); + + /*------ if the panner is just a static gain, switch to buffer_mix ------*/ + if (panSamples == 0 || deltaL32Q31 == 0 || pannerDelay >= samples) + { + buffer_mix(destBuf, srcBuf, currentGainL16Q15, samples); + if (pannerDelay >= samples) + { + /* if delay of ramping is greater than the current frame */ + panner->delaySamples = s32_sub_s32_s32_sat(pannerDelay, samples); + } + return; + } /* end of if (sampleCounter == 0) */ + + /*---------------- panner is doing ramped mix somewhere ----------------*/ + /*--- 1. if there still leftover delays in panner, mix that part ---*/ + if (pannerDelay > 0) + { + buffer_mix(destBuf, srcBuf, currentGainL16Q15, s16_extract_s32_l(pannerDelay)); + } + samples = s32_sub_s32_s32_sat(samples, pannerDelay); + + /*--- 2. now start processing ramped samples ---*/ + /* advance input / output pointers by what's processed so far */ + srcPtr += pannerDelay; + destPtr += pannerDelay; + + /* convert current gain to Q31 version */ + currentGainL32Q31 = s32_shl_s32_sat((int32)currentGainL16Q15, 16); + + /* if panner ramp is shorter than remaining sample number in the frame, */ + /* we only process this part with panner, and directly mix the rest */ + rampSamples = s16_extract_s32_l(s32_min_s32_s32(samples, panSamples)); + + /* process the ramped samples with panner */ + for (i = 0; i < rampSamples; i ++) + { + /* covert back the current gain in L16Q15 */ + currentGainL16Q15 = s16_extract_s32_h(currentGainL32Q31); +#if ((defined __hexagon__) || (defined __qdsp6__)) + + *destPtr = s16_add_s16_s16_sat(*destPtr, s16_extract_s40_h(Q6_P_mpy_RlRl_s1(*srcPtr, currentGainL16Q15))); + srcPtr++; + destPtr++; + +#else + /* apply the Q15 gain to input buffer sample */ + tmpL16 = s16_extract_s40_h(s40_mult_s16_s16_shift(*srcPtr, currentGainL16Q15, 1)); + srcPtr++; + /* mix the altered sample into running output buffer */ + *destPtr = s16_add_s16_s16_sat(*destPtr, tmpL16); + destPtr++; + +#endif + /* update the current gain, adding delta to it, Q31 */ + currentGainL32Q31 = s32_add_s32_s32_sat(currentGainL32Q31, deltaL32Q31); + } /* end of for (i = 0; i < rampSamples; i ++) */ + samples = s32_sub_s32_s32_sat(samples, rampSamples); + + /*--- 3. if still have samples left, mix them with static gain ---*/ + if (samples > 0) + { + buffer_mix(destPtr, srcPtr, targetGainL16Q15, samples); + } + + /*------------------------ advance the panner ---------------------------*/ + if (panSamples > 0) + { + panSamples = s32_sub_s32_s32_sat(panSamples, rampSamples); + if (panSamples == 0) + { deltaL32Q31 = 0; } + } + panner->delaySamples = 0; + panner->sampleCounter = panSamples; + panner->deltaL32Q31 = deltaL32Q31; +} /*---------------- end of function buffer_mix_with_panner ----------------*/ + +/*===========================================================================*/ +/* FUNCTION : get_cur_cir_scale */ +/* */ +/* DESCRIPTION: Return current angle of an angle panner */ +/* */ +/* INPUTS: panner: angle panner struct */ +/* OUTPUTS: return current angle in L32Q16 */ +/* */ +/* IMPLEMENTATION NOTES: */ +/* Angle values are unit 0~32 Q16, corresponding to 0~180 degrees. */ +/*===========================================================================*/ +int32 get_cur_cir_scale +( + anglePannerStruct panner /* angle panner struct */ +) +{ + int32 productL32, deltaL32Q24; + int16 aHighL16, bHighL16; + uint16 aLowUL16, bLowUL16; + boolean negative; + + if (panner.sampleCounter == 0 || panner.deltaL32Q24 == 0) + { /* if counter zeroed, target angle already achieved */ + return panner.targetAngleL32Q16; + } + else + { + /* calculate diff of current angle from target angle, Q16 */ +#if 1 + deltaL32Q24 = panner.deltaL32Q24; + negative=FALSE; +#else + if (panner.deltaL32Q24 < 0) + { + deltaL32Q24 = -panner.deltaL32Q24; + negative = TRUE; + } + else + { + deltaL32Q24 = panner.deltaL32Q24; + negative = FALSE; + } +#endif + /* multiply delta and sample counter, result down-shift 8 */ + aLowUL16 = s16_extract_s32_l(deltaL32Q24); + aHighL16 = s16_extract_s32_h(deltaL32Q24); + bLowUL16 = s16_extract_s32_l(panner.sampleCounter); + bHighL16 = s16_extract_s32_h(panner.sampleCounter); + + productL32 = s32_saturate_s40(s40_shl_s40(s40_add_s40_s40(u32_mult_u16_u16(aLowUL16, bLowUL16), 0), -8)); + productL32 = s32_add_s32_s32(productL32, s32_shl_s32_sat(s32_mult_s16_u16(aHighL16, bLowUL16), 8)); + productL32 = s32_add_s32_s32(productL32, s32_shl_s32_sat(s32_mult_s16_u16(bHighL16, aLowUL16), 8)); + productL32 = s32_add_s32_s32(productL32, s32_shl_s32_sat(s32_mult_s16_s16(aHighL16, bHighL16), 24)); + + productL32 = negative ? -productL32 : productL32; + + /* get current gain */ + return s32_sub_s32_s32_sat(panner.targetAngleL32Q16, productL32); + } +} /*------------- end of function get_cur_cir_scale ------------------*/ + + +/*===========================================================================*/ +/* FUNCTION : cir_scale_setup */ +/* */ +/* DESCRIPTION: According to new target angle and ramp sample number, setup */ +/* an angle panner's target angle, delta and sample counter. */ +/* */ +/* INPUTS: panner-> angle panner struct */ +/* newAngleL32Q16: new target angle, L32Q16 */ +/* rampSamples: number of samples in the ramp */ +/* OUTPUTS: panner-> angle panner struct, with values set */ +/* */ +/* IMPLEMENTATION NOTES: */ +/*===========================================================================*/ +void cir_scale_setup +( + anglePannerStruct *panner, /* angle panner struct */ + int32 newAngleL32Q16, /* new target angle */ + int32 rampSamples /* number of samples in the ramp */ +) +{ + int32 changeL32Q16, currentAngleL32Q16; + + /* substitute angle panner struct values with shorter names */ + int32 sampleCounter = panner->sampleCounter; + int32 deltaL32Q24 = panner->deltaL32Q24; + + /*----------------------------- if no ramp ------------------------------*/ + if (rampSamples <= 0) + { + /* set counter and delta to zero */ + sampleCounter = 0; + deltaL32Q24 = 0; + } /* end of if (rampSamples <= 0) */ + + /*--------------- if there is ramp, then need some work here ------------*/ + else /* (rampSamples > 0) */ + { + /*------ get current gain ------*/ + currentAngleL32Q16 = get_cur_cir_scale(*panner); + + /*------ determine change -------*/ + changeL32Q16 = s32_sub_s32_s32_sat(newAngleL32Q16, currentAngleL32Q16); + /* if no change, then reset counter and delta */ + if (changeL32Q16 == 0) + { + sampleCounter = 0; + deltaL32Q24 = 0; + } + /* else, set counter and calculate delta */ + else /* (changeL16Q15 != 0) */ + { + sampleCounter = rampSamples; + /* Q16/Q0, add Q8: delta will be in Q24 */ + + //deltaL32Q24 = divide_qx(changeL32Q16, sampleCounter, 8); + + + deltaL32Q24 = s32_saturate_s40(audio_divide_dp(changeL32Q16, sampleCounter, -21)); + + + // temp = divide_qx(changeL32Q16, sampleCounter, 8); + // if (temp != deltaL32Q24) + // deltaL32Q24 = temp; + } + } /* end of else (rampSamples > 0) */ + + /*--------- store new values back into angle panner struct --------------*/ + panner->targetAngleL32Q16 = newAngleL32Q16; // update target angle + panner->sampleCounter = sampleCounter; + panner->deltaL32Q24 = deltaL32Q24; +} /*---------------- end of function cir_scale_setup ---------------------*/ + +/*===========================================================================*/ +/* FUNCTION : dscale_setup */ +/* */ +/* DESCRIPTION: According to new target gain and ramp sample number, setup */ +/* a panner's target gain, delta and sample counter. */ +/* */ +/* INPUTS: panner-> panner struct */ +/* newGainL16Q15: new target panner gain */ +/* rampSamples: number of samples in the ramp */ +/* newDelay: delay after setup to execute the panner, in sample */ +/* OUTPUTS: panner-> panner struct, with values set */ +/* */ +/* IMPLEMENTATION NOTES: */ +/* By inputting zero ramSamples and zero newDelay, this function can */ +/* set a static gain to the panner immediately. */ +/*===========================================================================*/ +void dscale_setup +( + pannerStruct *panner, /* panner struct */ + int16 newGainL16Q15, /* new target panner gain */ + int32 rampSamples, /* number of samples in the ramp */ + int32 newDelay /* delay of ramping process */ +) +{ + int16 changeL16Q15, currentGainL16Q15; + + /* substitute panner struct values with shorter names */ + int32 sampleCounter = panner->sampleCounter; + int32 deltaL32Q31 = panner->deltaL32Q31; + int32 delaySamples = panner->delaySamples; + + /*----------------------------- if no ramp ------------------------------*/ + if (rampSamples <= 0) + { + + delaySamples = newDelay; + if (newDelay == 0) + { + /* set counter, delta, and delay to zero */ + sampleCounter = 0; + deltaL32Q31 = 0; + } + else /* the panner has a delay, but become static after the delay */ + { + currentGainL16Q15 = panner_get_current(*panner); + sampleCounter = 1; + deltaL32Q31 = s32_shl_s32_sat(s16_sub_s16_s16_sat(newGainL16Q15, currentGainL16Q15), 16); + } + } /* end of if (rampSamples <= 0) */ + + /*--------------- if there is ramp, then need some work here ------------*/ + else /* (rampSamples > 0) */ + { + /*------ set new delay samples ------*/ + delaySamples = newDelay; + + /*------ get current gain ------*/ + currentGainL16Q15 = panner_get_current(*panner); + + /*------ determine change -------*/ + changeL16Q15 = s16_sub_s16_s16_sat(newGainL16Q15, currentGainL16Q15); + /* if no change, then reset counter and delta */ + if (changeL16Q15 == 0) + { + sampleCounter = 0; + deltaL32Q31 = 0; + } + /* else, set counter and calculate delta */ + else /* (changeL16Q15 != 0) */ + { + sampleCounter = rampSamples; + /* Q15/Q0, s16_add_s16_s16 Q16: delta will be in Q31 */ + deltaL32Q31 = divide_qx(changeL16Q15, sampleCounter, 16); + } + } /* end of else (rampSamples > 0) */ + + /*------------- store new values back into panner struct ----------------*/ + panner->targetGainL16Q15 = newGainL16Q15; // update target gain + panner->sampleCounter = sampleCounter; + panner->deltaL32Q31 = deltaL32Q31; + panner->delaySamples = delaySamples; +} /*------------------- end of function dscale_setup -------------*/ \ No newline at end of file diff --git a/modules/cmn/common/utils/src/basic_math.c b/modules/cmn/common/utils/src/basic_math.c new file mode 100644 index 0000000..00401b9 --- /dev/null +++ b/modules/cmn/common/utils/src/basic_math.c @@ -0,0 +1,506 @@ +/**========================================================================= + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause +============================================================================ */ + +/* FILE NAME: basic_math.c * + * DESCRIPTION: * + * Contains basic math operations such as square-root, logarithm, * + * exponential etc. * + * * + * FUNCTION LIST: * + * int32_t div_compute () * + * int32_t sqrt_compute () * + * int32_t audio_log_compute () * + * int32_t exp_compute () * + * int32_t div_int32_t () * + *===========================================================================*/ + +/*===========================================================================* + * INCLUDE HEADER FILES * + *===========================================================================*/ +#include +#include +#include "basic_math.h" +#include "audio_fft_basic_ops.h" + +#ifndef __qdsp6__ + +#include "audio_divide_qx.h" +#define div_s s16_div_s16_s16_sat + +/*============================================================================*/ +/* */ +/* FUNCTION: sqrt_compute */ +/* DESCRIPTION: */ +/* Implements a cubic spline approximation for computing square-root of */ +/* of a 32-bit fractional number */ +/* PRECONDITIONS: */ +/* pwrInL32: Input 32-bit power value */ +/* iqInL16: Integer part of Q-factor for the input power */ +/* iqOutL16: Integer part of Q-factor for the square-root output */ +/* Input and output Q-factors can be specified any value from 0 to 31 */ +/* Output will be rounded and saturated according to the iqOutL16 value */ +/* POSTCONDITIONS: */ +/* sqrtL32: Returns the 32-bit square-root output */ +/* */ +/* Square root too expensive, so the square root is approximated */ +/* with third order polynomial computed by Song Wang. The input power is */ +/* normalized to be in the range 1 <= cal_in < 4 and the approximate */ +/* square-root is computed and then it is normalized back appropriately. */ +/* */ +/* sqrtL32 = ((S0*pwrInL32 + S1)*pwrInL32 + S2)*pwrInL32 + S3. */ +/* S0 = 0.0089 Q15, S1 = -0.1029 Q29, S2 = 0.6605 Q29, S3 = 0.4334 Q29 */ +/* sqrt error ratio is less than -50 dB for all positive real numbers */ +/*============================================================================*/ +int32_t sqrt_compute ( int32_t pwrInL32, + int16_t iqInL16, + int16_t iqOutL16 + ) +{ + int32_t accu32, sqrtL32, rnd32; + int16_t pwrInL16, pwr_norm, mant_norm, pwr_offset, out_norm; + + if (pwrInL32 <= 0) + { + sqrtL32 = 0; + return sqrtL32; + } + + // Computing normalization factor for extracting the mantissa of the input power + pwr_norm = s16_norm_s32(pwrInL32); // Compute norm of the input power + mant_norm = (iqInL16-1) - pwr_norm; // Compute integer part of log2 norm of the input power + mant_norm = (mant_norm >>1)<<1; // Compute highest even integer less than log2 norm + pwr_offset = (iqInL16-2) - mant_norm; // Compute offset for shifting the Qfactor to Q2.29 + + // Normalizing the power input so that it falls in the range: 1 <= pwr_in < 4 + // and then changing its Qfactor to Q2.29 to allow maximum precision represenation + pwrInL32 = s32_shl_s32_sat(pwrInL32, pwr_offset); + pwrInL16 = s16_round_s32_sat(pwrInL32); // Round to 16-bits Q2.13 + + // Implementing cubic approximation to compute the square-root + accu32 = (int32_t )s32_mult_s32_s16_rnd_sat(pwrInL32, SQRT_S0); // S0*x + sqrtL32 = s32_add_s32_s32_sat(accu32, SQRT_S1); // S0*x+S1 + accu32 = (int32_t )s32_mult_s32_s16_rnd_sat(sqrtL32, pwrInL16); // (S0*x+S1)*x + accu32 = s32_shl_s32_sat(accu32, 2); + sqrtL32 = s32_add_s32_s32_sat(accu32, SQRT_S2); // (S0*x+S1)*x+S2 + accu32 = (int32_t )s32_mult_s32_s16_rnd_sat(sqrtL32, pwrInL16); // ((S0*x+S1)*x+S2)*x + accu32 = s32_shl_s32_sat(accu32, 2); + sqrtL32 = s32_add_s32_s32_sat(accu32, SQRT_S3); // ((S0*x+S1)*x+S2)*x+S3 + + // Scaling back and rounding and adjusting the Q-factor of the square-root result + out_norm = (iqOutL16-2) - (mant_norm >>1); + rnd32 = s32_shl_s32_sat(((int32_t )0x0001),out_norm-1); // Rounding value for shifting + sqrtL32 = s32_add_s32_s32_sat(sqrtL32, rnd32); // Rounding the result before shifting + sqrtL32 = s32_shl_s32_sat(sqrtL32, -out_norm); + + return sqrtL32; +} +/*============================================================================*/ +/* */ +/* FUNCTION: audio_log_compute */ +/* DESCRIPTION: */ +/* Implements a normalized cubic approximation for computing log10 */ +/* of a 32-bit number */ +/* PRECONDITIONS: */ +/* pwrInL32: Input 32-bit power value */ +/* iqInL16: Integer part of Q-factor for the input power */ +/* iqOutL16: Integer part of Q-factor for the log10 output */ +/* logConstL16Q14: Constant to convert from log2 to log_base */ +/* To compute log10, let logConstL16Q14 = log10(2) in Q14 */ +/* Input and output Q-factors can be specified any value from 0 to 31 */ +/* Output will be rounded and saturated according to the iqOutL16 value */ +/* Depending on the value of logConstL16Q14 you can compute log with */ +/* any base. logConstL16Q14 = log_base(2) in Q14 */ +/* POSTCONDITIONS: */ +/* logL32: Returns the 32-bit log10 output */ +/* */ +/* Logarithm is too expensive, so it is approximated as a cubic */ +/* third order polynomial. The input power is expressed in mantissa and */ +/* exponent with mantissa in the range [1,2] and log2 approximation is */ +/* applied only to mantissa and log2 of the input power is computed. */ +/* logL32 = ((C0*mant + C1)*mant + C2)*mant + C3 + exponent. */ +/* C0 = 0.1840 Q15, C1 = -1.1688 Q2.29, C2 = 3.2187 Q2.29, C3 = -2.2340 Q2.29 */ +/* mean squared error ratio is less than -50 dB for all positive real numbers */ +/* Finally, log to any base is computed from log2 using the logConst value */ +/*============================================================================*/ +int32_t audio_log_compute ( int32_t pwrInL32, + int16_t iqInL16, + int16_t iqOutL16, + int16_t logConstL16Q14 + ) +{ + int32_t accu32, logL32, rnd32, out_exp32; + int16_t pwrInL16, pwr_norm, exp_norm, mant_norm, pwr_offset, out_norm; + + if (pwrInL32 <= 0) + { + logL32 = MIN_32; + return logL32; + } + + // Computing normalization factor for extracting the mantissa of the input power + pwr_norm = s16_norm_s32(pwrInL32); // Compute norm of the input power + mant_norm = (iqInL16-1) - pwr_norm; // Compute integer part of log2 norm of the input power + pwr_offset =(iqInL16-2) - mant_norm; // Compute offset for shifting the Qfactor to Q2.29 + + // Normalizing the power input so that it falls in the range: 1 <= pwr_in < 2 + // and then changing its Qfactor to Q2.29 to allow maximum precision represenation + pwrInL32 = s32_shl_s32_sat(pwrInL32, pwr_offset); + pwrInL16 = s16_round_s32_sat(pwrInL32); // Round to 16-bits Q2.13 + + // Implementing cubic approximation to compute log2 of the mantissa + accu32 = (int32_t )s32_mult_s32_s16_rnd_sat(pwrInL32, LOG_C0); // C0*x + logL32 = s32_add_s32_s32_sat(accu32, LOG_C1); // C0*x+C1 + accu32 = (int32_t )s32_mult_s32_s16_rnd_sat(logL32, pwrInL16); // (C0*x+C1)*x + accu32 = s32_shl_s32_sat(accu32, 2); + logL32 = s32_add_s32_s32_sat(accu32, LOG_C2); // (C0*x+C1)*x+C2 + accu32 = (int32_t )s32_mult_s32_s16_rnd_sat(logL32, pwrInL16); // ((C0*x+C1)*x+C2)*x + accu32 = s32_shl_s32_sat(accu32, 2); + logL32 = s32_add_s32_s32_sat(accu32, LOG_C3); // ((C0*x+C1)*x+C2)*x+C3 + + // Normalizing the exponent so as to express it with highest precision + out_exp32 = (int32_t )mant_norm; + exp_norm = s16_norm_s32(out_exp32); + out_exp32 = s32_shl_s32_sat(out_exp32, exp_norm); // Qfactor shifted Q(31-exp_norm).exp_norm + + // Converting from log2 to log10 of the input + // After conversion, logL32 is Q3.28 and out_exp32 is Q(32-exp_norm).(exp_norm-1) + logL32 = (int32_t )s32_mult_s32_s16_rnd_sat(logL32, logConstL16Q14); + out_exp32 = (int32_t )s32_mult_s32_s16_rnd_sat(out_exp32, logConstL16Q14); + + // Rounding and adjusting the Q-factor of the log of the mantissa + out_norm = iqOutL16-3; + rnd32 = s32_shl_s32_sat(((int32_t )0x0001),out_norm-1); // Rounding value for shifting + logL32 = s32_add_s32_s32_sat(logL32, rnd32); // Rounding the result before shifting + logL32 = s32_shl_s32_sat(logL32, -out_norm); + + // Adjusting the Q-factor of the input exp and adding it to the output + out_exp32 = s32_shl_s32_sat(out_exp32, (32-exp_norm)-iqOutL16); + logL32 = s32_add_s32_s32_sat(logL32, out_exp32); + + return logL32; +} +/*============================================================================*/ +/* */ +/* FUNCTION: exp_compute */ +/* DESCRIPTION: */ +/* Implements a normalized cubic approximation for computing exp */ +/* of a 32-bit number */ +/* PRECONDITIONS: */ +/* xInL32: Input 32-bit number */ +/* iqInL16: Integer part of Q-factor for the input */ +/* iqOutL16: Integer part of Q-factor for the exp output */ +/* expConstL16Q13: Constant to convert from 2^(x) to exp(x) */ +/* To compute exp(x), let expConstL16Q13 = log2(exp) in Q13 */ +/* Input and output Q-factors can be specified any value from 0 to 31 */ +/* Output will be rounded and saturated according to the iqOutL16 value */ +/* Depending on the value of expConstL16Q13 you can compute 2^(x) or */ +/* exp(x) or 10^(x). expConstL16Q13 = log2(base_power) */ +/* Since it is in Q13 format, cannot use more than 16^(x) with it. */ +/* POSTCONDITIONS: */ +/* expL32: Returns the 32-bit exp output */ +/* */ +/* Exponential is too expensive, so it is approximated as a cubic */ +/* third order polynomial. The input number is scaled down to the range */ +/* [0,1] and exp approximation is applied and the output is scaled back */ +/* to the original range. */ +/* expL32 = (((C0*a*x + C1)*a*x + C2)*a*x + C3)*2.^n. */ +/* a = log2(e) = 1.4427 Q2.13 - can be modified to compute power to any base */ +/* C0 = 0.0778 Q15, C1 = 0.2258 Q2.29, C2 = 0.6962 Q2.29, C3 = 1 Q2.29 */ +/* mean squared error ratio is less than -50 dB for all real numbers */ +/*============================================================================*/ +int32_t exp_compute ( int32_t xInL32, + int16_t iqInL16, + int16_t iqOutL16, + int16_t expConstL16Q13 + ) +{ + int32_t accu32, expL32, rnd32, x_shift32; + int16_t xInL16, x_floor, x_norm, out_norm; + + // Computing normalization factor for adjusting the Q-factor of the input + x_norm = s16_norm_s32(xInL32); + + x_norm = s16_min_s16_s16(x_norm, 11); + // Normalize the input by shifting its iq to (iq-x_norm+2) for better precision + xInL32 = s32_shl_s32_sat(xInL32, x_norm-2); + + // Multiply the input with log2(e) to enable computing 2^x instead of e^x + xInL32 = (int32_t )s32_mult_s32_s16_rnd_sat(xInL32, expConstL16Q13); + xInL32 = s32_shl_s32_sat(xInL32,2); // Qfactor compensation for expConstL16Q13 (Q2.29) + + // Extract the integer part of the input so as to subtract it from the input + x_floor = s16_saturate_s32(s32_shl_s32_sat(xInL32, iqInL16-29-x_norm)); + + // Shift back the integer part and subtract it from the input to get its fractional part + x_shift32 = s32_shl_s32_sat((int32_t )x_floor, x_norm+29-iqInL16); + xInL32 = s32_sub_s32_s32_sat(xInL32,x_shift32); + + // Re-scale the fractional part of the input so as to shift its Qfactor to Q2.29 + xInL32 = s32_shl_s32_sat(xInL32, iqInL16-x_norm); + xInL16 = s16_round_s32_sat(xInL32); // Round to 16-bits Q2.13 + + // Implementing cubic approximation to compute 2^x of the fractional part + accu32 = (int32_t )s32_mult_s32_s16_rnd_sat(xInL32, EXP_C0); // C0*x + expL32 = s32_add_s32_s32_sat(accu32, EXP_C1); // C0*x+C1 + accu32 = (int32_t )s32_mult_s32_s16_rnd_sat(expL32, xInL16); // (C0*x+C1)*x + accu32 = s32_shl_s32_sat(accu32, 2); + expL32 = s32_add_s32_s32_sat(accu32, EXP_C2); // (C0*x+C1)*x+C2 + accu32 = (int32_t )s32_mult_s32_s16_rnd_sat(expL32, xInL16); // ((C0*x+C1)*x+C2)*x + accu32 = s32_shl_s32_sat(accu32, 2); + expL32 = s32_add_s32_s32_sat(accu32, EXP_C3); // ((C0*x+C1)*x+C2)*x+C3 + + // Scaling back and rounding and adjusting the Q-factor of the square-root result + out_norm = (iqOutL16-2) - x_floor; + rnd32 = s32_shl_s32_sat(((int32_t )0x0001),out_norm-1); // Rounding value for shifting + expL32 = s32_add_s32_s32_sat(expL32, rnd32); // Rounding the result before shifting + expL32 = s32_shl_s32_sat(expL32, -out_norm); + + return expL32; +} + +#endif /* __qdsp6__ */ + +/*============================================================================*/ +/* */ +/* FUNCTION: sqrt_compute_Nth */ +/* DESCRIPTION: */ +/* Implements an Nth order polynomial approximation for computing */ +/* square-root of a 32-bit fractional number */ +/* PRECONDITIONS: */ +/* pwrInL32: Input 32-bit power value */ +/* iqInL16: Integer part of Q-factor for the input power */ +/* iqOutL16: Integer part of Q-factor for the square-root output */ +/* Input and output Q-factors can be specified any value from 0 to 31 */ +/* Output will be rounded and saturated according to the iqOutL16 value */ +/* POSTCONDITIONS: */ +/* sqrtL32: Returns the 32-bit square-root output */ +/* */ +/* Square root too expensive, so the square root is approximated */ +/* with third order polynomial computed by Song Wang. The input power is */ +/* normalized to be in the range 1 <= cal_in < 4 and the approximate */ +/* square-root is computed and then it is normalized back appropriately. */ +/* */ +/* sqrtL32 = ((S0*pwrInL32 + S1)*pwrInL32 + S2)*pwrInL32 + S3. */ +/* sqrtL32 = sqrtL32 * pwrInL32 + S4 */ +/* sqrtL32 = sqrtL32 * pwrInL32 + S5 */ +/*============================================================================*/ +#if SQRT_NTH_ORD == 5 // 5th +/* approx sqrt(x), 1<= x <4, by 5th order poly, < -74dB error */ + #define SQRT_NTH_S0_L16 23669 + #define SQRT_NTH_S0_Q -10 + #define SQRT_NTH_S1_Q29 -5896532 + #define SQRT_NTH_S2_Q29 38497248 + #define SQRT_NTH_S3_Q29 -143483846 + #define SQRT_NTH_S4_Q29 460002699 + #define SQRT_NTH_S5_Q29 187372645 +#endif +#if SQRT_NTH_ORD == 4 // 4th +/* approx sqrt(x), 1<= x <4, by 4th order poly, < -64dB error */ + #define SQRT_NTH_S0_L16 -20234 + #define SQRT_NTH_S0_Q -8 + #define SQRT_NTH_S1_Q29 17131329 + #define SQRT_NTH_S2_Q29 -96397849 + #define SQRT_NTH_S3_Q29 411055888 + #define SQRT_NTH_S4_Q29 206376491 +#endif +#if SQRT_NTH_ORD == 3 // 3rd +/* approx sqrt(x), 1<= x <4, by 3rd order poly, < -52dB error */ + #define SQRT_NTH_S0_L16 17249 + #define SQRT_NTH_S0_Q -6 + #define SQRT_NTH_S1_Q29 -52587488 + #define SQRT_NTH_S2_Q29 349139229 + #define SQRT_NTH_S3_Q29 235903316 +#endif + +int32 sqrt_compute_Nth ( int32 pwrInL32, + int16 iqInL16, + int16 iqOutL16 + ) +{ + int32 accu32, sqrtL32, rnd32; + int16 pwrInL16, pwr_norm, mant_norm, pwr_offset, out_norm; + + if (pwrInL32 <= 0) + { + sqrtL32 = 0; + return sqrtL32; + } + + // Computing normalization factor for extracting the mantissa of the input power + pwr_norm = s16_norm_s32(pwrInL32); // Compute norm of the input power + mant_norm = (iqInL16-1) - pwr_norm; // Compute integer part of log2 norm of the input power + mant_norm = (mant_norm >>1)<<1; // Compute highest even integer less than log2 norm + pwr_offset = (iqInL16-2) - mant_norm; // Compute offset for shifting the Qfactor to Q2.29 + + // Normalizing the power input so that it falls in the range: 1 <= pwr_in < 4 + // and then changing its Qfactor to Q2.29 to allow maximum precision represenation + pwrInL32 = s32_shl_s32_sat(pwrInL32, pwr_offset); + pwrInL16 = s16_round_s32_sat(pwrInL32); // Round to 16-bits Q2.13 + + // Implementing cubic approximation to compute the square-root + accu32 = (int32 )s32_mult_s32_s16_rnd_sat(pwrInL32, SQRT_NTH_S0_L16); // S0*x + accu32 = s32_shl_s32_sat(accu32, SQRT_NTH_S0_Q); // S0 Q + sqrtL32 = s32_add_s32_s32_sat(accu32, SQRT_NTH_S1_Q29); // S0*x+S1 + accu32 = (int32 )s32_mult_s32_s16_rnd_sat(sqrtL32, pwrInL16); // (S0*x+S1)*x + accu32 = s32_shl_s32_sat(accu32, 2); + sqrtL32 = s32_add_s32_s32_sat(accu32, SQRT_NTH_S2_Q29); // (S0*x+S1)*x+S2 + accu32 = (int32 )s32_mult_s32_s16_rnd_sat(sqrtL32, pwrInL16); // ((S0*x+S1)*x+S2)*x + accu32 = s32_shl_s32_sat(accu32, 2); + sqrtL32 = s32_add_s32_s32_sat(accu32, SQRT_NTH_S3_Q29); // ((S0*x+S1)*x+S2)*x+S3 +#if SQRT_NTH_ORD > 3 + accu32 = (int32 )s32_mult_s32_s16_rnd_sat(sqrtL32, pwrInL16); // (((S0*x+S1)*x+S2)*x+S3)*x + accu32 = s32_shl_s32_sat(accu32, 2); + sqrtL32 = s32_add_s32_s32_sat(accu32, SQRT_NTH_S4_Q29); // (((S0*x+S1)*x+S2)*x+S3)*x+S4 +#endif +#if SQRT_NTH_ORD > 4 + accu32 = (int32 )s32_mult_s32_s16_rnd_sat(sqrtL32, pwrInL16); // ((((S0*x+S1)*x+S2)*x+S3)*x+S4)*x + accu32 = s32_shl_s32_sat(accu32, 2); + sqrtL32 = s32_add_s32_s32_sat(accu32, SQRT_NTH_S5_Q29); // ((((S0*x+S1)*x+S2)*x+S3)*x+S4)*x+S5 +#endif + // Scaling back and rounding and adjusting the Q-factor of the square-root result + out_norm = (iqOutL16-2) - (mant_norm >>1); + rnd32 = s32_shl_s32_sat(((int32 )0x0001),out_norm-1); // Rounding value for shifting + sqrtL32 = s32_add_s32_s32_sat(sqrtL32, rnd32); // Rounding the result before shifting + sqrtL32 = s32_shl_s32_sat(sqrtL32, -out_norm); + + return sqrtL32; +} + + +/*============================================================================*/ +/* */ +/* FUNCTION: div_int32 */ +/* DESCRIPTION: */ +/* Divides two int32 numbers and returns the quotient with the */ +/* specified Q-factor */ +/* PRECONDITIONS: */ +/* numL32: numerator for the division */ +/* denL32: denominator for the division */ +/* iqL16: integer part of qfactor for the quotient */ +/* numL32 and denL32 must have the same Q-factor */ +/* POSTCONDITIONS: */ +/* quotL32: returned quotient with the specified Q-factor */ +/* */ +/*============================================================================*/ +int32 div_int32( int32 numL32, + int32 denL32, + int16 iqL16 + ) +{ + int32 tmpdata1, quotL32=0; + int16 qexp, tmp16; + + if(numL32 ==0) + { + return quotL32; + } + + if(denL32 !=0) + { +// tmpdata1 = div32_normalized(numL32, denL32, &qexp); /* do normalized division */ + tmpdata1 = div_compute(numL32, denL32, &qexp); + +// MOV_MSB( tmpdata1, NBITS, quotL32, 2*NBITS, int32 ); + quotL32 = ( tmpdata1 << NBITS ); + + qexp = qexp - iqL16; + quotL32 = s32_shl_s32_sat( quotL32, qexp); /* denormalize the result */ + } + else + { + if(iqL16 > 0) + { + tmp16 = 0x8000 >> iqL16; + } + else + { + tmp16 = MAX_16; + } +// MOV_MSB( tmp16, NBITS, quotL32, 2*NBITS, int32 ); + quotL32 = (((int32 )tmp16) << NBITS ); + } + return quotL32; +} + +/*============================================================================*/ +/* */ +/* FUNCTION: div_compute */ +/* DESCRIPTION: */ +/* The two inputs are normalized so as to represent them with full */ +/* 32-bit precision in the range [0,1). The normalized inputs are */ +/* truncated to upper 16-bits and divided and the normalized */ +/* quotient is returned with 16-bit precision where the 16-bits */ +/* are in LSB. The normalized quotient is in Q16.15 format. */ +/* The required shift factor to adjust the quotient value */ +/* is returned in the pointer qShiftL16Ptr. */ +/* To get the output in a desired 32-bit Qformat Qm.(31-m), */ +/* left shift the output by qShiftL16Ptr+(16-m). */ +/* To get the output in a desired 16-bit Qformat Qm.(15-m)with */ +/* 16-bit result in the LSB of the 32-bit output, left shift the */ +/* output by qShiftL16Ptr-m. */ +/* PRECONDITIONS: */ +/* numL32: numerator for division */ +/* denL32: denominator for division */ +/* qShiftL16Ptr: pointer to output shift factor */ +/* POSTCONDITIONS: */ +/* outL32: returned normalized quotient value */ +/* qShiftL16Ptr: pointer to modified shift factor for */ +/* denormalizing the quotient */ +/*============================================================================*/ + +int32 div_compute( int32 numL32, + int32 denL32, + int16 *qShiftL16Ptr + ) +{ + int32 outL32=0; + int16 num_exp, den_exp, tmp1, tmp2, sign = 1; + + if(numL32 == 0) + { + qShiftL16Ptr[0] = 0; + return outL32; + } + if(numL32 < 0) + { + numL32 = s32_neg_s32_sat(numL32); + sign = -1; + } + if(denL32 < 0) + { + denL32 = s32_neg_s32_sat(denL32); + sign = -sign; + } + + if(denL32 != 0) + { + // Compute the exponents of the numerator and denominator + num_exp = s16_norm_s32( numL32 ); + den_exp = s16_norm_s32( denL32 ); + + // Normalize the inputs such that numerator < denominator and are in full 32-bit precision + tmp1 = s16_extract_s32_h( s32_shl_s32_sat( numL32, num_exp-1 )); + tmp2 = s16_extract_s32_h( s32_shl_s32_sat( denL32, den_exp )); + + // Perform division with 16-bit precision + outL32 = (int32 )div_s( tmp1, tmp2); + + // Shift factor value for de-normalizing the quotient + qShiftL16Ptr[0] = den_exp - num_exp +1; + } + else + { + outL32 = 0xFFFF; + qShiftL16Ptr[0] = 15; + } + + // Negate the quotient if either numerator or denominator is negative + if(sign < 0) + { + outL32 = -outL32; + } + + return outL32; +} diff --git a/modules/cmn/common/utils/src/basic_op.c b/modules/cmn/common/utils/src/basic_op.c new file mode 100644 index 0000000..2085810 --- /dev/null +++ b/modules/cmn/common/utils/src/basic_op.c @@ -0,0 +1,5435 @@ +/* +# Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. +# SPDX-License-Identifier: BSD-3-Clause +*/ + +/*************************************************************************** +* +* File Name: basic_op.c (mathevrc.c with op counting added) +* +* Purpose: Contains functions which implement the primitive +* arithmetic operations. +* +* The functions in this file are listed below. Some of them are +* defined in terms of other basic operations. One of the +* routines, s16_saturate_s32() is static. This is not a basic +* operation, and is not referenced outside the scope of this +* file. +* +* +* s16_saturate_s32() +* s32_saturate_s64() +* u16_abs_s16_sat() +* s16_add_s16_s16() +* s16_add_s16_s16_sat() +* s16_and_s16_s16() +* s32_and_s32_s32() +* s16_complement_s16() +* clearOverflow() +* s16_extract_s32_h() +* s32_extract_s64_l() +* u32_extract_s64_l() +* s16_extract_s64_h() +* s16_extract_s32_l() +* u16_extract_s32_l() +* s16_extract_s64_l() +* isOverflow() +* u32_abs_s32_sat() +* s16_min_s16_s16() +* s32_min_s32_s32() +* s32_max_s32_s32() +* s16_max_s16_s16() +* s32_add_s32_s32() +* s64_add_s32_s32() +* s64_add_s32_u32() +* s64_add_s64_s64() +* s64_add_s64_s32() +* s32_add_s32_s32_sat() +* s32_deposit_s16_l() +* s32_mac_s32_s16_s16_sat() +* s64_mac_s64_s16_s16_shift() +* s64_mac_s64_s16_s16_shift_sat() +* s32_msu_s32_s16_s16_sat() +* s64_mult_s32_s16_shift() +* s64_mult_s32_s16() +* s64_mult_u32_s16() +* s64_mult_s32_u16_shift() +* s64_mult_u32_s16_shift() +* s32_mult_s32_s32_rnd_sat() +* s64_mult_s32_u32_shift() +* s64_mult_lp_s32_u32_shift() +* s64_mult_s32_s32_shift() +* s64_mult_lp_s32_s32_shift() +* s64_mult_s16_u16_shift() +* s32_mult_s16_u16() +* s64_mult_s16_s16_shift() +* s32_mult_s16_s16() +* u32_mult_u16_u16() +* s64_mult_u16_u16_shift() +* s32_mult_s16_s16_shift_sat() +* s32_neg_s32_sat() +* s64_shl_s64() +* s32_shl_s32_rnd_sat() +* s32_shl_s32_sat() +* s32_shr_s32_sat() +* s32_sub_s32_s32() +* s32_sub_s32_s32_sat() +* s16_mac_s32_s16_s16_sat_rnd() +* s16_msu_s32_s16_s16_sat_rnd() +* s16_norm_s32() +* s16_norm_s16() +* s32_cl1_s32() +* popOverflow() +* s16_round_s32_sat() +* setOverflow() +* s16_shl_s16_sat_rnd() +* s16_shl_s16_sat() +* s16_shr_s16_sat() +* s16_sub_s16_s16() +* s64_sub_s64_s64() +* s16_sub_s16_s16_sat() +* s32_mls_s32_s16_sat() +* s16_norm_s64() +* s32_deposit_s16_h() +* s32_mult_s32_s16_rnd_sat() +* changed() +* change_if_valid() +**************************************************************************/ + +/*_________________________________________________________________________ +| | +| Include Files | +|_________________________________________________________________________| +*/ + +#include "audio_basic_op.h" +#include "ar_defs.h" +#ifdef WMOPS_FX +#include "const_fx.h" +#endif + +int giOverflow = 0; +int giOldOverflow = 0; + +// local function: +#if !defined(s16_saturate_s32) +/*************************************************************************** +* +* FUNCTION NAME: s16_saturate_s32 +* +* PURPOSE: +* +* Limit the 32 bit input to the range of a 16 bit word. +* +* +* INPUTS: +* +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* swOut +* 16 bit short signed integer (int16) whose value +* falls in the range +* 0xffff 8000 <= swOut <= 0x0000 7fff. +* +* KEYWORDS: saturation, limiting, limit, s16_saturate_s32, 16 bits +* +*************************************************************************/ + +int16 s16_saturate_s32(int32 var1) +{ + int16 swOut; + + if (var1 > SHORTWORD_MAX) + { + swOut = SHORTWORD_MAX; + giOverflow = 1; + } + else if (var1 < SHORTWORD_MIN) + { + swOut = SHORTWORD_MIN; + giOverflow = 1; + } + else + swOut = (int16) var1; /* automatic type conversion */ + +#ifdef WMOPS_FX + counter_fx.saturate++; +#endif + return (swOut); + +} +#endif // #if !defined(s16_saturate_s32) + +/***************************************************************************/ +/***************************************************************************/ +/*------------------------------ Public Functions -------------------------*/ +/***************************************************************************/ +/***************************************************************************/ + +#if !defined(s32_saturate_s64) + +/*************************************************************************** +* +* FUNCTION NAME: s32_saturate_s64 +* +* PURPOSE: +* +* Limit the input int64 (possibly exceeding 32 bit dynamic +* range) having to the 32 output wordsize. +* +* INPUTS: +* +* var1 +* A int64 whose range is +* 0x8000 0000 0000 0000 <= var1 <= 0x7fff ffff ffff ffff. +* i.e. a 64 bit number. Not modified. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 32 bit long integer (int32) where the DSP's +* rules of saturation are followed: +* for: 0x8000 0000 <= dVal1 <= 0x7fff ffff +* input == output, no saturation +* for: 0x8000 0000 > dVal1 output 0x8000 0000 +* for: dVal1 > 0x7fff ffff output 0x7fff ffff +* +* KEYWORDS: saturation, limiting, limit, s16_saturate_s32, 32 bits +* +*************************************************************************/ +int32 s32_saturate_s64(int64 var1) +{ + + if (var1 > (int64) LONGWORD_MAX) + { + var1 = (int64) LONGWORD_MAX; + giOverflow = 1; + } + else if (var1 < (int64) LONGWORD_MIN) + { + var1 = (int64) LONGWORD_MIN; + giOverflow = 1; + } + +#ifdef WMOPS_FX + counter_fx.saturate++; +#endif + + return ((long) var1); + +} +#endif // #if !defined(s32_saturate_s64) + +#if !defined(u16_abs_s16_sat) +/*************************************************************************** +* +* FUNCTION NAME: u16_abs_s16_sat +* +* PURPOSE: +* +* Take the absolute value of the 16 bit input. An input of +* -0x8000 results in a return value of 0x7fff. +* +* INPUTS: +* +* var1 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var1 <= 0x0000 7fff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* swOut +* 16 bit short signed integer (int16) whose value +* falls in the range +* 0x0000 0000 <= swOut <= 0x0000 7fff. +* +* IMPLEMENTATION: +* +* Take the absolute value of the 16 bit input. An input of +* -0x8000 results in a return value of 0x7fff. +* +* KEYWORDS: absolute value, abs +* +*************************************************************************/ +uint16 u16_abs_s16_sat(int16 var1) +{ + int16 swOut; + + if (var1 == SHORTWORD_MIN) + { + swOut = SHORTWORD_MAX; + giOverflow = 1; + } + else + { + if (var1 < 0) + swOut = -var1; + else + swOut = var1; + } + +#ifdef WMOPS_FX + counter_fx.abs_sat++; +#endif + + return (swOut); + +} +#endif //#if !defined(u16_abs_s16_sat) + +#if !defined(s16_add_s16_s16) +/*************************************************************************** +* +* FUNCTION NAME: s16_add_s16_s16 +* +* PURPOSE: +* +* Perform the addition of the two 16 bit input variable WITHOUT +* saturation. +* +* INPUTS: +* +* var1 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var1 <= 0x0000 7fff. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* swOut +* 16 bit short signed integer (int16) whose value +* falls in the range +* 0xffff 8000 <= swOut <= 0x0000 7fff. +* +* IMPLEMENTATION: +* +* Perform the addition of the two 16 bit input variable without +* saturation. +* +* swOut = var1 + var2 +* +* No protection from overflow. +* KEYWORDS: s16_add_s16_s16, addition +* +*************************************************************************/ + +int16 s16_add_s16_s16(int16 var1, int16 var2) +{ + int16 swOut; + + swOut = var1 + var2; + +#ifdef WMOPS_FX + counter_fx.add16++; +#endif + + return (swOut); + +} +#endif //#if !defined(s16_add_s16_s16) + +#if !defined(s16_add_s16_s16_sat) +/*************************************************************************** +* +* FUNCTION NAME: s16_add_s16_s16_sat +* +* PURPOSE: +* +* Perform the addition of the two 16 bit input variable with +* saturation. +* +* INPUTS: +* +* var1 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var1 <= 0x0000 7fff. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* swOut +* 16 bit short signed integer (int16) whose value +* falls in the range +* 0xffff 8000 <= swOut <= 0x0000 7fff. +* +* IMPLEMENTATION: +* +* Perform the addition of the two 16 bit input variable with +* saturation. +* +* swOut = var1 + var2 +* +* swOut is set to 0x7fff if the operation results in an +* overflow. swOut is set to 0x8000 if the operation results +* in an underflow. +* +* KEYWORDS: s16_add_s16_s16, addition +* +*************************************************************************/ +int16 s16_add_s16_s16_sat(int16 var1, int16 var2) +{ + int32 L_sum; + int16 swOut; + + L_sum = (int32) var1 + var2; + swOut = s16_saturate_s32(L_sum); + +#ifdef WMOPS_FX + counter_fx.add16_sat++; + counter_fx.saturate--; +#endif + + return (swOut); + +} +#endif //#if !defined(s16_add_s16_s16_sat) + +#if !defined(s16_and_s16_s16) +/*************************************************************************** +* +* FUNCTION NAME: s16_and_s16_s16 +* +* PURPOSE: +* +* Perform the logical AND of the two 16 bit input variables +* +* INPUTS: +* +* var1 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000<= var1 <= 0x0000 7fff. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000<= var1 <= 0x0000 7fff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* outputL16 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000<= var1 <= 0x0000 7fff. +* +* IMPLEMENTATION: +* +* Perform the logical AND of the two 16 bit input variables +* +* outputL16 = var1 & var2 +* +* +* KEYWORDS: and, logical and +* +*************************************************************************/ + +int16 s16_and_s16_s16(int16 var1, int16 var2) +{ + int16 outputL16; + + outputL16 = var1 & var2; +#ifdef WMOPS_FX + counter_fx.and++; +#endif + + return (outputL16); + +} +#endif //#if !defined(s16_and_s16_s16) + +#if !defined(s32_and_s32_s32) +/*************************************************************************** +* +* FUNCTION NAME: s32_and_s32_s32 +* +* PURPOSE: +* +* Perform the logical AND of the two 32 bit input variables +* +* INPUTS: +* +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range 0x8000 0000 <= var1 <= 0x7fff ffff. +* var2 +* 32 bit signed integer (int32) whose value +* falls in the range 0x8000 0000 <= var1 <= 0x7fff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* outputL32 +* 32 bit signed integer (int32) whose value +* falls in the range 0x8000 0000 <= var1 <= 0x7fff ffff. +* +* IMPLEMENTATION: +* +* Perform the logical AND of the two 32 bit input variables +* +* outputL32 = var1 & var2 +* +* +* KEYWORDS: and, logical and +* +*************************************************************************/ + +int32 s32_and_s32_s32(int32 var1, int32 var2) +{ + int32 outputL32; + + outputL32 = var1 & var2; +#ifdef WMOPS_FX + counter_fx.and++; +#endif + + return (outputL32); + +} +#endif //#if !defined(s32_and_s32_s32) + +#if !defined(s16_complement_s16) +/**************************************************************************** +* +* FUNCTION NAME: s16_complement_s16 +* +* PURPOSE: +* +* performs 1's complement operation +* +* INPUTS: +* +* var1: 16-bit value to be 1's complemented. +* +* +* OUTPUTS: 1' complemented out of the input. +* +* RETURN VALUE: 1' complemented out of the input. +* +* +* KEYWORDS: s16_complement_s16 +* +***************************************************************************/ +int16 s16_complement_s16(int16 var1) +{ + int16 out16; + + out16 = ~var1; + +#ifdef WMOPS_FX + counter_fx.complement++; +#endif + + return out16; + +} +#endif //#if !defined(s16_complement_s16) + +#if !defined(clearOverflow) +/**************************************************************************** +* +* FUNCTION NAME: clearOverflow +* +* PURPOSE: +* +* Clear the overflow flag +* +* INPUTS: +* +* none +* +* +* OUTPUTS: global overflow flag is cleared +* previous value stored in giOldOverflow +* +* RETURN VALUE: previous value of overflow +* +* +* KEYWORDS: saturation, limit, overflow +* +***************************************************************************/ +int clearOverflow(void) +{ + giOldOverflow = giOverflow; + giOverflow = 0; + +#ifdef WMOPS_FX + counter_fx.clearOverflow++; +#endif + + return (giOldOverflow); + +} +#endif /* if !defined(clearOverflow) */ + +#if !defined(s16_extract_s32_h) +/*************************************************************************** +* +* FUNCTION NAME: s16_extract_s32_h +* +* PURPOSE: +* +* Extract the 16 MS bits of a 32 bit int32. Return the 16 bit +* number as a int16. This is used as a "truncation" of a fractional +* number. +* +* INPUTS: +* +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* swOut +* 16 bit short signed integer (int16) whose value +* falls in the range +* 0xffff 8000 <= swOut <= 0x0000 7fff. +* +* IMPLEMENTATION: +* +* KEYWORDS: assign, truncate +* +*************************************************************************/ +int16 s16_extract_s32_h(int32 var1) +{ + int16 var2; + + var2 = (int16) (UMAX_16 & (var1 >> 16)); + +#ifdef WMOPS_FX + counter_fx.extract++; +#endif + + return (var2); + +} +#endif //#if !defined(s16_extract_s32_h) + +#if !defined(s32_extract_s64_l) +/*************************************************************************** +* +* FUNCTION NAME: s32_extract_s64_l +* +* PURPOSE: +* +* Extract the 32 LS bits of a 64 bit int64. Return the 32 bit +* number as a int32. This is used as a "truncation" of a fractional +* number. +* +* INPUTS: +* +* var1 +* 64 bit long signed integer (int64) whose value +* falls in the range +* 0x8000 0000 0000 0000 <= var1 <= 0x7fff ffff ffff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* lwOut +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x 8000 0000<= lwOut <= 0x7fff ffff. +* +* IMPLEMENTATION: +* +* KEYWORDS: assign, truncate +* +*************************************************************************/ +int32 s32_extract_s64_l(int64 var1) +{ + int32 var2; + + var2 = (int32) (UMAX_32 & (var1 )); + +#ifdef WMOPS_FX + counter_fx.extract++; +#endif + + return (var2); + +} +#endif //#if !defined(s32_extract_s64_l) + +#if !defined(u32_extract_s64_l) +/*************************************************************************** +* +* FUNCTION NAME: u32_extract_s64_l +* +* PURPOSE: +* +* Extract the 32 LS bits of a 64 bit int64. Return the 32 bit +* number as a uint32. This is used as a "truncation" of a fractional +* number. +* +* INPUTS: +* +* var1 +* 64 bit long signed integer (int64) whose value +* falls in the range +* 0x8000 0000 0000 0000 <= var1 <= 0x7fff ffff ffff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* lwOut +* 32 bit unsigned integer (uint32) whose value +* falls in the range +* 0x 0000 0000<= lwOut <= 0xffff ffff. +* +* IMPLEMENTATION: +* +* KEYWORDS: assign, truncate +* +*************************************************************************/ +uint32 u32_extract_s64_l(int64 var1) +{ + uint32 var2; + + var2 = (uint32) (UMAX_32 & (var1 )); + +#ifdef WMOPS_FX + counter_fx.extract++; +#endif + + return (var2); + +} +#endif //#if !defined(s32_extract_s64_l) + +#if !defined(s16_extract_s64_h) +/*************************************************************************** +* +* FUNCTION NAME: s16_extract_s64_h +* +* PURPOSE: +* +* Extract the 16 MS bits of a 64 bit int64. Return the 16 bit +* number as a int16. This is used as a "truncation" of a fractional +* number. +* +* INPUTS: +* +* var1 +* 64 bit long signed integer (int64) whose value +* falls in the range +* 0x8000 0000 0000 0000 <= var1 <= 0x7fff ffff ffff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* swOut +* 16 bit short signed integer (int16) whose value +* falls in the range +* 0xffff 8000 <= swOut <= 0x0000 7fff. +* +* IMPLEMENTATION: +* +* KEYWORDS: assign, truncate +* +*************************************************************************/ +int16 s16_extract_s64_h(int64 var1) +{ + int16 var2; + + var2 = (int16) (UMAX_16 & (var1 >> 16)); + +#ifdef WMOPS_FX + counter_fx.extract++; +#endif + + return (var2); + +} +#endif //#if !defined(s16_extract_s64_h) + +#if !defined(s16_extract_s64_h_rnd) +/*************************************************************************** +* +* FUNCTION NAME: s16_extract_s64_h_rnd +* +* PURPOSE: +* +* Extract the 16 MS bits of a 64 bit int64 with rounding. Return the 16 bit +* number as a int16. This is used as a "truncation" of a fractional +* number. +* +* INPUTS: +* +* var1 +* 64 bit long signed integer (int64) whose value +* falls in the range +* 0x8000 0000 0000 0000 <= var1 <= 0x7fff ffff ffff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* swOut +* 16 bit short signed integer (int16) whose value +* falls in the range +* 0xffff 8000 <= swOut <= 0x0000 7fff. +* +* IMPLEMENTATION: +* +* KEYWORDS: assign, truncate +* +*************************************************************************/ +int16 s16_extract_s64_h_rnd(int64 var1) +{ + int16 var2; + var1 = var1 + (int64)0x8000; + var2 = (int16) (UMAX_16 & (var1 >> 16)); +#ifdef WMOPS_FX + counter_fx.extract++; +#endif + return (var2); +} +#endif //#if !defined(s16_extract_s64_h_rnd) + +#if !defined(s16_extract_s32_l) +/*************************************************************************** +* +* FUNCTION NAME: s16_extract_s32_l +* +* PURPOSE: +* +* Extract the 16 LS bits of a 32 bit int32. Return the 16 bit +* number as a int16. The upper portion of the input int32 +* has no impact whatsoever on the output. +* +* INPUTS: +* +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* swOut +* 16 bit short signed integer (int16) whose value +* falls in the range +* 0xffff 8000 <= swOut <= 0x0000 7fff. +* +* +* KEYWORDS: extract, assign +* +*************************************************************************/ +int16 s16_extract_s32_l(int32 var1) +{ + int16 var2; + + var2 = (int16) (UMAX_16 & var1); + +#ifdef WMOPS_FX + counter_fx.extract++; +#endif + + return (var2); + +} +#endif //#if !defined(s16_extract_s32_l) + +#if !defined(u16_extract_s32_l) +/*************************************************************************** +* +* FUNCTION NAME: u16_extract_s32_l +* +* PURPOSE: +* +* Extract the 16 LS bits of a 32 bit int32. Return the 16 bit +* number as a uint16. The upper portion of the input int32 +* has no impact whatsoever on the output. +* +* INPUTS: +* +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* swOut +* 16 bit short unsigned integer (uint16) whose value +* falls in the range +* 0x0000 0000 <= swOut <= 0x0000 ffff. +* +* +* KEYWORDS: extract, assign +* +*************************************************************************/ +uint16 u16_extract_s32_l(int32 var1) +{ + uint16 var2; + + var2 = (uint16) (UMAX_16 & var1); + +#ifdef WMOPS_FX + counter_fx.extract++; +#endif + + return (var2); + +} +#endif //#if !defined(u16_extract_s32_l) + +#if !defined(s16_extract_s64_l) +/*************************************************************************** +* +* FUNCTION NAME: s16_extract_s64_l +* +* PURPOSE: +* +* Extract the 16 LS bits of a 64 bit int64. Return the 16 bit +* number as a int16. This is used as a "truncation" of a fractional +* number. +* +* INPUTS: +* +* var1 +* 64 bit long signed integer (int64) whose value +* falls in the range +* 0x8000 0000 0000 0000 <= var1 <= 0x 7fff ffff ffff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* swOut +* 16 bit short signed integer (int16) whose value +* falls in the range +* 0xffff 8000 <= swOut <= 0x0000 7fff. +* +* IMPLEMENTATION: +* +* KEYWORDS: assign, truncate +* +*************************************************************************/ + +int16 s16_extract_s64_l(int64 var1) +{ + int16 var2; + + var2 = (int16) (UMAX_16 & var1); + +#ifdef WMOPS_FX + counter_fx.extract++; +#endif + + return (var2); + +} +#endif //#if !defined(s16_extract_s64_l) + +#if !defined(isOverflow) +/**************************************************************************** +* +* FUNCTION NAME: isOverflow +* +* PURPOSE: +* +* Check to see whether an overflow/saturation/limiting has occurred +* +* INPUTS: +* +* none +* +* +* OUTPUTS: none +* +* RETURN VALUE: 1 if overflow has been flagged +* 0 otherwise +* +* KEYWORDS: saturation, limit, overflow +* +***************************************************************************/ +int isOverflow(void) +{ + return (giOverflow); +} +#endif // #if !defined(isOverflow) + +#if !defined(u32_abs_s32_sat) +/*************************************************************************** +* +* FUNCTION NAME: u32_abs_s32_sat +* +* PURPOSE: +* +* Take the absolute value of the 32 bit input. An input of +* 0x8000 0000 results in a return value of 0x7fff ffff. +* +* INPUTS: +* +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* +* +* +* KEYWORDS: absolute value, abs +* +*************************************************************************/ +uint32 u32_abs_s32_sat(int32 var1) +{ + int32 L_Out; + + if (var1 == LONGWORD_MIN) + { + L_Out = LONGWORD_MAX; + giOverflow = 1; + } + else + { + if (var1 < 0) + L_Out = -var1; + else + L_Out = var1; + } + +#ifdef WMOPS_FX + counter_fx.abs_sat++; +#endif + + return (L_Out); + +} +#endif //#if !defined(u32_abs_s32_sat) + +#if !defined(s16_min_s16_s16) +/*************************************************************************** +* +* FUNCTION NAME: s16_min_s16_s16 +* +* PURPOSE: +* +* Perform the addition of the two 16 bit input variables +* +* INPUTS: +* +* var1 +* 16 bit long signed integer (int16) whose value +* falls in the range +* 0x0000 8000 <= var1 <= 0x0000 7fff . +* var2 +* 16 bit long signed integer (int16) whose value +* falls in the range +* 0x0000 8000 <= var1 <= 0x0000 7fff . +* +* OUTPUTS: +* +* min of var1 and var2 +* +* RETURN VALUE: +* +* out +* 16 bit long signed integer (int16) whose value +* falls in the range +* 0x0000 8000 <= var1 <= 0x0000 7fff .* +* IMPLEMENTATION: +* +* gets the minimum of the inputs. +* KEYWORDS: minimum, optimum +* +*************************************************************************/ +int16 s16_min_s16_s16(int16 var1, int16 var2) +{ + int16 out; + out = (var1var2)?var1:var2; + +#ifdef WMOPS_FX + counter_fx.minmax++; +#endif + + return (out); + +} /* end of s32_max_s32_s32 func.*/ +#endif //#if !defined(s32_max_s32_s32) + +#if !defined(s16_max_s16_s16) +/*************************************************************************** +* +* FUNCTION NAME: s16_max_s16_s16 +* +* PURPOSE: +* +* Perform the addition of the two 16 bit input variables +* +* INPUTS: +* +* var1 +* 16 bit long signed integer (int16) whose value +* falls in the range +* 0x0000 8000 <= var1 <= 0x0000 7fff . +* var2 +* 16 bit long signed integer (int16) whose value +* falls in the range +* 0x0000 8000 <= var1 <= 0x0000 7fff . +* +* OUTPUTS: +* +* max of var1 and var2 +* +* RETURN VALUE: +* +* out +* 16 bit long signed integer (int16) whose value +* falls in the range +* 0x0000 8000 <= var1 <= 0x0000 7fff .* +* IMPLEMENTATION: +* +* gets the maximum of the inputs. +* KEYWORDS: maximum, optimum +* +*************************************************************************/ +int16 s16_max_s16_s16(int16 var1, int16 var2) +{ + int16 out; + + out = (var1>var2)?var1:var2; + +#ifdef WMOPS_FX + counter_fx.minmax++; +#endif + + return (out); + +} /* end of s16_max_s16_s16 func.*/ +#endif //#if !defined(s16_max_s16_s16) + +#if !defined(s32_add_s32_s32) +/*************************************************************************** +* +* FUNCTION NAME: s32_add_s32_s32 +* +* PURPOSE: +* +* Perform the addition of the two 32 bit input variables +* +* INPUTS: +* +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* var2 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var2 <= 0x7fff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* +* IMPLEMENTATION: +* +* Perform the addition of the two 32 bit input variables +* L_Out = var1 + var2 +* +* The result might overflow and there is no protection against it. +* KEYWORDS: s32_add_s32_s32, addition +* +*************************************************************************/ +int32 s32_add_s32_s32(int32 var1, int32 var2) +{ + int32 L_Sum; + + L_Sum = var1 + var2; + +#ifdef WMOPS_FX + counter_fx.add32++; +#endif + + return (L_Sum); + +} +#endif //#if !defined(s32_add_s32_s32) + +#if !defined(s64_add_s32_s32) +/*************************************************************************** +* +* FUNCTION NAME: s64_add_s32_s32 +* +* PURPOSE: +* +* Perform the addition of the two 32 bit input variables +* +* INPUTS: +* +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* var2 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var2 <= 0x7fff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 64 bit long signed integer (int64) whose value +* falls in the range +* 0x8000 0000 0000 0000 <= var1 <= 0x7fff ffff ffff ffff. +* +* IMPLEMENTATION: +* +* Perform the addition of the two 32 bit input variables +* L_Out = var1 + var2 +* +* The result might overflow and there is no protection against it. +* KEYWORDS: s64_add_s32_s32, addition +* +*************************************************************************/ +int64 s64_add_s32_s32(int32 var1, int32 var2) +{ + int64 L_Sum; + + L_Sum = (int64) var1 + (int64) var2; + +#ifdef WMOPS_FX + counter_fx.add32++; +#endif + + return (L_Sum); + +} +#endif //#if !defined(s64_add_s32_s32) + +#if !defined(s64_add_s32_u32) +/*************************************************************************** +* +* FUNCTION NAME: s64_add_s32_u32 +* +* PURPOSE: +* +* Perform the addition of the two 32 bit input variables +* +* INPUTS: +* +* L_var1 +* 32 bit long signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= L_var1 <= 0x7fff ffff. +* L_var2 +* 32 bit long unsigned integer (uint32) whose value +* falls in the range +* 0x0 <= L_var2 <= 0xffff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 64 bit long signed integer (int64) whose value +* falls in the range +* 0x8000 0000 <= L_var1 <= 0x7fff ffff. +* +* IMPLEMENTATION: +* +* Perform the addition of the two 32 bit input variables +* L_Out = L_var1 + L_var2 +* +* The result might overflow and there is no protection against it. +* KEYWORDS: add, addition +* +***************************************************************************/ +int64 s64_add_s32_u32(int32 var1, uint32 var2) +{ + int64 L_Sum; + + L_Sum = (int64) var1 + (int64)var2; + +#ifdef WMOPS_FX + counter_fx.add64++; +#endif + + return (L_Sum); +} +#endif //#if !defined(s64_add_s32_u32) + +#if !defined(s64_add_s64_s64) +/*************************************************************************** +* +* FUNCTION NAME: s64_add_s64_s64 +* +* PURPOSE: +* +* Perform the addition of the two 64 bit input variables +* +* INPUTS: +* +* var1 +* 64 bit long signed integer (int64) whose value +* falls in the range +* 0x8000 0000 0000 0000 <= var1 <= 0x7fff ffff ffff ffff. +* var2 +* 64 bit long signed integer (int64) whose value +* falls in the range +* 0x8000 0000 0000 0000 <= var1 <= 0x7fff ffff ffff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 64 bit long signed integer (int64) whose value +* falls in the range +* 0x8000 0000 0000 0000 <= var1 <= 0x7fff ffff ffff ffff. +* +* IMPLEMENTATION: +* +* Perform the addition of the two 64 bit input variables +* L_Out = var1 + var2 +* +* The result might overflow and there is no protection against it. +* KEYWORDS: s64_add_s64_s64, addition +* +*************************************************************************/ +int64 s64_add_s64_s64(int64 var1, int64 var2) +{ + int64 L_Sum; + + L_Sum = var1 + var2; + +#ifdef WMOPS_FX + counter_fx.add64++; +#endif + + return (L_Sum); +} +#endif //#if !defined(s64_add_s64_s64) + + +#if !defined(s64_add_s64_s32) +/*************************************************************************** +* +* FUNCTION NAME: s64_add_s64_s32 +* +* PURPOSE: +* +* Perform the addition of the two 32 bit input variables +* +* INPUTS: +* +* var1 +* 64 bit signed integer (int64) whose value +* falls in the range +* 0x8000 0000 0000 0000 <= var1 <= 0x7fff ffff ffff ffff. +* var2 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var2 <= 0x7fff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 64 bit long signed integer (int64) whose value +* falls in the range +* 0x8000 0000 0000 0000 <= var1 <= 0x7fff ffff ffff ffff. +* +* IMPLEMENTATION: +* +* Perform the addition of the a 32 bit and a 64 bit input variables +* L_Out = var1 + var2 +* +* The result might overflow and there is no protection against it. +* KEYWORDS: s64_add_s64_s32, addition +* +*************************************************************************/ +int64 s64_add_s64_s32(int64 var1, int32 var2) +{ + int64 L_Sum; + + L_Sum = (int64) var1 + var2; + +#ifdef WMOPS_FX + counter_fx.add64++; +#endif + + return (L_Sum); +} +#endif //#if !defined(s64_add_s64_s32) + +#if !defined(s32_add_s32_s32_sat) +/*************************************************************************** +* +* FUNCTION NAME: s32_add_s32_s32_sat +* +* PURPOSE: +* +* Perform the addition of the two 32 bit input variables with +* saturation. +* +* INPUTS: +* +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* var2 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var2 <= 0x7fff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* +* IMPLEMENTATION: +* +* Perform the addition of the two 32 bit input variables with +* saturation. +* +* L_Out = var1 + var2 +* +* L_Out is set to 0x7fff ffff if the operation results in an +* overflow. L_Out is set to 0x8000 0000 if the operation +* results in an underflow. +* +* KEYWORDS: s32_add_s32_s32_sat, addition +* +*************************************************************************/ +int32 s32_add_s32_s32_sat(int32 var1, int32 var2) +{ + int32 L_Sum; + int64 dSum; + + dSum = (int64) var1 + (int64) var2; + L_Sum = var1 + var2; + + if (dSum != (int64) L_Sum) + { + /* overflow occurred */ + L_Sum = s32_saturate_s64(dSum); // OP_COUNT(-4); + +#ifdef WMOPS_FX + counter_fx.saturate--; +#endif + + } + +#ifdef WMOPS_FX + counter_fx.add32_sat++; +#endif + + return (L_Sum); +} +#endif //#if !defined(s32_add_s32_s32_sat) + +#if !defined(s32_deposit_s16_l) +/*************************************************************************** +* +* FUNCTION NAME: s32_deposit_s16_l +* +* PURPOSE: +* +* Put the 16 bit input into the 16 LSB's of the output int32 with +* sign extension i.e. the top 16 bits are set to either 0 or 0xffff. +* +* INPUTS: +* +* var1 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var1 <= 0x0000 7fff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 32 bit signed integer (int32) whose value +* falls in the range +* 0xffff 8000 <= var1 <= 0x0000 7fff. +* +* KEYWORDS: deposit, assign +* +*************************************************************************/ + +int32 s32_deposit_s16_l(int16 var1) +{ + int32 L_Out; + + L_Out = var1; + +#ifdef WMOPS_FX + counter_fx.deposit++; +#endif + + return (L_Out); +} +#endif //#if !defined(s32_deposit_s16_l) + +#if !defined(s32_mac_s32_s16_s16_sat) +/*************************************************************************** +* +* FUNCTION NAME: s32_mac_s32_s16_s16_sat +* +* PURPOSE: +* +* Multiply accumulate. Multiply two 16 bit +* numbers together with saturation. Add that result to the +* 32 bit input with saturation. Return the 32 bit result. +* +* INPUTS: +* +* var1 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var1 <= 0x0000 7fff. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* var3 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var2 <= 0x7fff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* +* IMPLEMENTATION: +* +* Multiply two 16 bit numbers together. +* Add that result to the 32 bit input with saturation. +* Return the 32 bit result. +* +* Please note that this is not a true multiply accumulate as +* most processors would implement it. The 0x8000*0x8000 +* causes and overflow for this instruction. On most +* processors this would cause an overflow only if the 32 bit +* input added to it were positive or zero. +* +* KEYWORDS: mac, multiply accumulate +* +*************************************************************************/ + +int32 s32_mac_s32_s16_s16_sat(int32 var3, int16 var1, int16 var2) +{ + int32 L_product; + + L_product = (int32) var1 *var2; /* integer multiply */ + + L_product = s32_add_s32_s32_sat(var3, L_product); // OP_COUNT(-2); + +#ifdef WMOPS_FX + counter_fx.mac16_sat++; + counter_fx.add32_sat--; +#endif + + return (L_product); +} +#endif // #if !defined(s32_mac_s32_s16_s16_sat) + +#if !defined(s64_mac_s64_s16_s16_shift) +/*************************************************************************** +* +* FUNCTION NAME: s64_mac_s64_s16_s16_shift +* +* PURPOSE: +* +* Multiply accumulate. Multiply two 16 bit +* numbers. Shift the result and add that result to the +* 64 bit input. Return the 64 bit result. +* +* INPUTS: +* +* var1 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var1 <= 0x0000 7fff. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* var3 +* 64 bit signed integer (int64) whose value +* falls in the range +* 0x8000 0000 0000 0000 <= var2 <= 0x7fff ffff ffff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 64 bit signed integer (int64) whose value +* falls in the range +* 0x8000 0000 0000 0000 <= var1 <= 0x7fff ffff ffff ffff. +* +* IMPLEMENTATION: +* +* Multiply two 16 bit numbers together. +* +* Shift the result and add it to the 64 bit input. +* Return the 64 bit result. +* +* KEYWORDS: mac, multiply accumulate +* +*************************************************************************/ + +int64 s64_mac_s64_s16_s16_shift(int64 var3, int16 var1, int16 var2, int16 shift) +{ + int32 productL32; + int64 resultL64; + + productL32 = (int32) var1 *var2; /* integer multiply */ + if(shift > 0) + { + resultL64 = ((int64)productL32)<>(-shift); + } + resultL64 = resultL64 + var3; + + +#ifdef WMOPS_FX + counter_fx.mac16++; +#endif + + return (resultL64); +} +#endif //#if !defined(s64_mac_s64_s16_s16_shift) + +#if !defined(s64_mac_s64_s16_s16_shift_nosat) +/*************************************************************************** +* +* FUNCTION NAME: s64_mac_s64_s16_s16_shift_nosat +* +* PURPOSE: +* +* Multiply accumulate. Multiply two 16 bit +* numbers. Shift the result and add that result to the +* 64 bit input. Return the 64 bit result without saturation. +* +* INPUTS: +* +* var1 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var1 <= 0x0000 7fff. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* var3 +* 64 bit signed integer (int64) whose value +* falls in the range +* 0x8000 0000 0000 0000 <= var2 <= 0x7fff ffff ffff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 64 bit signed integer (int64) whose value +* falls in the range +* 0x8000 0000 0000 0000 <= var1 <= 0x7fff ffff ffff ffff. +* +* IMPLEMENTATION: +* +* Multiply two 16 bit numbers together. +* +* Shift the result and add it to the 64 bit input. +* Return the 64 bit result without saturation. +* +* KEYWORDS: mac, multiply accumulate +* +*************************************************************************/ + +int64 s64_mac_s64_s16_s16_shift_nosat(int64 var3, int16 var1, int16 var2, int16 shift) +{ + int32 productL32; + int64 resultL64; + + productL32 = (int32) var1 *var2; /* integer multiply */ + if(shift > 0) + { + resultL64 = ((int64)productL32)<>(-shift); + } + resultL64 = resultL64 + var3; + + +#ifdef WMOPS_FX + counter_fx.mac16++; +#endif + + return (resultL64); +} +#endif //#if !defined(s64_mac_s64_s16_s16_shift_nosat) + + +#if !defined(s64_mac_s64_s16_s16_shift_sat) +/*************************************************************************** +* +* FUNCTION NAME: s64_mac_s64_s16_s16_shift_sat +* +* PURPOSE: +* +* Multiply accumulate. Multiply two 16 bit +* numbers . Shift the result and add that result to the +* 64 bit input with saturation. Return the 64 bit result. +* +* INPUTS: +* +* var1 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var1 <= 0x0000 7fff. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* var3 +* 64 bit signed integer (int64) whose value +* falls in the range +* 0x8000 0000 0000 0000 <= var2 <= 0x7fff ffff ffff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 64 bit signed integer (int64) whose value +* falls in the range +* 0x8000 0000 0000 0000 <= var1 <= 0x7fff ffff ffff ffff. +* +* IMPLEMENTATION: +* +* Multiply two 16 bit numbers. +* +* Shift the result and add that result to the 64 bit input with saturation. +* Return the 64 bit result. +* +* KEYWORDS: mac, multiply accumulate +* +*************************************************************************/ + +int64 s64_mac_s64_s16_s16_shift_sat(int64 var3, int16 var1, int16 var2, int16 shift) +{ + int32 resultL32; + int64 resultL64; + + resultL64 = s64_mac_s64_s16_s16_shift(var3, var1, var2, shift); + resultL32 = s32_saturate_s64(resultL64); + +#ifdef WMOPS_FX + counter_fx.saturate--; + counter_fx.mac16--; + counter_fx.mac16_sat++; +#endif + + return (resultL32); +} +#endif // #if !defined(s64_mac_s64_s16_s16_shift_sat) + +#if !defined(s32_msu_s32_s16_s16_sat) +/*************************************************************************** +* +* FUNCTION NAME: s32_msu_s32_s16_s16_sat +* +* PURPOSE: +* +* Multiply and subtract. Multiply two 16 bit +* numbers together with saturation. Subtract that result from +* the 32 bit input with saturation. Return the 32 bit result. +* +* INPUTS: +* +* var1 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var1 <= 0x0000 7fff. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* var3 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var2 <= 0x7fff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* +* IMPLEMENTATION: +* +* Multiply two 16 bit numbers together with +* saturation. The only numbers which will cause saturation on +* the multiply are 0x8000 * 0x8000. +* +* Subtract that result from the 32 bit input with saturation. +* Return the 32 bit result. +* +* Please note that this is not a true multiply accumulate as +* most processors would implement it. The 0x8000*0x8000 +* causes and overflow for this instruction. On most +* processors this would cause an overflow only if the 32 bit +* input added to it were negative or zero. +* +* KEYWORDS: mac, multiply accumulate, msu +* +*************************************************************************/ + +int32 s32_msu_s32_s16_s16_sat(int32 var3, int16 var1, int16 var2) +{ + int32 L_product; + + L_product = (int32) var1 *var2; /* integer multiply */ + if (L_product == (int32) LONGWORD_HALF) + { + /* the event 0x8000 * 0x8000, the only possible saturation + * in the multiply */ + L_product = s32_saturate_s64((int64) var3 - (int64)(LONGWORD_MIN)); // OP_COUNT(-4); + +#ifdef WMOPS_FX + counter_fx.saturate--; +#endif + + } + else + { + /* no overflow possible in mult */ + L_product <<= 1; + L_product = s32_sub_s32_s32_sat(var3, L_product); // OP_COUNT(-2); /* LT 6/96 */ + +#ifdef WMOPS_FX + counter_fx.add32_sat--; +#endif + + } + +#ifdef WMOPS_FX + counter_fx.mac16_sat++; +#endif + + return (L_product); +} +#endif //#if !defined(s32_msu_s32_s16_s16_sat) + +#if !defined(s64_mult_s32_s16_shift) +/*************************************************************************** +* +* FUNCTION NAME: s64_mult_s32_s16_shift +* +* PURPOSE: +* +* Perform multiplication of a 32-bit number with 16-bit number with +* Q factor compensation. +* +* INPUTS: +* +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range 0x8000 0000 <= var1 <= 0x7fff ffff. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* shift +* 16 bit number that gives shift for Q factor compensation. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 64 bit signed integer (int64) whose value +* falls in the range +* 0x8000 0000 0000 0000 <= var1 <= 0x7fff ffff ffff ffff. +* +* IMPLEMENTATION: +* multiplies a 32 -bit number with 16-bit number and shifts +* it left or right. +* KEYWORDS: multiply, mult, mpy +* +*************************************************************************/ +int64 s64_mult_s32_s16_shift( int32 var1, int16 var2, int16 shift) +{ + int64 resultL64; + + resultL64 = s64_mult_s32_s32(var1, (int32)var2); + resultL64 = s64_shl_s64(resultL64,shift-16); + + + return resultL64; +} +#endif //#if !defined(s64_mult_s32_s16_shift) + +#if !defined(s64_mult_s32_s16) +/*************************************************************************** +* +* FUNCTION NAME: s64_mult_s32_s16 +* +* PURPOSE: +* +* Perform multiplication of a 32-bit number with 16-bit number. +* +* INPUTS: +* +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range 0x8000 0000 <= var1 <= 0x7fff ffff. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 64 bit long long signed integer (int64). +* +* IMPLEMENTATION: +* multiplies a 32 -bit number with 16-bit number and save into 64 bit +* KEYWORDS: multiply, mult, mpy +* +*************************************************************************/ +int64 s64_mult_s32_s16(int32 var1, int16 var2) +{ + int32 tempProdL32; + int64 resultL64; + int16 inL32LW; + int16 inL32MW; + + inL32LW = s16_extract_s32_l(var1); + inL32MW = s16_extract_s32_h(var1); + + resultL64 = s64_shl_s64(s32_mult_s16_s16(var2, inL32MW), 16); + tempProdL32 = s32_mult_s16_u16(var2, inL32LW); + resultL64 = s64_add_s64_s64(resultL64, tempProdL32); + +#ifdef WMOPS_FX + counter_fx.mult_32_16++; + counter_fx.extract -= 2; + counter_fx.sh--; + counter_fx.mult16 -=2; + counter_fx.add64--; +#endif + + return resultL64; +} /* End of s64_mult_s32_s16 function*/ +#endif // !defined(s64_mult_s32_s16) + +#if !defined(s64_mult_u32_s16) +/*************************************************************************** +* +* FUNCTION NAME: s64_mult_u32_s16 +* +* PURPOSE: +* +* Perform multiplication of a 32-bit number with 16-bit number. +* +* INPUTS: +* +* var1 +* 32 bit unsigned integer (uint32) whose value +* falls in the range 0x0000 0000 <= var1 <= 0xffff ffff. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 64 bit long long signed integer (int64). +* +* IMPLEMENTATION: +* multiplies a 32 -bit number with 16-bit number and save into 64 bit +* KEYWORDS: multiply, mult, mpy +* +*************************************************************************/ +int64 s64_mult_u32_s16(uint32 var1, int16 var2) +{ + int32 tempProdL32; + int64 resultL64; + int16 inL32LW; + int16 inL32MW; + + inL32LW = s16_extract_s32_l(var1); + inL32MW = s16_extract_s32_h(var1); + + resultL64 = s64_shl_s64(s32_mult_s16_u16(var2, inL32MW), 16); + tempProdL32 = s32_mult_s16_u16(var2, inL32LW); + resultL64 = s64_add_s64_s64(resultL64, tempProdL32); + +#ifdef WMOPS_FX + counter_fx.mult_32_16++; + counter_fx.extract -= 2; + counter_fx.sh--; + counter_fx.mult16 -=2; + counter_fx.add64--; +#endif + + return resultL64; +} /* End of s64_mult_u32_s16 function*/ +#endif // !defined(s64_mult_u32_s16) + +#if !defined(s64_mult_s32_u16_shift) +/*************************************************************************** +* +* FUNCTION NAME: s64_mult_s32_u16_shift +* +* PURPOSE: +* +* Perform multiplication of a 32-bit number with 16-bit number with +* Q factor compensation. +* +* INPUTS: +* +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range 0x8000 0000 <= var1 <= 0x7fff ffff. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* shift +* 16 bit number that gives shift for Q factor compensation. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 64 bit signed integer (int64) whose value +* falls in the range +* 0x8000 0000 0000 0000 <= var1 <= 0x7fff ffff ffff ffff. +* +* IMPLEMENTATION: +* multiplies a 32 -bit number with 16-bit number and shifts +* it left or right. +* KEYWORDS: multiply, mult, mpy +* +*************************************************************************/ +int64 s64_mult_s32_u16_shift( int32 var1, uint16 var2, int16 shift) +{ + int64 resultL64; + + + resultL64 = s64_mult_s32_s32(var1, (int32)var2); + resultL64 = s64_shl_s64(resultL64,shift-16); + + + return resultL64; +} +#endif //#if !defined(s64_mult_s32_u16_shift) + +#if !defined(s64_mult_u32_s16_shift) +/*************************************************************************** +* +* FUNCTION NAME: s64_mult_u32_s16_shift +* +* PURPOSE: +* +* Perform multiplication of a 32-bit number with 16-bit number with +* Q factor compensation. +* +* INPUTS: +* +* var1 +* 32 bit unsigned integer (int32) whose value +* falls in the range 0x0000 0000 <= var1 <= 0xffff ffff. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* shift +* 16 bit number that gives shift for Q factor compensation. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 64 bit signed integer (int64) whose value +* falls in the range +* 0x8000 0000 0000 0000 <= var1 <= 0x7fff ffff ffff ffff. +* +* IMPLEMENTATION: +* multiplies a 32 -bit number with 16-bit number and shifts +* it left or right. +* KEYWORDS: multiply, mult, mpy +* +*************************************************************************/ +int64 s64_mult_u32_s16_shift( uint32 var1, int16 var2, int16 shift) +{ + int64 resultL64; + + + resultL64 = s64_mult_s32_u32((int32)var2, var1); + resultL64 = s64_shl_s64(resultL64,shift-16); + + + return resultL64; +} +#endif //#if !defined(s64_mult_u32_s16_shift) + +#if !defined(s32_mult_s32_s32_rnd_sat) +/*************************************************************************** +* +* FUNCTION NAME: s32_mult_s32_s32_rnd_sat +* +* PURPOSE: +* +* Perform a fractional multipy of the two 32 bit input numbers +* with rounding and saturation. Output a 32 bit number. +* +* INPUTS: +* +* var1 +* 32 bit short signed integer (int32) whose value +* falls in the range 0x8000 0000 <= var1 <= 0x7fff 0000. +* var2 +* 32 bit short signed integer (int32) whose value +* falls in the range 0x8000 0000 <= var2 <= 0x7fff 0000. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* +* IMPLEMENTATION: +* Multiplies two 32 bit numbers and produces a 32-bit number as output. +* It does the following operations: +* resultL64 = in1L32 * in2L32; +* s32_sat_s40_round(resultL64) to 32 bits. +* KEYWORDS: multiply, mult, mpy +* +*************************************************************************/ +int32 s32_mult_s32_s32_rnd_sat(int32 var1, int32 var2) +{ + int32 resultL32; + int16 in1MW, in2MW; + uint16 in1LW, in2LW; + int32 temp1ProdL32, temp2ProdL32; + uint32 prodUL32; + int64 tempSumL64; + uint32 lProdL32; + + in1LW = (uint16) s16_extract_s32_l(var1); // lower word of first input + in2LW = (uint16) s16_extract_s32_l(var2); // lower word of second input + + in2MW = s16_extract_s32_h(var2); // higher word of first input + in1MW = s16_extract_s32_h(var1); // higher word of second input + + + prodUL32 = u32_mult_u16_u16(in1LW, in2LW); + temp1ProdL32 = s32_mult_s16_u16(in1MW, in2LW); + temp2ProdL32 = s32_mult_s16_u16(in2MW, in1LW); + + lProdL32 = (uint32)s16_extract_s32_h(prodUL32); + tempSumL64 = s64_add_s32_s32(temp1ProdL32, temp2ProdL32); + tempSumL64 = s64_add_s64_s32(tempSumL64, (int32)lProdL32); + tempSumL64 = s64_add_s64_s32(tempSumL64, (int32)0x8000); // rounding + + temp2ProdL32 = s32_mult_s16_s16(in1MW, in2MW); + + tempSumL64 = s64_shl_s64(tempSumL64,-16); + tempSumL64 = s64_add_s64_s32(tempSumL64, temp2ProdL32); + + resultL32 = s32_saturate_s64(tempSumL64); + +#ifdef WMOPS_FX + counter_fx.mult_32_32++; + counter_fx.saturate--; + counter_fx.extract -= 5; + counter_fx.mult16 -=4; + counter_fx.add32--; + counter_fx.add64 -=3; + counter_fx.sh--; +#endif + + return (resultL32); +} +#endif //#if !defined(s32_mult_s32_s32_rnd_sat) + +#if !defined(s64_mult_s32_u32_shift) +/*************************************************************************** +* +* FUNCTION NAME: s64_mult_s32_u32_shift +* +* PURPOSE: +* +* Perform a fractional multipy of the two 32 bit input numbers +* with shift. Output a 32 bit number. +* +* INPUTS: +* +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range 0x8000 0000 <= var1 <= 0x7fff ffff. +* var2 +* 32 bit unsigned integer (uint32) whose value +* falls in the range 0x8000 0000 <= var2 <= 0x7fff ffff. +* shift +* shift value of 3 is allowed since thats what is allowed in DSP +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 64 bit long signed integer (int64) +* +* IMPLEMENTATION: +* Multiplies two 32 bit numbers and produces a 64-bit number as output. +* It does the following operations: +* resultL64 = in1L32 * uin2L32 << shift; +* KEYWORDS: multiply, mult, mpy +* +*************************************************************************/ +int64 s64_mult_s32_u32_shift(int32 var1, uint32 var2, int16 shift) +{ + int64 resultL64; + + resultL64 = s64_mult_s32_u32(var1, var2); + resultL64 = s64_shl_s64(resultL64,shift-32); + + + return resultL64; +} +#endif //#if !defined(s64_mult_s32_u32_shift) + +#if !defined(s64_mult_lp_s32_u32_shift) +/*************************************************************************** +* +* FUNCTION NAME: s64_mult_lp_s32_u32_shift +* +* PURPOSE: +* +* Perform a low precision fractional multipy of the two 32 bit input numbers +* with shift. Output a 64 bit number. +* +* INPUTS: +* +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range 0x8000 0000 <= var1 <= 0x7fff ffff. +* var2 +* 32 bit unsigned integer (uint32) whose value +* falls in the range 0x8000 0000 <= var2 <= 0x7fff ffff. +* shift +* shift value of 3 is allowed since thats what is allowed in DSP +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 64 bit long signed integer (int64) +* +* IMPLEMENTATION: +* Multiplies two 32 bit numbers and produces a 64-bit number as output. +* It does the following operations: +* resultL64 = in1L32 * uin2L32 << shift; +* KEYWORDS: multiply, mult, mpy +* +*************************************************************************/ +int64 s64_mult_lp_s32_u32_shift(int32 var1, uint32 var2, int16 shift) +{ + int16 in1MW; + uint16 in1LW, in2LW, uin2MW; + int32 temp1ProdL32, temp2ProdL32; + int64 tempSumL64; + + in1LW = (uint16) s16_extract_s32_l(var1); // lower word of first input + in2LW = (uint16) s16_extract_s32_l(var2); // lower word of second input + + in1MW = s16_extract_s32_h(var1); // higher word of first input + uin2MW = s16_extract_s32_h(var2); // higher word of second input + + //temp1ProdL32 is signed32 = signed * unSigned + //temp1ProdL32 is unSigned32 = unSigned * unSigned + temp1ProdL32 = s32_mult_s16_u16(in1MW, in2LW); + temp2ProdL32 = u32_mult_u16_u16(uin2MW, in1LW); + + // cross product terms added. + //tempSumL64 is signed64 = signed32 + unSigned32 + //that is why we need to use s64_add_s64_u32() + //to save unSigned number from becoming sign extended + tempSumL64 = s64_add_s32_u32(temp1ProdL32, temp2ProdL32); + + // cross product + tempSumL64 = s64_shl_s64(tempSumL64,shift); + + temp2ProdL32 = s32_mult_s16_u16(in1MW, uin2MW); + + tempSumL64 = s64_shl_s64(tempSumL64,-16); + tempSumL64 = s64_add_s64_s64(tempSumL64, s64_shl_s64(temp2ProdL32,shift)); + +#ifdef WMOPS_FX + counter_fx.mult_32_32++; + counter_fx.extract -= 5; + counter_fx.mult16 -=3; + counter_fx.add32--; + counter_fx.add64 -=2; + counter_fx.sh -= 4; +#endif + + return (tempSumL64); +} +#endif // #if !defined(s64_mult_lp_s32_u32_shift) + +#if !defined(s64_mult_s32_s32_shift) +/*************************************************************************** +* +* FUNCTION NAME: s64_mult_s32_s32_shift +* +* PURPOSE: +* +* Perform full precision multiplication of two 32 bit input numbers +* with shift. Output a 64 bit number. +* +* INPUTS: +* +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range 0x8000 0000 <= var1 <= 0x7fff ffff. +* var2 +* 32 bit signed integer (int32) whose value +* falls in the range 0x8000 0000 <= var2 <= 0x7fff ffff. +* shift +* 16 bit number that gives shift for Q factor compensation. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 64 bit long signed integer (int64) +* +* IMPLEMENTATION: +* Multiplies two 32 bit numbers and produces a 64-bit number as output. +* +* KEYWORDS: multiply, mult, mpy +* +*************************************************************************/ +int64 s64_mult_s32_s32_shift(int32 var1, int32 var2, int16 shift) +{ + int64 resultL64; + + resultL64 = s64_mult_s32_s32(var1, var2); + resultL64 = s64_shl_s64(resultL64,shift-32); + + + return resultL64; +} +#endif //#if !defined(s64_mult_s32_s32_shift) + +#if !defined(s64_mult_lp_s32_s32_shift) +/*************************************************************************** +* +* FUNCTION NAME: s64_mult_lp_s32_s32_shift +* +* PURPOSE: +* +* Perform a low precision multipy of the two 32 bit input numbers with shift +* Output a 64 bit number. +* +* INPUTS: +* +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range 0x8000 0000 <= var1 <= 0x7fff 7fff. +* var2 +* 32 bit signed integer (int32) whose value +* falls in the range 0x8000 0000 <= var2 <= 0x7fff 7fff. +* shift +* shift value of 3 is allowed since thats what is allowed in DSP +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 64 bit long signed integer (int64) +* +* IMPLEMENTATION: +* Multiplies two 32 bit numbers and produces a 64-bit number as output. +* It does the following operations: +* resultL64 = in1L32 * in2L32 << shift; +* KEYWORDS: multiply, mult, mpy +* +*************************************************************************/ +int64 s64_mult_lp_s32_s32_shift(int32 var1, int32 var2, int16 shift) +{ + int16 in1MW, in2MW; + uint16 in1LW, in2LW; + int32 temp1ProdL32, temp2ProdL32; + int64 tempSumL64; + + in1LW = (uint16) s16_extract_s32_l(var1); // lower word of first input + in2LW = (uint16) s16_extract_s32_l(var2); // lower word of second input + + in1MW = s16_extract_s32_h(var1); // higher word of first input + in2MW = s16_extract_s32_h(var2); // higher word of second input + + temp1ProdL32 = s32_mult_s16_u16(in1MW, in2LW); + temp2ProdL32 = s32_mult_s16_u16(in2MW, in1LW); + + tempSumL64 = s64_add_s32_s32(temp1ProdL32, temp2ProdL32); + tempSumL64 = s64_shl_s64(tempSumL64,shift); + + temp2ProdL32 = s32_mult_s16_s16(in1MW, in2MW); + + tempSumL64 = s64_shl_s64(tempSumL64,-16); + tempSumL64 = s64_add_s64_s64(tempSumL64, s64_shl_s64(temp2ProdL32,shift)); + +#ifdef WMOPS_FX + //counter_fx.mult_32_32++; + counter_fx.mult_32_32 = counter_fx.mult_32_32 + 1.0; + counter_fx.extract -= 5; + counter_fx.mult16 -=3; + counter_fx.add32--; + counter_fx.add64 -=2; + counter_fx.sh -= 4; +#endif + + return (tempSumL64); +} +#endif //#if !defined(s64_mult_lp_s32_s32_shift) + +#if !defined(s64_mult_s16_u16_shift) +/*************************************************************************** +* +* FUNCTION NAME: s64_mult_s16_u16_shift +* +* PURPOSE: +* +* Perform a fractional multipy of the two 16 bit input numbers +* with out saturation. Output a 32 bit number. +* +* INPUTS: +* +* var1 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var1 <= 0x0000 7fff. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 64 bit signed integer (int64) whose value +* falls in the range +* 0x8000 0000 0000 0000 <= var1 <= 0x7fff ffff ffff ffff. +* +* IMPLEMENTATION: +* +* Multiply the two 16 bit input numbers and shift the result. +* Output the 64 - bit result. +* +* KEYWORDS: multiply, mult, mpy +* +*************************************************************************/ +int64 s64_mult_s16_u16_shift(int16 var1, uint16 var2, int16 shift) +{ + int64 resultL64; + int32 productL32; + + productL32 = (int32) var1*var2; + + if(shift > 0) + { /* positive shift indicating left shift */ + resultL64 = (int64) productL32 << shift ; + } + else + { /* negative shift indicating right shift */ + resultL64 = (int64) productL32 >> (-shift); + } /* end of shifting */ + +#ifdef WMOPS_FX + counter_fx.mult16++; +#endif + + return (resultL64); +} +#endif //#if !defined(s64_mult_s16_u16_shift) + +#if !defined(s32_mult_s16_u16) +/*************************************************************************** +* +* FUNCTION NAME: s32_mult_s16_u16 +* +* PURPOSE: +* +* Perform a multiplication of a signed number with unsigned number. +* output is a signed 32-bit number. +* +* INPUTS: +* +* var1 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var1 <= 0x0000 7fff. +* var2 +* 16 bit short unsigned integer (uint16) whose value +* falls in the range 0x0000 0000 <= var2 <= 0x0000 ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* productL32 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* +* IMPLEMENTATION: +* Does a signed times unsigned 16 bit multiplication and returns the value. +* +* KEYWORDS: multiply, mult, mpy +* +*************************************************************************/ + +int32 s32_mult_s16_u16(int16 var1, uint16 var2) +{ + int32 productL32; + + productL32 = (int32)var1*var2; + +#ifdef WMOPS_FX + counter_fx.mult16++; +#endif + + return (productL32); +} +#endif //#if !defined(s32_mult_s16_u16) + +#if !defined(s64_mult_s16_s16_shift) +/*************************************************************************** +* +* FUNCTION NAME: s64_mult_s16_s16_shift +* +* PURPOSE: +* +* Perform a fractional multipy of the two 16 bit input numbers +* with out saturation. Output a 32 bit number. +* +* INPUTS: +* +* var1 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var1 <= 0x0000 7fff. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 64 bit long signed integer (int64) whose value +* falls in the range +* 0x8000 0000 0000 0000 <= var1 <= 0x7fff ffff ffff ffff. +* +* IMPLEMENTATION: +* +* Multiply the two 16 bit input numbers and shift the result. +* Output the 64 - bit result. +* +* KEYWORDS: multiply, mult, mpy +* +*************************************************************************/ +int64 s64_mult_s16_s16_shift(int16 var1, int16 var2, int16 shift) +{ + int64 productL64; + int32 productL32; + + productL32 = (int32) var1 *var2; /* integer multiply */ + + if(shift > 0) + { /* positive shift indicating left shift */ + productL64 = (int64) productL32 << shift ; + } + else + { /* negative shift indicating right shift */ + productL64 = (int64) productL32 >> (-shift) ; + } /* end of shifting */ + + +#ifdef WMOPS_FX + counter_fx.mult16++; +#endif + + return (productL64); +} +#endif //#if !defined(s64_mult_s16_s16_shift) + +#if !defined(s32_mult_s16_s16) +/*************************************************************************** +* +* FUNCTION NAME: s32_mult_s16_s16 +* +* PURPOSE: +* +* Perform a fractional multipy of the two 16 bit input numbers +* with saturation. Output a 32 bit number. +* +* INPUTS: +* +* var1 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var1 <= 0x0000 7fff. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* +* IMPLEMENTATION: +* +* Multiply the two the two 16 bit input numbers. If the +* result is within this range, left shift the result by one +* and output the 32 bit number. The only possible overflow +* occurs when var1==var2==-0x8000. In this case output +* 0x7fff ffff. +* +* KEYWORDS: multiply, mult, mpy +* +*************************************************************************/ +int32 s32_mult_s16_s16(int16 var1, int16 var2) +{ + int32 productL32; + + productL32 = (int32) var1 *var2; /* integer multiply */ + +#ifdef WMOPS_FX + counter_fx.mult16++; +#endif + + return (productL32); +} +#endif //#if !defined(s32_mult_s16_s16) + +#if !defined(u32_mult_u16_u16) +/*************************************************************************** +* +* FUNCTION NAME: u32_mult_u16_u16 +* +* PURPOSE: +* +* Perform a fractional multipy of the two 16 bit input unsigned numbers +* with saturation. Output a 32 bit unsigned number. +* +* INPUTS: +* +* var1 +* 16 bit short unsigned integer (uint16) whose value +* falls in the range 0x0000 8000 <= var1 <= 0x0000 7fff. +* var2 +* 16 bit short unsigned integer (uint16) whose value +* falls in the range 0x0000 8000 <= var2 <= 0x0000 7fff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 32 bit long unsigned integer (uint32) whose value +* falls in the range +* 0x0000 0000 <= var1 <= 0xffff ffff. +* +* IMPLEMENTATION: +* +* Multiply the two the two 16 bit input numbers. +* +* KEYWORDS: multiply, mult, mpy +* +*************************************************************************/ +uint32 u32_mult_u16_u16(uint16 var1, uint16 var2) +{ + uint32 productL32; + + productL32 = (uint32) var1 *var2; /* integer multiply */ + +#ifdef WMOPS_FX + counter_fx.mult16++; +#endif + + return (productL32); +} +#endif //#if !defined(u32_mult_u16_u16) + +#if !defined(s64_mult_u16_u16_shift) +/*************************************************************************** +* +* FUNCTION NAME: s64_mult_u16_u16_shift +* +* PURPOSE: +* +* Perform a fractional multipy of the two 16 bit input unsigned numbers +* with saturation. Output a 32 bit unsigned number. +* +* INPUTS: +* +* var1 +* 16 bit short unsigned integer (uint16) whose value +* falls in the range 0x0000 8000 <= var1 <= 0x0000 7fff. +* var2 +* 16 bit short unsigned integer (uint16) whose value +* falls in the range 0x0000 8000 <= var2 <= 0x0000 7fff. +* +* shift a singed value shift. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 64 bit long unsigned integer (uint64) whose value +* falls in the range +* 0x0000 0000 0000 0000 <= var1 <= 0xffff ffff ffff ffff. +* +* IMPLEMENTATION: +* +* Multiply the two the two 16 bit input numbers. +* +* KEYWORDS: multiply, mult, mpy +* +*************************************************************************/ +int64 s64_mult_u16_u16_shift(uint16 var1, uint16 var2, int16 shift) +{ + uint32 productL32; + int64 outputL64; + + productL32 = (uint32) var1 *var2; /* integer multiply */ + + outputL64 = s64_shl_s64(productL32, shift); + +#ifdef WMOPS_FX + counter_fx.mult16++; + counter_fx.sh--; +#endif + + return (outputL64); +} +#endif //#if !defined(s64_mult_u16_u16_shift) + +#if !defined(s32_mult_s16_s16_shift_sat) +/*************************************************************************** +* +* FUNCTION NAME: s32_mult_s16_s16_shift_sat +* +* PURPOSE: +* +* Perform a fractional multipy of the two 16 bit input numbers +* with saturation. Output a 32 bit number. +* +* INPUTS: +* +* var1 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var1 <= 0x0000 7fff. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= L_var1 <= 0x7fff ffff. +* +* IMPLEMENTATION: +* +* Multiply the two the two 16 bit input numbers. If the +* result is within this range, left shift the result by one +* and output the 32 bit number. The only possible overflow +* occurs when var1==var2==-0x8000. In this case output +* 0x7fff ffff. +* +* KEYWORDS: multiply, mult, mpy +* +*************************************************************************/ +int32 s32_mult_s16_s16_shift_sat(int16 var1, int16 var2) + { + int32 productL32; + + if (var1 == SHORTWORD_MIN && var2 == SHORTWORD_MIN) + { + productL32 = LONGWORD_MAX; /* overflow */ + giOverflow = 1; + } + else + { + productL32 = (int32) var1 *var2; /* integer multiply */ + + productL32 = productL32 << 1; + } + +#ifdef WMOPS_FX + counter_fx.mult16_sat++; +#endif + + return (productL32); +} +#endif //#if !defined(s32_mult_s16_s16_shift_sat) + +#if !defined(s32_neg_s32_sat) +/*************************************************************************** +* +* FUNCTION NAME: s32_neg_s32_sat +* +* PURPOSE: +* +* Negate the 32 bit input. 0x8000 0000's negated value is +* 0x7fff ffff. +* +* INPUTS: +* +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0001 <= var1 <= 0x7fff ffff. +* +* KEYWORDS: s16_neg_s16_sat, negative +* +*************************************************************************/ +int32 s32_neg_s32_sat(int32 var1) +{ + int32 L_Out; + + if (var1 == LONGWORD_MIN) + { + L_Out = LONGWORD_MAX; + giOverflow = 1; + } + else + L_Out = -var1; + +#ifdef WMOPS_FX + counter_fx.neg_sat++; +#endif + + return (L_Out); +} +#endif //#if !defined(s32_neg_s32_sat) + +#if !defined(s64_shl_s64) +/*************************************************************************** +* +* FUNCTION NAME: s64_shl_s64 +* +* PURPOSE: +* +* shift a 64 bit value left or right depending on the shift value. +* +* INPUTS: +* +* var1 +* 64 bit long signed integer (int64) whose value +* falls in the range +* 0x8000 0000 0000 0000 <= var1 <= 0x7fff ffff ffff ffff. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* var1 +* 64 bit long signed integer (int64) whose value +* falls in the range +* 0x8000 0000 0000 0000 <= var1 <= 0x7fff ffff ffff ffff. +* +* +* IMPLEMENTATION: +* +* +* +* KEYWORDS: +* +*************************************************************************/ +int64 s64_shl_s64(int64 var1, int16 shift) +{ + int64 resultL64; + + if(shift > 0) + { + resultL64 = var1 << shift; + } + else + { + resultL64 = var1 >> (-shift); + } +#ifdef WMOPS_FX + counter_fx.sh++; +#endif + + return resultL64; +} +#endif //#if !defined(s64_shl_s64) + +#if !defined(s32_shl_s32_rnd_sat) +/*************************************************************************** +* +* FUNCTION NAME: s32_shl_s32_rnd_sat +* +* PURPOSE: +* +* Shift and round. Perform a shift right. After shifting, use +* the last bit shifted out of the LSB to round the result up +* or down. +* +* INPUTS: +* +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* +* +* IMPLEMENTATION: +* +* Shift and round. Perform a shift right. After shifting, use +* the last bit shifted out of the LSB to round the result up +* or down. This is just like s16_shl_s16_sat_rnd above except that the +* input/output is 32 bits as opposed to 16. +* +* if var2 is positve perform a arithmetic left shift +* with saturation (see s32_shl_s32_sat() above). +* +* If var2 is zero simply return var1. +* +* If var2 is negative perform a arithmetic right shift (s32_shr_s32_sat) +* of var1 by (-var2)+1. Add the LS bit of the result to +* var1 shifted right (s32_shr_s32_sat) by -var2. +* +* Note that there is no constraint on var2, so if var2 is +* -0xffff 8000 then -var2 is 0x0000 8000, not 0x0000 7fff. +* This is the reason the s32_shl_s32_sat function is used. +* +* +* KEYWORDS: +* +*************************************************************************/ +int32 s32_shl_s32_rnd_sat(int32 var1, int16 shift) +{ + int32 L_Out, L_rnd; + + if (shift < -31) + { + L_Out = 0; + } + else if (shift < 0) + { + /* right shift */ + L_rnd = s32_shl_s32_sat(var1, (int16)(shift + 1)) & 0x1; + L_Out = s32_add_s32_s32_sat(s32_shl_s32_sat(var1, shift), L_rnd); + +#ifdef WMOPS_FX + counter_fx.sh_sat-=2; + counter_fx.add32_sat--; +#endif + + } + else + { + L_Out = s32_shl_s32_sat(var1, shift); + +#ifdef WMOPS_FX + counter_fx.sh_sat--; +#endif + + } + +#ifdef WMOPS_FX + counter_fx.sh_sat++; +#endif + + return (L_Out); +} +#endif //#if !defined(l_s16_shl_s16_sat_rnd) + +#if !defined(s32_shl_s32_sat) +/*************************************************************************** +* +* FUNCTION NAME: s32_shl_s32_sat +* +* PURPOSE: +* +* Arithmetic shift left (or right). +* Arithmetically shift the input left by var2. If var2 is +* negative then an arithmetic shift right (s32_shr_s32_sat) of var1 by +* -var2 is performed. +* +* INPUTS: +* +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* +* +* IMPLEMENTATION: +* +* Arithmetically shift the 32 bit input left by var2. This +* operation maintains the sign of the input number. If var2 is +* negative then an arithmetic shift right (s32_shr_s32_sat) of var1 by +* -var2 is performed. See description of s32_shr_s32_sat for details. +* +* Equivalent to the Full-Rate GSM ">> n" operation. Note that +* ANSI-C does not guarantee operation of the C ">>" or "<<" +* operator for negative numbers. +* +* KEYWORDS: shift, arithmetic shift left, +* +*************************************************************************/ +int32 s32_shl_s32_sat(int32 var1, int16 shift) +{ + int32 L_Mask, L_Out = 0; + int i, iOverflow = 0; + + if (shift == 0 || var1 == 0) + { + L_Out = var1; + } + else if (shift < 0) + { + if (shift <= -31) + { + if (var1 > 0) + L_Out = 0; + else + L_Out = UMAX_32; + } + else + { + L_Out = s32_shr_s32_sat(var1, (int16)(-shift)); + +#ifdef WMOPS_FX + counter_fx.sh_sat--; +#endif + + } + } + else + { + if (shift >= 31) + iOverflow = 1; + else + { + if (var1 < 0) + L_Mask = LONGWORD_SIGN; /* sign bit mask */ + else + L_Mask = 0x0; + L_Out = var1; + for (i = 0; i < shift && !iOverflow; i++) + { + /* check the sign bit */ + L_Out = (L_Out & LONGWORD_MAX) << 1; + if ((L_Mask ^ L_Out) & LONGWORD_SIGN) + iOverflow = 1; + } + } + + if (iOverflow) + { + /* s16_saturate_s32 */ + if (var1 > 0) + L_Out = LONGWORD_MAX; + else + L_Out = LONGWORD_MIN; + + giOverflow = 1; + } + } + +#ifdef WMOPS_FX + counter_fx.sh_sat++; +#endif + + return (L_Out); +} +#endif //#if !defined(s32_shl_s32_sat) + +#if !defined(s32_shl_s32) +/*************************************************************************** +* +* FUNCTION NAME: s32_shl_s32 +* +* PURPOSE: +* +* Arithmetic shift left (or right) without saturation. +* Arithmetically shift the input left by var2. If var2 is +* negative then an arithmetic shift right (s32_shr_s32) of var1 by +* -var2 is performed. +* +* INPUTS: +* +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* +* +* IMPLEMENTATION: +* +* Arithmetically shift the 32 bit input left by var2. This +* operation maintains the sign of the input number. +* +* Equivalent to the Full-Rate GSM ">> n" operation. Note that +* ANSI-C does not guarantee operation of the C ">>" or "<<" +* operator for negative numbers. +* +* KEYWORDS: shift, arithmetic shift left, +* +*************************************************************************/ +int32 s32_shl_s32(int32 var1, int16 shift) +{ + int32 L_Out; + + if (shift == 0 || var1 == 0) + { + L_Out = var1; + } + else if (shift < 0) + { + // perform right shift + L_Out = s32_shr_s32(var1, (int16)(-shift)); + } + else + { + L_Out = var1 << shift; + } + +#ifdef WMOPS_FX + counter_fx.sh_sat++; +#endif + + return (L_Out); +} +#endif //#if !defined(s32_shl_s32) + +#if !defined(s32_shr_s32_sat) +/*************************************************************************** +* +* FUNCTION NAME: s32_shr_s32_sat +* +* PURPOSE: +* +* Arithmetic shift right (or left). +* Arithmetically shift the input right by var2. If var2 is +* negative then an arithmetic shift left (s32_shl_s32_sat) of var1 by +* -var2 is performed. +* +* INPUTS: +* +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* +* +* IMPLEMENTATION: +* +* Arithmetically shift the input right by var2. This +* operation maintains the sign of the input number. If var2 is +* negative then an arithmetic shift left (s32_shl_s32_sat) of var1 by +* -var2 is performed. See description of s32_shl_s32_sat for details. +* +* The input is a 32 bit number, as is the output. +* +* Equivalent to the Full-Rate GSM ">> n" operation. Note that +* ANSI-C does not guarantee operation of the C ">>" or "<<" +* operator for negative numbers. +* +* KEYWORDS: shift, arithmetic shift right, +* +*************************************************************************/ +int32 s32_shr_s32_sat(int32 var1, int16 shift) +{ + int32 L_Mask, L_Out; + + if (shift == 0 || var1 == 0) + { + L_Out = var1; + } + else if (shift < 0) + { + /* perform a left shift */ + /*----------------------*/ + if (shift <= -31) + { + /* s16_saturate_s32 */ + if (var1 > 0) + { + L_Out = LONGWORD_MAX; + giOverflow = 1; + } + else + { + L_Out = LONGWORD_MIN; + giOverflow = 1; + } + } + else + { + L_Out = s32_shl_s32_sat(var1, (int16)(-shift)); // OP_COUNT(-2); + +#ifdef WMOPS_FX + counter_fx.sh_sat--; +#endif + + } + } + else + { + + if (shift >= 31) + { + if (var1 > 0) + L_Out = 0; + else + L_Out = UMAX_32; + } + else + { + L_Mask = 0; + if (var1 < 0) + { + L_Mask = ~L_Mask << (32 - shift); + } + var1 >>= shift; + L_Out = L_Mask | var1; + } + } + +#ifdef WMOPS_FX + counter_fx.sh_sat++; +#endif + + return (L_Out); +} +#endif //#if !defined(s32_shr_s32_sat) + +#if !defined(s32_shr_s32) +/*************************************************************************** +* +* FUNCTION NAME: s32_shr_s32 +* +* PURPOSE: +* +* Arithmetic shift right (or left) without saturation. +* Arithmetically shift the input right by var2. If var2 is +* negative then an arithmetic shift left (s32_shl_s32) of var1 by +* -var2 is performed. +* +* INPUTS: +* +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* +* +* IMPLEMENTATION: +* +* Arithmetically shift the input right by var2. This +* operation maintains the sign of the input number. If var2 is +* negative then an arithmetic shift left (s32_shl_s32) of var1 by +* -var2 is performed. See description of s32_shl_s32 for details. +* +* The input is a 32 bit number, as is the output. +* +* Equivalent to the Full-Rate GSM ">> n" operation. Note that +* ANSI-C does not guarantee operation of the C ">>" or "<<" +* operator for negative numbers. +* +* KEYWORDS: shift, arithmetic shift right, +* +*************************************************************************/ +int32 s32_shr_s32(int32 var1, int16 shift) +{ + int32 L_Mask, L_Out; + + if (shift == 0 || var1 == 0) + { + L_Out = var1; + } + else if (shift < 0) + { + /* perform a left shift */ + /*----------------------*/ + L_Out = s32_shl_s32(var1, (int16)(-shift)); // OP_COUNT(-2); + +#ifdef WMOPS_FX + counter_fx.sh_sat--; +#endif + + } + else + { + L_Mask = 0; + if (var1 < 0) + { + L_Mask = (~L_Mask) << (32 - shift); + } + var1 >>= shift; + L_Out = L_Mask | var1; + } + +#ifdef WMOPS_FX + counter_fx.sh_sat++; +#endif + + return (L_Out); +} +#endif //#if !defined(s32_shr_s32) + +#if !defined(s32_sub_s32_s32) +/*************************************************************************** +* +* FUNCTION NAME: s32_sub_s32_s32 +* +* PURPOSE: +* +* Perform the subtraction of the two 32 bit input variables with +* saturation. +* +* INPUTS: +* +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* var2 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var2 <= 0x7fff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* +* IMPLEMENTATION: +* +* Perform the subtraction of the two 32 bit input variables with +* saturation. +* +* L_Out = var1 - var2 +* +* L_Out is set to 0x7fff ffff if the operation results in an +* overflow. L_Out is set to 0x8000 0000 if the operation +* results in an underflow. +* +* KEYWORDS: s16_sub_s16_s16, subtraction +* +*************************************************************************/ +int32 s32_sub_s32_s32(int32 var1, int32 var2) +{ + int32 L_Sum; + + L_Sum = var1 - var2; + +#ifdef WMOPS_FX + counter_fx.add32++; +#endif + + return (L_Sum); +} +#endif //#if !defined(s32_sub_s32_s32) + +#if !defined(s32_sub_s32_s32_sat) +/*************************************************************************** +* +* FUNCTION NAME: s32_sub_s32_s32_sat +* +* PURPOSE: +* +* Perform the subtraction of the two 32 bit input variables with +* saturation. +* +* INPUTS: +* +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* var2 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var2 <= 0x7fff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* +* IMPLEMENTATION: +* +* Perform the subtraction of the two 32 bit input variables with +* saturation. +* +* L_Out = var1 - var2 +* +* L_Out is set to 0x7fff ffff if the operation results in an +* overflow. L_Out is set to 0x8000 0000 if the operation +* results in an underflow. +* +* KEYWORDS: s16_sub_s16_s16, subtraction +* +*************************************************************************/ +int32 s32_sub_s32_s32_sat(int32 var1, int32 var2) +{ + int32 L_Sum; + int64 dSum; + + dSum = (int64) var1 - (int64) var2; + L_Sum = var1 - var2; + + if (dSum != L_Sum) + { + /* overflow occurred */ + L_Sum = s32_saturate_s64(dSum); + +#ifdef WMOPS_FX + counter_fx.saturate--; +#endif + + } + +#ifdef WMOPS_FX + counter_fx.add32_sat++; +#endif + + return (L_Sum); +} +#endif //#if !defined(s32_sub_s32_s32_sat) + +#if !defined(s16_mac_s32_s16_s16_sat_rnd) +/*************************************************************************** +* +* FUNCTION NAME:s16_mac_s32_s16_s16_sat_rnd +* +* PURPOSE: +* +* Multiply accumulate and round. Multiply two 16 +* bit numbers together with saturation. Add that result to +* the 32 bit input with saturation. Finally round the result +* into a 16 bit number. +* +* +* INPUTS: +* +* var1 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var1 <= 0x0000 7fff. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* var3 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var2 <= 0x7fff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* swOut +* 16 bit short signed integer (int16) whose value +* falls in the range +* 0xffff 8000 <= swOut <= 0x0000 7fff. +* +* IMPLEMENTATION: +* +* Multiply two 16 bit numbers together with +* saturation. The only numbers which will cause saturation on +* the multiply are 0x8000 * 0x8000. +* +* Add that result to the 32 bit input with saturation. +* Round the 32 bit result by adding 0x0000 8000 to the input. +* The result may overflow due to the s16_add_s16_s16. If so, the result +* is saturated. The 32 bit rounded number is then shifted +* down 16 bits and returned as a int16. +* +* Please note that this is not a true multiply accumulate as +* most processors would implement it. The 0x8000*0x8000 +* causes and overflow for this instruction. On most +* processors this would cause an overflow only if the 32 bit +* input added to it were positive or zero. +* +* KEYWORDS: mac, multiply accumulate, macr +* +*************************************************************************/ +int16 s16_mac_s32_s16_s16_sat_rnd(int32 var3, int16 var1, int16 var2) +{ + return (s16_round_s32_sat(s32_mac_s32_s16_s16_sat(var3, var1, var2))); +} +#endif //#if !defined(s16_mac_s32_s16_s16_sat_rnd) + +#if !defined(s16_msu_s32_s16_s16_sat_rnd) +/*************************************************************************** +* +* FUNCTION NAME: s16_msu_s32_s16_s16_sat_rnd +* +* PURPOSE: +* +* Multiply subtract and round. Multiply two 16 +* bit numbers together with saturation. Subtract that result from +* the 32 bit input with saturation. Finally round the result +* into a 16 bit number. +* +* +* INPUTS: +* +* var1 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var1 <= 0x0000 7fff. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* var3 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var2 <= 0x7fff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* swOut +* 16 bit short signed integer (int16) whose value +* falls in the range +* 0xffff 8000 <= swOut <= 0x0000 7fff. +* +* IMPLEMENTATION: +* +* Multiply two 16 bit numbers together with +* saturation. The only numbers which will cause saturation on +* the multiply are 0x8000 * 0x8000. +* +* Subtract that result from the 32 bit input with saturation. +* Round the 32 bit result by adding 0x0000 8000 to the input. +* The result may overflow due to the s16_add_s16_s16. If so, the result +* is saturated. The 32 bit rounded number is then shifted +* down 16 bits and returned as a int16. +* +* Please note that this is not a true multiply accumulate as +* most processors would implement it. The 0x8000*0x8000 +* causes and overflow for this instruction. On most +* processors this would cause an overflow only if the 32 bit +* input added to it were positive or zero. +* +* KEYWORDS: mac, multiply accumulate, macr +* +*************************************************************************/ +int16 s16_msu_s32_s16_s16_sat_rnd(int32 var3, int16 var1, int16 var2) +{ + return (s16_round_s32_sat(s32_msu_s32_s16_s16_sat(var3, var1, var2))); +} +#endif //#if !defined(s16_msu_s32_s16_s16_sat_rnd) + + +#if !defined(s16_neg_s16_sat) +/*************************************************************************** +* +* FUNCTION NAME: s16_neg_s16_sat +* +* PURPOSE: +* +* Negate the 16 bit input. 0x8000's negated value is 0x7fff. +* +* INPUTS: +* +* var1 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var1 <= 0x0000 7fff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* swOut +* 16 bit short signed integer (int16) whose value +* falls in the range +* 0xffff 8001 <= swOut <= 0x0000 7fff. +* +* KEYWORDS: s16_neg_s16_sat, negative, invert +* +*************************************************************************/ +int16 s16_neg_s16_sat(int16 var1) +{ + int16 swOut; + + if (var1 == SHORTWORD_MIN) + { + swOut = SHORTWORD_MAX; + giOverflow = 1; + } + else + swOut = -var1; + +#ifdef WMOPS_FX + counter_fx.neg_sat++; +#endif + + return (swOut); +} +#endif //#if !defined(s16_neg_s16_sat) + +#if !defined(s16_norm_s32) +/*************************************************************************** +* +* FUNCTION NAME: s16_norm_s32 +* +* PURPOSE: +* +* Get normalize shift count: +* +* A 32 bit number is input (possiblly unnormalized). Output +* the positive (or zero) shift count required to normalize the +* input. +* +* INPUTS: +* +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* swOut +* 16 bit short signed integer (int16) whose value +* falls in the range +* 0 <= swOut <= 31 +* +* +* +* IMPLEMENTATION: +* +* Get normalize shift count: +* +* A 32 bit number is input (possiblly unnormalized). Output +* the positive (or zero) shift count required to normalize the +* input. +* +* If zero in input, return 0 as the shift count. +* +* For non-zero numbers, count the number of left shift +* required to get the number to fall into the range: +* +* 0x4000 0000 >= normlzd number >= 0x7fff ffff (positive number) +* or +* 0x8000 0000 <= normlzd number < 0xc000 0000 (negative number) +* +* Return the number of shifts. +* +* This instruction corresponds exactly to the Full-Rate "norm" +* instruction. +* +* KEYWORDS: norm, normalization +* +*************************************************************************/ +int16 s16_norm_s32(int32 var1) +{ + int16 swShiftCnt; + + if (var1 != 0) + { + if (!(var1 & LONGWORD_SIGN)) + { + /* positive input */ + for (swShiftCnt = 0; !(var1 <= LONGWORD_MAX && var1 >= LONGWORD_HALF); + swShiftCnt++) + { + var1 = var1 << 1; + } + } + else + { + /* negative input */ + for (swShiftCnt = 0; + !(var1 >= LONGWORD_MIN && var1 < (int32) 0xc0000000L); + swShiftCnt++) + { + var1 = var1 << 1; + } + } + } + else + { + swShiftCnt = 0; + } + +#ifdef WMOPS_FX + counter_fx.norm++; +#endif + + return (swShiftCnt); +} +#endif //#if !defined(s16_norm_s32) + +#if !defined(s16_norm_s16) +/*************************************************************************** +* +* FUNCTION NAME: s16_norm_s16 +* +* PURPOSE: +* +* Get normalize shift count: +* +* A 16 bit number is input (possiblly unnormalized). Output +* the positive (or zero) shift count required to normalize the +* input. +* +* INPUTS: +* +* var1 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var1 <= 0x0000 7fff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* swOut +* 16 bit short signed integer (int16) whose value +* falls in the range +* 0 <= swOut <= 15 +* +* +* +* IMPLEMENTATION: +* +* Get normalize shift count: +* +* A 16 bit number is input (possiblly unnormalized). Output +* the positive (or zero) shift count required to normalize the +* input. +* +* If zero in input, return 0 as the shift count. +* +* For non-zero numbers, count the number of left shift +* required to get the number to fall into the range: +* +* 0x4000 >= normlzd number >= 0x7fff (positive number) +* or +* 0x8000 <= normlzd number < 0xc000 (negative number) +* +* Return the number of shifts. +* +* This instruction corresponds exactly to the Full-Rate "norm" +* instruction. +* +* KEYWORDS: norm, normalization +* +*************************************************************************/ +int16 s16_norm_s16(int16 var1) +{ + short swShiftCnt; + int32 var; + + var = s32_deposit_s16_h(var1); + swShiftCnt = s16_norm_s32(var); + +#ifdef WMOPS_FX + counter_fx.norm++; + counter_fx.deposit--; + counter_fx.norm--; +#endif + + return (swShiftCnt); +} +#endif //#if !defined(s16_norm_s16) + +#if !defined(s32_cl1_s32) +/*************************************************************************** +* +* FUNCTION NAME: s32_cl1_s32 +* +* PURPOSE: +* +* Count leading ones: +* +* A 32 bit number is input (possiblly unnormalized). Output +* the number of consecutive 1's starting with the most significant bit +* +* +* INPUTS: +* +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range 0x8000 0000<= var1 <= 0x7fff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* cnt +* 32 bit signed integer (int32) whose value +* falls in the range +* 0 <= cnt <= 31 +* +* +* +* IMPLEMENTATION: +* +* +* KEYWORDS: leading ones +* +*************************************************************************/ +int32 s32_cl1_s32(int32 var1) +{ + int32 cnt = 0; + unsigned int mask = 1 << (31-cnt); //start from MSB + + while (0 != (var1 & mask )) + { + cnt++; + mask = 1 << (31-cnt); + } + +#ifdef WMOPS_FX + counter_fx.norm++; + counter_fx.deposit--; + counter_fx.norm--; +#endif + + return (cnt); +} +#endif //#if !defined(s32_cl1_s32) + +#if !defined(popOverflow) +/**************************************************************************** +* +* FUNCTION NAME: popOverflow +* +* PURPOSE: +* +* Pull the old overflow state from the "stack". Replace the current +* overflow status with its predecessor. +* +* INPUTS: +* +* none +* +* +* OUTPUTS: none +* +* RETURN VALUE: value of datum about the be lost (usually the +* temporary saturation state) +* +* KEYWORDS: saturation, limit, overflow +* +***************************************************************************/ +int popOverflow(void) +{ + int i; + + i = giOverflow; + giOverflow = giOldOverflow; + return (i); +} +#endif // #if !defined(popOverflow) + +#if !defined(s16_round_s32_sat) +/*************************************************************************** +* +* FUNCTION NAME: s16_round_s32_sat +* +* PURPOSE: +* +* Round the 32 bit int32 into a 16 bit shortword with saturation. +* +* INPUTS: +* +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* swOut +* 16 bit short signed integer (int16) whose value +* falls in the range +* 0xffff 8000 <= swOut <= 0x0000 7fff. +* +* IMPLEMENTATION: +* +* Perform a two's s16_complement_s16 round on the input int32 with +* saturation. +* +* This is equivalent to adding 0x0000 8000 to the input. The +* result may overflow due to the s16_add_s16_s16. If so, the result is +* saturated. The 32 bit rounded number is then shifted down +* 16 bits and returned as a int16. +* +* +* KEYWORDS: round +* +*************************************************************************/ +int16 s16_round_s32_sat(int32 var1) +{ + int32 L_Prod; + + L_Prod = s32_add_s32_s32_sat(var1, 0x00008000L); /* round MSP */ + +#ifdef WMOPS_FX + counter_fx.round_sat++; + counter_fx.add32_sat--; +#endif + + return (s16_extract_s32_h(L_Prod)); +} +#endif //#if !defined(round) + +#if !defined(setOverflow) +/**************************************************************************** +* +* FUNCTION NAME: set overflow +* +* PURPOSE: +* +* Clear the overflow flag +* +* INPUTS: +* +* none +* +* +* OUTPUTS: global overflow flag is cleared +* previous value stored in giOldOverflow +* +* RETURN VALUE: previous value of overflow +* +* +* KEYWORDS: saturation, limit, overflow +* +***************************************************************************/ +int setOverflow(void) +{ + giOldOverflow = giOverflow; + giOverflow = 1; + return (giOldOverflow); +} +#endif + +#if !defined(s16_shl_s16_sat_rnd) +/*************************************************************************** +* +* FUNCTION NAME: s16_shl_s16_sat_rnd +* +* PURPOSE: +* +* Shift and round. Perform a shift right. After shifting, use +* the last bit shifted out of the LSB to round the result up +* or down. +* +* INPUTS: +* +* var1 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var1 <= 0x0000 7fff. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* swOut +* 16 bit short signed integer (int16) whose value +* falls in the range +* 0xffff 8000 <= swOut <= 0x0000 7fff. +* +* +* IMPLEMENTATION: +* +* Shift and round. Perform a shift right. After shifting, use +* the last bit shifted out of the LSB to round the result up +* or down. +* +* If var2 is positive perform a arithmetic left shift +* with saturation (see s16_shl_s16_sat() above). +* +* If var2 is zero simply return var1. +* +* If var2 is negative perform a arithmetic right shift (s16_shr_s16_sat) +* of var1 by (-var2)+1. Add the LS bit of the result to var1 +* shifted right (s16_shr_s16_sat) by -var2. +* +* Note that there is no constraint on var2, so if var2 is +* -0xffff 8000 then -var2 is 0x0000 8000, not 0x0000 7fff. +* This is the reason the s16_shl_s16_sat function is used. +* +* +* KEYWORDS: +* +*************************************************************************/ +int16 s16_shl_s16_sat_rnd(int16 var1, int16 var2) +{ + int16 swOut, swRnd; + + if (var2 >= 0) + { + swOut = s16_shl_s16_sat(var1, var2); // OP_COUNT(-1); + +#ifdef WMOPS_FX + counter_fx.sh_sat--; +#endif + + } + else + { + /* right shift */ + if (var2 < -15) + { + swOut = 0; + } + else + { + swRnd = s16_shl_s16_sat(var1, (int16)(var2 + 1)) & 0x1; + swOut = s16_add_s16_s16_sat(s16_shl_s16_sat(var1, var2), swRnd); + +#ifdef WMOPS_FX + counter_fx.sh_sat-=2; + counter_fx.add16--; +#endif + + } + } + +#ifdef WMOPS_FX + counter_fx.sh_sat++; +#endif + + return (swOut); +} +#endif // #if !defined(s16_shl_s16_sat_rnd) + +#if !defined(s16_shl_s16_sat) +/*************************************************************************** +* +* FUNCTION NAME: s16_shl_s16_sat +* +* PURPOSE: +* +* Arithmetically shift the input left by var2. +* +* +* INPUTS: +* +* var1 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var1 <= 0x0000 7fff. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* swOut +* 16 bit short signed integer (int16) whose value +* falls in the range +* 0xffff 8000 <= swOut <= 0x0000 7fff. +* +* IMPLEMENTATION: +* +* If Arithmetically shift the input left by var2. If var2 is +* negative then an arithmetic shift right (s16_shr_s16_sat) of var1 by +* -var2 is performed. See description of s16_shr_s16_sat for details. +* When an arithmetic shift left is performed the var2 LS bits +* are zero filled. +* +* The only exception is if the left shift causes an overflow +* or underflow. In this case the LS bits are not modified. +* The number returned is 0x8000 in the case of an underflow or +* 0x7fff in the case of an overflow. +* +* The s16_shl_s16_sat is equivalent to the Full-Rate GSM "<< n" operation. +* Note that ANSI-C does not guarantee operation of the C ">>" +* or "<<" operator for negative numbers - it is not specified +* whether this shift is an arithmetic or logical shift. +* +* KEYWORDS: asl, arithmetic shift left, shift +* +*************************************************************************/ + +int16 s16_shl_s16_sat(int16 var1, int16 shift) +{ + int16 swOut; + int32 L_Out; + + if (shift == 0 || var1 == 0) + { + swOut = var1; + } + else if (shift < 0) + { + /* perform a right shift */ + /*-----------------------*/ + if (shift <= -15) + { + if (var1 < 0) + swOut = 0xffff; + else + swOut = 0x0; + } + else + { + swOut = s16_shr_s16_sat(var1, (int16)(-shift)); + +#ifdef WMOPS_FX + counter_fx.sh_sat--; +#endif + + } + } + else + { + /* shift > 0 */ + if (shift >= 15) + { + /* s16_saturate_s32 */ + if (var1 > 0) + swOut = SHORTWORD_MAX; + else + swOut = SHORTWORD_MIN; + giOverflow = 1; + } + else + { + L_Out = (int32) var1 *(1 << shift); + + swOut = (int16) L_Out; /* copy low portion to swOut, overflow + * could have hpnd */ + if (swOut != L_Out) + { + /* overflow */ + if (var1 > 0) + swOut = SHORTWORD_MAX; /* s16_saturate_s32 */ + else + swOut = SHORTWORD_MIN; /* s16_saturate_s32 */ + giOverflow = 1; + } + } + } + +#ifdef WMOPS_FX + counter_fx.sh_sat++; +#endif + + return (swOut); +} +#endif //#if !defined(s16_shl_s16_sat) + +#if !defined(s16_shl_s16) +/*************************************************************************** +* +* FUNCTION NAME: s16_shl_s16 +* +* PURPOSE: +* +* Arithmetically shift the input left by var2 without saturation +* +* +* INPUTS: +* +* var1 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var1 <= 0x0000 7fff. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* swOut +* 16 bit short signed integer (int16) whose value +* falls in the range +* 0xffff 8000 <= swOut <= 0x0000 7fff. +* +* IMPLEMENTATION: +* +* If Arithmetically shift the input left by var2. If var2 is +* negative then an arithmetic shift right (s16_shr_s16) of var1 by +* -var2 is performed. See description of s16_shr_s16 for details. +* When an arithmetic shift left is performed the var2 LS bits +* are zero filled. +* +* The only exception is if the left shift causes an overflow +* or underflow. In this case the LS bits are not modified. +* The number returned is 0x8000 in the case of an underflow or +* 0x7fff in the case of an overflow. +* +* The s16_shl_s16_sat is equivalent to the Full-Rate GSM "<< n" operation. +* Note that ANSI-C does not guarantee operation of the C ">>" +* or "<<" operator for negative numbers - it is not specified +* whether this shift is an arithmetic or logical shift. +* +* KEYWORDS: asl, arithmetic shift left, shift +* +*************************************************************************/ + +int16 s16_shl_s16(int16 var1, int16 shift) +{ + int16 swOut; + int32 L_Out; + + if (shift == 0 || var1 == 0) + { + swOut = var1; + } + else if (shift < 0) + { + /* perform a right shift */ + /*-----------------------*/ + swOut = s16_shr_s16(var1, (int16)(-shift)); + } + else + { + L_Out = (int32) var1 *(1 << shift); + + swOut = (int16) L_Out; /* copy low portion to swOut, overflow */ + } + +#ifdef WMOPS_FX + counter_fx.sh_sat++; +#endif + + return (swOut); +} +#endif //#if !defined(s16_shl_s16) + +#if !defined(s16_shr_s16_sat) +/*************************************************************************** +* +* FUNCTION NAME: s16_shr_s16_sat +* +* PURPOSE: +* +* Arithmetic shift right (or left). +* Arithmetically shift the input right by var2. If var2 is +* negative then an arithmetic shift left (s16_shl_s16_sat) of var1 by +* -var2 is performed. +* +* INPUTS: +* +* var1 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var1 <= 0x0000 7fff. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* swOut +* 16 bit short signed integer (int16) whose value +* falls in the range +* 0xffff 8000 <= swOut <= 0x0000 7fff. +* +* IMPLEMENTATION: +* +* Arithmetically shift the input right by var2. This +* operation maintains the sign of the input number. If var2 is +* negative then an arithmetic shift left (s16_shl_s16_sat) of var1 by +* -var2 is performed. See description of s16_shl_s16_sat for details. +* +* Equivalent to the Full-Rate GSM ">> n" operation. Note that +* ANSI-C does not guarantee operation of the C ">>" or "<<" +* operator for negative numbers. +* +* KEYWORDS: shift, arithmetic shift right, +* +*************************************************************************/ +int16 s16_shr_s16_sat(int16 var1, int16 shift) +{ + int16 swMask, swOut; + + if (shift == 0 || var1 == 0) + swOut = var1; + + else if (shift < 0) + { + /* perform an arithmetic left shift */ + /*----------------------------------*/ + if (shift <= -15) + { + /* s16_saturate_s32 */ + if (var1 > 0) + swOut = SHORTWORD_MAX; + else + swOut = SHORTWORD_MIN; + giOverflow = 1; + } + else + { + swOut = s16_shl_s16_sat(var1, (int16)(-shift)); // OP_COUNT(-1); + +#ifdef WMOPS_FX + counter_fx.sh_sat--; +#endif + + } + } + else + { + /* positive shift count */ + /*----------------------*/ + + if (shift >= 15) + { + if (var1 < 0) + swOut = 0xffff; + else + swOut = 0x0; + } + else + { + /* take care of sign extension */ + /*-----------------------------*/ + + swMask = 0; + if (var1 < 0) + { + swMask = ~swMask << (16 - shift); + } + + var1 >>= shift; + swOut = swMask | var1; + } + } + +#ifdef WMOPS_FX + counter_fx.sh_sat++; +#endif + + return (swOut); +} +#endif //#if !defined(s16_shr_s16_sat) + +#if !defined(s16_shr_s16) +/*************************************************************************** +* +* FUNCTION NAME: s16_shr_s16 +* +* PURPOSE: +* +* Arithmetic shift right (or left) without saturation. +* Arithmetically shift the input right by var2. If var2 is +* negative then an arithmetic shift left (s16_shl_s16) of var1 by +* -var2 is performed. +* +* INPUTS: +* +* var1 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var1 <= 0x0000 7fff. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* swOut +* 16 bit short signed integer (int16) whose value +* falls in the range +* 0xffff 8000 <= swOut <= 0x0000 7fff. +* +* IMPLEMENTATION: +* +* Arithmetically shift the input right by var2. This +* operation maintains the sign of the input number. If var2 is +* negative then an arithmetic shift left (s16_shl_s16) of var1 by +* -var2 is performed. See description of s16_shl_s16 for details. +* +* Equivalent to the Full-Rate GSM ">> n" operation. Note that +* ANSI-C does not guarantee operation of the C ">>" or "<<" +* operator for negative numbers. +* +* KEYWORDS: shift, arithmetic shift right, +* +*************************************************************************/ +int16 s16_shr_s16(int16 var1, int16 shift) +{ + int16 swMask, swOut; + + if (shift == 0 || var1 == 0) + { + swOut = var1; + } + else if (shift < 0) + { + /* perform an arithmetic left shift */ + /*----------------------------------*/ + swOut = s16_shl_s16(var1, (int16)(-shift)); // OP_COUNT(-1); + +#ifdef WMOPS_FX + counter_fx.sh_sat--; +#endif + } + else + { + /* positive shift count */ + /*----------------------*/ + /* take care of sign extension */ + /*-----------------------------*/ + + swMask = 0; + if (var1 < 0) + { + swMask = ~swMask << (16 - shift); + } + + var1 >>= shift; + swOut = swMask | var1; + } + +#ifdef WMOPS_FX + counter_fx.sh_sat++; +#endif + + return (swOut); +} +#endif //#if !defined(s16_shr_s16_sat) + +#if !defined(s16_sub_s16_s16) +/*************************************************************************** +* +* FUNCTION NAME: s16_sub_s16_s16 +* +* PURPOSE: +* +* Perform the subtraction of the two 16 bit input variable with +* saturation. +* +* INPUTS: +* +* var1 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var1 <= 0x0000 7fff. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* swOut +* 16 bit short signed integer (int16) whose value +* falls in the range +* 0xffff 8000 <= swOut <= 0x0000 7fff. +* +* IMPLEMENTATION: +* +* Perform the subtraction of the two 16 bit input variable +* +* swOut = var1 - var2 +* +* +* KEYWORDS: s16_sub_s16_s16, subtraction +* +*************************************************************************/ +int16 s16_sub_s16_s16(int16 var1, int16 var2) +{ + int16 swOut; + + + swOut = var1 - var2; + +#ifdef WMOPS_FX + counter_fx.add16++; +#endif + + return (swOut); +} +#endif //#if !defined(s16_sub_s16_s16) + +#if !defined(s64_sub_s64_s64) +/*************************************************************************** +* +* FUNCTION NAME: s64_sub_s64_s64 +* +* PURPOSE: +* +* Perform the subtraction of the two 16 bit input variable with +* saturation. +* +* INPUTS: +* +* var1 +* 64 bit short signed integer (int16) +* var2 +* 64 bit short signed integer (int16) +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* swOut +* 64 bit short signed integer (int16) +* +* IMPLEMENTATION: +* +* Perform the subtraction of the two 64 bit input variable +* +* swOut = var1 - var2 +* +* +* KEYWORDS: s16_sub_s16_s16, subtraction +* +*************************************************************************/ +int64 s64_sub_s64_s64(int64 var1, int64 var2) +{ + int64 swOut; + + swOut = var1 - var2; + +#ifdef WMOPS_FX + counter_fx.add64++; +#endif + + return (swOut); +} +#endif //#if !defined(s64_sub_s64_s64) + +#if !defined(s16_sub_s16_s16_sat) +/*************************************************************************** +* +* FUNCTION NAME: s16_sub_s16_s16_sat +* +* PURPOSE: +* +* Perform the subtraction of the two 16 bit input variable with +* saturation. +* +* INPUTS: +* +* var1 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var1 <= 0x0000 7fff. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* swOut +* 16 bit short signed integer (int16) whose value +* falls in the range +* 0xffff 8000 <= swOut <= 0x0000 7fff. +* +* IMPLEMENTATION: +* +* Perform the subtraction of the two 16 bit input variable with +* saturation. +* +* swOut = var1 - var2 +* +* swOut is set to 0x7fff if the operation results in an +* overflow. swOut is set to 0x8000 if the operation results +* in an underflow. +* +* KEYWORDS: s16_sub_s16_s16, subtraction +* +*************************************************************************/ +int16 s16_sub_s16_s16_sat(int16 var1, int16 var2) +{ + int32 L_diff; + int16 swOut; + + L_diff = (int32) var1 - var2; + swOut = s16_saturate_s32(L_diff); + +#ifdef WMOPS_FX + counter_fx.add16_sat++; +#endif + + return (swOut); +} +#endif //#if !defined(s16_sub_s16_s16_sat) + +#if !defined(s32_mls_s32_s16_sat) +/*************************************************************************** +* +* FUNCTION NAME: s32_mls_s32_s16_sat +* +* PURPOSE: +* +* Perform a special case of multipy of the one 32 bit signed number +* with 16 bit signed number. Output a 32 bit number. +* +* INPUTS: +* +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range 0x8000 0000 <= var1 <= 0x7fff ffff. +* var2 +* 16 bit signed integer (int16) whose value +* falls in the range 0x8000 <= var2 <= 0x7fff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 32 bit signed integer +* +* IMPLEMENTATION: +* +* +* KEYWORDS: multiply, sat, mac +* +*************************************************************************/ +int32 s32_mls_s32_s16_sat( int32 Lv, int16 v ) +{ + int32 Temp ; + + Temp = Lv & (int32) 0x0000ffff ; + Temp = Temp * (int32) v ; + Temp = s32_shr_s32_sat( Temp, (int16) 15 ) ; + Temp = s32_mac_s32_s16_s16_sat( Temp, v, s16_extract_s32_h(Lv) ) ; + + return Temp ; +} +#endif //#if !defined(s32_mls_s32_s16_sat) + + +#if !defined(s16_norm_s64) +/******************************************************************** +* FUNCTION : s16_norm_s64(). +* +* PURPOSE : +* calculates the shift required to normalize a 32 +* bit number. +* +* INPUT ARGUMENTS : +* var1 (int64) input in Qin +* +* OUTPUT ARGUMENTS : +* None. +* +* INPUT/OUTPUT ARGUMENTS : +* None +* +* RETURN ARGUMENTS : +* (int16)shift value to use to normalize the value. +* +* Note: var1 can be negative, if Linput=0, return 0 +* +***********************************************************************/ +int16 s16_norm_s64(int64 var1) +{ + int16 shift; + shift = 0; + + if (var1 != 0) + { // if input!=0 + while ((var1 > MIN_32) && (var1 < MAX_32)) + { // case where input is within 32 bit + var1 = var1<<1; + shift++; + } + + while ((var1 < MIN_32) || (var1 > MAX_32)) + { // case where input is out of 32 bit + var1 = var1>>1; + shift--; + } + } + +#ifdef WMOPS_FX + counter_fx.norm++; +#endif + + return (shift); +} +#endif //#if !defined(s16_norm_s64) + +#if !defined(s32_deposit_s16_h) +/*************************************************************************** +* +* FUNCTION NAME: s32_deposit_s16_h +* +* PURPOSE: +* +* Put the 16 bit input into the 16 MSB's of the output Word32. The +* LS 16 bits are zeroed. +* +* INPUTS: +* +* var1 +* 16 bit short signed integer (Word16) whose value +* falls in the range 0xffff 8000 <= var1 <= 0x0000 7fff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 32 bit signed integer (Word32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff 0000. +* +* +* KEYWORDS: deposit, assign, fractional assign +* +*************************************************************************/ +int32 s32_deposit_s16_h(int16 var1) +{ + int32 var2; + + var2 = (int32) var1 << 16; + +#ifdef WMOPS_FX + counter_fx.deposit++; +#endif + + return (var2); +} +#endif //#if !defined(s32_deposit_s16_h) + + +#if !defined(s32_mult_s32_s16_rnd_sat) +/*************************************************************************** +* +* FUNCTION NAME: s32_mult_s32_s16_rnd_sat +* +* PURPOSE: +* +* Perform a fractional multipy of a 32 bit input number and +* 16-bit number with rounding and saturation. Output a 32 bit number. +* +* INPUTS: +* +* var1 +* 32 bit short signed integer (int32) whose value +* falls in the range 0x8000 0000 <= var1 <= 0x7fff 0000. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0x8000 <= var2 <= 0x7fff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 32 bit long signed integer (int32) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* +* IMPLEMENTATION: +* Multiplies a 32 bit number with 16-bit number and produces a 32-bit number as output. +* It does the following operations: +* resultL64 = in1L32 * in2L16; +* s32_sat_s40_round(resultL64) to 32 bits. +* KEYWORDS: multiply, mult, mpy +* +*************************************************************************/ +int32 s32_mult_s32_s16_rnd_sat(int32 var1, int16 var2) +{ + int32 resultL32; + int16 in1MW; + uint16 in1LW; + int32 temp1ProdL32, temp2ProdL32; + + in1LW = (uint16) s16_extract_s32_l(var1); // lower word of first input + in1MW = s16_extract_s32_h(var1); // higher word of second input + + temp1ProdL32 = s32_mult_s16_u16(var2, in1LW); + temp1ProdL32 = s32_add_s32_s32_sat(temp1ProdL32, (int32 )0x4000); // for rounding purposes + temp1ProdL32 = s32_shl_s32_sat(temp1ProdL32, -15); // right shift + + temp2ProdL32 = s32_mult_s16_s16_shift_sat(var2, in1MW); + resultL32 = s32_add_s32_s32_sat(temp1ProdL32, temp2ProdL32); + +#ifdef WMOPS_FX + counter_fx.mult_32_16++; + counter_fx.saturate--; + counter_fx.extract -= 2; + counter_fx.mult16 -=3; + counter_fx.add32--; + counter_fx.sh--; +#endif + + return (resultL32); +} +#endif //#if !defined(s32_mult_s32_s16_rnd_sat) + +/*************************************************************************** +* FUNCTION : changed +* +* DESCRIPTION: determine if two values are different +* +* INPUTS: +* value1: integer value 1 +* value2: integer value 2 +* +* OUTPUTS: return TRUE if different, and FALSE if same +* +* IMPLEMENTATION NOTES: +****************************************************************************/ +boolean changed(int value1, int value2) +{ + return (value1 == value2) ? FALSE : TRUE; +} /*------------------ end of function changed ------------------------*/ + + +/***************************************************************************** +* FUNCTION : change_if_valid +* +* DESCRIPTION: If the new value is not invalid, assign it to the register +* +* INPUTS: originalValue: value to be changed +* newValue: new integer value +* OUTPUTS: newValue if it is valid, and origional if invalid +* +* IMPLEMENTATION NOTES: +****************************************************************************/ +int change_if_valid +( + int originalValue, /* value to be changed */ + int newValue /* new value, could be invalid */ +) +{ + return (newValue == (int)0xFFFFFFFF) ? originalValue : newValue; +} + +#if !defined(s16_extract_s64_h_sat) +/*************************************************************************** +* +* FUNCTION NAME: s16_extract_s64_h_sat +* +* PURPOSE: +* +* Extract bits 16 to 31 of a 64 bit int64, with 32-bit saturation. +* Return the 16 bit number as a int16. +* +* INPUTS: +* +* var1 +* 64 bit long signed integer (int64) whose value +* falls in the range +* 0x 8000 0000 0000 0000<= var1 <= 0x 7fff ffff ffff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* swOut +* 16 bit short signed integer (int16) whose value +* falls in the range +* 0xffff 8000 <= swOut <= 0x0000 7fff. +* +* IMPLEMENTATION: +* +* KEYWORDS: assign, extract, saturate +* +*************************************************************************/ + +int16 s16_extract_s64_h_sat(int64 var1) +{ + return s16_extract_s32_h(s32_saturate_s64(var1)); +} +#endif //#if !defined(s16_extract_s64_h_sat) + +#if !defined(s64_mac_s64_s16_s16_s1) +/*************************************************************************** +* +* FUNCTION NAME: s64_mac_s64_s16_s16_s1 +* +* PURPOSE: +* +* Multiply accumulate. Fractionally multiply two 16 bit +* numbers together. Add that result to the +* 64 bit input. Return the 64 bit result. +* +* INPUTS: +* +* var1 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var1 <= 0x0000 7fff. +* var2 +* 16 bit short signed integer (int16) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* var3 +* 64 bit integer (int64) whose value +* falls in the range +* 0x8000 0000 0000 0000 <= var2 <= 0x7fff ffff ffff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 64 bit signed integer (int64) whose value +* falls in the range +* 0x8000 0000 0000 0000 <= L_Out <= 0x7fff ffff ffff ffff. +* +* IMPLEMENTATION: +* +* Fractionally multiply two 16 bit numbers together without +* saturation into a 64-bit result. +* +* Add that result to the 64 bit input without saturation. +* Return the 64 bit result. +* +* KEYWORDS: mac, multiply accumulate +* +*************************************************************************/ +int64 s64_mac_s64_s16_s16_s1(int64 var3, int16 var1, int16 var2) +{ + return s64_mac_s64_s16_s16_shift(var3, var1, var2, 1); +} +#endif // #if !defined(s64_mac_s64_s16_s16_s1) + +#if !defined(s64_mult_s32_u16) +/*************************************************************************** +* +* FUNCTION NAME: s64_mult_s32_u16 +* +* PURPOSE: +* +* Perform multiplication of a 32-bit signed number with 16-bit unsigned number. +* +* INPUTS: +* +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range 0x8000 0000 <= var1 <= 0x7fff ffff. +* var2 +* 16 bit short unsigned integer (uint16) whose value +* falls in the range 0x0000 8000 <= var2 <= 0x0000 7fff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 64 bit long long signed integer (int64). +* +* IMPLEMENTATION: +* multiplies a 32 -bit number with 16-bit number and save into 64 bit +* KEYWORDS: multiply, mult, mpy +* +*************************************************************************/ +int64 s64_mult_s32_u16( int32 var1, uint16 var2) +{ + int64 resultL64; + uint16 inL32LW; + int16 inL32MW; + uint32 tempProdUL32; + + inL32LW = (uint16)s16_extract_s32_l(var1); + inL32MW = s16_extract_s32_h(var1); + + tempProdUL32 = u32_mult_u16_u16(var2, inL32LW); + resultL64 = s64_shl_s64(s32_mult_s16_u16(inL32MW,var2),16); + resultL64 = s64_add_s64_s64(resultL64, tempProdUL32); + return resultL64; +} /* End of l_mult_s32xs16 function*/ +#endif //#if !defined(s64_mult_s32_u16) + +#if !defined(s64_mult_s32_s32) +/*************************************************************************** +* +* FUNCTION NAME: s64_mult_s32_s32 +* +* PURPOSE: +* +* Perform a fractional multipy of the two 32 bit input numbers. +* Output a 32 bit number. +* +* INPUTS: +* +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range 0x8000 0000 <= var1 <= 0x7fff ffff. +* var2 +* 32 bit signed integer (int32) whose value +* falls in the range 0x8000 0000 <= var2 <= 0x7fff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 64 bit long signed integer (int64) +* +* IMPLEMENTATION: +* Multiplies two 32 bit numbers and produces a 64-bit number as output. +* It does the following operations: +* resultL64 = in1L32 * in2L32 ; +* KEYWORDS: multiply, mult, mpy +* +*************************************************************************/ +int64 s64_mult_s32_s32(int32 var1, int32 var2) +{ + return (int64) var1 * var2; +} +#endif //#if !defined(s64_mult_s32_s32) + + +#if !defined(s64_mult_s32_u32) +/*************************************************************************** +* +* FUNCTION NAME: s64_mult_s32_u32 +* +* PURPOSE: +* +* Perform multipy of the two 32 bit input numbers. +* Output a 64 bit number. +* +* INPUTS: +* +* var1 +* 32 bit signed integer (int32) whose value +* falls in the range 0x8000 0000 <= var1 <= 0x7fff ffff. +* var2 +* 32 bit unsigned integer (uint32) whose value +* falls in the range 0x0000 0000 <= var2 <= 0xffff ffff. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* L_Out +* 64 bit long signed integer (int64) +* +* IMPLEMENTATION: +* Multiplies two 32 bit numbers and produces a 64-bit number as output. +* It does the following operations: +* resultL64 = in1L32 * uin2L32 ; +* KEYWORDS: multiply, mult, mpy +* +*************************************************************************/ +int64 s64_mult_s32_u32(int32 var1, uint32 var2) +{ + return (var1 * (int64)var2); +} +#endif // !defined(s64_mult_s32_u32) + + + + diff --git a/modules/cmn/common/utils/src/clips.c b/modules/cmn/common/utils/src/clips.c new file mode 100644 index 0000000..ad7e78b --- /dev/null +++ b/modules/cmn/common/utils/src/clips.c @@ -0,0 +1,75 @@ +#include "audio_clips.h" +#include "ar_defs.h" + +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +/*===========================================================================*/ +/* FUNCTION : clip_16 */ +/* */ +/* DESCRIPTION: Read interface value and limit its range. */ +/* */ +/* INPUTS: input-> pointer to the interface value */ +/* lowerBound: valid minimum value */ +/* upperBound: valid maximum value */ +/* OUTPUTS: input-> pointer to the interface value */ +/* function returns if the input value is clipped */ +/*===========================================================================*/ +bool_t clip_16 +( + int16_t *input, /* ptr to input value */ + int16_t lowerBound, /* valid minimum value */ + int16_t upperBound /* valid maxmum value */ +) +{ + if (*input < lowerBound) + { + *input = lowerBound; + return TRUE; + } + + if (*input > upperBound) + { + *input = upperBound; + return TRUE; + } + return FALSE; +} /*----------------------- end of function clip_16 -------------------------*/ + + +/*===========================================================================*/ +/* FUNCTION : clip_32 */ +/* */ +/* DESCRIPTION: Read interface value and limit its range. */ +/* */ +/* INPUTS: input-> pointer to the interface value */ +/* lowerBound: valid minimum value */ +/* upperBound: valid maximum value */ +/* OUTPUTS: input-> pointer to the interface value */ +/* function returns if the input value is clipped */ +/* */ +/* IMPLEMENTATION NOTES: */ +/*===========================================================================*/ +bool_t clip_32 +( + int32_t *input, /* ptr to input value */ + int32_t lowerBound, /* valid minimum value */ + int32_t upperBound /* valid maxmum value */ +) +{ + if (*input < lowerBound) + { + *input = lowerBound; + return TRUE; + } + + if (*input > upperBound) + { + *input = upperBound; + return TRUE; + } + return FALSE; +} /*----------------------- end of function clip_16 -------------------------*/ + diff --git a/modules/cmn/common/utils/src/crossfade.c b/modules/cmn/common/utils/src/crossfade.c new file mode 100644 index 0000000..899f6f9 --- /dev/null +++ b/modules/cmn/common/utils/src/crossfade.c @@ -0,0 +1,677 @@ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +/*============================================================================ + FILE: crossfade.c + + OVERVIEW: Cross fading of two input signals and generate an output. + + DEPENDENCIES: None +============================================================================*/ + +/*---------------------------------------------------------------------------- + * Include Files + * -------------------------------------------------------------------------*/ +#if defined CAPI_STANDALONE +#include "capi_util.h" +#elif defined APPI_EXAMPLE_STANDALONE +#include "appi_util.h" +#else +#include +#endif +#include "crossfade.h" +#include "audio_basic_op.h" +#include "audio_divide_qx.h" + +/*---------------------------------------------------------------------------- + * Function Definitions + * -------------------------------------------------------------------------*/ + + + +/*====================================================================== + + FUNCTION audio_cross_fade_get_mem_req + + DESCRIPTION Determine lib mem size. Called once at audio connection set up time. + + DEPENDENCIES Input pointers must not be NULL. + + PARAMETERS cross_fade_lib_mem_req_ptr: [out] Pointer to lib mem requirement structure + cross_fade_static_ptr: [in] Pointer to static structure. + + SIDE EFFECTS None + +======================================================================*/ +CROSS_FADE_RESULT audio_cross_fade_get_mem_req(cross_fade_lib_mem_req_t *cross_fade_lib_mem_req_ptr, cross_fade_static_t* cross_fade_static_ptr) +{ + + + uint32_t cross_fade_lib_mem_size, cross_fade_static_size, cross_fade_mode_size, cross_fade_config_size, cross_fade_data_size, total_mem_size=0; + + + + // clear memory + memset(cross_fade_lib_mem_req_ptr,0,sizeof(cross_fade_lib_mem_req_t)); + + //------ + //cross_fade_lib_mem_t + //------ + //cross_fade_static_t + //------ + //cross_fade_mode_t + //------ + //cross_fade_config_t + //------ + //cross_fade_data_t + //------ + + cross_fade_lib_mem_size = ALIGN8(sizeof(cross_fade_lib_mem_t)); + total_mem_size += cross_fade_lib_mem_size; + + cross_fade_static_size = ALIGN8(sizeof(cross_fade_static_t)); + total_mem_size += cross_fade_static_size; + + cross_fade_mode_size = ALIGN8(sizeof(cross_fade_mode_t)); + total_mem_size += cross_fade_mode_size; + + cross_fade_config_size = ALIGN8(sizeof(cross_fade_config_t)); + total_mem_size += cross_fade_config_size; + + cross_fade_data_size = ALIGN8(sizeof(cross_fade_data_t)); + total_mem_size += cross_fade_data_size; + + + + // store total lib mem size + cross_fade_lib_mem_req_ptr->cross_fade_lib_mem_size = total_mem_size; + + + // maximal lib stack mem consumption + cross_fade_lib_mem_req_ptr->cross_fade_lib_stack_size = cross_fade_max_stack_size; + + + + return CROSS_FADE_SUCCESS; +} + + +/*====================================================================== + + FUNCTION audio_cross_fade_init_memory + + DESCRIPTION Performs partition and initialization of lib memory. + Called once at audio connection set up time. + + DEPENDENCIES Input pointers must not be NULL. + + PARAMETERS cross_fade_lib_ptr: [in, out] Pointer to lib structure + cross_fade_static_ptr: [in] Pointer to static structure + mem_ptr: [in] Pointer to the lib memory + mem_size: [in] Size of the memory pointed by mem_ptr + + SIDE EFFECTS None + +======================================================================*/ +CROSS_FADE_RESULT audio_cross_fade_init_memory(cross_fade_lib_t *cross_fade_lib_ptr, cross_fade_static_t* cross_fade_static_ptr, int8_t* mem_ptr, uint32_t mem_size) +{ + cross_fade_lib_mem_t* cross_fade_lib_mem_ptr = NULL; + int8_t *temp_ptr=mem_ptr; + + uint32_t cross_fade_lib_mem_size, cross_fade_static_size, cross_fade_mode_size, cross_fade_config_size, cross_fade_data_size, total_mem_size=0; + + + // re-calculate lib mem size + cross_fade_lib_mem_size = ALIGN8(sizeof(cross_fade_lib_mem_t)); + total_mem_size += cross_fade_lib_mem_size; + + cross_fade_static_size = ALIGN8(sizeof(cross_fade_static_t)); + total_mem_size += cross_fade_static_size; + + cross_fade_mode_size = ALIGN8(sizeof(cross_fade_mode_t)); + total_mem_size += cross_fade_mode_size; + + cross_fade_config_size = ALIGN8(sizeof(cross_fade_config_t)); + total_mem_size += cross_fade_config_size; + + cross_fade_data_size = ALIGN8(sizeof(cross_fade_data_t)); + total_mem_size += cross_fade_data_size; + + + // error out if the mem space given is not enough + if (mem_size < total_mem_size) + { + + return CROSS_FADE_MEMERROR; + } + + + // before initializing lib_mem_ptr, it is FW job to make sure that pMem is 8 bytes aligned(with enough space) + memset(mem_ptr,0,mem_size); // clear the mem + cross_fade_lib_ptr->cross_fade_lib_mem_ptr = (void*)mem_ptr; + + + //------ + //cross_fade_lib_mem_t + //------ + //cross_fade_static_t + //------ + //cross_fade_mode_t + //------ + //cross_fade_config_t + //------ + //cross_fade_data_t + //------ + + + // lib memory partition starts here + cross_fade_lib_mem_ptr = (cross_fade_lib_mem_t*)cross_fade_lib_ptr->cross_fade_lib_mem_ptr; + temp_ptr += cross_fade_lib_mem_size; + + cross_fade_lib_mem_ptr->cross_fade_static_ptr = (cross_fade_static_t*)temp_ptr; + cross_fade_lib_mem_ptr->cross_fade_static_size = cross_fade_static_size; + cross_fade_lib_mem_ptr->cross_fade_static_ptr->sample_rate = cross_fade_static_ptr->sample_rate; + cross_fade_lib_mem_ptr->cross_fade_static_ptr->data_width = cross_fade_static_ptr->data_width; + temp_ptr += cross_fade_lib_mem_ptr->cross_fade_static_size; + + cross_fade_lib_mem_ptr->cross_fade_mode_ptr = (cross_fade_mode_t*)temp_ptr; + cross_fade_lib_mem_ptr->cross_fade_mode_size = cross_fade_mode_size; + *cross_fade_lib_mem_ptr->cross_fade_mode_ptr = 0; + temp_ptr += cross_fade_lib_mem_ptr->cross_fade_mode_size; + + cross_fade_lib_mem_ptr->cross_fade_config_ptr = (cross_fade_config_t*)temp_ptr; + cross_fade_lib_mem_ptr->cross_fade_config_size = cross_fade_config_size; + cross_fade_lib_mem_ptr->cross_fade_config_ptr->converge_num_samples = AUDIO_CROSS_FADE_CONVERGE_PERIOD_SAMPLES; + cross_fade_lib_mem_ptr->cross_fade_config_ptr->total_period_msec = AUDIO_CROSS_FADE_TOTAL_PERIOD_MSEC; + temp_ptr += cross_fade_lib_mem_ptr->cross_fade_config_size; + + cross_fade_lib_mem_ptr->cross_fade_data_ptr = (cross_fade_data_t*)temp_ptr; + cross_fade_lib_mem_ptr->cross_fade_data_size = cross_fade_data_size; + audio_cross_fade_init(cross_fade_lib_mem_ptr->cross_fade_config_ptr, cross_fade_lib_mem_ptr->cross_fade_data_ptr, (int32_t)cross_fade_static_ptr->sample_rate); + temp_ptr += cross_fade_lib_mem_ptr->cross_fade_data_size; + + + // check to see if memory partition is correct + if (temp_ptr != mem_ptr + total_mem_size) + { + + return CROSS_FADE_MEMERROR; + } + + + return CROSS_FADE_SUCCESS; +} + + +/*====================================================================== + + FUNCTION audio_cross_fade_get_param + + DESCRIPTION Get the params from lib mem + + DEPENDENCIES Input pointers must not be NULL. + + PARAMETERS cross_fade_lib_ptr: [in] Pointer to lib structure + param_id: [in] ID of the param + mem_ptr: [out] Pointer to the memory where params are to be stored + mem_size:[in] Size of the memory pointed by mem_ptr + param_size_ptr: [out] Pointer to param size which indicates the size of the retrieved param(s) + + SIDE EFFECTS None + +======================================================================*/ +CROSS_FADE_RESULT audio_cross_fade_get_param(cross_fade_lib_t *cross_fade_lib_ptr, uint32_t param_id, int8_t* mem_ptr, uint32_t mem_size, uint32_t *param_size_ptr) +{ + + memset(mem_ptr,0,mem_size); + + switch (param_id) + { + case CROSS_FADE_PARAM_LIB_VER: + { + // check if the memory buffer has enough space to write the parameter data + if(mem_size >= sizeof(cross_fade_lib_ver_t)) + { + cross_fade_lib_ver_t* cross_fade_lib_ver_ptr = (cross_fade_lib_ver_t *)mem_ptr; + *cross_fade_lib_ver_ptr = CROSS_FADE_LIB_VER; + *param_size_ptr = sizeof(cross_fade_lib_ver_t); + + } + else + { + + return CROSS_FADE_MEMERROR; + } + break; + } + case CROSS_FADE_PARAM_MODE: + { + // check if the memory buffer has enough space to write the parameter data + if(mem_size >= sizeof(cross_fade_mode_t)) + { + memscpy(mem_ptr, sizeof(cross_fade_mode_t), ((cross_fade_lib_mem_t*)(cross_fade_lib_ptr->cross_fade_lib_mem_ptr))->cross_fade_mode_ptr, sizeof(cross_fade_mode_t)); + *param_size_ptr = sizeof(cross_fade_mode_t); + + } + else + { + + return CROSS_FADE_MEMERROR; + } + break; + } + case CROSS_FADE_PARAM_CONFIG: + { + // check if the memory buffer has enough space to write the parameter data + if(mem_size >= sizeof(cross_fade_config_t)) + { + memscpy(mem_ptr, sizeof(cross_fade_config_t), ((cross_fade_lib_mem_t*)(cross_fade_lib_ptr->cross_fade_lib_mem_ptr))->cross_fade_config_ptr, sizeof(cross_fade_config_t)); + *param_size_ptr = sizeof(cross_fade_config_t); + + } + else + { + + return CROSS_FADE_MEMERROR; + } + break; + } + default: + { + + return CROSS_FADE_FAILURE; + } + } + + + + return CROSS_FADE_SUCCESS; +} + + +/*====================================================================== + + FUNCTION audio_cross_fade_set_param + + DESCRIPTION Set the params in the lib memory + + DEPENDENCIES Input pointers must not be NULL. + + PARAMETERS cross_fade_lib_ptr: [in, out] Pointer to lib structure + param_id: [in] ID of the param + mem_ptr: [in] Pointer to the memory where the values stored are used to set up the params in the lib memory + mem_size:[in] Size of the memory pointed by mem_ptr + + SIDE EFFECTS None + +======================================================================*/ +CROSS_FADE_RESULT audio_cross_fade_set_param(cross_fade_lib_t *cross_fade_lib_ptr, uint32_t param_id, int8_t* mem_ptr, uint32_t mem_size) +{ + + + switch (param_id) + { + case CROSS_FADE_PARAM_MODE: + { + // check if the memory buffer has enough space to write the parameter data + if(mem_size == sizeof(cross_fade_mode_t)) + { + // set the calibration params in the lib memory + memscpy(((cross_fade_lib_mem_t*)(cross_fade_lib_ptr->cross_fade_lib_mem_ptr))->cross_fade_mode_ptr, mem_size, mem_ptr, mem_size); + + } + else // + { + + return CROSS_FADE_MEMERROR; + } + + break; + } + case CROSS_FADE_PARAM_CONFIG: + { + // check if the memory buffer has enough space to write the parameter data + if(mem_size == sizeof(cross_fade_config_t)) + { + + + // set the calibration params in the lib memory + memscpy(((cross_fade_lib_mem_t*)(cross_fade_lib_ptr->cross_fade_lib_mem_ptr))->cross_fade_config_ptr, mem_size, mem_ptr, mem_size); + + // configures some state variables + audio_cross_fade_cfg(((cross_fade_lib_mem_t*)(cross_fade_lib_ptr->cross_fade_lib_mem_ptr))->cross_fade_config_ptr, + ((cross_fade_lib_mem_t*)(cross_fade_lib_ptr->cross_fade_lib_mem_ptr))->cross_fade_data_ptr, + (int32_t)((cross_fade_lib_mem_t*)(cross_fade_lib_ptr->cross_fade_lib_mem_ptr))->cross_fade_static_ptr->sample_rate); + + + } + else // + { + + return CROSS_FADE_MEMERROR; + } + + break; + } + default: + { + + return CROSS_FADE_FAILURE; + } + } + + + + return CROSS_FADE_SUCCESS; +} + + +/*====================================================================== + + FUNCTION audio_cross_fade_process + + DESCRIPTION cross fade the two inputs and outputs a single output. + rampdown of old filter output and rampup of new filter output. + + DEPENDENCIES Input pointers must not be NULL. + audio_cross_fade_init_memory must be called prior to any call to audio_cross_fade_process. + Length of output buffer must be at least as large as the + length of the input buffer. + + PARAMETERS cross_fade_lib_ptr: [in] Pointer to lib structure + out_ptr: [out] Pointer to single-channel output PCM samples + in_ptr[TOTAL_INPUT]: [in] Pointer to two single-channel input PCM samples + block_size: [in] Number of samples to be processed + + SIDE EFFECTS None. + +======================================================================*/ +CROSS_FADE_RESULT audio_cross_fade_process(cross_fade_lib_t *cross_fade_lib_ptr, int8_t *out_ptr, int8_t *in_ptr[TOTAL_INPUT], uint32_t block_size) +{ + + + int16_t *pIn1L16 = 0, *pIn2L16 = 0, *pOutL16 = 0; + int32_t *pIn1L32 = 0, *pIn2L32 = 0, *pOutL32 = 0; + + cross_fade_lib_mem_t* cross_fade_lib_mem_ptr = (cross_fade_lib_mem_t*)(cross_fade_lib_ptr->cross_fade_lib_mem_ptr); + cross_fade_static_t* pStatic = cross_fade_lib_mem_ptr->cross_fade_static_ptr; + cross_fade_data_t* pData = cross_fade_lib_mem_ptr->cross_fade_data_ptr; + cross_fade_config_t* pCfg = cross_fade_lib_mem_ptr->cross_fade_config_ptr; + + int32_t samples_left_in_crossfade_period; + + + // ensure correctness of cross fade processing + if(pData->total_period_samples < block_size) + { + return CROSS_FADE_FAILURE; + } + + + if(pStatic->data_width == CF_BITS32) + { + pIn1L32 = (int32_t *)in_ptr[CUR_INPUT]; + pIn2L32 = (int32_t *)in_ptr[NEW_INPUT]; + pOutL32 = (int32_t *)out_ptr; + + pIn1L16 = NULL; + pIn2L16 = NULL; + pOutL16 = NULL; + } + else if(pStatic->data_width == CF_BITS16) + { + pIn1L16 = (int16_t *)in_ptr[CUR_INPUT]; + pIn2L16 = (int16_t *)in_ptr[NEW_INPUT]; + pOutL16 = (int16_t *)out_ptr; + + pIn1L32 = NULL; + pIn2L32 = NULL; + pOutL32 = NULL; + } + else + { + return CROSS_FADE_FAILURE; + } + + // If still in convergence mode, no need to do cross-fading, but simply output the old outputs + if(pData->sample_count < pCfg->converge_num_samples) + { + /* convergence period*/ + pData->state = CONVERGENCE_STATE; + + // if convergence period is greater than frame size, copy the whole old output frame into current output frame + if(pData->converge_num_of_samples >= block_size) + { + memscpy(out_ptr, (pStatic->data_width<<1)*block_size, in_ptr[CUR_INPUT], (pStatic->data_width<<1)*block_size); + + pData->converge_num_of_samples -= block_size; + } + else // if convergence period is smaller than frame size 1) copy only converge_num_of_samples samples from old output. 2) For the remaining samples, do cross-fading + { + // copy only converge_num_of_samples samples from old output. + memscpy(out_ptr, (pStatic->data_width<<1)*pData->converge_num_of_samples, in_ptr[CUR_INPUT], (pStatic->data_width<<1)*pData->converge_num_of_samples); + + // For the remaining samples, do cross-fading + if(pStatic->data_width == CF_BITS32) + { + audio_cross_fade_32(pData,&pIn1L32[pData->converge_num_of_samples],&pIn2L32[pData->converge_num_of_samples], + &pOutL32[pData->converge_num_of_samples],(block_size-pData->converge_num_of_samples)); + } + else + { + audio_cross_fade_16(pData,&pIn1L16[pData->converge_num_of_samples],&pIn2L16[pData->converge_num_of_samples], + &pOutL16[pData->converge_num_of_samples],(block_size-pData->converge_num_of_samples)); + } + + pData->converge_num_of_samples = 0; + } + pData->sample_count += block_size; + } + else // If already in cross-fading mode, continue doing cross-fading for the number of samples needed + { + // if num of samples processed is smaller than total period samples and one more frame of processing will be bigger than total period samples + if(((uint32_t)s32_add_s32_s32_sat(pData->sample_count,block_size) > pData->total_period_samples)&&(pData->sample_count < pData->total_period_samples)) + { + // find out how many more samples need to be processed to reach total period samples + samples_left_in_crossfade_period = s32_sub_s32_s32_sat(pData->total_period_samples, pData->sample_count); + + // do cross fading for the needed num of samples to complete the cross fade processing + if(pStatic->data_width == CF_BITS32) + { + audio_cross_fade_32(pData, pIn1L32, pIn2L32, pOutL32, samples_left_in_crossfade_period); + memscpy(pOutL32+samples_left_in_crossfade_period, (block_size-samples_left_in_crossfade_period)<<2, pIn2L32+samples_left_in_crossfade_period, (block_size-samples_left_in_crossfade_period)<<2); + } + else + { + audio_cross_fade_16(pData, pIn1L16, pIn2L16, pOutL16, samples_left_in_crossfade_period); + memscpy(pOutL16+samples_left_in_crossfade_period, (block_size-samples_left_in_crossfade_period)<<1, pIn2L16+samples_left_in_crossfade_period, (block_size-samples_left_in_crossfade_period)<<1); + } + pData->sample_count += samples_left_in_crossfade_period; + + } + else if(pData->sample_count < pData->total_period_samples) // if num of samples processed is smaller than total period samples + { + // do processing for one frame worth of samples; after this processing the number of totally processed samples should be still smaller than total period samples + if(pStatic->data_width == CF_BITS32) + { + audio_cross_fade_32(pData, pIn1L32, pIn2L32, pOutL32, block_size); + } + else + { + audio_cross_fade_16(pData, pIn1L16, pIn2L16, pOutL16, block_size); + } + pData->sample_count += block_size; + } + + } + + //cross fading finished and move the state to STEADY_STATE + if(pData->sample_count == pData->total_period_samples) + { + + pData->state = STEADY_STATE; + pData->sample_count = 0; + pData->converge_num_of_samples = pCfg->converge_num_samples; + *cross_fade_lib_mem_ptr->cross_fade_mode_ptr = 0; + } + + + + return CROSS_FADE_SUCCESS; +} + + + +/*====================================================================== + + FUNCTION audio_cross_fade_init + + DESCRIPTION init for the cross fading algorithm. + + + PARAMETERS pCfg: [in] Pointer to configuration structure + pData: [out] Pointer to data structure + samplingRate: [in] sampling rate of the input signal + + SIDE EFFECTS None + +======================================================================*/ +void audio_cross_fade_init ( cross_fade_config_t *pCfg, + cross_fade_data_t *pData, + int32_t samplingRate ) +{ + // config + audio_cross_fade_cfg(pCfg,pData,samplingRate); + + // reset + audio_cross_fade_reset(pData); + +} + + +void audio_cross_fade_reset (cross_fade_data_t *pData) +{ + + // Initialize the members of the data structure + pData->sample_count = 0; + + //rampup and rampdown scale factors + pData->ramp_up_scale_factor = 0; + pData->ramp_down_scale_factor = CF_ONE_Q31; + + //state of cross fade + pData->state = STEADY_STATE; + +} + + +void audio_cross_fade_cfg ( cross_fade_config_t *pCfg, + cross_fade_data_t *pData, + int32_t samplingRate ) +{ + int32_t tempL32; + + // convert the total period in to number of samples + /* (periodin_msec*fs/1000)*/ + tempL32 = s32_saturate_s64(s64_mult_s32_s16(samplingRate, (int16_t)pCfg->total_period_msec)); + pData->total_period_samples = s32_mult_s32_s32_rnd_sat(tempL32, CF_ONE_BY_1000_IN_0DOT32); + + //cross fade step size 1/(totaltime-convergeperiod) in Q31 format + pData->cross_fade_step_size = s32_div_s32_s32_sat(CF_ONE_Q31, + s32_sub_s32_s32_sat(pData->total_period_samples, (int32_t)pCfg->converge_num_samples),31); // Q31 format + + //converge number of samples + pData->converge_num_of_samples = pCfg->converge_num_samples; +} + + + + +void audio_cross_fade_16(cross_fade_data_t *pData, + int16_t *pInPtr1L16, + int16_t *pInPtr2L16, + int16_t *pOutPtrL16, + int32_t block_size) +{ + int32_t tempOutL32 = 0, temp1L32, temp2L32, i=0; + int16_t tempL16; + + if(pData->state == CONVERGENCE_STATE) + { + pData->ramp_down_scale_factor = CF_ONE_Q31; + pData->ramp_up_scale_factor = 0; + pData->state = CROSSFADE_STATE; + } + + /* Do cross-fading by multiplying two lines with slope 1 and -1 + * respectively, and then add together*/ + for (i = 0; i < block_size; i++) + { + temp1L32 = (int32_t)s64_mult_s32_s16_shift(pData->ramp_down_scale_factor, pInPtr1L16[i], 0); + temp2L32 = (int32_t)s64_mult_s32_s16_shift(pData->ramp_up_scale_factor, pInPtr2L16[i], 0); + tempOutL32 = s32_add_s32_s32_sat(temp1L32,temp2L32); + + //Extract the first 16-bit out and saved as int16_t + pOutPtrL16[i] = s16_saturate_s32(s32_shr_s32_sat(tempOutL32,15)); + + tempL16 = s16_min_s16_s16(pInPtr1L16[i], pInPtr2L16[i]); + if(pOutPtrL16[i] < tempL16) + { + pOutPtrL16[i] = tempL16; + } + + /* Compute the coefficients each buffer element nees to be multiplied*/ + pData->ramp_up_scale_factor = s32_add_s32_s32_sat(pData->ramp_up_scale_factor, pData->cross_fade_step_size); + pData->ramp_down_scale_factor = s32_sub_s32_s32_sat(CF_ONE_Q31, pData->ramp_up_scale_factor); + if(pData->ramp_down_scale_factor < 0 ) + pData->ramp_down_scale_factor = 0; + + } +} + + + +void audio_cross_fade_32(cross_fade_data_t *pData, + int32_t *pInPtr1L32, + int32_t *pInPtr2L32, + int32_t *pOutPtrL32, + int32_t block_size) +{ + int64_t tempOutL64 = 0, temp1L64, temp2L64; + int32_t tempL32, i=0; + + if(pData->state == CONVERGENCE_STATE) + { + pData->ramp_down_scale_factor = CF_ONE_Q31; + pData->ramp_up_scale_factor = 0; + pData->state = CROSSFADE_STATE; + } + + /* Do cross-fading by multiplying two lines with slope 1 and -1 + * respectively, and then add together*/ + for (i = 0; i < block_size; i++) + { + temp1L64 = s64_mult_s32_s32(pInPtr1L32[i], pData->ramp_down_scale_factor); + temp2L64 = s64_mult_s32_s32(pInPtr2L32[i], pData->ramp_up_scale_factor); + tempOutL64 = s64_add_s64_s64(temp1L64,temp2L64); + + //Extract the first 32-bit out and saved as int32_t + pOutPtrL32[i] = (int32_t)(s64_shl_s64(tempOutL64,-31)); + + tempL32 = s32_min_s32_s32(pInPtr1L32[i], pInPtr2L32[i]); + if(pOutPtrL32[i] < tempL32) + { + pOutPtrL32[i] = tempL32; + } + + /* Compute the coefficients each buffer element nees to be multiplied*/ + pData->ramp_up_scale_factor = s32_add_s32_s32_sat(pData->ramp_up_scale_factor, pData->cross_fade_step_size); + pData->ramp_down_scale_factor = s32_sub_s32_s32_sat(MAX_32, pData->ramp_up_scale_factor); + if(pData->ramp_down_scale_factor < 0 ) + pData->ramp_down_scale_factor = 0; + + } +} + diff --git a/modules/cmn/common/utils/src/divide_qx.c b/modules/cmn/common/utils/src/divide_qx.c new file mode 100644 index 0000000..6f7ecc5 --- /dev/null +++ b/modules/cmn/common/utils/src/divide_qx.c @@ -0,0 +1,546 @@ +/*----------------------------------------------------------------------- + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause +-----------------------------------------------------------------------*/ + +/** +@file divide_qx.c + +@brief This contains divide functions + +*/ + +/*---------------------------------------------------------------------------- + * Include Files + * -------------------------------------------------------------------------*/ +#include "audio_divide_qx.h" +#include "audio_basic_op.h" +#include "audio_basic_op_ext.h" + + +/*************************************************************************** +* +* FUNCTION NAME: s32_div_s32_s32_sat +* +* PURPOSE: +* +* Divide var1 by var2 and provide output in the requested Q format. +* The two inputs must be in the same Qformat. +* The two inputs are normalized so as to represent them with full +* 32-bit precision in the range [0,1). The normalized inputs are +* truncated to upper 16-bits and 16-bit division is performed. +* The quotient is re-normalized back to the requested Q format. +* +* INPUTS: +* +* var1 +* 32 bit signed integer (int16_t) whose value +* falls in the range 0x8000 0000 <= var1 <= 0x7fff ffff. +* var2 +* 32 bit signed integer (int16_t) whose value +* falls in the range 0x8000 0000 <= var1 <= 0x7fff ffff. +* var2 != 0. +* iqL16: +* Integer part of Qfactor for the output. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* outL32 +* 32 bit signed integer (int32_t) with +* Q format specified in the argument. +* +* +* KEYWORDS: div +* +*************************************************************************/ + +int32_t s32_div_s32_s32_sat(int32_t var1, int32_t var2, int16_t iqL16) +{ + int32_t tmp32, outL32=0; + int16_t qexp, tmp16; + +#ifdef WMOPS_FX + counter_fx.s32_div_s32_s32_normalized++; +#endif + + if(var1 ==0) + { + return outL32; + } + + if(var2 !=0) + { + tmp32 = s32_div_s32_s32_normalized(var1, var2, &qexp); + outL32 = s32_shl_s32_sat(tmp32, 16); + + qexp = qexp - iqL16; + outL32 = s32_shl_s32_sat( outL32, qexp); /* denormalize the result */ + } + else + { + if(iqL16 > 0) + { + tmp16 = 0x8000 >> iqL16; + } + else + { + tmp16 = MAX_16; + } + + outL32 = s32_shl_s32_sat((int32_t)tmp16, 16); + } + + return outL32; + +} +#if !defined(__qdsp6__) + +/* ------------------------------------------------------------- */ +/* CONSTANTS */ +/* ------------------------------------------------------------- */ +#define DSP_INV_LUT_BITS 4 /* number of bits to index the inverse lookup table (actually 2^(val-1) segments) */ +#define DSP_INV_LUT_SIZE (1+(1<<(DSP_INV_LUT_BITS-1))) /* number of entries in the inverse LUT */ + +/* ------------------------------------------------------------- */ +/* LOOK UP TABLES */ +/* */ +/* this LUT has been generated using the matlab function */ +/* "find_optimal_lut_mse_cantoni.m" in */ +/* \\vivekv\Public\Jaguar\cdma1x\matlab. See the matlab file */ +/* for details of the mathematical analysis. */ +/* ------------------------------------------------------------- */ +int16 invTable[DSP_INV_LUT_SIZE]= {32690, 29066, 26171, 23798, 21820, 20145, 18709, 17463, 16373}; + +/*==========================================================================*/ +/* FUNCTION: divide_int32_qx */ +/* */ +/* DESCRIPTION: numerator/denominator (both in the same Q) and outputs */ +/* quotient in Q0. */ +/* */ +/* INPUTS: numeratorL32: numerator in Qin. */ +/* denominatorL32: denominator in Qin. */ +/* OUTPUTS: quotient: quotient of operation in Q0. */ +/* */ +/* IMPLEMENTATION NOTES: */ +/* */ +/* if(num >= 2^31*den) */ +/* num = num - den; */ +/* quotient = quotient<<1+1; */ +/* else */ +/* quotient = quotient<<1; */ +/* num = num<<1; */ +/* */ +/*==========================================================================*/ +int32_t divide_int32_qx(int32_t numeratorL32, + int32_t denominatorL32, + int16_t outputQ) +{ + int32_t quotientL32=0; + int16_t nShift, dShift; + int16_t i, count; + int16_t negFlag=0; + int40 shiftNumerL40, tempL40; + + if(numeratorL32 < 0) + { + negFlag = 1; + numeratorL32 = -numeratorL32; + } + if(denominatorL32 < 0) + { + negFlag = !negFlag; + denominatorL32 = -denominatorL32; + } + if(numeratorL32 == 0) + { + return(0); + + } + if(denominatorL32 == 0) + { + return(0xFFFFFFFF); + + } + + + /* normalize numerator and denominator */ + nShift=s16_norm_s32(numeratorL32); + shiftNumerL40 = s32_shl_s32_sat(numeratorL32, nShift); + dShift = s16_norm_s32(denominatorL32); + denominatorL32 = s32_shl_s32_sat(denominatorL32, dShift); + + count = s16_add_s16_s16(dShift,1); + count = s16_sub_s16_s16(count, nShift); + count = s16_add_s16_s16(count, outputQ); + for(i = 0; i=0) + { + shiftNumerL40 = s40_shl_s40(tempL40,1); + quotientL32 = s32_add_s32_s32_sat(quotientL32,1); + } + }/* end of repetitions*/ + if(negFlag) + quotientL32 = -quotientL32; + return quotientL32; +} /* end of divide_int32() function. */ +#endif //#if !defined(__qdsp6__) +/* adding Q16 and Q23 divide function for bit-exact matching */ +static int32_t Q16_divideUU(int32_t numer, int32_t denom) +{ + uint32_t x, y, t; + int i; + x = numer>>16; + y = numer<<16; + for (i = 1; i<=32; i++) { + t = (int32_t)x>>31; // all 1's if x[31]==1 + x = (x<<1) | (y>>31); // shift x||y left by one bit + y <<= 1; // ... + if ((x|t)>=(uint32_t)denom) { + x -= denom; + y += 1; + } + } + // x is remainder, y is quotient + return x>=(uint32_t)denom/2 ? y+1 : y; +} + +int32_t Q16_divide(int32_t numer, int32_t denom) +{ + bool_t negative; + int32_t quotient; + + if (numer==0) return 0; + + negative = FALSE; + if (numer<0) numer = -numer, negative = TRUE; + if (denom<0) denom = -denom, negative = !negative; + + quotient = numer==denom ? 65536 : Q16_divideUU((uint32_t)numer, (uint32_t)denom); + return negative ? -quotient : quotient; +} + +/* end of added divide code */ + +/*===================================================================*/ +/* FUNCTION : audio_divide_dp (). */ +/*-------------------------------------------------------------------*/ +/* PURPOSE : double precision division */ +/*-------------------------------------------------------------------*/ +/* INPUT ARGUMENTS : */ +/* _ (int40) Lnum: numerator */ +/* _ (int40) Ldenom: denominator */ +/* _ (int16_t) Qadj: Q factor adjustor */ +/*-------------------------------------------------------------------*/ +/* OUTPUT ARGUMENTS : */ +/* _ None */ +/*-------------------------------------------------------------------*/ +/* INPUT/OUTPUT ARGUMENTS : */ +/* _ None */ +/*-------------------------------------------------------------------*/ +/* RETURN ARGUMENTS : */ +/* _(int32_t) Lquotient in Q(29+Qadj) */ +/* 0 if Ldenom=0 */ +/*===================================================================*/ +int40 audio_divide_dp(int40 Lnum, int40 Ldenom, int16_t Qadj) +{ + int40 Lacc,Lacc1; + int32_t Ltemp3; + int16_t th, dh, nh, t1h, i, n1; + uint16_t tl, dl, nl, t1l; + + int16_t densign = 0; + if (Ldenom == 0) return(0); + n1 = s16_norm_s40(Lnum); // normalize numerator + Lnum <<= n1; + if (Ldenom < 0)densign =-1; + if (Ldenom < 0)Ldenom =-Ldenom; + Qadj=n1 - Qadj; + n1=s16_norm_s40(Ldenom); + Ldenom <<= n1; + Qadj = n1-Qadj; + Ltemp3 = 0x55555555; + + for (i=0;i<5;i++) { + tl=s16_extract_s32_l(Ltemp3); + th=s16_extract_s32_h(Ltemp3); + dl=s16_extract_s32_l(s32_saturate_s40(Ldenom)); + dh=s16_extract_s32_h(s32_saturate_s40(Ldenom)); + + Lacc=s40_add_s40_s40(s40_mult_s16_u16_shift(th,dl,0),s40_mult_s16_u16_shift(dh,tl,0)); + Lacc=s40_add_s40_s40(Lacc,0x8000); + + Lacc >>=16; + Lacc1=s40_add_s40_s40(s40_mult_s16_s16_shift(th,dh,0),s64_mult_s32_s32(0xffff8000,0x8000)); + Lacc=s40_add_s40_s40(Lacc,Lacc1); + Lacc=s32_saturate_s40(Lacc); + + t1l=s16_extract_s32_l(s32_saturate_s40(Lacc)); + t1h=s16_extract_s32_h(s32_saturate_s40(Lacc)); + + Lacc= -0x8000; + Lacc=s40_sub_s40_s40(Lacc, s40_mult_s16_u16_shift(th,t1l,3)); + Lacc=s40_sub_s40_s40(Lacc, s40_mult_s16_u16_shift(t1h,tl,3)); + + Lacc >>=16; + Lacc=s40_sub_s40_s40(Lacc, s40_mult_s16_s16_shift(th,t1h,3)); + Ltemp3=s32_saturate_s40(Lacc); + } + if(densign < 0) Lacc = s40_sub_s40_s40(0,Lacc); + + Ltemp3=s32_saturate_s40(Lacc); + nl=s16_extract_s32_l(s32_saturate_s40(Lnum)); + nh=s16_extract_s32_h(s32_saturate_s40(Lnum)); + tl=s16_extract_s32_l(Ltemp3); + th=s16_extract_s32_h(Ltemp3); + Lacc=s40_add_s40_s40(s40_mult_s16_u16_shift(th,nl,0),s40_mult_s16_u16_shift(nh,tl,0)); + Lacc=s40_add_s40_s40(Lacc,0x8000); + + Lacc >>=16; + Lacc1=s40_mult_s16_s16_shift(th,nh,0); + Lacc=s40_add_s40_s40(Lacc,Lacc1); + + Lacc=s40_shl_s40(Lacc, Qadj); + return(s32_saturate_s40(Lacc)); +} + +/*===================================================================*/ +/* FUNCTION : audio_divide_sp() */ +/*-------------------------------------------------------------------*/ +/* PURPOSE : single precision division with Q6 friendly */ +/* implementation (low MIPS) */ +/* Low MIPS replacement for div_int32(). */ +/*-------------------------------------------------------------------*/ +/* INPUT ARGUMENTS : */ +/* _ (int32) Lnum: numerator */ +/* _ (int32) Ldenom: denominator */ +/* _ (int16) Qadj: Q factor adjustor */ +/*-------------------------------------------------------------------*/ +/* OUTPUT ARGUMENTS : */ +/* _ None */ +/*-------------------------------------------------------------------*/ +/* INPUT/OUTPUT ARGUMENTS : */ +/* _ None */ +/*-------------------------------------------------------------------*/ +/* RETURN ARGUMENTS : */ +/* _(int32) Lquotient in Q(31-Qadj) */ +/* Max value if Ldenom=0 */ +/*===================================================================*/ +#if !defined(__qdsp6__) +int32 audio_divide_sp(int32 Lnum, int32 Ldenom, int16 Qadj) +{ + int40 Lacc=0,Lacc1; + int32 Ltemp3; + int16 i, n1, densign = 0; + + // Return max value if the denominator is zero + if (Ldenom == 0) + { + if(Lnum >= 0) + { + return(0x7fffffffL); // Positive maximum + } + else + { + return(0x80000000L); // Negative maximum + } + } + + // Adjust the input Qadj factor so as to make it integer part of output Qfactor + Qadj = 1 - Qadj; + + // Normalize the numerator to improve precision + n1 = s16_norm_s32(Lnum); // normalize numerator + Lnum <<= n1; + + // Modify the adjustment factor based on numerator exponent + Qadj=n1 - Qadj; + + // For negative denominators, take care of the sign + if (Ldenom < 0) + { + densign =-1; + Ldenom =-Ldenom; + } + + // Normalize the denominator to improve precision + n1=s16_norm_s32(Ldenom); + Ldenom <<= n1; + + // Modify the adjustment factor based on denominator exponent + Qadj = n1-Qadj; + + // Set the initial guess for division iteration + Ltemp3 = 0x55555555L; + + // Division implementation using iterative procedure + // Implement 1/D ~ x*(2-D*x) with an initial guess x + // and iterate the inverse 5 times to get more accurate result + // x: Q1.30, D: Q0.31. + for (i=0;i<5;i++) + { + // Implement D*x and store the product as Q2.29 + // No rounding done in the product computation + Lacc1 = s64_sub_s64_s64(0x4000000000000000LL, s64_mult_s32_s32(Ltemp3, Ldenom)); + Lacc = s64_shl_s64(Lacc1, -32); + + // Lacc = (int64)Q6_P_asr_PI(Lacc1, 32); + Lacc = s64_mult_s32_s32((int32)Lacc, Ltemp3); + + // Ltemp3=(int32)(Q6_P_asr_PI(Lacc,29)); + Ltemp3 = (int32) s64_shl_s64(Lacc, -29); + Lacc = (int64)Ltemp3; + } + + // Reverse the sign if the denominator is negative + if(densign < 0) + { + Lacc = s64_sub_s64_s64(0,Lacc); + Ltemp3=s32_saturate_s64(Lacc); + } + + // Multiply the inverse with the numerator to get the result in Q1.30 + Lacc = s64_mult_s32_s32(Ltemp3, Lnum); + + // Adjust the shift factor of the result to the desired Q-factor + if (-33 >= Qadj) + { + if (0 <= Lacc) + { + Lacc = 0; + } + else + { + Lacc = -1; + } + } + else + { + Lacc=s64_shl_s64(Lacc, Qadj-31); + } + + // Return the adjusted result + return(s32_saturate_s64(Lacc)); +} + +/*-------------------------------------------------------------- + * dsplib_approx_invert() + * + * DSP library function "approx_invert". + * Approximates inversion: out ~= 2^(31-floor(lg2(in)))/in + * + * Approximation done with a linearly interpolated lookup table. With + * 9 point entries (8 line segments) the maximum error is 0.238%. The + * number to be inverted must be positive for valid results. If not + * positive, then the lookup table is invalidly indexed. + * + * If input is Qi and the output is Qo, then + * Qo = 32 + (*shift_factor) - Qi. + * + * Inputs: + * input integer to be inverted + * + * Return: + * int64 (shift_factor : result) + * where + * result Q-shifted inverse + * result ~= 2^(31-floor(lg2(input))) / input + * = 2^(32+(*shift_factor)) / input + * shift_factor (output_Q_factor - 32) of integer inverse + * shift_factor = -1-floor(lg2(input)) + * + * if input <=0: return + * result = 0xFFFFFFFF + * shift_factor = 0xFFFFFFFF + *----------------------------------------------------------------*/ +int64 dsplib_approx_invert(int32 input) +{ + int32 norm_divisor; + int32 n1,interp; + int32 index; + int32 r; + int32 result, shift_factor; + + /* check for negative input (invalid) */ + if (input <= 0) { + return (-1); + } + + /* bit-align-normalize input */ + r = (int32)s16_norm_s32(input); + norm_divisor = s32_shl_s32_sat(input, (int16)r); + + /* determine inverse LUT index and interpolation factor */ + n1 = norm_divisor >> (15-DSP_INV_LUT_BITS); + interp = n1 % (1<<16); + index = (n1 >> 16) - (DSP_INV_LUT_SIZE-1); + + /* inverse linear interpolation between LUT entries */ + result = (invTable[index]<<16) + interp * (invTable[index+1] - invTable[index]); + shift_factor = r-31; + + /* return values */ + return ( ((int64)shift_factor<<32)|(uint32)result ); + +} + +/*-------------------------------------------------------------- + * dsplib_approx_divide() + * + * DSP library function "approx_divide". + * Approximates inversion: out ~= numer*2^(31-floor(lg2(in)))/denom + * + * Approximation done with a linearly interpolated lookup table. With + * 9 point entries (8 line segments) the maximum error is 0.238%. The + * number to be inverted must be positive for valid results. If not + * positive, then the lookup table is invalidly indexed. + * + * + * Inputs: + * numer integer numerator + * denom integer denominator + * + * Return: + * int64 (shift_factor : result) + * where + * result - quotient + * result ~= numer*2^(31-floor(lg2(input))) / denom + * = numer*2^(32+(*shift_factor)) / denom + * shift_factor - Qfactor of (*result) + * + * + * Notes : (*result << *shift_factor) will be floating point result in Q0 + * + *----------------------------------------------------------------*/ +int64 dsplib_approx_divide(int32 numer,int32 denom) +{ + int32 norm_num,r,s_d,s_n; + int64 norm_denom; + int32 result, shift_factor; + + if (denom <= 0) { + return (-1); + } + + norm_denom = dsplib_approx_invert(denom); + r = (int32)norm_denom; + s_d = (int32)(norm_denom>>32); + s_d=s_d+1; + + s_n = (int32)s16_norm_s32(numer); + norm_num = s32_shl_s32_sat(numer, (int16)s_n); + + result = s32_mult_s32_s16_rnd_sat(norm_num, s16_round_s32_sat(r)); + shift_factor = s_d - s_n; + + return ( ((int64)shift_factor<<32)|(uint32)result ); +} +#endif + diff --git a/modules/cmn/common/utils/src/divide_qx_island.c b/modules/cmn/common/utils/src/divide_qx_island.c new file mode 100644 index 0000000..0c7e6f3 --- /dev/null +++ b/modules/cmn/common/utils/src/divide_qx_island.c @@ -0,0 +1,341 @@ +/*======================================================================= + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause +=========================================================================*/ + +#include "audio_divide_qx.h" +#include "audio_basic_op.h" +#include "audio_basic_op_ext.h" + +/*==========================================================================*/ +/* FUNCTION: divide_int32 */ +/* */ +/* DESCRIPTION: numerator/denominator (both in the same Q) and outputs */ +/* quotient in outputQL16. */ +/* */ +/* INPUTS: numeratorL32: numerator in Qin. */ +/* denominatorL32: denominator in Qin. */ +/* OUTPUTS: quotient: quotient of operation in Q31. */ +/* */ +/* REQUIREMENTS: numerator!= denominator. And both numerator and */ +/* denominator are positive. */ +/* IMPLEMENTATION NOTES: */ +/* */ +/* Since its easier to explain using Q0 output, lets assume that output Q */ +/* factor is 0. */ +/* if(num >= 2^31*den) */ +/* num = num - den; */ +/* quotient = quotient<<1+1; */ +/* else */ +/* quotient = quotient<<1; */ +/* num = num<<1; */ +/* */ +/*==========================================================================*/ +int32_t divide_int32(int32_t numeratorL32, int32_t denominatorL32) +{ + int32_t quotientL32=0; + int16_t nShift, dShift; + int16_t i, count; + int16_t negFlag=0; + int40 shiftNumerL40, tempL40; + + if(numeratorL32 < 0) + { + negFlag = 1; + numeratorL32 = -numeratorL32; + } + if(denominatorL32 < 0) + { + negFlag = !negFlag; + denominatorL32 = -denominatorL32; +} + + if( (numeratorL32 < denominatorL32)) + { + return 0; + } /* end of sanity checks */ + /* normalize numerator and denominator */ + nShift=s16_norm_s32(numeratorL32); + shiftNumerL40 = s32_shl_s32_sat(numeratorL32, nShift); + dShift = s16_norm_s32(denominatorL32); + denominatorL32 = s32_shl_s32_sat(denominatorL32, dShift); + + count = s16_add_s16_s16(dShift,1); + count = s16_sub_s16_s16(count, nShift); + for(i = 0; i=0) + { + shiftNumerL40 = s40_shl_s40(tempL40,1); + quotientL32 = s32_add_s32_s32(quotientL32,1); + } + }/* end of repetitions*/ + if(negFlag) + quotientL32 = -quotientL32; + return quotientL32; +} /*-------------------- end of function divide_int32 -----------------------*/ + +/*************************************************************************** +* +* FUNCTION NAME: s16_div_s16_s16_sat +* +* PURPOSE: +* +* Divide var1 by var2 using the shift and subtract method. +* Produces a result which is the fractional +* integer division of var1 by var2; var1 and var2 must be positive and +* var2 must be greater or equal to var1; the result is positive +* (leading bit equal to 0) and truncated to 16 bits. +* If var1 = var2 then div(var1,var2) = 32767. +* +* INPUTS: +* +* var1 +* 16 bit short signed integer (int16_t) whose value +* falls in the range 0xffff 8000 <= var1 <= 0x0000 7fff. +* var2 +* 16 bit short signed integer (int16_t) whose value +* falls in the range 0xffff 8000 <= var2 <= 0x0000 7fff. +* var2 != 0. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* var_out +* 16 bit short signed integer (int16_t) whose value +* falls in the range +* 0xffff 8000 <= swOut <= 0x0000 7fff. +* +* IMPLEMENTATION: +* +* In the case where var1==var2 the function returns 0x7fff. The output +* is undefined for invalid inputs. This implementation returns zero. +* +* KEYWORDS: div +* +*************************************************************************/ + +int16_t s16_div_s16_s16_sat(int16_t var1, int16_t var2) +{ + int16_t var_out = 0; + int16_t iteration; + int32_t L_num, L_denom; + +#ifdef WMOPS_FX + counter_fx.s16_div_s16_s16++; +#endif + + if ((var1 > var2) || (var1 < 0) || (var2 < 0)) + { + var_out = 0x7fff; +// printf ("Division Error var1=%d var2=%d\n", var1, var2); +// exit (0); + } + if (var2 == 0) + { + var_out = 0x7fff; +// printf ("Division by 0, Fatal error \n"); +// exit (0); + } + if (var1 == 0) + { + var_out = 0; + } + else + { + if (var1 == var2) + { + var_out = MAX_16; + } + else + { + L_num = (int32_t )var1; + L_denom = (int32_t )var2; + + for (iteration = 0; iteration < 15; iteration++) + { + var_out <<= 1; + L_num <<= 1; + + if (L_num >= L_denom) + { + L_num = s32_sub_s32_s32_sat (L_num, L_denom); + var_out = s16_add_s16_s16_sat (var_out, 1); + } + } + } + } + + return (var_out); + +} + +/*************************************************************************** +* +* FUNCTION NAME: s32_div_normalized s32_s32 +* +* PURPOSE: +* +* Divide var1 by var2 and provide normalized output. +* The two inputs are normalized so as to represent them with full +* 32-bit precision in the range [0,1). The normalized inputs are +* truncated to upper 16-bits and divided and the normalized +* quotient is returned with 16-bit precision where the 16-bits +* are in LSB. The normalized quotient is in Q16.15 format. +* The required shift factor to adjust the quotient value +* is returned in the pointer qShiftL16Ptr. +* To get the output in a desired 32-bit Qformat Qm.(31-m), +* left shift the output by qShiftL16Ptr+(16-m). +* To get the output in a desired 16-bit Qformat Qm.(15-m)with +* 16-bit result in the LSB of the 32-bit output, left shift the +* output by qShiftL16Ptr-m. +* +* INPUTS: +* +* var1 +* 32 bit signed integer (int16_t) whose value +* falls in the range 0x8000 0000 <= var1 <= 0x7fff ffff. +* var2 +* 32 bit signed integer (int16_t) whose value +* falls in the range 0x8000 0000 <= var1 <= 0x7fff ffff. +* var2 != 0. +* +* OUTPUTS: +* +* none +* +* RETURN VALUE: +* +* outL32 +* 32 bit signed integer (int32_t) whose value +* falls in the range +* 0x8000 0000 <= var1 <= 0x7fff ffff. +* qShiftL16Ptr: pointer to modified shift factor for +* denormalizing the quotient +* +* IMPLEMENTATION: +* +* In the case where var1=0, output is returned as 0. +* In the case where var2=0, output is maxed out. +* +* KEYWORDS: div +* +*************************************************************************/ + +int32_t s32_div_s32_s32_normalized(int32_t var1, int32_t var2, int16_t *qShiftL16Ptr) +{ + int32_t outL32=0; + int16_t var1_exp, var2_exp, tmp1, tmp2, sign = 1; + +#ifdef WMOPS_FX + counter_fx.s32_div_s32_s32_normalized++; +#endif + + if(var1 == 0) + { + qShiftL16Ptr[0] = 0; + return outL32; + } + if(var1 < 0) + { + var1 = s32_neg_s32_sat(var1); + sign = -1; + } + if(var2 < 0) + { + var2 = s32_neg_s32_sat(var2); + sign = -sign; + } + + if(var2 != 0) + { + // Compute the exponents of the numerator and denominator + var1_exp = s16_norm_s32( var1 ); + var2_exp = s16_norm_s32( var2 ); + + // Normalize the inputs such that numerator < denominator and are in full 32-bit precision + tmp1 = s16_extract_s32_h( s32_shl_s32_sat( var1, var1_exp-1 )); + tmp2 = s16_extract_s32_h( s32_shl_s32_sat( var2, var2_exp )); + + // Perform division with 16-bit precision + outL32 = (int32_t )s16_div_s16_s16_sat( tmp1, tmp2); + + // Shift factor value for de-normalizing the quotient + qShiftL16Ptr[0] = var2_exp - var1_exp +1; + } + else + { + outL32 = 0xFFFF; + qShiftL16Ptr[0] = 15; + } + + // Negate the quotient if either numerator or denominator is negative + if(sign < 0) + { + outL32 = -outL32; + } + + return outL32; + +} + +/*==========================================================================*/ +/* FUNCTION: divide_qx */ +/* */ +/* DESCRIPTION: numerator/denominator (both in the same Q) and outputs */ +/* quotient in Q31. */ +/* */ +/* INPUTS: numeratorL32: numerator in Qin. */ +/* denominatorL32: denominator in Qin. */ +/* outputQL16: Q factor in which output is required. */ +/* OUTPUTS: quotient: quotient of operation in Q(outputQL16). */ +/* */ +/* IMPLEMENTATION NOTES: */ +/* This is a wrapper function to divide two 32-bit numbers in the same */ +/* Q factor to produce output in required Q factor. */ +/*==========================================================================*/ +int32_t divide_qx(int32_t numeratorL32, int32_t denominatorL32, int16_t outputQL16) +{ + int32_t quotientL32Qx; + int16_t negativeFlag = 0; + int32_t oneL32Qx; + + /* --------- re-direct divide function for bit-exact matching ----------*/ + /* commented out for actual implementation: we just use divide_qx */ + // if (outputQL16 ==23) return Q23_divide(numeratorL32, denominatorL32); + // else if (outputQL16 ==16) return Q16_divide(numeratorL32, denominatorL32); + /* ------------------end of bit-exact matching code --------------------*/ + + if (numeratorL32==0) return 0; + + if (numeratorL32<0) + { + numeratorL32 = -numeratorL32; + negativeFlag = 1; + } + if (denominatorL32<0) + { + denominatorL32 = -denominatorL32; + negativeFlag = !negativeFlag; + } + + if( outputQL16 == 31) + { // if outout Q factor is 31 + oneL32Qx = (int32_t)0x7fffffffL; + } + else + { // else + oneL32Qx = 1<a(1),numcoefs->b(0), only 5 coefs are stored */ +#include "audio_iir_tdf2.h" +#include "audio_basic_op.h" +#include "audio_basic_op_ext.h" +#if ((defined __hexagon__) || (defined __qdsp6__)) +#include "audio_iir_tdf2_asm.h" +#include +#include +#endif + +#define SHIFTUP 3 +#define SHIFTDN SHIFTUP +#define PRECISION 0 +#ifndef __qdsp6__ +int iirTDF2(int16_t *inp, + int16_t *out, + uint16_t samples, + int32_t *numcoefs, + int32_t *dencoefs, + int32_t *mem, + int16_t shiftn, + int16_t shiftd) +{ +#if ((defined __hexagon__) || (defined __qdsp6__)) + long long ytemp,w1temp,w2temp,yscaled; + long long w1t,w2t; + + int b0, b1, b2, a1, a2, w1, w2, input, temp; + uint16_t i; + + b0 = *numcoefs++; + b1 = *numcoefs++; + b2 = *numcoefs++; + + a1 = *dencoefs++; + a2 = *dencoefs++; + + w1 = mem[0]; + w2 = mem[1]; + + /* repeat the loop for every sample*/ + /* equations are y = b0*x+w1 */ + /* w1= b1*x-a1*y+w2 */ + /* w2= b2*x-a2*y */ + + for(i=0; i< samples; i++) + { + input = *inp++; + + // ytemp = b0*x + ytemp = Q6_P_mpy_RR(b0, input); + // scaled version of y: b0*x + w1 + yscaled = Q6_P_aslacc_PR(w1, ytemp,(shiftn-SHIFTDN-16)); + + // round the result + ytemp = (yscaled + 0x1000)>>13; + + // calculate w1 + // b1*x + w1temp = Q6_P_mpy_RR(b1, input); + + // b1*x + w2 + w1temp = Q6_P_aslacc_PR(w2, w1temp, (shiftn-SHIFTDN-16)); + + // a1*yscaled + w1t = Q6_P_mpy_RR((int)yscaled, a1); + + // b1*x +w2 - a1*y + w1temp = Q6_P_aslnac_PR(w1temp, w1t,(shiftd-32)); /* q compensation */ + + w1 = Q6_R_sat_P(w1temp); + + // calculate w2 + // b2*x + w2temp = Q6_P_mpy_RR(b2, input); + + w2temp = Q6_P_asl_PR(w2temp, (shiftn-SHIFTDN-16)); + + // a2*yscaled + w2t = Q6_P_mpy_RR((int)yscaled, a2); + + // b2*x - a2*y + w2temp = Q6_P_aslnac_PR(w2temp, w2t,(shiftd-32)); /* q compensation */ + + w2 = Q6_R_sat_P(w2temp); + + // store the output + *out++= Q6_R_sath_R((int)ytemp); + } + + mem[0] = w1; + mem[1] = w2; + + return(0); + +#else //#if ((defined __hexagon__) || (defined __qdsp6__)) + int40 ytemp,w1temp,w2temp,yscaled; + int40 w1t,w2t; + + int32_t *pncs,*pdcs; + int32_t temp; + uint32_t flag=0; + uint16_t i; + + /* repeat the loop for every sample*/ + /* equations are y = b0*x+w1 */ + /* w1= b1*x-a1*y+w2 */ + /* w2= b2*x-a2*y */ + + for(i=0;iw2 */ + + yscaled = s40_add_s40_s32(yscaled, temp); + /* scaled version of y */ + + ytemp = s40_shl_s40(yscaled, SHIFTUP); + /* unscaled version of y*/ + ytemp = s40_add_s40_s32(ytemp, 0x8000); /* round the result */ + + ytemp = s40_shl_s40(ytemp, -16); + + /*calculate w1*/ + w1temp = s40_mult_s32_s16(*pncs++, *inp); /*b1*x in 48 bits */ + w1temp = s40_shl_s40(w1temp, -16); /*b1*x in 32 bits */ +#if PRECISION + w1temp = s40_shl_s40(w1temp,shiftn);/*b1*x in q format*/ + w1temp = s40_shl_s40(w1temp,-(SHIFTDN)); /*scaled version of b1*x */ +#else + w1temp = s40_shl_s40(w1temp, (shiftn-SHIFTDN)); +#endif + /* a1*yscaled */ + w1t = s40_mult_s32_s32_shift((int32_t)yscaled, *pdcs++,0); + w1t = s40_shl_s40(w1t,shiftd); /* q compensation */ + w1t = s40_add_s40_s32(w1t, -(*mem--)); /* w1t=a1*y-w2 */ + w1temp = s40_sub_s40_s40(w1temp, w1t); /* w1temp=b1*x-a1*y+w2 */ + /*w1temp is scaled by 3*/ + w1temp = s32_saturate_s40(w1temp); + + *mem++ = (int32_t)w1temp; + + /*calculate w2*/ + w2temp = s40_mult_s32_s16(*pncs++, *inp++); /*b2*x in 48 bits */ + w2temp = s40_shl_s40(w2temp, -16); /*b2*x in 32 bits */ +#if PRECISION + w2temp = s40_shl_s40(w2temp, shiftn); /*b2*x q compensation*/ + w2temp = s40_shl_s40(w2temp, -SHIFTDN); /*scale b2*x */ +#else + w2temp = s40_shl_s40(w2temp, (shiftn-SHIFTDN)); +#endif + /* a2*yscaled */ + w2t = s40_mult_s32_s32_shift((int32_t)yscaled, *pdcs++,0); + w2t = s40_shl_s40(w2t,shiftd); + w2temp = s40_sub_s40_s40(w2temp,w2t); /* w2temp scaled version */ + + w2temp = s32_saturate_s40(w2temp); + + *mem-- = (int32_t)w2temp; + /*simulate the bus saturation*/ + /*store the output*/ + *out++=s16_saturate_s32((int32_t)ytemp); + /* Performing this here in order */ + /* to support in-place computation*/ +} + return(flag); +#endif //#if ((defined __hexagon__) || (defined __qdsp6__)) +} +#endif + + +#ifndef QDSP6_ASM_IIRTDF2_32 +void iirTDF2_32(int32 *inp, + int32 *out, + int32 samples, + int32 *numcoefs, + int32 *dencoefs, + int64 *mem, + int16 shiftn, + int16 shiftd) +{ +#if ((defined __hexagon__) || (defined __qdsp6__)) + /* + * y in Q(64 - SHIFT - max(shiftn,shiftd) - 4) + * yScaled in Q(32 - SHIFT) + * w1 in Q(64 - SHIFT - max(shiftn,shiftd) - 3) + * w2 in Q(64 - SHIFT - max(shiftn,shiftd) - 1) + */ + int64 y, w1, w2; + int32 yScaled; // yScaled will be the scaled version of y used while calculating w1 and w2 + + int64 b0TimesX, b1TimesX, b2TimesX; + int64 a1TimesY, a2TimesY; + int64 w1Temp, w2Temp; + + int32 b0, b1, b2, a1, a2; + int32 i, p5; + + //int16 shiftDiff = shiftn - shiftd; + int32 shiftDiffX; + int32 shiftDiffY; + int32 shiftY; + if (shiftn < shiftd) { + shiftDiffX = shiftn - shiftd; + shiftDiffY = 0; + shiftY = shiftd-28; + } else { + shiftDiffX = 0; + shiftDiffY = shiftd - shiftn; + shiftY = shiftn-28; + } + + if (shiftY <= -1){ + p5 = (int32)(1 << (-shiftY - 1)); + } else { + p5 = 0; + } + + b0 = *numcoefs; + b1 = *(numcoefs + 1); + b2 = *(numcoefs + 2); + a1 = *dencoefs; + a2 = *(dencoefs + 1); + + /* repeat the loop for every sample*/ + /* equations are y = b0*x+w1 */ + /* w1= b1*x-a1*y+w2 */ // use yScaled + /* w2= b2*x-a2*y */ // use yScaled + + for (i = 0; i < samples; ++i) + { + /******************************* calculate y ********************************/ + b0TimesX = Q6_P_mpy_RR( b0 , *inp); + b0TimesX = Q6_P_asl_PR( b0TimesX, shiftDiffX - 4 ); + + w1Temp = Q6_P_asr_PI( mem[0], 1 ); + y = Q6_P_add_PP( b0TimesX , w1Temp ); + + // saturate y to 32-bits Q(32-SHIFT) + // amount of right shift = 64 - SHIFT - max(shiftn,shiftd) - 4 - (32 - SHIFT) + yScaled = Q6_R_sat_P(Q6_P_asl_PR(Q6_P_add_RP(p5,y),shiftY)); + + /******************************* calculate w1 *******************************/ + b1TimesX = Q6_P_mpy_RR( b1 , *inp ); + a1TimesY = Q6_P_mpy_RR( a1 , yScaled ); + + b1TimesX = Q6_P_asl_PR(b1TimesX, shiftDiffX - 3); + a1TimesY = Q6_P_asl_PR(a1TimesY, shiftDiffY - 3); + + w1 = Q6_P_sub_PP(b1TimesX,a1TimesY); + w2Temp = Q6_P_asr_PI( mem[1], 2 ); + w1 = Q6_P_add_PP(w1,w2Temp); + mem[0] = w1; + + /******************************* calculate w2 *******************************/ + b2TimesX = Q6_P_mpy_RR( b2 , *inp++ ); + // inp incremented to next input location in the above line + a2TimesY = Q6_P_mpy_RR( a2 , yScaled ); + b2TimesX = Q6_P_asl_PR(b2TimesX, shiftDiffX - 1); + a2TimesY = Q6_P_asl_PR(a2TimesY, shiftDiffY - 1); + + w2 = Q6_P_sub_PP( b2TimesX , a2TimesY); + mem[1] = w2; + + /***************************** store the output ******************************/ + *out++ = yScaled; + /* Performing this here in order to support in-place computation */ + } + +#else /* __qdsp6__ */ + + int64 y, w1, w2; + int32 yScaled; // yScaled will be the scaled version of y used while calculating w1 and w2 + /* + * y in Q(64 - SHIFT - max(shiftn,shiftd) - 4) + * yScaled in Q(32 - SHIFT) + * w1 in Q(64 - SHIFT - max(shiftn,shiftd) - 3) + * w2 in Q(64 - SHIFT - max(shiftn,shiftd) - 1) + */ + + int64 b0TimesX, b1TimesX, b2TimesX; + int64 a1TimesY, a2TimesY; + int64 w1Temp, w2Temp; + + int32 b0, b1, b2, a1, a2; + int32 i, p5; + + int16 shiftDiff = shiftn - shiftd; + int16 shiftY = 0; + if (shiftn < shiftd) { + shiftY = (int16)(-(28-shiftd)); + } else { + shiftY = (int16)(-(28-shiftn)); + } + + if (shiftY <= -1){ + p5 = (int32)(1 << (-shiftY - 1)); + } else { + p5 = 0; + } + + b0 = *numcoefs; + b1 = *(numcoefs + 1); + b2 = *(numcoefs + 2); + a1 = *dencoefs; + a2 = *(dencoefs + 1); + + /* repeat the loop for every sample*/ + /* equations are y = b0*x+w1 */ + /* w1= b1*x-a1*y+w2 */ // use yScaled + /* w2= b2*x-a2*y */ // use yScaled + + if (shiftn < shiftd) + { + for (i = 0; i < samples; ++i) + { + /******************************* calculate y ********************************/ + b0TimesX = s64_mult_s32_s32( b0 , *inp); // Q(59-shiftn) + b0TimesX = s64_shl_s64(b0TimesX, shiftDiff - 4); // Q(55-shiftd) + + w1Temp = *mem++; // after this, mem points to w2 // Q(55-shiftd) + y = s64_add_s64_s64( b0TimesX , w1Temp ); // Q(55-shiftd) + + // saturate y to 32-bits in Q27 + yScaled = s32_saturate_s64(s64_shl_s64(s64_add_s64_s32(y, p5),shiftY)); // Q27,rounding + + /******************************* calculate w1 *******************************/ + b1TimesX = s64_mult_s32_s32( b1 , *inp ); // Q(59-shiftn) + a1TimesY = s64_mult_s32_s32( a1 , yScaled ); // Q(59-shiftd) + b1TimesX = s64_shl_s64( b1TimesX , shiftDiff - 4 ); // Q(55-shiftd) + a1TimesY = s64_shl_s64( a1TimesY, -4); // Q(55-shiftd) + + w1 = s64_sub_s64_s64(b1TimesX,a1TimesY); // Q(55-shiftd) + w2Temp = *mem--; // after this, mem points to w1 // Q(55-shiftd) + w1 = s64_add_s64_s64(w1,w2Temp); // Q(55-shiftd) + *mem++ = w1; // after this, mem points to w2 // Q(55-shiftd) + + /******************************* calculate w2 *******************************/ + b2TimesX = s64_mult_s32_s32( b2 , *inp++ ); // Q(59-shiftn) + // inp incremented to next input location in the above line + a2TimesY = s64_mult_s32_s32( a2 , yScaled ); // Q(59-shiftd) + b2TimesX = s64_shl_s64( b2TimesX , shiftDiff - 4 ); // Q(55-shiftd) + a2TimesY = s64_shl_s64( a2TimesY, -4); // Q(55-shiftd) + + w2 = s64_sub_s64_s64( b2TimesX , a2TimesY); // Q(55-shiftd) + *mem-- = w2; // after this, mem points to w1 // Q(55-shiftd) + + /***************************** store the output ******************************/ + *out++ = yScaled; // Q27 + /* Performing this here in order to support in-place computation */ + } + } + else + { + for (i = 0; i < samples; ++i) + { + /******************************* calculate y ********************************/ + b0TimesX = s64_mult_s32_s32( b0 , *inp); // Q(59-shiftn) + b0TimesX = s64_shl_s64(b0TimesX, -4); // Q(55-shiftn) + + w1Temp = *mem++; // after this, mem points to w2 // Q(55-shiftn) + y = s64_add_s64_s64( b0TimesX , w1Temp ); // Q(55-shiftn) + + // saturate y to 32-bits in Q27 + yScaled = s32_saturate_s64(s64_shl_s64(s64_add_s64_s32(y, p5),shiftY)); // Q27, rounding + + /******************************* calculate w1 *******************************/ + b1TimesX = s64_mult_s32_s32( b1 , *inp ); // Q(59-shiftn) + a1TimesY = s64_mult_s32_s32( a1 , yScaled ); // Q(59-shiftd) + a1TimesY = s64_shl_s64( a1TimesY , -shiftDiff - 4 ); // Q(55-shiftn) + b1TimesX = s64_shl_s64( b1TimesX, -4); // Q(55-shiftn) + + w1 = s64_sub_s64_s64(b1TimesX,a1TimesY); // Q(55-shiftn) + w2Temp = *mem--; // after this, mem points to w1 // Q(55-shiftn) + w1 = s64_add_s64_s64(w1,w2Temp); // Q(55-shiftn) + *mem++ = w1; // after this, mem points to w2 // Q(55-shiftn) + + /******************************* calculate w2 *******************************/ + b2TimesX = s64_mult_s32_s32( b2 , *inp++ ); // Q(59-shiftn) + // inp incremented to next input location in the above line + a2TimesY = s64_mult_s32_s32( a2 , yScaled ); // Q(59-shiftd) + a2TimesY = s64_shl_s64( a2TimesY , -shiftDiff - 4 ); // Q(55-shiftn) + b2TimesX = s64_shl_s64( b2TimesX, -4 ); // Q(55-shiftn) + + w2 = s64_sub_s64_s64( b2TimesX , a2TimesY); // Q(55-shiftn) + *mem-- = w2; // after this, mem points to w1 // Q(55-shiftn) + + /***************************** store the output ******************************/ + *out++ = yScaled; // Q27 + /* Performing this here in order to support in-place computation */ + } + } +#endif /* __qdsp6__ for iirTDF2_32 */ +} +#endif /* ifndef QDSP6_ASM_IIRTDF2_32 */ + +#ifndef QDSP6_ASM_IIRTDF2_16 +void iirTDF2_16(int16 *inp, + int16 *out, + int32 samples, + int32 *numcoefs, + int32 *dencoefs, + int64 *mem, + int16 shiftn, + int16 shiftd) +{ +#if ((defined __hexagon__) || (defined __qdsp6__)) + + int64 y, w1, w2; + int32 yScaled; + /* + * y in Q(48 - SHIFT - max(shiftn,shiftd) - 4) + * yScaled in Q(16 - SHIFT) + * w1 in Q(48 - SHIFT - max(shiftn,shiftd) - 3) + * w2 in Q(48 - SHIFT - max(shiftn,shiftd) - 1) + */ + + int64 b0TimesX, b1TimesX, b2TimesX; + int64 a1TimesY, a2TimesY; + int64 w1Temp, w2Temp; + + int32 b0, b1, b2, a1, a2; + int32 i, p5; + + int32 shiftDiffX = 0; + int32 shiftDiffY = 0; + int32 shiftY = 0; + if (shiftn < shiftd) { + shiftY = (shiftd-12-GUARD_BITS_16); + shiftDiffX = shiftn - shiftd; + } else { + shiftY = (shiftn-12-GUARD_BITS_16); + shiftDiffY = shiftd - shiftn; + } + + if (shiftY <= -1){ + p5 = (int32)(1 << (-shiftY - 1)); + } else { + p5 = 0; + } + + b0 = *numcoefs; + b1 = *(numcoefs + 1); + b2 = *(numcoefs + 2); + a1 = *dencoefs; + a2 = *(dencoefs + 1); + + /* repeat the loop for every sample*/ + /* equations are y = b0*x+w1 */ + /* w1= b1*x-a1*y+w2 */ // use yScaled + /* w2= b2*x-a2*y */ // use yScaled + for (i = 0; i < samples; ++i) + { + /******************************* calculate y ********************************/ + b0TimesX = Q6_P_mpy_RR( b0 , *inp); //Q(47-shiftn) + b0TimesX = Q6_P_asl_PR( b0TimesX , shiftDiffX - 4 ); //Q(47-shiftd- 4) = Q(43-shiftd) + + w1Temp = mem[0]; //mem1=Q(43-shiftd) + y = Q6_P_add_PP( b0TimesX , w1Temp ); //Q(43-shiftd) + + // saturate y to 32-bits Q(32-SHIFT) + yScaled = Q6_R_sat_P(Q6_P_asl_PR(Q6_P_add_RP(p5,y),shiftY)); //Q(47-(15-shiftd)-shiftd-4) = Q(32 - 4) = Q(28), rounding + + /******************************* calculate w1 *******************************/ + b1TimesX = Q6_P_mpy_RR( b1 , *inp ); //Q(47 - shiftn) + a1TimesY = Q6_P_mpy_RR( a1 , yScaled ); //Q(28+32-shiftd) = Q(60-shiftd) + b1TimesX = Q6_P_asl_PR( b1TimesX , shiftDiffX - 4 ); //Q(47-shiftn + shiftn-shiftd -4) = Q(43-shiftd) + a1TimesY = Q6_P_asl_PR( a1TimesY, shiftDiffY-20+GUARD_BITS_16); //Q(60-shiftd-20+3) = Q(43-shiftd) + + w1 = Q6_P_sub_PP(b1TimesX,a1TimesY); //Q(43-shiftd) + w2Temp = mem[1]; //mem2=Q(43-shiftd) + w1 = Q6_P_add_PP(w1,w2Temp); //Q(43-shiftd) + mem[0] = w1; //mem1=Q(43-shiftd) + + /******************************* calculate w2 *******************************/ + b2TimesX = Q6_P_mpy_RR( b2 , *inp++ ); //Q(47-shiftn) + // inp incremented to next input location in the above line + a2TimesY = Q6_P_mpy_RR( a2 , yScaled ); //Q(28+32-shiftd) = Q(60-shiftd) + b2TimesX = Q6_P_asl_PR( b2TimesX , shiftDiffX - 4); //Q(47-shiftn+shiftn-shiftd-4) = Q(43-shiftd) + a2TimesY = Q6_P_asl_PR( a2TimesY, shiftDiffY-20+GUARD_BITS_16); //Q(60-shiftd-20+3) = Q(43-shiftd) + + w2 = Q6_P_sub_PP( b2TimesX , a2TimesY); //Q(43-shiftd) + mem[1] = w2; //mem2=Q(43-shiftd) + + /***************************** store the output ******************************/ + yScaled = Q6_R_add_RR_sat(yScaled, 0x1000); + *out++ = Q6_R_sath_R( yScaled >> (16-GUARD_BITS_16) ); + /* Performing this here in order to support in-place computation */ + } + +#else /* __qdsp6__ */ + + int64 y, w1, w2; + int32 yScaled; + /* + * y in Q(48 - SHIFT - max(shiftn,shiftd) - 4) + * yScaled in Q(16 - SHIFT) + * w1 in Q(48 - SHIFT - max(shiftn,shiftd) - 3) + * w2 in Q(48 - SHIFT - max(shiftn,shiftd) - 1) + */ + + int64 b0TimesX, b1TimesX, b2TimesX; + int64 a1TimesY, a2TimesY; + int64 w1Temp, w2Temp; + + int32 b0, b1, b2, a1, a2; + int32 i, p5; + + int16 shiftDiff = shiftn - shiftd; + int16 shiftY = 0; + if (shiftn < shiftd) { + shiftY = (int16)(-(12-shiftd+GUARD_BITS_16)); + } else { + shiftY = (int16)(-(12-shiftn+GUARD_BITS_16)); + } + + if (shiftY <= -1){ + p5 = (int32)(1 << (-shiftY - 1)); + } else { + p5 = 0; + } + + b0 = *numcoefs; + b1 = *(numcoefs + 1); + b2 = *(numcoefs + 2); + a1 = *dencoefs; + a2 = *(dencoefs + 1); + + /* repeat the loop for every sample*/ + /* equations are y = b0*x+w1 */ + /* w1= b1*x-a1*y+w2 */ // use yScaled + /* w2= b2*x-a2*y */ // use yScaled + + if (shiftn < shiftd) + { + for (i = 0; i < samples; ++i) + { + /******************************* calculate y ********************************/ + b0TimesX = s64_mult_s32_s16( b0 , *inp); //Q(47-shiftn) + b0TimesX = s64_shl_s64( b0TimesX , shiftDiff - 4 ); //Q(47-shiftd- 4) = Q(43-shiftd) + + w1Temp = *mem++; // after this, mem points to w2 //mem1=Q(43-shiftd) + y = s64_add_s64_s64( b0TimesX , w1Temp ); //Q(43-shiftd) + + // saturate y to 32-bits Q(32-SHIFT) + yScaled = s32_saturate_s64(s64_shl_s64(s64_add_s64_s32(y, p5),shiftY)); //Q(47-(15-shiftd)-shiftd-4) = Q(32 - 4) = Q(28), rounding + + /******************************* calculate w1 *******************************/ + b1TimesX = s64_mult_s32_s16( b1 , *inp ); //Q(47 - shiftn) + a1TimesY = s64_mult_s32_s32( a1 , yScaled ); //Q(28+32-shiftd) = Q(60-shiftd) + b1TimesX = s64_shl_s64( b1TimesX , shiftDiff - 4 ); //Q(47-shiftn + shiftn-shiftd -4) = Q(43-shiftd) + a1TimesY = s64_shl_s64( a1TimesY, -20+GUARD_BITS_16); //Q(60-shiftd-20+3) = Q(43-shiftd) + + w1 = s64_sub_s64_s64(b1TimesX,a1TimesY); //Q(43-shiftd) + w2Temp = *mem--; //mem2=Q(43-shiftd) + w1 = s64_add_s64_s64(w1,w2Temp); //Q(43-shiftd) + *mem++ = w1; // after this, mem points to w2*/ //mem1=Q(43-shiftd) + + /******************************* calculate w2 *******************************/ + b2TimesX = s64_mult_s32_s16( b2 , *inp++ ); //Q(47-shiftn) + // inp incremented to next input location in the above line + a2TimesY = s64_mult_s32_s32( a2 , yScaled ); //Q(28+32-shiftd) = Q(60-shiftd) + b2TimesX = s64_shl_s64( b2TimesX , shiftDiff - 4 ); //Q(47-shiftn+shiftn-shiftd-4) = Q(43-shiftd) + a2TimesY = s64_shl_s64( a2TimesY, -20+GUARD_BITS_16); //Q(60-shiftd-20+3) = Q(43-shiftd) + + w2 = s64_sub_s64_s64( b2TimesX , a2TimesY); //Q(43-shiftd) + *mem-- = w2; // after this, mem points to w1*/ //mem2=Q(43-shiftd) + + /***************************** store the output ******************************/ + yScaled = s32_add_s32_s32_sat(yScaled, 0x1000); //L32Q(28) + *out++ = s16_saturate_s32(yScaled >> (16-GUARD_BITS_16)); //Q(28-13)=Q15 + /* Performing this here in order to support in-place computation */ + } + } + else + { + for (i = 0; i < samples; ++i) + { + /******************************* calculate y ********************************/ + b0TimesX = s64_mult_s32_s16( b0 , *inp); // Q(48 - SHIFT - shiftn) //Q(47-shiftn) + b0TimesX = s64_shl_s64(b0TimesX, -4); //Q(43-shiftn) + + w1Temp = *mem++; // after this, mem points to w2 //mem1=Q(43-shiftn) + y = s64_add_s64_s64( b0TimesX , w1Temp ); //Q(43-shiftn) + + // saturate y to 32-bits Q(32-4) + yScaled = s32_saturate_s64(s64_shl_s64(s64_add_s64_s32(y, p5),shiftY)) ; // Q(43-shiftn-15+shiftn) = Q(28), rounding + + /******************************* calculate w1 *******************************/ + b1TimesX = s64_mult_s32_s16( b1 , *inp ); //Q(47-shiftn) + a1TimesY = s64_mult_s32_s32( a1 , yScaled ); //Q(28+32-shiftd) = Q(60-shiftd) + a1TimesY = s64_shl_s64( a1TimesY , -shiftDiff - 20 + GUARD_BITS_16); //Q(60-shiftd-shiftn+shiftd-20+3) = Q(43-shiftn) + b1TimesX = s64_shl_s64( b1TimesX, - 4); //Q(43-shiftn) + + w1 = s64_sub_s64_s64(b1TimesX,a1TimesY); //Q(43-shiftn) + w2Temp = *mem--; // after this, mem points to w1 //mem2=Q(43-shiftn), Q(43-shiftn) + w1 = s64_add_s64_s64(w1,w2Temp); //Q(43-shiftn) + *mem++ = w1; // after this, mem points to w2*/ //mem1 = Q(43-shiftn) + + /******************************* calculate w2 *******************************/ + b2TimesX = s64_mult_s32_s16( b2 , *inp++ ); //Q(47-shiftn) + // inp incremented to next input location in the above line + a2TimesY = s64_mult_s32_s32( a2 , yScaled ); //Q(28+32-shiftd) = Q(60-shiftd) + a2TimesY = s64_shl_s64( a2TimesY , -shiftDiff - 20 + GUARD_BITS_16); //Q(60-shiftd-shiftn+shiftd-20+3) = Q(43-shiftn) + b2TimesX = s64_shl_s64( b2TimesX, -4 ); //Q(43-shiftn) + + w2 = s64_sub_s64_s64( b2TimesX , a2TimesY); //Q(43-shiftn) + *mem-- = w2; // after this, mem points to w1*/ //mem2=Q(43-shiftn) + + /***************************** store the output ******************************/ + yScaled = s32_add_s32_s32_sat(yScaled, 0x1000); //L32Q(28) + *out++ = s16_saturate_s32(yScaled >> (16 - GUARD_BITS_16)); //Q(28-13) = Q(15) + /* Performing this here in order to support in-place computation */ + } + } + +#endif /* __qdsp6__ */ +} + +#endif /* ifndef QDSP6_ASM_IIRTDF2_16 */ + diff --git a/modules/cmn/common/utils/src/log10.c b/modules/cmn/common/utils/src/log10.c new file mode 100644 index 0000000..a8b3dc4 --- /dev/null +++ b/modules/cmn/common/utils/src/log10.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "audio_log10.h" +#include "audio_basic_op.h" + +/* Input log table */ +static const int16_t log2tbl[] = +{ + 0x7800, 0x782d, 0x785a, 0x7884, 0x78ae, + 0x78d6, 0x78fe, 0x7924, 0x794a, 0x796e, + 0x7992, 0x79b4, 0x79d6, 0x79f8, 0x7a18, + 0x7a38, 0x7a57, 0x7a75, 0x7a93, 0x7ab1, + 0x7acd, 0x7ae9, 0x7b05, 0x7b20, 0x7b3b, + 0x7b55, 0x7b6f, 0x7b88, 0x7ba1, 0x7bb9, + 0x7bd1, 0x7be9 +}; + + +/*============================================================================== + +FUNCTION int32_t log10_fixed(int32_t input) + +DESCRIPTION Calculates 10log10(x) using table look up for log2(input) +10*log10(input)=3.0103*log2(input) + +DEPENDENCIES none + +RETURN VALUE Signed 32 bit value 10log10(x) + +SIDE EFFECTS none + +===============================================================================*/ +int32_t log10_fixed(int32_t input) +{ + + int16_t tableOffset = 128; /* 2^7 for offset to table */ + int16_t constantTerm = 24660; /* 3.013 in Q13 */ + int16_t shiftNorm; + int32_t normalizedInput, intermediateTerm, exponent; + int32_t termA, termB; + int16_t index; + + + /* Normalize Input */ + shiftNorm = s16_norm_s32(input); /* Final normalization shift factor */ + normalizedInput = s32_shl_s32_sat(input, shiftNorm); /* normalize input */ + + /* Calculate index for log2(input) */ + intermediateTerm = s32_mult_s16_s16(s16_extract_s32_h(normalizedInput), tableOffset); + index = s16_add_s16_s16(s16_extract_s32_h(intermediateTerm), -32); /* get index to log2tbl */ + + /* get the exponent to subtract for q factor adjustment */ + exponent = s32_saturate_s40(s40_mult_s16_s16_shift(tableOffset, shiftNorm, 3)); /* - expon << 10 */ + + /* get 3.0103*log2(input) and 3.0103*exponent in Q23 */ + termA = s32_mult_s16_s16(log2tbl[index], constantTerm); /* 3.0103*log2(x) Q23 */ + termB = s32_mult_s16_s16(constantTerm, s16_extract_s32_l(exponent)); /* 3.0103*expon Q23 */ + + /* return the difference of termA and termB */ + return s32_sub_s32_s32(termA, termB); +} diff --git a/modules/cmn/common/utils/src/mathlib.c b/modules/cmn/common/utils/src/mathlib.c new file mode 100644 index 0000000..1462b4f --- /dev/null +++ b/modules/cmn/common/utils/src/mathlib.c @@ -0,0 +1,710 @@ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +/*===========================================================================*] +[* FILE NAME: mathlib.c *] +[* DESCRIPTION: *] +[* Contains math functions for post processor. *] +[* Part of the code is adapted from QSound math functions. *] +[* FUNCTION LIST: *] +[* See audpp_mathlib.h *] +[*===========================================================================*/ +#include "audpp_mathlib.h" +#include "audio_divide_qx.h" +#include "ar_defs.h" +/*============================================================================= + Q15 math +=============================================================================*/ +static int16_t Q15_init(int32_t xL32Q15) +{ + if (xL32Q15 > Q15_ONE) + { + return Q15_ONE; + } + else if (xL32Q15 < Q15_MINUSONE) + { + return Q15_MINUSONE; + } + else + { + return s16_extract_s32_l(xL32Q15); + } +} + +int16_t Q15_mult(int16_t aL16Q15, int16_t bL16Q15) +{ // one of the input doesn't have to be Q15 + if (aL16Q15==Q15_ONE) return bL16Q15; + if (bL16Q15==Q15_ONE) return aL16Q15; + if (aL16Q15==0 || bL16Q15==0) return Q15_ZERO; + return s16_extract_s64_h_rnd(s40_mult_s16_s16_shift(aL16Q15, bL16Q15, 1)); +} + +int16_t Q15_mult3(int16_t aL16Q15, int16_t bL16Q15, int16_t cL16Q15) +{ + return Q15_mult(Q15_mult(aL16Q15, bL16Q15), cL16Q15); +} + +void Q15_multBy(int16_t *aL16Q15, int16_t bL16Q15 ) +{ + *aL16Q15 = Q15_mult(*aL16Q15, bL16Q15); +} + +int16_t Q15_scaleByInt16(int16_t scaleL16Q15, int16_t xL16) +{ + if (scaleL16Q15 == Q15_ZERO || xL16==0) return 0; + if (scaleL16Q15 == Q15_ONE) return xL16; + return s16_extract_s40_h(s40_mult_s16_s16_shift(xL16, scaleL16Q15, 1)); +} + +int16_t Q15_negate(int16_t scaleL16Q15) +{ + return scaleL16Q15 == -32768 ? 0x7fff : (int16_t)-scaleL16Q15; +} + +int16_t Q15_initRatio(int32_t numer, int32_t denom) +{ + return Q15_initQ16(divide_qx(numer, denom, 16)); +} + +int16_t Q15_initQ16(int32_t initL32Q16) +{ + return Q15_init(initL32Q16 >> 1); +} + +int16_t Q15_initQ23(int32_t initL32Q23) +{ + return Q15_init((initL32Q23 + 128) >> 8); +} + +/* tables of scale values (L16Q15) for mB conversion */ +static const int16_t T1[] = +{ + 32767, 24403, 18174, +}; +static const int16_t T2[] = +{ + 32767, 32170, 31583, 31006, + 30440, 29885, 29339, 28804, + 28278, 27762, 27255, 26758, + 26269, 25790, 25319, 24857, +}; +static const int16_t T3[] = +{ + 32767, 32730, 32693, 32655, + 32617, 32580, 32542, 32505, + 32468, 32430, 32393, 32356, + 32318, 32281, 32244, 32207, +}; + +int16_t Q15_initMB(int16_t mB) +{ + int32_t product; + if (mB >= 0 ) return Q15_ONE; + if (mB <= -8000) return 0; + mB = -mB; + + product = 32768; + while (mB >= 2404) + { + product >>= 4; + mB -= 2404; + } + while (mB >= 602) + { + product >>= 1; + mB -= 602; + } + if (mB >= 256) + { + product = (product * T1[mB>>8] + (1<<14)) >> 15; + mB &= 255; + } + if (mB >= 16) + { + product = (product * T2[mB>>4] + (1<<14)) >> 15; + mB &= 15; + } + if (mB != 0) + { + product = (product * T3[mB] + (1<<14)) >> 15; + } + return (int16_t)product; +} + +/* binary search for next set of bits in mB calculation */ +static int16_t find_mb(int16_t *mB, const int16_t *row, int16_t n, + int16_t scaleL16, int16_t target) +{ + const int16_t *base = row; + const int16_t *end = row+n; + int16_t update = scaleL16; + int16_t test; + while (n > 1) + { + const int16_t* mid = base+n/2; + test = (int16_t)(((int32_t)scaleL16 * *mid + (1 << 14)) >> 15); + if (test < target) + { + end = mid; + } + else + { + base = mid; + update = test; + } + n = (int16_t)(end - base); + } + *mB = (int16_t)(*mB << 4 | (base - row)); + return update; +} + +int16_t Q15_getMB(int16_t scaleL16) +{ + int16_t mB = 0, mB2, index, x; + + if (scaleL16 == Q15_ZERO) return -9600; + if (scaleL16 == Q15_ONE ) return 0; + while (scaleL16 < 2048) + { + scaleL16 <<= 4; + mB += 2404; + } + while (scaleL16 < 16384) + { + scaleL16 <<= 1; + mB += 602; + } + if (scaleL16 == 16384) + { + return -mB-602; + } + + for (index = 0; index < 2 && T1[index+1] > scaleL16; index++) + { + } + mB2 = index; + x = T1[index]; + x = find_mb(&mB2, T2, 16, x, scaleL16); + x = find_mb(&mB2, T3, 16, x, scaleL16); + return -(mB + mB2); +} + + +/*============================================================================= + Q16 math +=============================================================================*/ +// Notes from origional code: +// Use basic C operations to do 40 bit multiplication; is made much +// slower and complicated because of the absence of a carry bit. +// Optimize this in assembler or using intrinsics if you can. +#define lower(x) ((x)&0xffff) +#define upper(x) ((x)>>16) + +int32_t Q16_mult(int32_t aL16Q16, int32_t bL16Q16) +{ +#if 1 //bit-exact version to QSound function Fix16_Multiply() + return (int32_t)(((int64)aL16Q16*bL16Q16+(1L<<15))>>16); + +#else + uint32_t a1, a2, b1, b2, a1b1, a1b2, a2b1, a2b2; + int32_t product; + boolean negative; + + if (aL16Q16 == 0 || bL16Q16 == 0) return 0; + + negative = FALSE; + if (aL16Q16 < 0) aL16Q16 = -aL16Q16, negative = TRUE; + if (bL16Q16 < 0) bL16Q16 = -bL16Q16, negative = !negative; + + if (aL16Q16 == Q16_ONE) + { + product = bL16Q16; + } + else if (bL16Q16 == Q16_ONE) + { + product = aL16Q16; + } + else if (aL16Q16 < Q16_ONE && bL16Q16 < Q16_ONE) + { + product = ((uint32_t)aL16Q16 * (uint32_t)bL16Q16 + Q16_ONE/2) >> 16; + } + else + { + a1 = upper(aL16Q16); + a2 = lower(aL16Q16); + b1 = upper(bL16Q16); + b2 = lower(bL16Q16); + a1b1 = a1 * b1; + a1b2 = a1 * b2; + a2b1 = a2 * b1; + a2b2 = a2 * b2; + product = (a1b1<<16) + a1b2 + a2b1 + ((a2b2+Q16_ONE/2) >> 16); + } + return negative ? -product : product; +#endif +} + +int32_t Q16_square(int32_t xL32Q16) +{ + uint32_t x1, x2, x1x1, x1x2, x2x2; + + if (xL32Q16 == 0 ) return 0; + if (xL32Q16 < 0 ) xL32Q16 = -xL32Q16; + if (xL32Q16 == Q16_ONE) return xL32Q16; + if (xL32Q16 < Q16_ONE) + return ((uint32_t)xL32Q16 * (uint32_t)xL32Q16 + Q16_ONE/2) >> 16; + + x1 = upper(xL32Q16); + x2 = lower(xL32Q16); + x1x1 = x1 * x1; + x1x2 = x1 * x2; + x2x2 = x2 * x2; + return (x1x1 << 16) + 2 * x1x2 + ((x2x2 + Q16_ONE/2) >> 16); +} + +int32_t Q16_sqrt(int32_t aL32Q16) +{ + uint32_t root, remHi, remLo, testDiv; + int16_t count; + + root = 0; // Clear root + remHi = 0; // Clear high part of partial remainder + remLo = aL32Q16; // Get argument into low part of partial remainder + count = 23; + + do + { + remHi = (remHi << 2) | (remLo >> 30); remLo <<= 2; // get 2 bits of arg + root <<= 1; // Get ready for the next bit in the root + testDiv = (root << 1) + 1; // Test divisor + if (remHi >= testDiv) + { + remHi -= testDiv; + root += 1; + } + } while (count-- != 0); + + return root; +} + +int32_t Q16_reciprocalU(int32_t denom) +{ + uint32_t x, y, t; + int i; + if(!(denom>0)) + { + return 0; + } + + x = 1; + y = 0; + for (i = 1; i<=31; i++) + { + x <<= 1; // shift x||y left by one bit + y <<= 1; // ... + if (x >= (uint32_t)denom) + { + x -= denom; + y += 1; + } + } + t = (int32_t)x>>31; // all 1's if x[31]==1 + x <<= 1; // shift x||y left by one bit + y <<= 1; // ... + if ((x|t) >= (uint32_t)denom) + { + x -= denom; + y += 1; + } + // x is remainder, y is quotient + return x >= (uint32_t)denom >> 1 ? y+1 : y; +} + +int32_t Q16_reciprocal(int32_t denom) +{ + if (denom < 0) + { + return -Q16_reciprocalU(-denom); + } + return Q16_reciprocalU(denom); +} + +/*---------------------------------------------------------------------------*/ +/* Q16_Divide_Truncated() */ +/* - Version of Q16_Divide without rounding. Used by variable delay */ +/*---------------------------------------------------------------------------*/ +int32_t Q16_divide_truncated(int32_t numerL32Q16, int32_t denomL32Q16) +{ + boolean negative; + uint32_t xUL32, yUL32, tUL32; + int16_t i; + + if (numerL32Q16 == 0) return 0; + + negative = FALSE; + if (numerL32Q16 < 0) + { + negative = TRUE; + numerL32Q16 = -numerL32Q16; + } + if (denomL32Q16 < 0) + { + negative = !negative; + denomL32Q16 = -denomL32Q16; + } + + xUL32 = numerL32Q16 >> 16; + yUL32 = numerL32Q16 << 16; + for (i = 1; i <= 32; i++) + { + tUL32 = (int32_t)xUL32 >> 31; // all 1's if x[31]==1 + xUL32 = (xUL32 << 1) | (yUL32 >> 31); // shift x||y left by one bit + yUL32 <<= 1; // upshift 1, remove one bit + if ((xUL32|tUL32) >= (uint32_t)denomL32Q16) + { + xUL32 -= denomL32Q16; + yUL32 += 1; + } + } /* end of for (i = 1; i <= 32; i++) */ + + /* x is remainder, y is quotient */ + return negative ? -(int32_t)yUL32 : (int32_t)yUL32; +} /*-------------- end of function Q16_divide_truncated ---------------------*/ + +/*============================================================================= + Q23 math +=============================================================================*/ +// Notes from origional code: +// Use basic C operations to do 40 bit multiplication; is made much +// slower and complicated because of the absence of a carry bit. +// Optimize this in assembler or using intrinsics if you can. +int32_t Q23_mult(int32_t aL32Q23, int32_t bL32Q23) +{ + boolean negative; + int32_t product; + uint32_t a1, a2, b1, b2, a1b2, a2b1, a2b2, w, u, v; + + if (aL32Q23 == 0 || bL32Q23 == 0) return 0; + + negative = FALSE; + if (aL32Q23 < 0) aL32Q23 = -aL32Q23, negative = TRUE; + if (bL32Q23 < 0) bL32Q23 = -bL32Q23, negative = !negative; + + if (aL32Q23 == Q23_ONE) + { + product = bL32Q23; + } + else if (bL32Q23 == Q23_ONE) + { + product = aL32Q23; + } + else + { + a1 = upper(aL32Q23); + a2 = lower(aL32Q23); + b1 = upper(bL32Q23); + b2 = lower(bL32Q23); + a1b2 = a1 * b2; + a2b1 = a2 * b1; + a2b2 = a2 * b2; + w = lower(a1b2)+lower(a2b1)+upper(a2b2)+(1<<(22-16)); + // s16_add_s16_s16 in rounding + u = a1*b1+upper(a1b2)+upper(a2b1)+upper(w); + // u,v is 40 bit product + v = (lower(w)<<16)+lower(a2b2); + product = (u<<9)|(v>>23); + } + return negative ? -product : product; +} + + +int32_t Q23_square(int32_t xL32Q23) +{ + uint32_t x1, x2, x1x2, x2x2, w, u, v; + + if (xL32Q23 == 0 ) return 0; + if (xL32Q23 < 0 ) xL32Q23 = -xL32Q23; + if (xL32Q23 == Q23_ONE) return xL32Q23; + x1 = upper(xL32Q23); + x2 = lower(xL32Q23); + x1x2 = x1 * x2; + x2x2 = x2 * x2; + w = 2*lower(x1x2) + upper(x2x2) + (1<<(22-16)); // s16_add_s16_s16 in rounding + u = x1*x1 + 2*upper(x1x2) + upper(w); + v = (lower(w)<<16) + lower(x2x2); + return (u<<9)|(v>>23); +} + +int32_t Q23_sqrt(int32_t aL32Q23) +{ + uint32_t root, remHi, remLo, testDiv; + + int count; + + if (aL32Q23 == 0 || aL32Q23 == Q23_ONE) return aL32Q23; + root = 0; // Clear root + remHi = 0; // Clear high part of partial remainder + remLo = aL32Q23; // Get argument into low part of partial remainder + count = 26; + + remHi = (remHi << 1) | (remLo >> 31); remLo <<= 1; // get 2 bits of arg + root <<= 1; // Get ready for the next bit in the root + testDiv = (root << 1) + 1; // Test divisor + if (remHi >= testDiv) + { + remHi -= testDiv; + root += 1; + } + do + { + remHi = (remHi << 2) | (remLo >> 30); remLo <<= 2; // get 2 bits of arg + root <<= 1; // Get ready for the next bit in the root + testDiv = (root << 1) + 1; // Test divisor + if (remHi >= testDiv) + { + remHi -= testDiv; + root += 1; + } + } while (count-- != 0); + + return root; +} + +int32_t Q23_reciprocalU(int32_t denom) +{ + uint32_t x, y, t, i; + if(!(denom>0)) + { + return 0; + } + + x = 1<<14; + y = 0; + for (i = 1; i <= 32; i++) + { + t = (int32_t)x>>31; // all 1's if x[31]==1 + x = (x<<1) | (y>>31); // shift x||y left by one bit + y <<= 1; // ... + if ((x|t)>=(uint32_t)denom) + { + x -= denom; + y += 1; + } + } + // x is remainder, y is quotient + return x>=(uint32_t)denom >> 1 ? y+1 : y; +} + +int32_t Q23_reciprocal(int32_t denom) +{ + if (denom<0) + { + return -Q23_reciprocalU(-denom); + } + return Q23_reciprocalU(denom); +} + + +int32_t Q23_sine0(int32_t xL32Q23) +{ + int32_t x2 = Q23_square(xL32Q23); + int32_t dz = xL32Q23; + int32_t sine = xL32Q23; + int32_t n = 2; + while (1) + { + dz = Q23_mult(dz, divide_int32(-x2, n*(n+1))); + if (dz==0) + { + break; + } + sine += dz; + n += 2; + } + if(!(sine >= -Q23_ONE && sine <= Q23_ONE)) + { + return 0; + } + return sine; +} + +int32_t Q23_cosine0(int32_t xL32Q23) +{ + int32_t x2 = Q23_square(xL32Q23); + int32_t dz = -x2/2; + int32_t cosine = Q23_ONE + dz; + int32_t n = 3; + while (1) + { + dz = Q23_mult(dz, divide_int32(-x2, (n*(n+1)))); + if (dz==0) break; + cosine += dz; + n += 2; + } + if(!(cosine >= -Q23_ONE && cosine <= Q23_ONE)) + { + return 0; + } + return cosine; +} + +int32_t Q23_sine(int32_t xL32Q23) +{ + if (xL32Q23 <= Q23_PI_2 ) return Q23_sine0(xL32Q23); + if (xL32Q23 <= Q23_PI ) return Q23_sine0(Q23_PI - xL32Q23); + if (xL32Q23 <= Q23_PI3_2) return -Q23_sine0(xL32Q23 - Q23_PI); + if (xL32Q23 <= Q23_TWOPI) return -Q23_sine0(Q23_TWOPI - xL32Q23); + if (xL32Q23 < 0 ) return -Q23_sine(-xL32Q23); + while (xL32Q23 >= Q23_TWOPI) xL32Q23 -= Q23_TWOPI; + return Q23_sine(xL32Q23); +} + + +int32_t Q23_cosine(int32_t xL32Q23) +{ + if (xL32Q23 <= Q23_PI_2 ) return Q23_cosine0(xL32Q23); + if (xL32Q23 <= Q23_PI ) return -Q23_cosine0(Q23_PI - xL32Q23); + if (xL32Q23 <= Q23_PI3_2) return -Q23_cosine0(xL32Q23 - Q23_PI); + if (xL32Q23 <= Q23_TWOPI) return Q23_cosine0(Q23_TWOPI - xL32Q23); + if (xL32Q23 < 0 ) return Q23_cosine(-xL32Q23); + while (xL32Q23 >= Q23_TWOPI) xL32Q23 -= Q23_TWOPI; + return Q23_cosine(xL32Q23); +} + + +int32_t Q23_exp0(int32_t xL32Q23) +{ + int32_t exp = Q23_ONE + xL32Q23; + int32_t dz = xL32Q23; + int32_t n = 2; + while ((dz = Q23_mult(dz, divide_int32(xL32Q23, n)))!=0) + { + exp += dz; + n += 1; + } + return exp; +} + +#define Q23_EXP_STEP (8496933) +#define Q23_EXP_SCALE (23098968) +#define Q23_INVEXP_STEP (-11901566) +#define Q23_INVEXP_SCALE (2030125) +#define Q23_EXP_ONE (22802601) +#define Q23_EXP_MINUSONE (3085996) + +int32_t Q23_exp(int32_t xL32Q23) +{ + int32_t expL32Q23; + if (xL32Q23 >= Q23_EXP_STEP) + { + expL32Q23 = Q23_EXP_SCALE; + while (1) + { + xL32Q23 -= Q23_EXP_STEP; + if (xL32Q23 < Q23_EXP_STEP) + { + break; + } + expL32Q23 = Q23_mult(expL32Q23, Q23_EXP_SCALE); + } + } + else if (xL32Q23 <= Q23_INVEXP_STEP) + { + expL32Q23 = Q23_INVEXP_SCALE; + while (1) + { + xL32Q23 -= Q23_INVEXP_STEP; + if (xL32Q23 > Q23_INVEXP_STEP) + { + break; + } + expL32Q23 = Q23_mult(expL32Q23, Q23_INVEXP_SCALE); + } + } + else + { + return Q23_exp0(xL32Q23); + } + return Q23_mult(expL32Q23, Q23_exp0(xL32Q23)); +} + + +#define Q23_ln2 (5814540) + +int32_t Q23_ln(int32_t xL32Q23) +{ + int32_t power2, n; + int32_t lnL32Q23, dzL32Q23, deltaL32Q23; + + if (xL32Q23 == Q23_ONE) + { + return 0; + } + + // Normalize argument to range 0.5 to 1 because the Taylor series + // expansion converges more quickly as x approaches 1. Shifts are + // fast and precise. + power2 = 0; + while (xL32Q23 > Q23_ONE) + { + xL32Q23 >>= 1; + power2++; + } + while (xL32Q23 < Q23_ONE>>1) + { + xL32Q23 <<= 1; + power2--; + } + + // ln(1-x) = -x - x**2/2 - x**3/3 - x**4/4 - x**5/5 ... + xL32Q23 = Q23_ONE - xL32Q23; + + lnL32Q23 = -xL32Q23; + dzL32Q23 = xL32Q23; + n = 2; + while (1) + { + dzL32Q23 = Q23_mult(dzL32Q23, xL32Q23); + deltaL32Q23 = divide_int32(dzL32Q23, n); + if (deltaL32Q23 == 0) + { + break; + } + lnL32Q23 -= deltaL32Q23; + n += 1; + } + + lnL32Q23 += power2 * Q23_ln2; + + return lnL32Q23; +} + +int32_t Q23_pow(int32_t xL32Q23, int32_t yL32Q23) +{ + if (xL32Q23 == 0) return 0; + if (yL32Q23 == 0) return Q23_ONE; + return Q23_exp(Q23_mult(yL32Q23, Q23_ln(xL32Q23))); +} + + +#define Q23_ln10_2000 (9658) + +int32_t Q23_initMB(int16_t mB) +{ + if (mB == 0) return Q23_ONE; + if (mB>=4816) return Q23_MAX; + if (mB<=-12000) return 0; + + return Q23_exp(mB * Q23_ln10_2000); +} + +int32_t Q23_getMB(int32_t scaleL32Q23) +{ + if (scaleL32Q23 == Q23_ONE) return 0; + if (scaleL32Q23 == 0 ) return (-2147483647 - 1); // INT_MIN + return (int32_t)Q23_mult(869, Q23_ln(scaleL32Q23)); +} + + diff --git a/modules/cmn/common/utils/src/simple_mm.c b/modules/cmn/common/utils/src/simple_mm.c new file mode 100644 index 0000000..5e2d902 --- /dev/null +++ b/modules/cmn/common/utils/src/simple_mm.c @@ -0,0 +1,216 @@ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +/*============================================================================ + FILE: simple_mm.c + + OVERVIEW: Simple memory management system for allocating and + initializing data within a contiguous block. + + DEPENDENCIES: None +============================================================================*/ + +/*---------------------------------------------------------------------------- + * Include Files + * -------------------------------------------------------------------------*/ + +#include +#include "simple_mm.h" +#include "AEEStdDef.h" + +/*---------------------------------------------------------------------------- + Constants +----------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------- + * Externalized Function Definitions + * -------------------------------------------------------------------------*/ + +/*====================================================================== + FUNCTION smm_init + + DESCRIPTION Initialize simple memory manager by connecting the mem + pointer in smm to the given starting address. + + DEPENDENCIES memstart must be 64-bit aligned + + PARAMETERS smm: [in and out] handle to mem manager + memstart: [in] starting address of memory + + RETURN VALUE None + + SIDE EFFECTS update smm->mem +======================================================================*/ +void smm_init(SimpleMemMgr *smm, void *memstart) +{ + if (smm != NULL) + { + smm->mem = memstart; + } +} + +/*====================================================================== + FUNCTION smm_malloc_size + + DESCRIPTION Determine effective size that will be allocated + by subsequent call to smm_malloc. + Aligns by 64 bits. + + DEPENDENCIES None + + PARAMETERS size: [in] input size in bytes + + RETURN VALUE Number of bytes that will be allocated by smm_malloc + + SIDE EFFECTS None +======================================================================*/ +size_t smm_malloc_size(size_t size) +{ + return ( ALIGN8(size) ); +} + +/*====================================================================== + FUNCTION smm_calloc_size + + DESCRIPTION Determine effective size that will be allocated + by subsequent call to smm_calloc. + Aligns by 64 bits. + + DEPENDENCIES None + + PARAMETERS nmemb: [in] number of elements to allocate + size: [in] size in bytes of each element + + RETURN VALUE Number of bytes that will be allocated by smm_calloc + + SIDE EFFECTS None +======================================================================*/ +size_t smm_calloc_size(size_t nmemb, size_t size) +{ + return ( ALIGN8(nmemb * size) ); +} + +size_t smm_calloc_size_4kaligned(size_t nmemb, size_t size) +{ + return ( 2 * ALIGN4096(nmemb * size) ); +} + +/*====================================================================== + FUNCTION smm_malloc + + DESCRIPTION Allocate a block of memory of the given size, + aligned on 64-bit boundary. Move smm->mem + to the next available 64-bit aligned address. + + DEPENDENCIES smm must be initialized by call to smm_init + + PARAMETERS smm: [in and out] handle to memory manager + size: [in] number of bytes to allocate + + RETURN VALUE pointer to the allocated data + + SIDE EFFECTS update smm->mem +======================================================================*/ +void *smm_malloc(SimpleMemMgr *smm, size_t size) +{ + int8 *p = NULL; + int8 *p1 = NULL; + if (smm != NULL) + { + p = (int8 *) smm->mem; + p1 = p + ALIGN8(size); + smm->mem = p1; + } + return p; +} + +void *smm_malloc_4kaligned(SimpleMemMgr *smm, size_t size) +{ + int8 *p = NULL; + int8 *p1 = NULL; + + uint32 *ptr; + if (smm != NULL) + { + ptr = (uint32 *) smm->mem; + p = (int8 *)ALIGN4096(ptr); + p1 = p + ALIGN8(size); + smm->mem = p1; + } + return p; +} + +/*====================================================================== + FUNCTION smm_calloc + + DESCRIPTION Allocate a block of memory of the given size, + aligned on 64-bit boundary. Clear the allocated + region to 0. Move smm->mem to the next available + 64-bit aligned address. + + DEPENDENCIES smm must be initialized by call to smm_init + + PARAMETERS smm: [in and out] handle to memory manager + nmemb: [in] number of elements + size: [in] size of each element in bytes + + RETURN VALUE pointer to the allocated data + + SIDE EFFECTS update smm->mem +======================================================================*/ +void *smm_calloc(SimpleMemMgr *smm, size_t nmemb, size_t size) +{ + void *p = NULL; + size_t total = nmemb * size; + + if (smm != NULL) + { + p = smm_malloc(smm, total); + if (p != NULL) + { + memset(p, 0, total); + } + } + + return p; +} + + +void *smm_calloc_4kaligned(SimpleMemMgr *smm, size_t nmemb, size_t size) +{ + void *p = NULL; + size_t total = nmemb * size; + + if (smm != NULL) + { + p = smm_malloc_4kaligned(smm, total); + if (p != NULL) + { + memset(p, 0, total); + } + } + + return p; +} + +/*====================================================================== + FUNCTION smm_free + + DESCRIPTION Free allocated memory. Does not do anything in this + implementation. + + DEPENDENCIES None + + PARAMETERS smm: [in and out] handle to memory manager + ptr: [in] pointer to previously allocated memory + + RETURN VALUE None + + SIDE EFFECTS None +======================================================================*/ +void smm_free(SimpleMemMgr *smm, void *ptr) +{ + /* No-op in this implementation. */ +} diff --git a/modules/cmn/common/utils/src/util.c b/modules/cmn/common/utils/src/util.c new file mode 100644 index 0000000..194b13f --- /dev/null +++ b/modules/cmn/common/utils/src/util.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + */ + +/*===========================================================================*] +[* FILE NAME: util.c *] +[* DESCRIPTION: *] +[* Various utility functions for post processor. *] +[* FUNCTION LIST : *] +[* changed: compare two integer values and report if different *] +[* find_freq: select closest frequency to match the specified *] +[* find_exact_freq: match exact frequency, return -1 if no match *] +[* ms_to_sample: convert millisecond to sample *] +[* time_to_sample: convert 100 nanosecond to sample *] +[*===========================================================================*/ +#include "audpp_util.h" +#include "audio_divide_qx.h" + +/*===========================================================================*/ +/* FUNCTION : find_freq */ +/* */ +/* DESCRIPTION: According to a specified frequency, find the best match */ +/* from a table of supported frequencies. */ +/* */ +/* INPUTS: freqHz: input reference frequency */ +/* designFreqArray-> array of available frequencies */ +/* arraySize: size of the above array */ +/* OUTPUTS: function returns the array index of the closest match */ +/* */ +/* IMPLEMENTATION NOTES: */ +/*===========================================================================*/ +int16 find_freq +( + int32 freqHz, /* input reference frequency */ + int32 const *designFreqArray, /* available design freq array */ + int16 arraySize /* size of the design freq array */ +) +{ + int32 minDiff = 0, diff; + int16 i, bestMatchedIndex = 0; //kwcheck : keeping -1 might lead to -ve index value, so changing it to 0 + /*-----------------------------------------------------------------------*/ + + for (i = 0; i < arraySize; i++) + { + /* compare the sampling rate with each of the avaiable ones in array */ + diff = u32_abs_s32_sat(s32_sub_s32_s32(designFreqArray[i], freqHz)); + if (i == 0 || minDiff > diff) + { /* log and keep the best match */ + bestMatchedIndex = i; + minDiff = diff; + } /* end of if (i == 0 || minDiff > diff) */ + } /* end of for (i = 0; i < arraySize; i++) */ + return bestMatchedIndex; +} /*------------------------- end of function find_freq ---------------------*/ + + +/*===========================================================================*/ +/* FUNCTION : find_exact_freq */ +/* */ +/* DESCRIPTION: According to a specified frequency, find the exact match */ +/* from a table of supported frequencies. Returns -1 if */ +/* there is no match. */ +/* */ +/* INPUTS: freqHz: input reference frequency */ +/* designFreqArray-> array of available frequencies */ +/* arraySize: size of the above array */ +/* OUTPUTS: function returns the matched index or -1 if no match */ +/* */ +/* IMPLEMENTATION NOTES: */ +/*===========================================================================*/ +int16 find_exact_freq +( + int32 freqHz, /* input reference frequency */ + int32 const *designFreqArray, /* available design freq array */ + int16 arraySize /* size of the design freq array */ +) +{ + int16 i; + + for (i = 0; i < arraySize; i++) + { + if (designFreqArray[i] == freqHz) + { + return i; // there exists exact match + } + } /* end of for (i = 0; i < arraySize; i++) */ + return -1; // there is no match +} /*------------------ end of function find_exact_freq ----------------------*/ + + +/*===========================================================================*/ +/* FUNCTION : ms_to_sample */ +/* */ +/* DESCRIPTION: Convert millisecond value to sample value. */ +/* */ +/* INPUTS: ms: value in millisecond */ +/* sampleRate: sampling rate */ +/* OUTPUTS: function returns the corresponding sample value */ +/* */ +/* IMPLEMENTATION NOTES: */ +/* 1. the version bit-exact with QSound uses one division */ +/* 2. second version avoids division */ +/*===========================================================================*/ +int32 ms_to_sample +( + int16 ms, /* value in millisecond */ + uint32 sampleRate /* samping rate */ +) +{ + /* the following code tries to avoid division */ + int32 tmpL32; + + /*-- ms * sampleRate --*/ + //tmpL32 = s32_mult_s16_u16(ms, sampleRate); + tmpL32 = (int32)s64_mult_s32_s16(sampleRate,ms); // uint16 -> int32 frequency change + /*-- rounding --*/ + // tmpL32 = s32_add_s32_s32(tmpL32, 500); + /*-- ms * sampleRate / 1000 , where 1099511628 is 0.001 in Q40 --*/ + tmpL32 = s32_saturate_s40(s40_mult_s32_s32_shift(tmpL32, 1099511628, 0)); + // s40_mult_s32_s32_shift contains >>32, the result is Q8 + return s32_shl_s32_sat(tmpL32, -8); // further >>8 to get Q0 + +} /*----------------------- end of function ms_to_sample -------------------*/ + + +/*===========================================================================*/ +/* FUNCTION : time_to_sample */ +/* */ +/* DESCRIPTION: Convert 100 nanoseconds (10^(-7) sec) value to sample value. */ +/* */ +/* INPUTS: time_100ns: value in 100ns (10^(-7) sec) */ +/* sampleRate: sampling rate */ +/* OUTPUTS: function returns the corresponding sample value */ +/* */ +/* IMPLEMENTATION NOTES: */ +/* time is in 100 ns units, 1 second==10000000 units */ +/*===========================================================================*/ +int16 time_to_sample +( + int16 time_100ns, /* value in 100 ns */ + uint32 sampleRate /* sampling rate */ +) +{ + int32 samplesPerUnitL32Q23; + + if (time_100ns == 0) + { + return 0; + } + + /* calculate samples per unit */ + samplesPerUnitL32Q23 = divide_qx(sampleRate, 10000000, 23); // Q23 + + /* return sample values, in int16 */ + return s16_saturate_s32(Q23_mult(time_100ns, samplesPerUnitL32Q23)); +} /*--------------------- end of function time_to_sample -------------------*/ + diff --git a/modules/cmn/pcm_mf_cnv/build/CMakeLists.txt b/modules/cmn/pcm_mf_cnv/build/CMakeLists.txt new file mode 100644 index 0000000..701636e --- /dev/null +++ b/modules/cmn/pcm_mf_cnv/build/CMakeLists.txt @@ -0,0 +1,148 @@ +#[[ + @file CMakeLists.txt + + @brief + + @copyright + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause-Clear +]] +cmake_minimum_required(VERSION 3.10) + +set(pcm_cnv_sources + ${LIB_ROOT}/capi/pcm_cnv/src/capi_pcm_mf_cnv.cpp + ${LIB_ROOT}/capi/pcm_cnv/src/capi_pcm_mf_cnv_island.cpp + ${LIB_ROOT}/capi/pcm_cnv/src/capi_pcm_mf_cnv_utils.cpp + ${LIB_ROOT}/capi/pcm_cnv/src/capi_pcm_mf_cnv_utils_island.cpp + ${LIB_ROOT}/lib/src/pc_converter.cpp + ${LIB_ROOT}/lib/src/pc_converter_island.cpp + ${LIB_ROOT}/lib/src/pc_init.cpp + ${LIB_ROOT}/lib/src/pc_process.cpp + ${LIB_ROOT}/lib/src/pc_process_island.cpp + ${LIB_ROOT}/lib/src/pc_float/pc_float.cpp + ) + +include_directories( + ${LIB_ROOT}/capi/pcm_cnv/api + ${LIB_ROOT}/capi/pcm_cnv/inc + ${LIB_ROOT}/capi/pcm_cnv/src + ${LIB_ROOT}/capi/mfc/api + ${LIB_ROOT}/capi/mfc/inc + ${LIB_ROOT}/lib/inc + ../../../audio/pcm_encoder/api + ../../../audio/pcm_encoder/inc + ../../../audio/pcm_decoder/api + ../../../audio/pcm_decoder/inc + ../../../processing/channel_mixer/lib/inc + ../../../processing/channel_mixer/api/ + ../../../processing/resamplers/dynamic_resampler/inc + ../../../processing/resamplers/iir_resampler/inc + ../../../../fwk/spf/utils/cmn/inc + ../../../../fwk/spf/utils/interleaver/inc + ../../../../fwk/spf/utils/lpi_pool/inc + ../../../../fwk/spf/interfaces/fwk/api + ../../../../fwk/spf/containers/cmn/graph_utils/inc +) + +add_library(pcm_cnv ${pcm_cnv_sources}) + +#Link dynamic_resampler and iir_resampler binaries to pcm_cnv library +add_library(dynamic_resampler STATIC IMPORTED GLOBAL) +set(lib_abs_path ${CMAKE_CURRENT_SOURCE_DIR}/../../../processing/resamplers/dynamic_resampler/bin/arm/libdynamic_resampler.a) +set_target_properties(dynamic_resampler PROPERTIES IMPORTED_LOCATION ${lib_abs_path}) +target_link_libraries(pcm_cnv INTERFACE dynamic_resampler) + +add_library(iir_resampler STATIC IMPORTED GLOBAL) +set(lib_abs_path ${CMAKE_CURRENT_SOURCE_DIR}/../../../processing/resamplers/iir_resampler/bin/arm/libiir_resampler.a) +set_target_properties(iir_resampler PROPERTIES IMPORTED_LOCATION ${lib_abs_path}) +target_link_libraries(pcm_cnv INTERFACE iir_resampler) + +set_property(GLOBAL APPEND PROPERTY GLOBAL_SPF_LIBS_LIST pcm_cnv) + +set(pcm_cnv_includes + ${LIB_ROOT}/capi/pcm_cnv/api + ${LIB_ROOT}/capi/pcm_cnv/inc + ${LIB_ROOT}/capi/pcm_cnv/src + ${LIB_ROOT}/capi/mfc/api + ${LIB_ROOT}/capi/mfc/inc + ${LIB_ROOT}/lib/inc + ../../../audio/pcm_encoder/api + ../../../audio/pcm_encoder/inc + ../../../audio/pcm_decoder/api + ../../../audio/pcm_decoder/inc + ../../../processing/channel_mixer/lib/inc + ../../../processing/channel_mixer/api/ + ../../../processing/resamplers/dynamic_resampler/inc + ../../../processing/resamplers/iir_resampler/inc + ../../../../fwk/spf/utils/cmn/inc + ../../../../fwk/spf/utils/interleaver/inc + ../../../../fwk/spf/utils/lpi_pool/inc + ../../../../fwk/spf/interfaces/fwk/api + ../../../../fwk/spf/containers/cmn/graph_utils/inc + ) + +spf_module_sources( + KCONFIG CONFIG_PCM_CNV + NAME pcm_cnv + MAJOR_VER 1 + MINOR_VER 0 + AMDB_ITYPE "capi" + AMDB_MTYPE "PP" + AMDB_MID "0x07001003" + AMDB_TAG "capi_pcm_cnv" + AMDB_MOD_NAME "MODULE_ID_PCM_CNV" + SRCS ${pcm_cnv_sources} + INCLUDES ${pcm_cnv_includes} + H2XML_HEADERS "${LIB_ROOT}/capi/pcm_cnv/api/pcm_converter_api.h" + CFLAGS "" +) + +spf_module_sources( + KCONFIG CONFIG_PCM_DECODER + NAME pcm_decoder + MAJOR_VER 1 + MINOR_VER 0 + AMDB_ITYPE "capi" + AMDB_MTYPE "decoder" + AMDB_MID "0x07001005" + AMDB_TAG "capi_pcm_dec" + AMDB_MOD_NAME "MODULE_ID_PCM_DEC" + AMDB_FMT_ID1 "MEDIA_FMT_ID_PCM" + SRCS ${pcm_cnv_sources} + INCLUDES ${pcm_cnv_includes} + H2XML_HEADERS "../../../audio/pcm_decoder/api/pcm_decoder_api.h" + CFLAGS "" +) + +spf_module_sources( + KCONFIG CONFIG_PCM_ENCODER + NAME pcm_encoder + MAJOR_VER 1 + MINOR_VER 0 + AMDB_ITYPE "capi" + AMDB_MTYPE "encoder" + AMDB_MID "0x07001004" + AMDB_TAG "capi_pcm_enc" + AMDB_MOD_NAME "MODULE_ID_PCM_ENC" + AMDB_FMT_ID1 "MEDIA_FMT_ID_PCM" + SRCS ${pcm_cnv_sources} + INCLUDES ${pcm_cnv_includes} + H2XML_HEADERS "../../../audio/pcm_encoder/api/pcm_encoder_api.h" + CFLAGS "" +) + +spf_module_sources( + KCONFIG CONFIG_MFC + NAME mfc + MAJOR_VER 1 + MINOR_VER 0 + AMDB_ITYPE "capi" + AMDB_MTYPE "PP" + AMDB_MID "0x07001015" + AMDB_TAG "capi_mfc" + AMDB_MOD_NAME "MODULE_ID_MFC" + SRCS ${pcm_cnv_sources} + INCLUDES ${pcm_cnv_includes} + H2XML_HEADERS "${LIB_ROOT}/capi/mfc/api/mfc_api.h" + CFLAGS "" +) diff --git a/modules/cmn/pcm_mf_cnv/capi/mfc/api/mfc_api.h b/modules/cmn/pcm_mf_cnv/capi/mfc/api/mfc_api.h new file mode 100644 index 0000000..9a8fb69 --- /dev/null +++ b/modules/cmn/pcm_mf_cnv/capi/mfc/api/mfc_api.h @@ -0,0 +1,352 @@ +#ifndef MFC_API_H +#define MFC_API_H + +/*============================================================================== + @file mfc_api.h + @brief This file contains MFC API +==============================================================================*/ + +/*=========================================================================== + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause +======================================================================== */ + +/*------------------------------------------------------------------------------ + Includes +------------------------------------------------------------------------------*/ +#include "chmixer_common_api.h" +#include "module_cmn_api.h" + +/*# @h2xml_title1 {Media Format Converter (MFC) Module API} + @h2xml_title_agile_rev {Media Format Converter (MFC) Module API} + @h2xml_title_date {December 11, 2018} */ + +/*------------------------------------------------------------------------------ + Defines +------------------------------------------------------------------------------*/ + +/** @ingroup ar_spf_mod_mfc_mod + Identifier for the input port of the MFC module (#MODULE_ID_MFC). */ +#define MFC_DATA_INPUT_PORT 0x2 + +/** @ingroup ar_spf_mod_mfc_mod + Identifier for the output port of the MFC module. */ +#define MFC_DATA_OUTPUT_PORT 0x1 + +/*------------------------------------------------------------------------------ + API's +------------------------------------------------------------------------------*/ + +/** @ingroup ar_spf_mod_mfc_mod + Identifier for the parameter that configures the resampler in the MFC + module to be either an IIR resampler or FIR resampler. + + For an FIR resampler, clients can set additional FIR resampler + configuration fields. + + @msgpayload + param_id_mfc_resampler_cfg_t +*/ +#define PARAM_ID_MFC_RESAMPLER_CFG 0x0800104D + +/*# @h2xmlp_parameter {"PARAM_ID_MFC_RESAMPLER_CFG", + PARAM_ID_MFC_RESAMPLER_CFG} + @h2xmlp_description {ID for the parameter that configures the resampler in + the MFC module (MODULE_ID_MFC)to be either an IIR + resampler, FIR resampler or IIR preferred resampler.} + @h2xmlp_toolPolicy {Calibration; RTC} */ + +/** @ingroup ar_spf_mod_mfc_mod + Payload for #PARAM_ID_MFC_RESAMPLER_CFG in the Media Format Converter + Module. +*/ +#include "spf_begin_pack.h" +struct param_id_mfc_resampler_cfg_t +{ + uint32_t resampler_type; + /**< Specifies the type of resampler to use. + + @valuesbul + - 0 -- FIR resampler (Default) + - 1 -- IIR resampler + - 2 -- IIR resampler preferred: Uses IIR when possible, if not will fallback on FIR + */ + + /*#< @h2xmle_description {Specifies the resampler type to be used in MFC. \n + If IIR is selected, the use_hw_rs, dynamic_mode, and delay_type fields + are not applicable and are ignored.\n + + If FIR resampler or IIR preferred is selected, the use_hw_rs field is applicable, + it is cached and used.\n + + If FIR resampler is selected dynamic_mode, and delay_type fields are also applicable + and they are cached and used.\n + + If the input or output sampling rates are fractional: + - IIR Preferred Resampler will fallback on FIR + - IIR Resampler will NOT fallback on FIR, module will disable itself \n + } + @h2xmle_rangeList {"FIR resampler"=0; + "IIR Resampler"=1; + "IIR Resampler Preferred"=2} + @h2xmle_default {0} */ + + uint32_t use_hw_rs; + /**< Specifies whether to use the hardware or software resampler for the + FIR resampler. + + @valuesbul + - 0 -- Software Resampler (Default) + - 1 -- Hardware Resampler + + @contcell + If the hardware resampler is selected, the dynamic_mode and delay_type + values are saved but they are ignored. + - The hardware resampler can be created only if the chip supports it. + - If hardware resampler creation fails for any reason, the software + resampler is created with a saved dynamic mode, and the client sets + the delay type. + + If the software resampler is selected, the dynamic_mode and delay_type + fields are saved and used. + + A Get parameter query on this API will return the actual resampler + being used. It is not simply what the client set. */ + + /*#< @h2xmle_description {Specifies whether to use the hardware or software + resampler for the FIR resampler or IIR preferred resampler. \n + If the hardware resampler is selected, the + dynamic_mode and delay_type values are saved but + they are ignored. \n + If the software resampler is selected, these + fields are saved and used. \n + For more details, see AudioReach Signal Processing Framework (SPF) API Reference.} + @h2xmle_rangeList {"Software Resampler"=0, + "Hardware Resampler"=1} + @h2xmle_default {0} */ + + uint16_t dynamic_mode; + /**< Specifies the operation mode for the FIR resampler. + + @valuesbul + - 0 -- Generic resampling (Default) + - 1 -- Dynamic resampling + + This dynamic_mode value is considered only if the software resampler + is used. */ + + /*#< @h2xmle_description {Specifies the operation mode for the FIR + resampler. This dynamic_mode value is + considered only if the software resampler is + used.} + @h2xmle_rangeList {"Generic resampling"=0; + "Dynamic resampling"=1} + @h2xmle_default {0} */ + + uint16_t delay_type; + /**< Specifies the delay type for the FIR resampler. + + @valuesbul + - 0 -- High delay with smooth transition (Default) + - 1 -- Low delay with visible transitional phase distortion + + This value is considered only if the software resampler is used and + the dynamic_mode value is set to 1. */ + + /*#< @h2xmle_description {Specifies the delay type for the FIR resampler. + This value is considered only if the software + resampler is used and the dynamic_mode value is + set to 1.} + @h2xmle_rangeList {"High delay with smooth transition"=0; + "Low delay with visible transitional phase + distortion"=1} + @h2xmle_default {0} */ +} +#include "spf_end_pack.h" +; +typedef struct param_id_mfc_resampler_cfg_t param_id_mfc_resampler_cfg_t; + + +/** @ingroup ar_spf_mod_mfc_mod + Identifier for the output media format parameter used by #MODULE_ID_MFC. + + @msgpayload + param_id_mfc_output_media_fmt_t @newpage +*/ +#define PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT 0x08001024 + +/*# @h2xmlp_parameter {"PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT", + PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT} + @h2xmlp_description {ID for the output media format parameter used by the + MFC module (MODULE_ID_MFC).} + @h2xmlp_toolPolicy {Calibration; RTC} + @h2xmlp_maxSize {72} */ + +/** @ingroup ar_spf_mod_mfc_mod + Payload of the #PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT parameter in the Media + Format Converter Module. Following this structure is the variable payload + for channel_map. + */ +#include "spf_begin_pack.h" +#include "spf_begin_pragma.h" +struct param_id_mfc_output_media_fmt_t +{ + int32_t sampling_rate; + /**< Sampling rate in samples per second. + + @valuesbul + - If the IIR resampler type is used: + - #PARAM_VAL_NATIVE (Default) + - #PARAM_VAL_UNSET + - 8, 16, 24, 32, 48 kHz + + - If the FIR resampler type is used, all values are allowed: + - #PARAM_VAL_NATIVE (Default) + - #PARAM_VAL_UNSET + - 8, 11.025, 12 kHz, 16, 22.05, 24, 32, 44.1, 48, 88.2, 96, 176.4, + 192, 352.8 kHz, 384 kHz @tablebulletend */ + + /*#< @h2xmle_description {Sampling rate in samples per second. \n + If the IIR resampler type is used, only the + following sample rates are allowed: + PARAM_VAL_NATIVE and PARAM_VAL_UNSET; and 8, 16, + 24, 32, and 48 kHz. \n + If the FIR resampler type is used, all values are + allowed.} + @h2xmle_rangeList {"PARAM_VAL_UNSET"= -2; + "PARAM_VAL_NATIVE"= -1; + "8 kHz"=8000; + "11.025 kHz"=11025; + "12 kHz"=12000; + "16 kHz"=16000; + "22.05 kHz"=22050; + "24 kHz"=24000; + "32 kHz"=32000; + "44.1 kHz"=44100; + "48 kHz"=48000; + "88.2 kHz"=88200; + "96 kHz"=96000; + "176.4 kHz"=176400; + "192 kHz"=192000; + "352.8 kHz"=352800; + "384 kHz"=384000} + @h2xmle_default {-1} */ + + int16_t bit_width; + /**< Bit width of the audio samples. + + @valuesbul + - #PARAM_VAL_NATIVE + - #PARAM_VAL_UNSET + - #BIT_WIDTH_16 + - #BIT_WIDTH_24 + - #BIT_WIDTH_32 + + Samples with a bit width of 16 (Q15 format) are stored in 16-bit + words. Samples with a bit width 24 bits (Q27 format) or 32 bits (Q31 + format) are stored in 32-bit words. */ + + /*#< @h2xmle_description {Bit width of the audio samples. \n + Samples with a bit width of 16 (Q15 format) are + stored in 16-bit words. \n + Samples with a bit width 24 bits (Q27 format) or + 32 bits (Q31 format) are stored in 32-bit words.} + @h2xmle_rangeList {"PARAM_VAL_NATIVE"= -1; + "PARAM_VAL_UNSET"= -2; + "BIT_WIDTH_16"=16; + "BIT_WIDTH_24"=24; + "BIT_WIDTH_32"=32} + @h2xmle_default {-1} */ + + int16_t num_channels; + /**< Number of channels in the array. + + @values -2 through 32 */ + + /*#< @h2xmle_description {Number of channels.} + @h2xmle_range {-2..MODULE_CMN_MAX_CHANNEL} + @h2xmle_default {-1} */ + + uint16_t channel_type[0]; + /**< Array of channel mappings. + + Specify a channel mapping for each output channel. If the number of + channels is not a multiple of four, zero padding must be added to the + array to align the packet to a multiple of 32 bits. + + If num_channels is set to PARAM_VAL_NATIVE (-1) or PARAM_VAL_UNSET(-2), + this field is ignored. */ + + /*#< @h2xmle_description {Array of channel mappings. Specify a channel + mapping for each output channel. \n + If the number of channels is not a multiple + of four, zero padding must be added to array + to align the packet to a multiple of 32 + bits . \n + If num_channels is set to PARAM_VAL_NATIVE + (-1) or PARAM_VAL_UNSET(-2), this field is + ignored.} + @h2xmle_variableArraySize {num_channels} + @h2xmle_rangeEnum {pcm_channel_map} + @h2xmle_default {1} */ +} +#include "spf_end_pragma.h" +#include "spf_end_pack.h" +; +typedef struct param_id_mfc_output_media_fmt_t param_id_mfc_output_media_fmt_t; + + +/*------------------------------------------------------------------------------ + Module +------------------------------------------------------------------------------*/ + +/** @ingroup ar_spf_mod_mfc_mod + Identifier for the Media Format Converter (MFC) module. + + @subhead4{Supported parameter IDs} + - #PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT @lstsp1 + - #PARAM_ID_MFC_RESAMPLER_CFG @lstsp1 + - #PARAM_ID_CHMIXER_COEFF + + Resampling is supported only for an integer sampling rate ratio, for + example, 48 kHz and 8 kHz. + + @subhead4{Supported input media format ID} + - Data Format : #DATA_FORMAT_FIXED_POINT @lstsp1 + - fmt_id : Don't care @lstsp1 + - Sample Rates : >0 to 384 kHz @lstsp1 + - Number of channels : 1 to 128 (for certain products this module supports only 32 channels) @lstsp1 + - Channel type : 0..128 @lstsp1 + - Bits per sample : 16, 32 @lstsp1 + - Q format : 15, 27, 31 @lstsp1 + - Interleaving : De-interleaved unpacked @lstsp1 + - Signed/unsigned : Don't care +*/ +#define MODULE_ID_MFC 0x07001015 + +/*# @h2xmlm_module {"MODULE_ID_MFC", MODULE_ID_MFC} + @h2xmlm_displayName {"MFC"} + @h2xmlm_modSearchKeys {resampler, channel mixer, byte converter, Audio, Voice} + @h2xmlm_description {ID for the Media Format Converter (MFC) module. + For more details, see AudioReach Signal Processing Framework (SPF) API Reference.} + @h2xmlm_dataMaxInputPorts {1} + @h2xmlm_dataInputPorts {IN=MFC_DATA_INPUT_PORT} + @h2xmlm_dataOutputPorts {OUT=MFC_DATA_OUTPUT_PORT} + @h2xmlm_dataMaxOutputPorts {1} + @h2xmlm_supportedContTypes {APM_CONTAINER_TYPE_SC, APM_CONTAINER_TYPE_GC} + @h2xmlm_isOffloadable {true} + @h2xmlm_stackSize {4096} + + @{ <-- Start of the Module --> + @h2xml_Select {param_id_mfc_resampler_cfg_t} + @h2xmlm_InsertParameter + @h2xml_Select {param_id_mfc_output_media_fmt_t} + @h2xmlm_InsertParameter + @h2xml_Select {param_id_chmixer_coeff_t} + @h2xmlm_InsertParameter + @h2xml_Select {chmixer_coeff_t} + @h2xmlm_InsertStructure + @} <-- End of the Module --> */ + + +#endif // MFC_API_H diff --git a/modules/cmn/pcm_mf_cnv/capi/mfc/build/CMakeLists.txt b/modules/cmn/pcm_mf_cnv/capi/mfc/build/CMakeLists.txt new file mode 100644 index 0000000..e3051d5 --- /dev/null +++ b/modules/cmn/pcm_mf_cnv/capi/mfc/build/CMakeLists.txt @@ -0,0 +1,16 @@ +#[[ + @file CMakeLists.txt + + @brief + + @copyright + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause +]] +cmake_minimum_required(VERSION 3.10) + +#Include directories +set(mfc_includes + ${LIB_ROOT}/api + ${LIB_ROOT}/inc + ) diff --git a/modules/cmn/pcm_mf_cnv/capi/mfc/inc/capi_mfc.h b/modules/cmn/pcm_mf_cnv/capi/mfc/inc/capi_mfc.h new file mode 100644 index 0000000..ba8f08a --- /dev/null +++ b/modules/cmn/pcm_mf_cnv/capi/mfc/inc/capi_mfc.h @@ -0,0 +1,33 @@ +/* ======================================================================== */ +/** +@file capi_mfc.h + + Header file to implement the media format convertor block*/ + +/* ========================================================================= + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause + ========================================================================== */ + +/*------------------------------------------------------------------------ + * Include files + * -----------------------------------------------------------------------*/ +#ifndef CAPI_MFC_H +#define CAPI_MFC_H + +#include "capi.h" +#include "ar_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +capi_err_t capi_mfc_get_static_properties(capi_proplist_t *init_set_properties, + capi_proplist_t *static_properties); +capi_err_t capi_mfc_init(capi_t *_pif, capi_proplist_t *init_set_properties); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif // CAPI_MFC_H diff --git a/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/api/pcm_converter_api.h b/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/api/pcm_converter_api.h new file mode 100644 index 0000000..570de87 --- /dev/null +++ b/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/api/pcm_converter_api.h @@ -0,0 +1,101 @@ +#ifndef PCM_CONVERTER_API_H_ +#define PCM_CONVERTER_API_H_ +/*============================================================================== + @file pcm_converter_api.h + @brief This file contains PCM converter APIs + +================================================================================ + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause +==============================================================================*/ + +/*------------------------------------------------------------------------ + Include files + -----------------------------------------------------------------------*/ +#include "chmixer_common_api.h" +#include "module_cmn_api.h" +#include "common_enc_dec_api.h" + +/*# @h2xml_title1 {PCM Converter Module API} + @h2xml_title_agile_rev {PCM Converter Module API} + @h2xml_title_date {March 26, 2019} */ + +/*------------------------------------------------------------------------------ + Defines +------------------------------------------------------------------------------*/ + +/** @ingroup ar_spf_mod_pcm_conv_mod + Enumerates the input port ID for the MFC module (#MODULE_ID_MFC). */ +#define PCM_CNV_DATA_INPUT_PORT 0x2 + +/** @ingroup ar_spf_mod_pcm_conv_mod + Enumerates the output port ID for the MFC module. */ +#define PCM_CNV_DATA_OUTPUT_PORT 0x1 + +/*------------------------------------------------------------------------------ + Module +------------------------------------------------------------------------------*/ + +/** @ingroup ar_spf_mod_pcm_conv_mod + Identifier for the PCM Converter module, which is used to convert the + properties of a PCM stream: endianness, interleaving, bit width, + number of channels, and so on. It cannot be used to convert the sampling + rate. This module has only one input port and one output port. + + @subhead4{Supported parameter IDs} + - #PARAM_ID_PCM_OUTPUT_FORMAT_CFG @lstsp1 + - #PARAM_ID_REMOVE_INITIAL_SILENCE @lstsp1 + - #PARAM_ID_REMOVE_TRAILING_SILENCE + + @subhead4{Supported input media format ID} + - Data format : #DATA_FORMAT_FIXED_POINT and #DATA_FORMAT_FLOATING_POINT @lstsp1 + - fmt_id : Don't care @lstsp1 + - Sample rates : 1..384 kHz @lstsp1 + - Number of channels: 1 to 128 (for certain products this module supports only 32 channels) @lstsp1 + - Bit width: @lstsep + - 16 (bits per sample 16 and Q15) @lstsp2 + - 24 (bits per sample 24 and Q23, bits per sample 32 and Q23, Q27, or + Q31) @lstsp2 + - 32 (bits per sample 32 and Q31) @lstsp1 + - 64 (only for floating point data) + - Interleaving: @lstsep + - Interleaved @lstsp2 + - De-interleaved unpacked @lstsp2 + - De-interleaved packed @lstsp1 + - Endianness: little, big + */ +#define MODULE_ID_PCM_CNV 0x07001003 + +/*# @h2xmlm_module {"MODULE_ID_PCM_CNV", MODULE_ID_PCM_CNV} + @h2xmlm_displayName {"PCM Converter"} + @h2xmlm_modSearchKeys {channel mixer, byte converter, endianness, Audio} + @h2xmlm_description {ID for the module used to convert the + properties of a PCM stream: endianness, + interleaving, bit width, number of channels,data format converter, + and so on. It cannot be used to convert the + sampling rate. \n + The module has only one input port and one + output port. For more details, see AudioReach Signal Processing Framework (SPF) API Reference.} + @h2xmlm_dataMaxInputPorts {1} + @h2xmlm_dataMaxOutputPorts {1} + @h2xmlm_dataInputPorts {IN = PCM_CNV_DATA_INPUT_PORT} + @h2xmlm_dataOutputPorts {OUT = PCM_CNV_DATA_OUTPUT_PORT} + @h2xmlm_supportedContTypes {APM_CONTAINER_TYPE_GC} + @h2xmlm_isOffloadable {true} + @h2xmlm_stackSize {4096} + + @{ <-- Start of the Module --> + @h2xml_Select {"param_id_pcm_output_format_cfg_t"} + @h2xmlm_InsertParameter + @h2xml_Select {"payload_pcm_output_format_cfg_t"} + @h2xmlm_InsertParameter + @h2xml_Select {param_id_pcm_output_format_cfg_t::data_format} + @h2xmle_rangeEnum {pcm_data_format} + @h2xml_Select {param_id_chmixer_coeff_t} + @h2xmlm_InsertParameter + @h2xml_Select {chmixer_coeff_t} + @h2xmlm_InsertStructure + @} <-- End of the Module --> */ + + +#endif // PCM_CONVERTER_API_H_ diff --git a/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/build/CMakeLists.txt b/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/build/CMakeLists.txt new file mode 100644 index 0000000..6cf876f --- /dev/null +++ b/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/build/CMakeLists.txt @@ -0,0 +1,33 @@ +#[[ + @file CMakeLists.txt + + @brief + + @copyright + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause +]] +cmake_minimum_required(VERSION 3.10) + +#Include directories +set (lib_incs_list + ${LIB_ROOT}/api + ${LIB_ROOT}/inc + ) + +#Add the source files +set (lib_srcs_list + ${LIB_ROOT}/src/capi_pcm_mf_cnv_island.cpp + ${LIB_ROOT}/src/capi_pcm_mf_cnv_utils_island.cpp + ${LIB_ROOT}/src/capi_pcm_mf_cnv.cpp + ${LIB_ROOT}/src/capi_pcm_mf_cnv_utils.cpp + ) + +#Call spf_build_static_library to generate the static library +spf_build_static_library(capi_pcm_mf_cnv + "${lib_incs_list}" + "${lib_srcs_list}" + "${lib_defs_list}" + "${lib_flgs_list}" + "${lib_link_libs_list}" + ) diff --git a/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/inc/capi_pcm_cnv.h b/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/inc/capi_pcm_cnv.h new file mode 100644 index 0000000..eec40ef --- /dev/null +++ b/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/inc/capi_pcm_cnv.h @@ -0,0 +1,35 @@ +/* ======================================================================== */ +/** +@file capi_pcm_cnv.h + + Header file to implement the Common Audio Post Processor Interface + for Tx/Rx Tuning PCM_CNV block +*/ + +/* ========================================================================= + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause + ========================================================================== */ + +/*------------------------------------------------------------------------ + * Include files + * -----------------------------------------------------------------------*/ +#ifndef CAPI_PCM_CNV_H +#define CAPI_PCM_CNV_H + +#include "capi.h" +#include "ar_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +capi_err_t capi_pcm_cnv_get_static_properties(capi_proplist_t *init_set_properties, + capi_proplist_t *static_properties); +capi_err_t capi_pcm_cnv_init(capi_t *_pif, capi_proplist_t *init_set_properties); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif // CAPI_PCM_CNV_H diff --git a/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/src/capi_pcm_mf_cnv.cpp b/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/src/capi_pcm_mf_cnv.cpp new file mode 100644 index 0000000..952193a --- /dev/null +++ b/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/src/capi_pcm_mf_cnv.cpp @@ -0,0 +1,1716 @@ +/* ========================================================================= + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause + * =========================================================================*/ + +/** + * @file capi_pcm_mf_cnv.cpp + * + * Cpp source file to implement the Common Audio Post Processor Interface + * PCM_CNV block + */ + +#include "capi_pcm_mf_cnv_utils.h" +#include "capi_pcm_mf_cnv_i.h" + +capi_err_t capi_pcm_mf_cnv_common_get_static_properties(capi_proplist_t * init_set_properties, + capi_proplist_t * static_properties, + capi_pcm_mf_cnv_type_t type); + +capi_err_t capi_pcm_mf_cnv_common_init(capi_t *_pif, capi_proplist_t *init_set_properties, capi_pcm_mf_cnv_type_t type); + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + Capi_v2 PCM_CNV function to get the static properties + ______________________________________________________________________________________________________________________*/ +capi_err_t capi_pcm_cnv_get_static_properties(capi_proplist_t *init_set_properties, capi_proplist_t *static_properties) +{ + // CNV_MSG(me_ptr->miid,DBG_HIGH_PRIO, "CNV get static properties called"); + return capi_pcm_mf_cnv_common_get_static_properties(init_set_properties, static_properties, CAPI_PCM_CONVERTER); +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + Initialize the CAPI PCM CNV Module. This function can allocate memory. + ____________________________________________________________________________________________________________________*/ +capi_err_t capi_pcm_cnv_init(capi_t *_pif, capi_proplist_t *init_set_properties) +{ + // CNV_MSG(me_ptr->miid,DBG_HIGH_PRIO, "CNV get static properties called"); + return capi_pcm_mf_cnv_common_init(_pif, init_set_properties, CAPI_PCM_CONVERTER); +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + Capi_v2 PCM ENC function to get the static properties + ____________________________________________________________________________________________________________________*/ +capi_err_t capi_pcm_enc_get_static_properties(capi_proplist_t *init_set_properties, capi_proplist_t *static_properties) +{ + // CNV_MSG(me_ptr->miid,DBG_HIGH_PRIO, "ENC get static properties called"); + return capi_pcm_mf_cnv_common_get_static_properties(init_set_properties, static_properties, CAPI_PCM_ENCODER); +} + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + Initialize the CAPI PCM ENC Module. This function can allocate memory. + ____________________________________________________________________________________________________________________*/ +capi_err_t capi_pcm_enc_init(capi_t *_pif, capi_proplist_t *init_set_properties) +{ + // CNV_MSG(me_ptr->miid,DBG_HIGH_PRIO, "ENC get static properties called"); + return capi_pcm_mf_cnv_common_init(_pif, init_set_properties, CAPI_PCM_ENCODER); +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + Capi_v2 PCM DEC function to get the static properties + ____________________________________________________________________________________________________________________*/ +capi_err_t capi_pcm_dec_get_static_properties(capi_proplist_t *init_set_properties, capi_proplist_t *static_properties) +{ + // CNV_MSG(me_ptr->miid,DBG_HIGH_PRIO, "DEC get static properties called"); + return capi_pcm_mf_cnv_common_get_static_properties(init_set_properties, static_properties, CAPI_PCM_DECODER); +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + Initialize the CAPI PCM DEC Module. This function can allocate memory. + ____________________________________________________________________________________________________________________*/ +capi_err_t capi_pcm_dec_init(capi_t *_pif, capi_proplist_t *init_set_properties) +{ + // CNV_MSG(me_ptr->miid,DBG_HIGH_PRIO, "DEC get static properties called"); + return capi_pcm_mf_cnv_common_init(_pif, init_set_properties, CAPI_PCM_DECODER); +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + Capi_v2 PCM_CNV function to get the static properties + ______________________________________________________________________________________________________________________*/ +capi_err_t capi_mfc_get_static_properties(capi_proplist_t *init_set_properties, capi_proplist_t *static_properties) +{ + // CNV_MSG(me_ptr->miid,DBG_HIGH_PRIO, "MFC get static properties called"); + return capi_pcm_mf_cnv_common_get_static_properties(init_set_properties, static_properties, CAPI_MFC); +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + Initialize the CAPI PCM CNV Module. This function can allocate memory. + ____________________________________________________________________________________________________________________*/ +capi_err_t capi_mfc_init(capi_t *_pif, capi_proplist_t *init_set_properties) +{ + // CNV_MSG(me_ptr->miid,DBG_HIGH_PRIO, "MFC get static properties called"); + return capi_pcm_mf_cnv_common_init(_pif, init_set_properties, CAPI_MFC); +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + Capi_v2 PCM_CNV function to get the static properties + ____________________________________________________________________________________________________________________*/ +capi_err_t capi_pcm_mf_cnv_common_get_static_properties(capi_proplist_t * init_set_properties, + capi_proplist_t * static_properties, + capi_pcm_mf_cnv_type_t type) +{ + capi_err_t capi_result = CAPI_EOK; + + if (NULL != static_properties) + { + capi_result = capi_pcm_mf_cnv_process_get_properties((capi_pcm_mf_cnv_t *)NULL, static_properties, type); + if (CAPI_FAILED(capi_result)) + { + // CNV_MSG(MIID_UNKNOWN, DBG_ERROR_PRIO, "[%d] get static properties failed!", type); + return capi_result; + } + } + else + { + CNV_MSG(MIID_UNKNOWN, + DBG_ERROR_PRIO, + " %d: Get static properties received bad pointer, 0x%p", + type, + static_properties); + } + + return capi_result; +} + +/*______________________________________________________________________________________________________________________ + + DESCRIPTION: + Initialize the CAPI PCM_CNV Module. This function can allocate memory. + ____________________________________________________________________________________________________________________*/ +capi_err_t capi_pcm_mf_cnv_common_init(capi_t *_pif, capi_proplist_t *init_set_properties, capi_pcm_mf_cnv_type_t type) +{ + capi_err_t capi_result = CAPI_EOK; + + if (NULL == _pif || NULL == init_set_properties) + { + CNV_MSG(MIID_UNKNOWN, + DBG_ERROR_PRIO, + " %d: Init received bad pointer, 0x%p, 0x%p", + type, + _pif, + init_set_properties); + + CAPI_SET_ERROR(capi_result, CAPI_EBADPARAM); + return capi_result; + } + + capi_pcm_mf_cnv_t *me_ptr = (capi_pcm_mf_cnv_t *)_pif; + memset(me_ptr, 0, sizeof(capi_pcm_mf_cnv_t)); + + me_ptr->vtbl.vtbl_ptr = capi_pcm_mf_cnv_get_vtable(); + + me_ptr->type = type; + + me_ptr->lib_enable = FALSE; + + if ((CAPI_PCM_DECODER == me_ptr->type)) + { + me_ptr->perf_mode = CAPI_PCM_PERF_MODE_LOW_LATENCY; + me_ptr->frame_size_us = 1000; + } + else + { + me_ptr->perf_mode = CAPI_PCM_PERF_MODE_INVALID; + me_ptr->frame_size_us = 0; + /* frame_size_type, frame_size_in_samples and frame_size_in_microsecond are set to zero at line 166 + while memsetting the me_ptr */ + } + + for (uint32_t i = 0; i < CAPI_PCM_CNV_MAX_IN_PORTS; i++) + { + capi_cmn_init_media_fmt_v2(&me_ptr->in_media_fmt[i]); + } + + for (uint32_t i = 0; i < CAPI_PCM_CNV_MAX_OUT_PORTS; i++) + { + capi_cmn_init_media_fmt_v2(&me_ptr->out_media_fmt[i]); + } + + for (uint32_t i = 0; i < CAPI_PCM_CNV_MAX_OUT_PORTS; i++) + { + me_ptr->configured_media_fmt[i].data_format = CAPI_MAX_FORMAT_TYPE; + me_ptr->configured_media_fmt[i].sampling_rate = PARAM_VAL_NATIVE; + me_ptr->configured_media_fmt[i].fmt.alignment = PARAM_VAL_NATIVE; + me_ptr->configured_media_fmt[i].fmt.bit_width = PARAM_VAL_NATIVE; + me_ptr->configured_media_fmt[i].fmt.endianness = PARAM_VAL_NATIVE; + me_ptr->configured_media_fmt[i].fmt.interleaved = PARAM_VAL_NATIVE; + me_ptr->configured_media_fmt[i].fmt.num_channels = PARAM_VAL_NATIVE; + me_ptr->configured_media_fmt[i].fmt.q_factor = PARAM_VAL_NATIVE; + me_ptr->configured_media_fmt[i].fmt.bits_per_sample = PARAM_VAL_NATIVE; + } + + if (NULL != init_set_properties) + { + capi_result |= capi_pcm_mf_cnv_process_set_properties(me_ptr, init_set_properties); + + if (CAPI_FAILED(capi_result)) + { + CNV_MSG(me_ptr->miid, DBG_ERROR_PRIO, "[%u] Initialization Set Property Failed", type); + return capi_result; + } + } + + if (CAPI_MFC == type) + { + me_ptr->dm_info.is_dm_disabled = TRUE; + capi_cmn_raise_dm_disable_event(&me_ptr->cb_info, me_ptr->miid, FWK_EXTN_DM_DISABLED_DM); + } + // CNV_MSG(me_ptr->miid,DBG_HIGH_PRIO, "[%u] Initialization completed !!", me_ptr->type); + + capi_result |= capi_cmn_raise_deinterleaved_unpacked_v2_supported_event(&me_ptr->cb_info); + + return capi_result; +} + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + PCM_CNV End function, returns the library to the uninitialized state and frees all the memory that was allocated. + This function also frees the virtual function table. + ____________________________________________________________________________________________________________________*/ +capi_err_t capi_pcm_mf_cnv_end(capi_t *_pif) +{ + capi_err_t capi_result = CAPI_EOK; + if (NULL == _pif) + { + CNV_MSG(MIID_UNKNOWN, DBG_ERROR_PRIO, "End received bad pointer, 0x%p", _pif); + CAPI_SET_ERROR(capi_result, CAPI_EBADPARAM); + return capi_result; + } + + capi_pcm_mf_cnv_t *me_ptr = (capi_pcm_mf_cnv_t *)(_pif); + capi_pcm_mf_cnv_mfc_deinit(me_ptr); + + CNV_MSG(me_ptr->miid, DBG_HIGH_PRIO, "End done"); + return capi_result; +} + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + Free coef sets pointer and clear the related + ____________________________________________________________________________________________________________________*/ +static void capi_free_coef_sets(capi_pcm_mf_cnv_t *me_ptr) +{ + // Freeing older memory + if (NULL != me_ptr->config.coef_sets_ptr) + { + posal_memory_free(me_ptr->config.coef_sets_ptr); + me_ptr->config.coef_sets_ptr = NULL; + me_ptr->config.num_coef_sets = 0; + me_ptr->coef_payload_size = 0; + } +} + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + Convert set param data format received to capi data format + ____________________________________________________________________________________________________________________*/ + +static capi_err_t capi_pcm_mf_cnv_map_dataformat_to_capi(param_id_pcm_output_format_cfg_t *header_ptr, + capi_pc_configured_mf_t * configured_media_fmt) +{ + + if (DATA_FORMAT_FIXED_POINT == header_ptr->data_format) + { + configured_media_fmt->data_format = CAPI_FIXED_POINT; + } + else if (DATA_FORMAT_FLOATING_POINT == header_ptr->data_format) + { + configured_media_fmt->data_format = CAPI_FLOATING_POINT; + } + else + { + return CAPI_EBADPARAM; + } + return CAPI_EOK; +} + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + Sets either a parameter value or a parameter structure containing multiple parameters. In the event of a failure, the + appropriate error code is returned. + ____________________________________________________________________________________________________________________*/ +capi_err_t capi_pcm_mf_cnv_set_param(capi_t * _pif, + uint32_t param_id, + const capi_port_info_t *port_info_ptr, + capi_buf_t * params_ptr) +{ + + capi_err_t capi_result = CAPI_EOK; + uint32_t port = 0; + if (NULL == _pif || NULL == params_ptr) + { + CNV_MSG(MIID_UNKNOWN, DBG_ERROR_PRIO, "set param received bad pointer, 0x%p, 0x%p", _pif, params_ptr); + return CAPI_SET_ERROR(capi_result, CAPI_EBADPARAM); + } + capi_pcm_mf_cnv_t *me_ptr = (capi_pcm_mf_cnv_t *)(_pif); + + switch (param_id) + { + case INTF_EXTN_PARAM_ID_DATA_PORT_OPERATION: + { + capi_result = capi_pcm_mf_cnv_handle_data_port_op(me_ptr, params_ptr); + break; + } + case FWK_EXTN_PCM_PARAM_ID_MEDIA_FORMAT_EXTN: + { + if (!((CAPI_PCM_CONVERTER == me_ptr->type) || (CAPI_PCM_DECODER == me_ptr->type) || + (CAPI_PCM_ENCODER == me_ptr->type))) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] FWK_EXTN_PCM_PARAM_ID_MEDIA_FORMAT_EXTN not supported by this type", + me_ptr->type); + return CAPI_EUNSUPPORTED; + } + + if ((TRUE == port_info_ptr->is_valid) && (TRUE != port_info_ptr->is_input_port)) + { + CAPI_SET_ERROR(capi_result, CAPI_EBADPARAM); + break; + } + + if (params_ptr->actual_data_len >= sizeof(fwk_extn_pcm_param_id_media_fmt_extn_t)) + { + fwk_extn_pcm_param_id_media_fmt_extn_t *extn_ptr = + (fwk_extn_pcm_param_id_media_fmt_extn_t *)(params_ptr->data_ptr); + + capi_pc_configured_mf_t *configured_media_fmt = &me_ptr->configured_media_fmt[port]; + + if (CAPI_PCM_ERROR == pcm_mf_cnv_is_supported_extn_media_type(extn_ptr)) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] Set param received bad param, 0x%p, alignment = %d, bit_width = %d, " + "endianness = %d", + me_ptr->type, + param_id, + extn_ptr->alignment, + extn_ptr->bit_width, + extn_ptr->endianness); + return CAPI_SET_ERROR(capi_result, CAPI_EBADPARAM); + } + + if (CAPI_DATA_FORMAT_INVALID_VAL != extn_ptr->bit_width) + { + me_ptr->extn_in_media_fmt[port].bit_width = extn_ptr->bit_width; + } + + me_ptr->extn_in_media_fmt[port].alignment = extn_ptr->alignment; + + if (CAPI_DATA_FORMAT_INVALID_VAL != extn_ptr->endianness) + { + me_ptr->extn_in_media_fmt[port].endianness = extn_ptr->endianness; + } + + if (PARAM_VAL_NATIVE == configured_media_fmt->fmt.bit_width) + { + me_ptr->extn_out_media_fmt[port].bit_width = me_ptr->extn_in_media_fmt[port].bit_width; + } + + if (PARAM_VAL_NATIVE == configured_media_fmt->fmt.alignment) + { + me_ptr->extn_out_media_fmt[port].alignment = me_ptr->extn_in_media_fmt[port].alignment; + } + + if (PARAM_VAL_NATIVE == configured_media_fmt->fmt.endianness) + { + me_ptr->extn_out_media_fmt[port].endianness = me_ptr->extn_in_media_fmt[port].endianness; + } + } + else + { + CAPI_SET_ERROR(capi_result, CAPI_ENEEDMORE); + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] set param failed because of length issues, 0x%p, 0x%p, in_len = %d, " + "needed_len " + "= %d", + me_ptr->type, + _pif, + param_id, + params_ptr->actual_data_len, + sizeof(fwk_extn_pcm_param_id_media_fmt_extn_t)); + } + break; + } + case FWK_EXTN_PARAM_ID_CONTAINER_FRAME_DURATION: + { + if (params_ptr->actual_data_len < sizeof(fwk_extn_param_id_container_frame_duration_t)) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "Param id 0x%lx Bad param size %lu", + param_id, + params_ptr->actual_data_len); + capi_result |= CAPI_ENEEDMORE; + break; + } + + fwk_extn_param_id_container_frame_duration_t *fm_dur = + (fwk_extn_param_id_container_frame_duration_t *)params_ptr->data_ptr; + + // This variable is updated for only for MFC to determine if it is a fractional resampling case + me_ptr->frame_size_us = fm_dur->duration_us; + + pc_set_cntr_frame_size(&me_ptr->pc[0], me_ptr->frame_size_us); + capi_pcm_mf_cnv_update_dm_disable(me_ptr); + break; + } + case FWK_EXTN_PARAM_ID_THRESHOLD_CFG: + { + if (params_ptr->actual_data_len < sizeof(fwk_extn_param_id_threshold_cfg_t)) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] Param id 0x%lx Bad param size %lu", + me_ptr->type, + (uint32_t)param_id, + params_ptr->actual_data_len); + return CAPI_EUNSUPPORTED; + } + fwk_extn_param_id_threshold_cfg_t *fm_dur = (fwk_extn_param_id_threshold_cfg_t *)params_ptr->data_ptr; + + // This variable is updated only for PCM DEC and PCM ENC to raise threshold based on SG perf mode + me_ptr->frame_size_us = fm_dur->duration_us; + CNV_MSG(me_ptr->miid, DBG_HIGH_PRIO, "[%u] Received frame_size_us = %ld", me_ptr->type, fm_dur->duration_us); + break; + } + case PARAM_ID_PCM_OUTPUT_FORMAT_CFG: + { + if (!((CAPI_PCM_CONVERTER == me_ptr->type) || (CAPI_PCM_DECODER == me_ptr->type) || + (CAPI_PCM_ENCODER == me_ptr->type))) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] PARAM_ID_PCM_OUTPUT_FORMAT_CFG not supported by this type", + me_ptr->type); + return CAPI_EUNSUPPORTED; + } + + if (params_ptr->actual_data_len >= sizeof(param_id_pcm_output_format_cfg_t)) + { + param_id_pcm_output_format_cfg_t *header_ptr = (param_id_pcm_output_format_cfg_t *)(params_ptr->data_ptr); + if (MEDIA_FMT_ID_PCM != header_ptr->fmt_id) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] Set: OUTPUT FORMAT CFG: Unsupported fmt_id %d provided", + me_ptr->type, + header_ptr->fmt_id); + CAPI_SET_ERROR(capi_result, CAPI_EFAILED); + return capi_result; + } + + payload_pcm_output_format_cfg_t *data_ptr = + (payload_pcm_output_format_cfg_t *)(params_ptr->data_ptr + sizeof(param_id_pcm_output_format_cfg_t)); + if (!pcm_mf_cnv_is_supported_out_media_type(data_ptr, header_ptr->data_format)) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] Set: OUTPUT FORMAT CFG: Unsupported parameters provided", + me_ptr->type); + CAPI_SET_ERROR(capi_result, CAPI_EFAILED); + return capi_result; + } + + capi_standard_data_format_v2_t * out_std_ptr = &me_ptr->out_media_fmt[port].format; + capi_standard_data_format_v2_t * inp_std_ptr = &me_ptr->in_media_fmt[port].format; + fwk_extn_pcm_param_id_media_fmt_extn_t *out_extn_ptr = &me_ptr->extn_out_media_fmt[port]; + fwk_extn_pcm_param_id_media_fmt_extn_t *inp_extn_ptr = &me_ptr->extn_in_media_fmt[port]; + capi_pc_configured_mf_t * configured_media_fmt = &me_ptr->configured_media_fmt[port]; + payload_pcm_output_format_cfg_t * fmt = &configured_media_fmt->fmt; + + pick_if_not_unset(&fmt->bit_width, data_ptr->bit_width); + pick_if_not_unset(&fmt->alignment, data_ptr->alignment); + pick_if_not_unset(&fmt->bits_per_sample, data_ptr->bits_per_sample); + pick_if_not_unset(&fmt->q_factor, data_ptr->q_factor); + pick_if_not_unset(&fmt->endianness, data_ptr->endianness); + pick_if_not_unset(&fmt->interleaved, data_ptr->interleaved); + pick_if_not_unset(&fmt->num_channels, data_ptr->num_channels); + + if ((PARAM_VAL_UNSET != fmt->num_channels) && (PARAM_VAL_NATIVE != fmt->num_channels)) + { + uint8_t *channel_mapping = (uint8_t *)(data_ptr + 1); + for (uint32_t ch = 0; ch < data_ptr->num_channels; ch++) + { + configured_media_fmt->channel_type[ch] = (uint16_t)channel_mapping[ch]; + } + } + + // important: module overwrites to V2 if configuration is set to V1. + + pcm_to_capi_interleaved_with_native(&out_std_ptr->data_interleaving, + fmt->interleaved, + inp_std_ptr->data_interleaving); + + pick_config_or_input(&out_std_ptr->bits_per_sample, fmt->bits_per_sample, inp_std_ptr->bits_per_sample); + pick_config_or_input(&out_std_ptr->q_factor, fmt->q_factor, inp_std_ptr->q_factor); + pick_config_or_input(&out_std_ptr->num_channels, fmt->num_channels, inp_std_ptr->num_channels); + pick_config_or_input(&out_extn_ptr->alignment, fmt->alignment, inp_extn_ptr->alignment); + pick_config_or_input(&out_extn_ptr->bit_width, fmt->bit_width, inp_extn_ptr->bit_width); + pick_config_or_input(&out_extn_ptr->endianness, fmt->endianness, inp_extn_ptr->endianness); + + if ((0 != out_std_ptr->num_channels) && (CAPI_DATA_FORMAT_INVALID_VAL != out_std_ptr->num_channels)) + { + uint16_t *src_channel_type = (PARAM_VAL_NATIVE == fmt->num_channels) + ? &me_ptr->in_media_fmt[port].channel_type[0] + : &configured_media_fmt->channel_type[0]; + + memscpy(&out_std_ptr->channel_type[0], + sizeof(capi_channel_type_t) * out_std_ptr->num_channels, + src_channel_type, + sizeof(capi_channel_type_t) * out_std_ptr->num_channels); + } + + out_std_ptr->data_is_signed = TRUE; // this param assumes data is signed. + + // Error for unsupported data format already caught above + capi_pcm_mf_cnv_map_dataformat_to_capi(header_ptr, configured_media_fmt); + + if (CAPI_FLOATING_POINT == configured_media_fmt->data_format) + { + if (((CAPI_PCM_CONVERTER != me_ptr->type) && (CAPI_PCM_DECODER != me_ptr->type) && + (CAPI_PCM_ENCODER != me_ptr->type)) || + (!pc_is_floating_point_data_format_supported())) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] Floating point data format not supported by this module/chipset", + me_ptr->type); + return CAPI_EUNSUPPORTED; + } + // If the out data format is floating point then q factor value would be invalid. + out_std_ptr->q_factor = CAPI_DATA_FORMAT_INVALID_VAL; + } + + // resampler_reinit is set to FALSE + capi_result |= capi_pcm_mf_cnv_lib_init(me_ptr, FALSE); + capi_result |= capi_pcm_mf_cnv_check_and_raise_output_media_format_event(me_ptr); + } + else + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] Param id 0x%lx Bad param size %lu", + me_ptr->type, + (uint32_t)param_id, + params_ptr->actual_data_len); + CAPI_SET_ERROR(capi_result, CAPI_ENEEDMORE); + } + break; + } + case PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT: + { + if (!(CAPI_MFC == me_ptr->type)) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT not supported by this type", + me_ptr->type); + return CAPI_EUNSUPPORTED; + } + + CNV_MSG(me_ptr->miid, DBG_HIGH_PRIO, "[%u] PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT received", me_ptr->type); + + if (params_ptr->actual_data_len >= sizeof(param_id_mfc_output_media_fmt_t)) + { + param_id_mfc_output_media_fmt_t *data_ptr = + reinterpret_cast(params_ptr->data_ptr); + + uint32_t required_size = + sizeof(param_id_mfc_output_media_fmt_t) + (data_ptr->num_channels * sizeof(uint16)); + if (params_ptr->max_data_len < required_size) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "MFC: MFC_PARAM_ID_OUTPUT_MEDIA_FORMAT invalid param size %lu, required_size %d", + params_ptr->max_data_len, + required_size); + CAPI_SET_ERROR(capi_result, CAPI_ENEEDMORE); + break; + } + + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "[%u] PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT SR: %lu CH: %lu BW: %lu ", + me_ptr->type, + data_ptr->sampling_rate, + data_ptr->num_channels, + data_ptr->bit_width); + + capi_pc_configured_mf_t * configured_media_fmt = &me_ptr->configured_media_fmt[port]; + capi_standard_data_format_v2_t *out_std_ptr = &me_ptr->out_media_fmt[port].format; + capi_standard_data_format_v2_t *inp_std_ptr = &me_ptr->in_media_fmt[port].format; + + // Sampling rate + if (((384000 >= data_ptr->sampling_rate) && (0 < data_ptr->sampling_rate)) || + (PARAM_VAL_NATIVE == data_ptr->sampling_rate)) + { + // Valid or native input + configured_media_fmt->sampling_rate = data_ptr->sampling_rate; + } + else if (PARAM_VAL_UNSET != data_ptr->sampling_rate) + { + // Invalid input, abort + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "MFC: Sampling rate must be > 0 and <= 384kHz. Received %lu", + data_ptr->sampling_rate); + CAPI_SET_ERROR(capi_result, CAPI_EFAILED); + break; + } + + // Channels + if (((CAPI_MAX_CHANNELS_V2 >= data_ptr->num_channels) && (0 < data_ptr->num_channels)) || + (PARAM_VAL_NATIVE == data_ptr->num_channels)) + { + // valid or native + configured_media_fmt->fmt.num_channels = data_ptr->num_channels; + } + else if (PARAM_VAL_UNSET != data_ptr->num_channels) + { + // Invalid input, abort + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "MFC: Number of channels must be between > 0 and <= %d. Received %lu", + CAPI_MAX_CHANNELS_V2, + data_ptr->num_channels); + CAPI_SET_ERROR(capi_result, CAPI_EFAILED); + break; + } + + bool_t ch_type_unsup = FALSE; + // This check should be done only if channels are valid i.e. not 0 or native(-1) or unset(-2) + if ((PARAM_VAL_NATIVE != data_ptr->num_channels) && (PARAM_VAL_UNSET != data_ptr->num_channels)) + { + for (uint32_t i = 0; i < data_ptr->num_channels; i++) + { + if ((data_ptr->channel_type[i] < (uint16_t)PCM_CHANNEL_L) || + (data_ptr->channel_type[i] > (uint16_t)PCM_MAX_CHANNEL_MAP_V2)) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "MFC: Unsupported channel type channel idx %d, channel type %d received", + (int)i, + (int)data_ptr->channel_type[i]); + ch_type_unsup = TRUE; + break; + } + } + if (ch_type_unsup) + { + CAPI_SET_ERROR(capi_result, CAPI_EFAILED); + break; + } + } + + if ((CAPI_MAX_CHANNELS_V2 >= data_ptr->num_channels) && (0 < data_ptr->num_channels)) + { + for (uint32_t ch = 0; ch < data_ptr->num_channels; ch++) + { + configured_media_fmt->channel_type[ch] = (uint16_t)data_ptr->channel_type[ch]; + } + } + + // Bitwidth + if (((16 == data_ptr->bit_width) || (24 == data_ptr->bit_width) || (32 == data_ptr->bit_width) || + (PARAM_VAL_NATIVE == data_ptr->bit_width))) + { + configured_media_fmt->fmt.bit_width = data_ptr->bit_width; + } + else if (PARAM_VAL_UNSET != data_ptr->bit_width) + { + // Invalid input, abort + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "MFC: Bit width must either be 16 or 24 or 32 bits per sample. Received %lu", + data_ptr->bit_width); + CAPI_SET_ERROR(capi_result, CAPI_EFAILED); + break; + } + + pick_config_or_input(&out_std_ptr->sampling_rate, + configured_media_fmt->sampling_rate, + inp_std_ptr->sampling_rate); + + pick_config_or_input(&out_std_ptr->num_channels, + configured_media_fmt->fmt.num_channels, + inp_std_ptr->num_channels); + + if ((0 != out_std_ptr->num_channels) && (CAPI_DATA_FORMAT_INVALID_VAL != out_std_ptr->num_channels)) + { + uint16_t *src_channel_type = (PARAM_VAL_NATIVE == configured_media_fmt->fmt.num_channels) + ? &me_ptr->in_media_fmt[port].channel_type[0] + : &configured_media_fmt->channel_type[0]; + + memscpy(&out_std_ptr->channel_type[0], + sizeof(capi_channel_type_t) * out_std_ptr->num_channels, + src_channel_type, + sizeof(capi_channel_type_t) * out_std_ptr->num_channels); + } + + capi_pcm_mf_cnv_mfc_set_output_bps_qf(configured_media_fmt); + + pick_config_or_input(&out_std_ptr->bits_per_sample, + configured_media_fmt->fmt.bits_per_sample, + inp_std_ptr->bits_per_sample); + + // set corresponding q factor and bits per sample + // In this function out_std_ptr->bits_per_sample changes + pick_config_or_input(&out_std_ptr->q_factor, configured_media_fmt->fmt.q_factor, inp_std_ptr->q_factor); + + // for internal checks if bw is set, use it else copy bw derived from input mf + if (PARAM_VAL_NATIVE == configured_media_fmt->fmt.bit_width) // bit-width + { + me_ptr->extn_out_media_fmt[port].bit_width = me_ptr->extn_in_media_fmt[port].bit_width; + } + else + { + me_ptr->extn_out_media_fmt[port].bit_width = configured_media_fmt->fmt.bit_width; + } + + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "Output bw set =%lu, bw configured %lu, inp extn bw %lu", + me_ptr->extn_out_media_fmt[port].bit_width, + configured_media_fmt->fmt.bits_per_sample, + me_ptr->extn_in_media_fmt[port].bit_width); + + me_ptr->extn_out_media_fmt[port].alignment = PC_LSB_ALIGNED; + me_ptr->extn_out_media_fmt[port].endianness = PC_LITTLE_ENDIAN; + + out_std_ptr->data_is_signed = TRUE; // this param assumes data is signed. + + // resampler_reinit is set to FALSE + capi_result |= capi_pcm_mf_cnv_lib_init(me_ptr, FALSE); + + if (me_ptr->input_media_fmt_received) + { // raise only if we have the full MF + capi_result |= capi_pcm_mf_cnv_check_and_raise_output_media_format_event(me_ptr); + } + else if ((PARAM_VAL_NATIVE != configured_media_fmt->sampling_rate) && + (PARAM_VAL_NATIVE != configured_media_fmt->fmt.num_channels) && + (PARAM_VAL_NATIVE != configured_media_fmt->fmt.bits_per_sample)) + { + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "[%u] Raising out mf because received all valid fields, " + "eventhough input mf is not yet received", + me_ptr->type); + + // Implies all valid fields and that out qfactor and channel types are also correctly set + // Setting other fields assuming defaults + out_std_ptr->bitstream_format = MEDIA_FMT_ID_PCM; + + // MFC only supports unpacked, hence + out_std_ptr->data_interleaving = CAPI_DEINTERLEAVED_UNPACKED_V2; + + out_std_ptr->data_is_signed = TRUE; + out_std_ptr->minor_version = CAPI_MEDIA_FORMAT_MINOR_VERSION; + capi_result |= capi_pcm_mf_cnv_check_and_raise_output_media_format_event(me_ptr); + } + + CNV_MSG(me_ptr->miid, DBG_HIGH_PRIO, "[%u] Set: MFC_PARAM_ID_OUTPUT_MEDIA_FORMAT: Done", me_ptr->type); + } + else + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "CAPI PCM_CNV: MFC_PARAM_ID_OUTPUT_MEDIA_FORMAT invalid param size %lu", + params_ptr->max_data_len); + capi_result = CAPI_ENEEDMORE; + } + break; + } + case PARAM_ID_CHMIXER_COEFF: + { + CNV_MSG(me_ptr->miid, DBG_HIGH_PRIO, "[%u] PARAM_ID_CHMIXER_COEFF called", me_ptr->type); + + uint32_t param_size = params_ptr->actual_data_len; + if (param_size >= sizeof(param_id_chmixer_coeff_t)) + { + size_t req_param_size = 0, req_param_size_al = 0; + uint32_t index = 0, total_payload_size = 0, total_payload_aligned_size = 0; + + const param_id_chmixer_coeff_t *const p_param = (param_id_chmixer_coeff_t *)params_ptr->data_ptr; + + param_size = param_size - sizeof(param_id_chmixer_coeff_t); + + // error check for coefficients index + if (0 == p_param->num_coeff_tbls) + { + CNV_MSG(me_ptr->miid, DBG_ERROR_PRIO, "[%u] Set param received zero coefficients", me_ptr->type); + + // Freeing older memory + capi_free_coef_sets(me_ptr); + + // resampler_reinit is set to FALSE + capi_result |= capi_pcm_mf_cnv_lib_init(me_ptr, FALSE); + break; + } + + // pointer to the first array + uint8_t *first_data_ptr = (uint8_t *)(sizeof(param_id_chmixer_coeff_t) + params_ptr->data_ptr); + uint8_t *new_coef_arr_ptr = first_data_ptr; + + if (param_size < sizeof(chmixer_coeff_t)) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] Set coeff, Bad param size %lu, required param size %lu", + me_ptr->type, + param_size, + sizeof(chmixer_coeff_t)); + return CAPI_ENEEDMORE; + } + chmixer_coeff_t *coeff_param_ptr = (chmixer_coeff_t *)first_data_ptr; + + // Size and error check loop + for (index = 0; index < p_param->num_coeff_tbls; index++) + { + // Size of each tbl + req_param_size = + sizeof(chmixer_coeff_t) + (sizeof(uint16_t) * coeff_param_ptr->num_output_channels) + + (sizeof(uint16_t) * coeff_param_ptr->num_input_channels) + + (sizeof(int16_t) * coeff_param_ptr->num_output_channels * coeff_param_ptr->num_input_channels); + + // if req param size is not 4byte align add padding len and store in req_param_size_aligned + req_param_size_al = ALIGN_4_BYTES(req_param_size); + + if (param_size < req_param_size_al) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] Set coeff, Bad param size %lu, required param size %lu", + me_ptr->type, + param_size, + req_param_size_al); + CAPI_SET_ERROR(capi_result, CAPI_ENEEDMORE); + return capi_result; + } + + // error check for num channels + if ((coeff_param_ptr->num_input_channels > CAPI_MAX_CHANNELS_V2) || + (0 == coeff_param_ptr->num_input_channels) || + (coeff_param_ptr->num_output_channels > CAPI_MAX_CHANNELS_V2) || + (0 == coeff_param_ptr->num_output_channels)) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] Set coeff, invalid number of channels. inp ch %hu, out ch %hu", + me_ptr->type, + coeff_param_ptr->num_input_channels, + coeff_param_ptr->num_output_channels); + return CAPI_EBADPARAM; + } + + const uint16_t *out_ch_map = (uint16_t *)((uint8_t *)coeff_param_ptr + sizeof(chmixer_coeff_t)); + const uint16_t *in_ch_map = out_ch_map + coeff_param_ptr->num_output_channels; + + // error check for channel type + capi_result |= capi_pcm_mf_cnv_check_ch_type(me_ptr, in_ch_map, coeff_param_ptr->num_input_channels); + capi_result |= capi_pcm_mf_cnv_check_ch_type(me_ptr, out_ch_map, coeff_param_ptr->num_output_channels); + if (CAPI_FAILED(capi_result)) + { + CNV_MSG(me_ptr->miid, DBG_ERROR_PRIO, "[%u] Set coeff, invalid channel type", me_ptr->type); + return capi_result; + } + + // Update size + param_size = param_size - req_param_size_al; + + // Update param size and ptrs to point to the next coeff array provided its not last coefficient payload + if (index != (p_param->num_coeff_tbls - 1)) + { + if (param_size < sizeof(chmixer_coeff_t)) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] Set coeff, Bad param size %lu, required param size %lu", + me_ptr->type, + param_size, + sizeof(chmixer_coeff_t)); + return CAPI_ENEEDMORE; + } + + // update to move to the next array + new_coef_arr_ptr = (uint8_t *)(new_coef_arr_ptr + req_param_size_al); + coeff_param_ptr = (chmixer_coeff_t *)(new_coef_arr_ptr); + } + + // Update total payload size + total_payload_size += req_param_size; + total_payload_aligned_size += req_param_size_al; + } + + if (me_ptr->coef_payload_size != total_payload_size) + { + // Freeing older memory + capi_free_coef_sets(me_ptr); + + uint32_t size_to_malloc = + total_payload_size + + (p_param->num_coeff_tbls * (sizeof(capi_pcm_mf_cnv_chmixer_coef_set_t) - sizeof(chmixer_coeff_t))); + + me_ptr->config.coef_sets_ptr = + (capi_pcm_mf_cnv_chmixer_coef_set_t *)posal_memory_malloc(size_to_malloc, + (POSAL_HEAP_ID)me_ptr->heap_info.heap_id); + if (NULL == me_ptr->config.coef_sets_ptr) + { + CNV_MSG(me_ptr->miid, DBG_ERROR_PRIO, "[%u] Set param failed to allocate memory", me_ptr->type); + return CAPI_ENOMEMORY; + } + } + + // For first iteration + // Start of chmixer channel arrays + uint8_t *new_coeff_arr = (uint8_t *)me_ptr->config.coef_sets_ptr; + // start of channel maps + uint8_t *new_ch_map_arr = + new_coeff_arr + (p_param->num_coeff_tbls * sizeof(capi_pcm_mf_cnv_chmixer_coef_set_t)); + + // reset coeff param ptr to point to start of first array input cfg + coeff_param_ptr = (chmixer_coeff_t *)first_data_ptr; + + // Processing loop + for (index = 0; index < p_param->num_coeff_tbls; index++) + { + uint32_t padding = 0; + // Size of each tbl + uint32_t tbl_size = + (sizeof(uint16_t) * coeff_param_ptr->num_output_channels) + + (sizeof(uint16_t) * coeff_param_ptr->num_input_channels) + + (sizeof(int16_t) * coeff_param_ptr->num_output_channels * coeff_param_ptr->num_input_channels); + + if (0 != (tbl_size % 4)) + { + padding = 1; // it can only be 2bytes less since every entry is 2bytes + } + + // input ptrs + const uint16_t *out_ch_map = (uint16_t *)((uint8_t *)coeff_param_ptr + sizeof(chmixer_coeff_t)); + const uint16_t *in_ch_map = out_ch_map + coeff_param_ptr->num_output_channels; + const int16_t * coef_ptr = (int16_t *)(in_ch_map + coeff_param_ptr->num_input_channels); + + // Assign pointers after malloc + capi_pcm_mf_cnv_chmixer_coef_set_t *coef_set = (capi_pcm_mf_cnv_chmixer_coef_set_t *)new_coeff_arr; + coef_set->num_in_ch = coeff_param_ptr->num_input_channels; + coef_set->num_out_ch = coeff_param_ptr->num_output_channels; + + coef_set->out_ch_map = (uint16_t *)(new_ch_map_arr); + coef_set->in_ch_map = (uint16_t *)(coef_set->out_ch_map + coef_set->num_out_ch); + coef_set->coef_ptr = (int16_t *)(coef_set->in_ch_map + coef_set->num_in_ch); + +#ifdef PCM_CNV_DEBUG + + uint8_t i = 0; + uint8_t j = 0; + + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "[%u] SET PARAM FOR COEFF: Inp Number of Channels %lu", + me_ptr->type, + coeff_param_ptr->num_input_channels); + + for (i = 0; i < coef_set->num_in_ch; i++) + { + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "[%u] Inp Channel_Type[%hhu]: %d", + me_ptr->type, + i, + in_ch_map[i]); + } + + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "[%u] Out Number of Channels %lu", + me_ptr->type, + coeff_param_ptr->num_output_channels); + + for (j = 0; j < coef_set->num_out_ch; j++) + { + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "[%u] Out Channel_Type[%hhu]: %d", + me_ptr->type, + j, + out_ch_map[j]); + } + + uint8_t a = i * j; + for (j = 0; j < a; j++) + { + CNV_MSG(me_ptr->miid, DBG_HIGH_PRIO, "[%u] coefficients: %x", me_ptr->type, *(coef_ptr + j)); + } +#endif + // Copy values + uint32_t size_to_cpy = + ((coef_set->num_in_ch * sizeof(uint16_t)) + (coef_set->num_out_ch * sizeof(uint16_t)) + + (coef_set->num_in_ch * coef_set->num_out_ch * sizeof(int16_t))); + + memscpy(coef_set->out_ch_map, size_to_cpy, out_ch_map, size_to_cpy); + + // Update in and out params to point to the next coeff array provided its not last coefficient payload + if (index != (p_param->num_coeff_tbls - 1)) + { + coeff_param_ptr = (chmixer_coeff_t *)(coef_ptr + (coeff_param_ptr->num_output_channels * + coeff_param_ptr->num_input_channels) + + padding); + // point to next array + new_coeff_arr = new_coeff_arr + sizeof(capi_pcm_mf_cnv_chmixer_coef_set_t); + // point to next map + new_ch_map_arr = (uint8_t *)(coef_set->coef_ptr + (coef_set->num_out_ch * coef_set->num_in_ch)); + } + } // end of for loop + + // Update number of coeff sets + me_ptr->config.num_coef_sets = p_param->num_coeff_tbls; + + // Update data length + params_ptr->actual_data_len = total_payload_aligned_size; + me_ptr->coef_payload_size = total_payload_size; + + // resampler_reinit is set to FALSE + capi_result |= capi_pcm_mf_cnv_lib_init(me_ptr, FALSE); + } + else + { + CNV_MSG(me_ptr->miid, DBG_ERROR_PRIO, "[%u] Set coeff, Bad param size %lu", me_ptr->type, param_size); + CAPI_SET_ERROR(capi_result, CAPI_ENEEDMORE); + break; + } + break; + } + case PARAM_ID_MFC_RESAMPLER_CFG: + { + if (CAPI_MFC != me_ptr->type) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] PARAM_ID_MFC_RESAMPLER_CFG not supported by this type", + me_ptr->type); + return CAPI_EUNSUPPORTED; + } + + if (params_ptr->actual_data_len < sizeof(param_id_mfc_resampler_cfg_t)) + { + CNV_MSG(me_ptr->miid, DBG_ERROR_PRIO, "CAPI PCM_CNV: Bad param size %lu", params_ptr->actual_data_len); + CAPI_SET_ERROR(capi_result, CAPI_ENEEDMORE); + break; + } + + param_id_mfc_resampler_cfg_t *rs_config = (param_id_mfc_resampler_cfg_t *)(params_ptr->data_ptr); + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "[%u] Received PARAM_ID_MFC_RESAMPLER_CFG type %d (0:FIR_RESAMPLER, 1:IIR_RESAMPLER, 2: " + "IIR_PREFERRED), use hw rs: %d dynamic mode %d, delay type %d", + me_ptr->type, + rs_config->resampler_type, + rs_config->use_hw_rs, + rs_config->dynamic_mode, + rs_config->delay_type); + + // Only allow iir resampler to be configured after start if not in dm mode + // dm mode indirectly checks valid sampling rates AND capi_pcm_mf_cnv_is_fractional_media_type + if ((IIR_RESAMPLER == rs_config->resampler_type) && (capi_pcm_mf_cnv_is_dm_enabled(me_ptr))) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "Error! Cannot use iir resampler when dm is enabled, module disabling itself"); + // disable module + me_ptr->lib_enable = FALSE; + capi_pcm_mf_cnv_raise_process_check_event(me_ptr); + pc_deinit((pc_lib_t *)&me_ptr->pc[port]); + return CAPI_EUNSUPPORTED; + } + + // Step 1: If iir preferred is selected, set flag and decide the rs type for now + if (IIR_PREFERRED == rs_config->resampler_type) + { + me_ptr->pc[0].iir_pref_set = TRUE; + + // If media formats are already configured and we know dm mode is true, fallback to FIR + // Otherwise we set to IIR + if (capi_pcm_mf_cnv_is_dm_enabled(me_ptr)) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "Cannot use iir resampler when dm is enabled. " + "Since rs type is IIR_PREFERRED, falling back to sw fir resampler"); + + // Change rs type + rs_config->resampler_type = FIR_RESAMPLER; + } + else + { + // Set to IIR, if mfs dont match later in pc lib we will take care + rs_config->resampler_type = IIR_RESAMPLER; + } + } + else + { + me_ptr->pc[0].iir_pref_set = FALSE; + } + + bool_t fir_iir_rs_switch = FALSE; + bool_t enter_lib_init = FALSE; + + // Step 2: if resampler type changed between IIR and FIR, have to reinit so we call lib init + // By this time resampler type has already been decided as either IIR or FIR + if (rs_config->resampler_type != me_ptr->pc[0].resampler_type) + { + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "[%u] PARAM_ID_MFC_RESAMPLER_CFG: Switching to resampler type %d (0:FIR_RESAMPLER, 1:IIR_RESAMPLER " + ")", + me_ptr->type, + rs_config->resampler_type); + fir_iir_rs_switch = TRUE; + enter_lib_init = TRUE; + } + + /*Step 3: Check to prevent resampler from re-init if same cfg is received. + * If FIR resampler: call lib init only if + a)use hw rs flag changed b) dyn mode is different c) in dyn mode delay type is diff */ + else if ((FIR_RESAMPLER == rs_config->resampler_type) && + ((me_ptr->pc[0].use_hw_rs != rs_config->use_hw_rs) || + (me_ptr->pc[0].dynamic_mode != rs_config->dynamic_mode) || + ((1 == rs_config->dynamic_mode) && (me_ptr->pc[0].delay_type != rs_config->delay_type)))) + { + + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "[%u] PARAM_ID_MFC_RESAMPLER_CFG: Config changed, entering lib init", + me_ptr->type); + enter_lib_init = TRUE; + } + + // saving new config it in capi after comparing + me_ptr->pc[0].resampler_type = (pc_resampler_type_t)rs_config->resampler_type; + me_ptr->pc[0].use_hw_rs = rs_config->use_hw_rs; + me_ptr->pc[0].dynamic_mode = rs_config->dynamic_mode; + me_ptr->pc[0].delay_type = rs_config->delay_type; + + // If enter lib init is set to true, sending in fir_iir_rs_switch flag for reordering logic inside + if (enter_lib_init) + { + capi_result |= capi_pcm_mf_cnv_lib_init(me_ptr, fir_iir_rs_switch); + } + + break; + } + case FWK_EXTN_DM_PARAM_ID_CHANGE_MODE: + { + // if iir was set first it will fail now, in start + // If iir pref was set, by now it should have already got set to iir or fir + if ((CAPI_MFC != me_ptr->type) || (FIR_RESAMPLER != me_ptr->pc[0].resampler_type)) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] FWK_EXTN_DM_PARAM_ID_CHANGE_MODE not supported by this type, rs type %d", + me_ptr->type, + me_ptr->pc[0].resampler_type); + return CAPI_EUNSUPPORTED; + } + fwk_extn_dm_param_id_change_mode_t *mode_ptr = (fwk_extn_dm_param_id_change_mode_t *)params_ptr->data_ptr; + + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "[%u] Set param received FWK_EXTN_DM_PARAM_ID_CHANGE_MODE with mode = %d", + me_ptr->type, + mode_ptr->dm_mode); + + if ((FWK_EXTN_DM_FIXED_INPUT_MODE == mode_ptr->dm_mode) || + (FWK_EXTN_DM_FIXED_OUTPUT_MODE == mode_ptr->dm_mode)) + { + if (!capi_pcm_mf_cnv_is_dm_enabled(me_ptr) && (me_ptr->pc[0].hwsw_rs_lib_ptr)) + { + // enable the dm mode for FIR resampler + me_ptr->dm_info.is_dm_disabled = FALSE; + capi_cmn_raise_dm_disable_event(&me_ptr->cb_info, me_ptr->miid, FWK_EXTN_DM_ENABLED_DM); + } + + if (me_ptr->pc[0].hwsw_rs_lib_ptr) + { + // DM config should be reset at this point + // when module receives FWK_EXTN_DM_PARAM_ID_SET_SAMPLES setparam then only it should move to fixed-out + // processing. + me_ptr->dm_info.req_out_samples = 0; + me_ptr->dm_info.expected_in_samples = 0; + hwsw_rs_lib_set_dm_config(me_ptr->pc[0].hwsw_rs_lib_ptr, + FWK_EXTN_DM_FIXED_INPUT_MODE, // yes, fixed-input + me_ptr->dm_info.req_out_samples); + } + me_ptr->dm_info.dm_mode = mode_ptr->dm_mode; + } + else + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "CAPI PCM_CNV: Set param id 0x%lx, invalid payload %lu", + param_id, + mode_ptr->dm_mode); + CAPI_SET_ERROR(capi_result, CAPI_ENEEDMORE); + } + break; + } + case FWK_EXTN_DM_PARAM_ID_SET_SAMPLES: + case FWK_EXTN_DM_PARAM_ID_SET_MAX_SAMPLES: + { + if ((CAPI_MFC != me_ptr->type) || (FIR_RESAMPLER != me_ptr->pc[0].resampler_type) || + !capi_pcm_mf_cnv_is_dm_enabled(me_ptr)) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] FWK_EXTN_DM_PARAM_ID_SET_SAMPLES/SET_MAX_SAMPLES not supported by this " + "type,rs type %d is_dm_enabled %d", + me_ptr->type, + me_ptr->pc[0].resampler_type, + capi_pcm_mf_cnv_is_dm_enabled(me_ptr)); + return CAPI_EUNSUPPORTED; + } + + if (!me_ptr->input_media_fmt_received) + { + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "[%u] Inp media fmt not received yet, cannot set DM samples", + me_ptr->type); + return CAPI_ENOTREADY; + } + + fwk_extn_dm_param_id_req_samples_t *req_samples_ptr = + (fwk_extn_dm_param_id_req_samples_t *)params_ptr->data_ptr; + // only single port for now + if (CAPI_PCM_CNV_MAX_IN_PORTS != req_samples_ptr->num_ports) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] Set param id 0x%lx, mismatched port count %lu", + me_ptr->type, + param_id, + req_samples_ptr->num_ports); + CAPI_SET_ERROR(capi_result, CAPI_EBADPARAM); + break; + } + + if ((FWK_EXTN_DM_FIXED_OUTPUT_MODE == me_ptr->dm_info.dm_mode) && (!req_samples_ptr->is_input)) + { + if (FWK_EXTN_DM_PARAM_ID_SET_SAMPLES == param_id) + { + // handle fixed output + // get samples needed, port index can be ignored for single port + me_ptr->dm_info.req_out_samples = req_samples_ptr->req_samples[0].samples_per_channel; + capi_result |= capi_pcm_mf_cnv_get_fixed_out_samples(me_ptr); + + // set to lib + hwsw_rs_lib_set_dm_config(me_ptr->pc[0].hwsw_rs_lib_ptr, + me_ptr->dm_info.dm_mode, + me_ptr->dm_info.req_out_samples); + } + else + { + capi_result |= + capi_pcm_mf_cnv_get_max_in_samples(me_ptr, req_samples_ptr->req_samples[0].samples_per_channel); + } + } + else if ((FWK_EXTN_DM_FIXED_INPUT_MODE == me_ptr->dm_info.dm_mode) && (req_samples_ptr->is_input)) + { + me_ptr->dm_info.req_out_samples = 0; // wont be used inside lib + hwsw_rs_lib_set_dm_config(me_ptr->pc[0].hwsw_rs_lib_ptr, + me_ptr->dm_info.dm_mode, + me_ptr->dm_info.req_out_samples); + } + else + { + // error + CAPI_SET_ERROR(capi_result, CAPI_EBADPARAM); + } + break; + } + + case FWK_EXTN_DM_PARAM_ID_CONSUME_PARTIAL_INPUT: + { + if (params_ptr->actual_data_len < sizeof(fwk_extn_dm_param_id_consume_partial_input_t)) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "CAPI PCM_DEC: Param id 0x%lx Bad param size %lu", + (uint32_t)param_id, + params_ptr->actual_data_len); + capi_result |= CAPI_ENEEDMORE; + break; + } + fwk_extn_dm_param_id_consume_partial_input_t *payload_ptr = + (fwk_extn_dm_param_id_consume_partial_input_t *)params_ptr->data_ptr; + me_ptr->dm_info.should_consume_partial_input = payload_ptr->should_consume_partial_input; + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "CAPI PCM_DEC: Receieved FWK_EXTN_DM_PARAM_ID_CONSUME_PARTIAL_INPUT = %lu", + payload_ptr->should_consume_partial_input); + + pc_set_consume_partial_input(&me_ptr->pc[0], me_ptr->dm_info.should_consume_partial_input); + + break; + } + case PARAM_ID_REMOVE_INITIAL_SILENCE: + { + if (params_ptr->actual_data_len < sizeof(param_id_remove_initial_silence_t)) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "CAPI PCM_DEC: Param id 0x%lx Bad param size %lu", + (uint32_t)param_id, + params_ptr->actual_data_len); + capi_result |= CAPI_ENEEDMORE; + break; + } + param_id_remove_initial_silence_t *payload_ptr = (param_id_remove_initial_silence_t *)params_ptr->data_ptr; + me_ptr->initial_silence_in_samples = payload_ptr->samples_per_ch_to_remove + PCM_DECODER_DELAY; + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "CAPI PCM_DEC: Receieved PARAM_ID_REMOVE_INITIAL_SILENCE %lu + %lu samples per ch ", + payload_ptr->samples_per_ch_to_remove, + PCM_DECODER_DELAY); + break; + } + case PARAM_ID_REMOVE_TRAILING_SILENCE: + { + if (params_ptr->actual_data_len < sizeof(param_id_remove_trailing_silence_t)) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "CAPI PCM_DEC: Param id 0x%lx Bad param size %lu", + (uint32_t)param_id, + params_ptr->actual_data_len); + capi_result |= CAPI_ENEEDMORE; + break; + } + param_id_remove_trailing_silence_t *payload_ptr = (param_id_remove_trailing_silence_t *)params_ptr->data_ptr; + me_ptr->trailing_silence_in_samples = payload_ptr->samples_per_ch_to_remove > PCM_TRAILING_DELAY + ? (payload_ptr->samples_per_ch_to_remove - PCM_TRAILING_DELAY) + : 0; + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "CAPI PCM_DEC: Receieved PARAM_ID_REMOVE_TRAILING_SILENCE %lu - %lu samples per ch", + payload_ptr->samples_per_ch_to_remove, + PCM_TRAILING_DELAY); + break; + } + case PARAM_ID_PCM_ENCODER_FRAME_SIZE: + { + if (CAPI_PCM_ENCODER != me_ptr->type) + { + CNV_MSG(me_ptr->miid, DBG_ERROR_PRIO, "PARAM_ID_PCM_ENCODER_FRAME_SIZE is only supported for pcm encoder"); + return CAPI_EUNSUPPORTED; + } + if (params_ptr->max_data_len >= sizeof(param_id_pcm_encoder_frame_size_t)) + { + param_id_pcm_encoder_frame_size_t *frame_size_cfg_ptr = + (param_id_pcm_encoder_frame_size_t *)params_ptr->data_ptr; + + if (((PCM_ENCODER_CONFIG_FRAME_SIZE_IN_SAMPLES == frame_size_cfg_ptr->frame_size_type) && + (frame_size_cfg_ptr->frame_size_in_samples > 0) && + (frame_size_cfg_ptr->frame_size_in_samples <= PCM_ENCODER_CONFIG_MAX_FRAME_SIZE_IN_SAMPLES)) || + ((PCM_ENCODER_CONFIG_FRAME_SIZE_IN_MICROSECONDS == frame_size_cfg_ptr->frame_size_type) && + (frame_size_cfg_ptr->frame_size_in_us > 0) && + (frame_size_cfg_ptr->frame_size_in_us <= PCM_ENCODER_CONFIG_MAX_FRAME_SIZE_IN_MICROSECOND))) + { + me_ptr->frame_size = *frame_size_cfg_ptr; + } + else + { + CNV_MSG(me_ptr->miid, DBG_ERROR_PRIO, "Received bad param for PARAM_ID_PCM_ENCODER_FRAME_SIZE"); + return CAPI_EBADPARAM; + } + if (me_ptr->input_media_fmt_received) + { + capi_result |= capi_pcm_encoder_update_frame_size(me_ptr); + } + } + else + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] Get param id 0x%lx, invalid payload size %lu", + me_ptr->type, + param_id, + params_ptr->actual_data_len); + capi_result = CAPI_ENEEDMORE; + } + break; + } + + default: + CNV_MSG(me_ptr->miid, DBG_ERROR_PRIO, "[%u] Set, unsupported param ID 0x%x", me_ptr->type, (int)param_id); + CAPI_SET_ERROR(capi_result, CAPI_EUNSUPPORTED); + break; + } + return capi_result; +} + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + Gets either a parameter value or a parameter structure containing multiple parameters. In the event of a failure, + the + appropriate error code is returned. + ____________________________________________________________________________________________________________________*/ +capi_err_t capi_pcm_mf_cnv_get_param(capi_t * _pif, + uint32_t param_id, + const capi_port_info_t *port_info_ptr, + capi_buf_t * params_ptr) +{ + capi_err_t capi_result = CAPI_EOK; + uint32_t port = 0; + if (NULL == _pif || NULL == params_ptr) + { + CNV_MSG(MIID_UNKNOWN, DBG_ERROR_PRIO, "Get param received bad pointer, 0x%p, 0x%p", _pif, params_ptr); + return CAPI_EBADPARAM; + } + + capi_pcm_mf_cnv_t *me_ptr = (capi_pcm_mf_cnv_t *)_pif; + + switch (param_id) + { + case FWK_EXTN_PCM_PARAM_ID_MEDIA_FORMAT_EXTN: + { + if (!((CAPI_PCM_CONVERTER == me_ptr->type) || (CAPI_PCM_DECODER == me_ptr->type) || + (CAPI_PCM_ENCODER == me_ptr->type))) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] FWK_EXTN_PCM_PARAM_ID_MEDIA_FORMAT_EXTN not supported by this type", + me_ptr->type); + return CAPI_EUNSUPPORTED; + } + + if ((params_ptr->max_data_len >= sizeof(fwk_extn_pcm_param_id_media_fmt_extn_t))) + { + if (TRUE == port_info_ptr->is_input_port) + { + fwk_extn_pcm_param_id_media_fmt_extn_t *extn_ptr = + (fwk_extn_pcm_param_id_media_fmt_extn_t *)(params_ptr->data_ptr); + + extn_ptr->bit_width = me_ptr->extn_in_media_fmt[port].bit_width; + extn_ptr->alignment = me_ptr->extn_in_media_fmt[port].alignment; + extn_ptr->endianness = me_ptr->extn_in_media_fmt[port].endianness; + } + else + { + fwk_extn_pcm_param_id_media_fmt_extn_t *extn_ptr = + (fwk_extn_pcm_param_id_media_fmt_extn_t *)(params_ptr->data_ptr); + + extn_ptr->bit_width = me_ptr->extn_out_media_fmt[port].bit_width; + extn_ptr->alignment = me_ptr->extn_out_media_fmt[port].alignment; + extn_ptr->endianness = me_ptr->extn_out_media_fmt[port].endianness; + } + + params_ptr->actual_data_len = sizeof(fwk_extn_pcm_param_id_media_fmt_extn_t); +#if 0 + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "[%u] FWK_EXTN_PCM_PARAM_ID_MEDIA_FORMAT_EXTN, get_param done", + me_ptr->type); +#endif + } + break; + } + case PARAM_ID_MFC_RESAMPLER_CFG: + { + if (CAPI_MFC != me_ptr->type) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] PARAM_ID_MFC_RESAMPLER_CFG not supported by this type", + me_ptr->type); + return CAPI_EUNSUPPORTED; + } + + if (params_ptr->max_data_len >= sizeof(param_id_mfc_resampler_cfg_t)) + { + param_id_mfc_resampler_cfg_t *rs_config = (param_id_mfc_resampler_cfg_t *)(params_ptr->data_ptr); + memset(rs_config, 0, sizeof(param_id_mfc_resampler_cfg_t)); + rs_config->resampler_type = me_ptr->pc[0].resampler_type; + + if (0 == rs_config->resampler_type) + { + if (me_ptr->pc[0].hwsw_rs_lib_ptr) + { + rs_config->use_hw_rs = me_ptr->pc[0].hwsw_rs_lib_ptr->this_resampler_instance_using; + rs_config->dynamic_mode = me_ptr->pc[0].hwsw_rs_lib_ptr->sw_rs_config_param.dynamic_mode; + rs_config->delay_type = me_ptr->pc[0].hwsw_rs_lib_ptr->sw_rs_config_param.delay_type; + } + else + { + rs_config->use_hw_rs = me_ptr->pc[0].use_hw_rs; + rs_config->dynamic_mode = me_ptr->pc[0].dynamic_mode; + rs_config->delay_type = me_ptr->pc[0].delay_type; + } + } + params_ptr->actual_data_len = sizeof(param_id_mfc_resampler_cfg_t); + } + else + { + CAPI_SET_ERROR(capi_result, CAPI_ENEEDMORE); + } + break; + } + case PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT: + { + if (!(CAPI_MFC == me_ptr->type)) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT not supported by this type", + me_ptr->type); + return CAPI_EUNSUPPORTED; + } + + capi_standard_data_format_v2_t *out_std_ptr = &me_ptr->out_media_fmt[port].format; + + uint32_t size_chmap = (CAPI_DATA_FORMAT_INVALID_VAL != out_std_ptr->num_channels) + ? (out_std_ptr->num_channels * sizeof(uint16_t)) + : 0; + uint32_t required_size = sizeof(param_id_mfc_output_media_fmt_t) + size_chmap; + + if (params_ptr->max_data_len >= required_size) + { + param_id_mfc_output_media_fmt_t *mf_ptr = + reinterpret_cast(params_ptr->data_ptr); + + mf_ptr->bit_width = capi_pcm_mf_cnv_mfc_set_extn_inp_mf_bitwidth(out_std_ptr->q_factor); + mf_ptr->num_channels = out_std_ptr->num_channels; + mf_ptr->sampling_rate = out_std_ptr->sampling_rate; + + memscpy(mf_ptr->channel_type, + params_ptr->max_data_len - sizeof(param_id_mfc_output_media_fmt_t), + out_std_ptr->channel_type, + size_chmap); + + params_ptr->actual_data_len = required_size; + } + else + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "CAPI PCM_CNV: MFC_PARAM_ID_OUTPUT_MEDIA_FORMAT invalid param size %lu", + params_ptr->max_data_len); + params_ptr->actual_data_len = required_size; + capi_result = CAPI_ENEEDMORE; + } + break; + } + case FWK_EXTN_DM_PARAM_ID_CHANGE_MODE: + { + if ((CAPI_MFC != me_ptr->type) || (FIR_RESAMPLER != me_ptr->pc[0].resampler_type)) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] FWK_EXTN_DM_PARAM_ID_CHANGE_MODE not supported by this type, rs type %d", + me_ptr->type, + me_ptr->pc[0].resampler_type); + return CAPI_EUNSUPPORTED; + } + + if (params_ptr->max_data_len >= sizeof(fwk_extn_dm_param_id_change_mode_t)) + { + fwk_extn_dm_param_id_change_mode_t *mode_ptr = (fwk_extn_dm_param_id_change_mode_t *)params_ptr->data_ptr; + mode_ptr->dm_mode = me_ptr->dm_info.dm_mode; + params_ptr->actual_data_len = sizeof(fwk_extn_dm_param_id_change_mode_t); + } + else + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] Get param id 0x%lx, invalid payload size %lu", + param_id, + params_ptr->actual_data_len); + capi_result = CAPI_ENEEDMORE; + } + break; + } + case FWK_EXTN_DM_PARAM_ID_SET_SAMPLES: + case FWK_EXTN_DM_PARAM_ID_SET_MAX_SAMPLES: + { + if ((CAPI_MFC != me_ptr->type) || (FIR_RESAMPLER != me_ptr->pc[0].resampler_type)) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] FWK_EXTN_DM_PARAM_ID_SET_SAMPLES/SET_MAX_SAMPLES not supported by this " + "type,rs type %d", + me_ptr->type, + me_ptr->pc[0].resampler_type); + return CAPI_EUNSUPPORTED; + } + + if (params_ptr->max_data_len >= sizeof(fwk_extn_dm_param_id_req_samples_t)) + { + fwk_extn_dm_param_id_req_samples_t *req_samples_ptr = + (fwk_extn_dm_param_id_req_samples_t *)params_ptr->data_ptr; + + if ((FWK_EXTN_DM_FIXED_OUTPUT_MODE == me_ptr->dm_info.dm_mode) && + (FWK_EXTN_DM_PARAM_ID_SET_SAMPLES == param_id) && capi_pcm_mf_cnv_is_dm_enabled(me_ptr)) + { + req_samples_ptr->is_input = FALSE; + req_samples_ptr->num_ports = 1; + req_samples_ptr->req_samples[0].samples_per_channel = me_ptr->dm_info.req_out_samples; + req_samples_ptr->req_samples[0].port_index = 0; + params_ptr->actual_data_len = sizeof(fwk_extn_dm_param_id_req_samples_t); + } + } + else + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] Get param id 0x%lx, invalid payload size %lu", + me_ptr->type, + param_id, + params_ptr->actual_data_len); + capi_result = CAPI_ENEEDMORE; + } + break; + } + case PARAM_ID_PCM_ENCODER_FRAME_SIZE: + { + if (CAPI_PCM_ENCODER != me_ptr->type) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] PARAM_ID_PCM_ENCODER_FRAME_SIZE not supported by this type", + me_ptr->type); + params_ptr->actual_data_len = 0; + return CAPI_EUNSUPPORTED; + } + if (params_ptr->max_data_len >= sizeof(param_id_pcm_encoder_frame_size_t)) + { + param_id_pcm_encoder_frame_size_t *pcm_encoder_frame_size = + (param_id_pcm_encoder_frame_size_t *)params_ptr->data_ptr; + pcm_encoder_frame_size->frame_size_type = me_ptr->frame_size.frame_size_type; + pcm_encoder_frame_size->frame_size_in_samples = me_ptr->frame_size.frame_size_in_samples; + pcm_encoder_frame_size->frame_size_in_us = me_ptr->frame_size.frame_size_in_us; + + params_ptr->actual_data_len = sizeof(param_id_pcm_encoder_frame_size_t); + CNV_MSG(me_ptr->miid, DBG_HIGH_PRIO, "[%u] PARAM_ID_PCM_ENCODER_FRAME_SIZE, get_param done", me_ptr->type); + } + else + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] Get param id 0x%lx, invalid payload size %lu", + me_ptr->type, + param_id, + params_ptr->actual_data_len); + params_ptr->actual_data_len = 0; + capi_result = CAPI_ENEEDMORE; + } + break; + } + default: + CNV_MSG(me_ptr->miid, DBG_ERROR_PRIO, "[%u] Get, unsupported param ID 0x%x", me_ptr->type, (int)param_id); + CAPI_SET_ERROR(capi_result, CAPI_EUNSUPPORTED); + break; + } + return capi_result; +} + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + Function to set the properties of PCM_CNV module + ____________________________________________________________________________________________________________________*/ +capi_err_t capi_pcm_mf_cnv_set_properties(capi_t *_pif, capi_proplist_t *props_ptr) +{ + + capi_err_t capi_result = CAPI_EOK; + if (NULL == _pif || NULL == props_ptr) + { + CNV_MSG(MIID_UNKNOWN, DBG_ERROR_PRIO, "Set properties received bad pointer, 0x%p", _pif); + CAPI_SET_ERROR(capi_result, CAPI_EBADPARAM); + return capi_result; + } + + capi_pcm_mf_cnv_t *me_ptr = (capi_pcm_mf_cnv_t *)_pif; + + capi_result |= capi_pcm_mf_cnv_process_set_properties(me_ptr, props_ptr); + + return capi_result; +} + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + Function to get the properties of PCM_CNV module + ____________________________________________________________________________________________________________________*/ +capi_err_t capi_pcm_mf_cnv_get_properties(capi_t *_pif, capi_proplist_t *props_ptr) +{ + capi_err_t capi_result = CAPI_EOK; + if (NULL == _pif || NULL == props_ptr) + { + CNV_MSG(MIID_UNKNOWN, DBG_ERROR_PRIO, "Get properties received bad pointer, 0x%p", _pif); + CAPI_SET_ERROR(capi_result, CAPI_EBADPARAM); + return capi_result; + } + + capi_pcm_mf_cnv_t *me_ptr = (capi_pcm_mf_cnv_t *)_pif; + + capi_result |= capi_pcm_mf_cnv_process_get_properties(me_ptr, props_ptr, me_ptr->type); + + return capi_result; +} diff --git a/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/src/capi_pcm_mf_cnv_i.h b/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/src/capi_pcm_mf_cnv_i.h new file mode 100644 index 0000000..a3ce549 --- /dev/null +++ b/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/src/capi_pcm_mf_cnv_i.h @@ -0,0 +1,72 @@ +/* ========================================================================= + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause + * =========================================================================*/ + +/** + * @file capi_pcm_mf_cnv_i.h + */ + +#include "capi_pcm_mf_cnv_utils.h" + +capi_vtbl_t *capi_pcm_mf_cnv_get_vtable(); + +capi_err_t capi_pcm_mf_cnv_process(capi_t *_pif, capi_stream_data_t *input[], capi_stream_data_t *output[]); + +capi_err_t capi_pcm_mf_cnv_end(capi_t *_pif); + +capi_err_t capi_pcm_mf_cnv_set_param(capi_t * _pif, + uint32_t param_id, + const capi_port_info_t *port_info_ptr, + capi_buf_t * params_ptr); + +capi_err_t capi_pcm_mf_cnv_get_param(capi_t * _pif, + uint32_t param_id, + const capi_port_info_t *port_info_ptr, + capi_buf_t * params_ptr); + +capi_err_t capi_pcm_mf_cnv_set_properties(capi_t *me_ptr, capi_proplist_t *props_ptr); + +capi_err_t capi_pcm_mf_cnv_get_properties(capi_t *me_ptr, capi_proplist_t *props_ptr); + +capi_err_t capi_pcm_mf_cnv_check_buffers(capi_pcm_mf_cnv_t *me_ptr, + capi_buf_t * src_buf_ptr, + capi_buf_t * dest_buf_ptr, + uint32_t port, + uint32_t eos_flag); + +capi_err_t capi_pcm_mf_cnv_check_scratch_buffer(capi_pcm_mf_cnv_t *me_ptr, + capi_buf_t * src_buf_ptr, + capi_buf_t * dest_buf_ptr, + uint32_t port); + +static inline void pcm_to_capi_interleaved_with_native(capi_interleaving_t *capi_value, + int16_t cfg_value, + capi_interleaving_t inp_value) +{ + switch (cfg_value) + { + case PCM_INTERLEAVED: + { + *capi_value = CAPI_INTERLEAVED; + break; + } + case PCM_DEINTERLEAVED_PACKED: + { + *capi_value = CAPI_DEINTERLEAVED_PACKED; + break; + } + case PCM_DEINTERLEAVED_UNPACKED: + { + *capi_value = CAPI_DEINTERLEAVED_UNPACKED_V2; + break; + } + case PARAM_VAL_NATIVE: + { + *capi_value = inp_value; + break; + } + default: + break; + } +} diff --git a/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/src/capi_pcm_mf_cnv_island.cpp b/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/src/capi_pcm_mf_cnv_island.cpp new file mode 100644 index 0000000..92f8112 --- /dev/null +++ b/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/src/capi_pcm_mf_cnv_island.cpp @@ -0,0 +1,281 @@ +/* ========================================================================= + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause + * =========================================================================*/ + +/** + * @file capi_pcm_mf_cnv_island.cpp + * + * Cpp source file to implement the Common Audio Post Processor Interface + * PCM_CNV block + */ + +#include "capi_pcm_mf_cnv_i.h" +#include "capi_pcm_mf_cnv_utils.h" +#define ALIGN4(o) (((o)+3)&(~3)) + +static capi_vtbl_t vtbl = { capi_pcm_mf_cnv_process, capi_pcm_mf_cnv_end, + capi_pcm_mf_cnv_set_param, capi_pcm_mf_cnv_get_param, + capi_pcm_mf_cnv_set_properties, capi_pcm_mf_cnv_get_properties }; + +capi_vtbl_t *capi_pcm_mf_cnv_get_vtable() +{ + return &vtbl; +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + Check if the scratch buffer allocated had enough size to handle incoming data. + LOGIC: + max_data_len_per_ch will be set to 0 during init or library init (even if scratch buffer is allocated for enc/dec) + At process time, src_buf_max_len_per_ch is calculated from input buffer + If this is more than the stored value, scratch buffer allocation logic is called. + + Since scratch buffer allocation depends on the factors like ordering or the modules, calculation is a little complex. + So we don't want it to be called at every process. To achieve this, we use the variable me_ptr->max_data_len_per_ch + Module reordering can only happen at capi_pcm_mf_cnv_lib_init, so this variable is reset to 0 there. + In other situations, if local_max_data_len_per_ch lesser than stored, we don't have to realloc the scratch buffers +______________________________________________________________________________________________________________________*/ +capi_err_t capi_pcm_mf_cnv_check_scratch_buffer(capi_pcm_mf_cnv_t *me_ptr, + capi_buf_t * src_buf_ptr, + capi_buf_t * dest_buf_ptr, + uint32_t port) +{ + capi_err_t capi_result = CAPI_EOK; + uint32_t src_buf_max_len_per_ch = 0; + + if (CAPI_DEINTERLEAVED_UNPACKED_V2 == me_ptr->in_media_fmt[port].format.data_interleaving) + { + src_buf_max_len_per_ch = src_buf_ptr[port].max_data_len; + } + else + { + src_buf_max_len_per_ch = src_buf_ptr[port].max_data_len / me_ptr->in_media_fmt[port].format.num_channels; + } + + src_buf_max_len_per_ch = ALIGN4(src_buf_max_len_per_ch); + if (me_ptr->max_data_len_per_ch < src_buf_max_len_per_ch) + { + me_ptr->max_data_len_per_ch = src_buf_max_len_per_ch; + capi_result = capi_pcm_mf_cnv_allocate_scratch_buffer(me_ptr, src_buf_max_len_per_ch); + if (CAPI_EOK != capi_result) + { + CNV_MSG(me_ptr->miid, DBG_ERROR_PRIO, "[%u] Error in pcm convert process - %d", me_ptr->type, capi_result); + capi_pcm_mf_cnv_reset_buffer_len(me_ptr, src_buf_ptr, dest_buf_ptr, port); + // Resetting the value of max_data_len_per_ch = 0 when scratch buffer allocation failed + me_ptr->max_data_len_per_ch = 0; + } + } + return capi_result; +} + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + PCM_CNV module Data Process function to process an input buffer and generates an output buffer. + 1. Checks for the buffer size vs threshold value in case of CAPI_PCM_ENCODER and CAPI_PCM_DECODER + 2. If there is EOS marker, partial data is handled even for threshold modules + 3. For non-threshold modules, buffer sizes are assumed to be correct, so no checking is done to save mips + 4. Scratch buffer sizes are checked against stored value in case an alloc or realloc is needed + ____________________________________________________________________________________________________________________*/ +capi_err_t capi_pcm_mf_cnv_process(capi_t *_pif, capi_stream_data_t *input[], capi_stream_data_t *output[]) +{ + capi_err_t capi_result = CAPI_EOK; + uint32_t actual_input_bytes = 0; + bool_t all_input_consumed = FALSE; + uint32_t port = 0; + + bool_t is_output_present = + ((output) && (output[port]) && (output[port]->buf_ptr) && (output[port]->buf_ptr->data_ptr)); + bool_t is_input_present = ((input) && (input[port]) && (input[port]->buf_ptr) && (input[port]->buf_ptr->data_ptr)); + + if (is_input_present && (!is_output_present)) + { + // mark input is unconsumed + input[0]->buf_ptr->actual_data_len = 0; + } + + if (!(is_output_present && is_input_present)) + { + CNV_MSG(MIID_UNKNOWN, DBG_LOW_PRIO, "[%u] Cannot process: Output or input is NULL"); + return AR_EOK; + } + + actual_input_bytes = input[0]->buf_ptr->actual_data_len; + capi_pcm_mf_cnv_t *me_ptr = (capi_pcm_mf_cnv_t *)_pif; + + capi_buf_t *src_buf_ptr = input[port]->buf_ptr; + capi_buf_t *dest_buf_ptr = output[port]->buf_ptr; + + /*We will call process for pcm decoder and pcm enc eventhough lib is disabled and no memory is allocated + This is because pcm dec and enc need to raise threshold and cannot be disabled + In this case, memcpy and return without accessing lib memory */ + if (me_ptr->lib_enable == FALSE) + { + bool_t is_inplace = (input[0]->buf_ptr[0].data_ptr == output[0]->buf_ptr[0].data_ptr); + // even if processing is not done, input data needs to be copied to output & output actual len be set. + uint32_t out_max_data_len = output[0]->buf_ptr[0].max_data_len; + uint32_t num_bytes_to_cpy = MIN(input[0]->buf_ptr[0].actual_data_len, out_max_data_len); + for (uint32_t i = 0; i < input[0]->bufs_num; i++) + { + if (!is_inplace) + { + memscpy(output[0]->buf_ptr[i].data_ptr, out_max_data_len, input[0]->buf_ptr[i].data_ptr, num_bytes_to_cpy); + } + } + + // first update only first ch len's since module only supports CAPI_DEINTERLEAVED_UNPACKED_V2 + output[0]->buf_ptr[0].actual_data_len = num_bytes_to_cpy; + input[0]->buf_ptr[0].actual_data_len = num_bytes_to_cpy; + + /** return here if library is not enabled*/ + return CAPI_EOK; + } + + capi_result = capi_pcm_mf_cnv_check_scratch_buffer(me_ptr, src_buf_ptr, dest_buf_ptr, port); + + if (CAPI_EOK != capi_result) + { + return capi_result; + } + + // if configured for fixed output, but input samples differ from expected number, reset values + // if input actual len is more than expected module needs to consume only whats expected. + // if input is less than expected, module operates in fixed input mode. + if ((CAPI_MFC == me_ptr->type) && (FIR_RESAMPLER == me_ptr->pc[0].resampler_type) && + capi_pcm_mf_cnv_is_dm_enabled(me_ptr) && (FWK_EXTN_DM_FIXED_OUTPUT_MODE == me_ptr->dm_info.dm_mode)) + { + uint32_t in_samples = src_buf_ptr->actual_data_len / (me_ptr->in_media_fmt[0].format.bits_per_sample >> 3); + + if (in_samples < me_ptr->dm_info.expected_in_samples) + { + // process with available input samples if EOS/EOF is set, else return need more + // Also consume available input if got set_param for consume_partial_data. + if (input[port]->flags.end_of_frame || input[port]->flags.marker_eos || + me_ptr->dm_info.should_consume_partial_input) + { + me_ptr->dm_info.expected_in_samples = 0; + } + else // return need more + { + CNV_MSG(me_ptr->miid, + DBG_LOW_PRIO, + "[%u] Need more inp samples! expected %lu, received %lu", + me_ptr->type, + me_ptr->dm_info.expected_in_samples, + in_samples); + + // mark input unconsumed, need to update only 1st ch. + input[port]->buf_ptr[0].actual_data_len = 0; + return CAPI_ENEEDMORE; + } + } + else if (in_samples > me_ptr->dm_info.expected_in_samples) + { + CNV_MSG(me_ptr->miid, + DBG_LOW_PRIO, + "[%u] Got more samples! expected %lu, received %lu", + me_ptr->type, + me_ptr->dm_info.expected_in_samples, + in_samples); + + //resampler will not consume more than expected samples, so update input buffer actual data len before. + //test case: gen_cntr_two_priority_sync_parallel_paths_2, test passed but glitch was observed because samples were lost. + src_buf_ptr->actual_data_len = + me_ptr->dm_info.expected_in_samples * (me_ptr->in_media_fmt[0].format.bits_per_sample >> 3); //todo: check dynamic resampler + } + } + + // PC library updates only reads/writes into first ch capi_buf_t[0] lengths, capi is expected to update rest of the + // channel buffers if the interleaving format is not unpacked V2 + me_ptr->pc[port].heap_id = (uint32_t)me_ptr->heap_info.heap_id; + capi_result = ar_result_to_capi_err(pc_process(&me_ptr->pc[port], + src_buf_ptr, + dest_buf_ptr, + &me_ptr->scratch_buf_1[port], + &me_ptr->scratch_buf_2[port])); + if (CAPI_ENEEDMORE == capi_result) + { + #ifdef PCM_CNV_DEBUG + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "[%u] needs more input, input actual_data_len per ch = %ld ", + me_ptr->type, + (input[port]->buf_ptr ? input[port]->buf_ptr[0].actual_data_len : 0)); +#endif + // Drop data for eof case. Mark data as unconsumed for non-eof case. + if (!input[port]->flags.end_of_frame) + { + input[port]->buf_ptr[0].actual_data_len = 0; + } + return capi_result; + } + + if (0 != capi_result) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] Error while pcm converter process, inp and out media fmts are ", + me_ptr->type); + capi_pcm_mf_cnv_print_media_fmt(me_ptr, &me_ptr->pc[port].core_lib.input_media_fmt, port); + capi_pcm_mf_cnv_print_media_fmt(me_ptr, &me_ptr->pc[port].core_lib.output_media_fmt, port); + } + + uint32_t is_gapless_case = FALSE; + + if ((me_ptr->initial_silence_in_samples > 0) || (me_ptr->trailing_silence_in_samples > 0)) + { + capi_media_fmt_v2_t *out_mf = NULL; + out_mf = me_ptr->out_media_fmt; + bool_t is_initial = TRUE; + uint32_t num_bytes_removed = 0; + uint32_t bytes_per_sample = (out_mf->format.bits_per_sample) / 8; + capi_stream_data_v2_t *in_stream_ptr = (capi_stream_data_v2_t *)input[0]; + module_cmn_md_list_t * metadata_list_ptr = in_stream_ptr->metadata_list_ptr; + + if (actual_input_bytes == input[0]->buf_ptr->actual_data_len) + { + all_input_consumed = TRUE; + } + // Detect last frame based on EOS. + if (in_stream_ptr->flags.marker_eos && all_input_consumed && me_ptr->trailing_silence_in_samples) + { + + is_initial = FALSE; + + uint32_t bytes_to_remove_per_channel = me_ptr->trailing_silence_in_samples * bytes_per_sample; + capi_cmn_gapless_remove_zeroes(&bytes_to_remove_per_channel, + out_mf, + &output[0], + is_initial, + metadata_list_ptr); + me_ptr->trailing_silence_in_samples = 0; + } + + if ((me_ptr->initial_silence_in_samples > 0) && (output[0]->buf_ptr->actual_data_len > 0)) + { + uint32_t bytes_to_remove_per_channel = me_ptr->initial_silence_in_samples * bytes_per_sample; + num_bytes_removed = bytes_to_remove_per_channel; + capi_cmn_gapless_remove_zeroes(&bytes_to_remove_per_channel, + out_mf, + &output[0], + is_initial, + metadata_list_ptr); + num_bytes_removed -= bytes_to_remove_per_channel; + if (output[0]->flags.is_timestamp_valid && num_bytes_removed) + { + is_gapless_case = TRUE; + output[0]->timestamp = + output[0]->timestamp + ((num_bytes_removed / bytes_per_sample) / out_mf->format.sampling_rate) * 1000000; + } + me_ptr->initial_silence_in_samples = bytes_to_remove_per_channel / bytes_per_sample; + } + } + + if (input[port]->flags.is_timestamp_valid && (FALSE == is_gapless_case)) + { + output[port]->timestamp = input[port]->timestamp - me_ptr->events_config.delay_in_us; + output[port]->flags.is_timestamp_valid = TRUE; + } + + return capi_result; +} diff --git a/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/src/capi_pcm_mf_cnv_utils.cpp b/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/src/capi_pcm_mf_cnv_utils.cpp new file mode 100644 index 0000000..509fa4d --- /dev/null +++ b/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/src/capi_pcm_mf_cnv_utils.cpp @@ -0,0 +1,1810 @@ +/* ========================================================================= + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause + * =========================================================================*/ + +/** + * @file capi_pcm_mf_cnv_utils.cpp + * + * C source file to implement the Audio Post Processor Interface for + * PCM_CNV Module + */ + +#include "capi_pcm_mf_cnv_utils.h" + +/*______________________________________________________________________________________________________________________ + + DESCRIPTION: + Resets the buffer lengths to zero. This can happen if some thing went wrong with the buffers during process +______________________________________________________________________________________________________________________*/ +void capi_pcm_mf_cnv_reset_buffer_len(capi_pcm_mf_cnv_t *me_ptr, + capi_buf_t * src_buf_ptr, + capi_buf_t * dest_buf_ptr, + uint32_t port) +{ + if (CAPI_DEINTERLEAVED_UNPACKED_V2 == me_ptr->in_media_fmt[port].format.data_interleaving) + { + for (uint32_t ch = 0; ch < me_ptr->in_media_fmt[port].format.num_channels; ch++) + { + src_buf_ptr[ch].actual_data_len = 0; + } + } + else + { + src_buf_ptr->actual_data_len = 0; + } + + if (CAPI_DEINTERLEAVED_UNPACKED_V2 == me_ptr->in_media_fmt[port].format.data_interleaving) + { + for (uint32_t ch = 0; ch < me_ptr->out_media_fmt[port].format.num_channels; ch++) + { + dest_buf_ptr[ch].actual_data_len = 0; + } + } + else + { + dest_buf_ptr->actual_data_len = 0; + } +} + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + Re-allocate the scratch buffer if needed + ____________________________________________________________________________________________________________________*/ + +capi_err_t capi_pcm_mf_cnv_realloc_scratch_buf(capi_pcm_mf_cnv_t *me_ptr, capi_buf_t *sb_ptr, uint32_t max_data_len) +{ + capi_err_t result = CAPI_EOK; + if ((0 != max_data_len) && (max_data_len != sb_ptr->max_data_len)) + { + if (NULL != sb_ptr->data_ptr) + { + posal_memory_free(sb_ptr->data_ptr); + sb_ptr->data_ptr = NULL; + } + + sb_ptr->data_ptr = (int8_t *)posal_memory_malloc(max_data_len, (POSAL_HEAP_ID)me_ptr->heap_info.heap_id); + if (NULL == sb_ptr->data_ptr) + { + CNV_MSG(me_ptr->miid, DBG_ERROR_PRIO, "[%u] Failed to allocate scratch buffer", me_ptr->type); + result = CAPI_ENOMEMORY; + // Setting sb_ptr->max_data_len = 0 when scratch buffer allocation failed + sb_ptr->max_data_len = 0; + } + else + { + // Setting sb_ptr->max_data_len = max_data_len when scratch buffer allocated + sb_ptr->max_data_len = max_data_len; + } + } + return result; +} + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + Allocates scratch buffer based on max data length + Re-scales the maximum length based on maximum channel and if any intermediate byte up-conversions are present + ____________________________________________________________________________________________________________________*/ +capi_err_t capi_pcm_mf_cnv_allocate_scratch_buffer(capi_pcm_mf_cnv_t *me_ptr, uint32_t max_data_len_per_ch) +{ + capi_err_t result = CAPI_EOK; + uint32_t port = 0; + uint32_t b1_max_data_len = 0; + uint32_t r1_max_data_len = 0; + uint32_t c_max_data_len = 0; + uint32_t r2_max_data_len = 0; + uint32_t b2_max_data_len = 0; + uint32_t max_data_len = max_data_len_per_ch * me_ptr->in_media_fmt[port].format.num_channels; + pc_media_fmt_t *inp_mf_ptr = &me_ptr->pc[port].core_lib.input_media_fmt; + pc_media_fmt_t *out_mf_ptr = NULL; + + CNV_MSG(me_ptr->miid, DBG_HIGH_PRIO, "[%u] Initial max data len %d", me_ptr->type, max_data_len); + + b1_max_data_len = max_data_len; + if (me_ptr->pc[port].core_lib.flags.BYTE_CNV_PRE) + { + out_mf_ptr = &me_ptr->pc[port].core_lib.pc_proc_info[BYTE_CNV_PRE].output_media_fmt; + b1_max_data_len = (b1_max_data_len * (out_mf_ptr->word_size >> 3)) / (inp_mf_ptr->word_size >> 3); + inp_mf_ptr = out_mf_ptr; + } + + r1_max_data_len = b1_max_data_len; + if (me_ptr->pc[port].core_lib.flags.RESAMPLER_PRE) + { + out_mf_ptr = &me_ptr->pc[port].core_lib.pc_proc_info[RESAMPLER_PRE].output_media_fmt; + uint32_t in_samples = CAPI_CMN_CEIL(r1_max_data_len, (inp_mf_ptr->num_channels * (inp_mf_ptr->word_size >> 3))); + uint32_t out_samples = CAPI_CMN_CEIL(in_samples * out_mf_ptr->sampling_rate, inp_mf_ptr->sampling_rate); + r1_max_data_len = out_samples * (out_mf_ptr->word_size >> 3) * out_mf_ptr->num_channels; + inp_mf_ptr = out_mf_ptr; + } + + c_max_data_len = r1_max_data_len; + if (me_ptr->pc[port].core_lib.flags.CHANNEL_MIXER) + { + out_mf_ptr = &me_ptr->pc[port].core_lib.pc_proc_info[CHANNEL_MIXER].output_media_fmt; + c_max_data_len = (c_max_data_len * out_mf_ptr->num_channels) / (inp_mf_ptr->num_channels); + inp_mf_ptr = out_mf_ptr; + } + + r2_max_data_len = c_max_data_len; + if (me_ptr->pc[port].core_lib.flags.RESAMPLER_POST) + { + out_mf_ptr = &me_ptr->pc[port].core_lib.pc_proc_info[RESAMPLER_POST].output_media_fmt; + uint32_t in_samples = CAPI_CMN_CEIL(r2_max_data_len, (inp_mf_ptr->num_channels * (inp_mf_ptr->word_size >> 3))); + uint32_t out_samples = CAPI_CMN_CEIL(in_samples * out_mf_ptr->sampling_rate, inp_mf_ptr->sampling_rate); + r2_max_data_len = out_samples * (out_mf_ptr->word_size >> 3) * out_mf_ptr->num_channels; + inp_mf_ptr = out_mf_ptr; + } + + b2_max_data_len = r2_max_data_len; + if (me_ptr->pc[port].core_lib.flags.BYTE_CNV_POST) + { + out_mf_ptr = &me_ptr->pc[port].core_lib.pc_proc_info[BYTE_CNV_POST].output_media_fmt; + b2_max_data_len = (b2_max_data_len * (out_mf_ptr->word_size >> 3)) / (inp_mf_ptr->word_size >> 3); + inp_mf_ptr = out_mf_ptr; + } + + max_data_len = max_data_len >= b1_max_data_len ? max_data_len : b1_max_data_len; + max_data_len = max_data_len >= r1_max_data_len ? max_data_len : r1_max_data_len; + max_data_len = max_data_len >= c_max_data_len ? max_data_len : c_max_data_len; + max_data_len = max_data_len >= r2_max_data_len ? max_data_len : r2_max_data_len; + max_data_len = max_data_len >= b2_max_data_len ? max_data_len : b2_max_data_len; + + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "[%u] b1 %lu, r1 %lu, c %lu, r2 %lu, b2 %lu", + me_ptr->type, + b1_max_data_len, + r1_max_data_len, + c_max_data_len, + r2_max_data_len, + b2_max_data_len); + + CNV_MSG(me_ptr->miid, DBG_HIGH_PRIO, "[%u] Final max data len %d", me_ptr->type, max_data_len); + if (0 == me_ptr->pc[port].core_lib.no_of_buffering_stages) + { + return result; + } + else if (1 == me_ptr->pc[port].core_lib.no_of_buffering_stages) + { + // Catching the capi_err_t of capi_pcm_mf_cnv_realloc_scratch_buf in result variable that are CAPI_EOK and + // CAPI_ENOMEMORY + result = capi_pcm_mf_cnv_realloc_scratch_buf(me_ptr, &me_ptr->scratch_buf_1[port], max_data_len); + if (CAPI_FAILED(result)) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] Scratch buffer 1 allocation failed, size = %lu, result = %lu", + me_ptr->type, + max_data_len, + result); + } + else + { + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "[%u] Scratch buffer 1 allocated, size = %lu, result = %lu", + me_ptr->type, + max_data_len, + result); + } + } + else + { + // Catching the capi_err_t of capi_pcm_mf_cnv_realloc_scratch_buf in result variable that are CAPI_EOK and + // CAPI_ENOMEMORY + result = capi_pcm_mf_cnv_realloc_scratch_buf(me_ptr, &me_ptr->scratch_buf_1[port], max_data_len); + if (CAPI_FAILED(result)) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] Scratch buffer 1 allocation failed, so not reallocting memory for scratch buffer 2, size = %lu, " + "result = %lu", + me_ptr->type, + max_data_len, + result); + } + else + { + // Catching the capi_err_t of capi_pcm_mf_cnv_realloc_scratch_buf in result variable that are CAPI_EOK and + // CAPI_ENOMEMORY + result |= capi_pcm_mf_cnv_realloc_scratch_buf(me_ptr, &me_ptr->scratch_buf_2[port], max_data_len); + if (CAPI_FAILED(result)) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] Scratch buffer 1 allocated but buffer 2 failed so freeing the scratch buffer 1, size = %lu, " + "result = %lu", + me_ptr->type, + max_data_len, + result); + // Freeing scratch buffer 1 when scratch buffer 2 failed and resetting the value of max_data_len = 0 + posal_memory_free(me_ptr->scratch_buf_1[port].data_ptr); + me_ptr->scratch_buf_1[port].data_ptr = NULL; + me_ptr->scratch_buf_1[port].max_data_len = 0; + } + else + { + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "[%u] Scratch buffer 1 and 2 allocated, size = %lu, result = %lu", + me_ptr->type, + max_data_len, + result); + } + } + } + return result; +} + +/*=========================================================================== + FUNCTION : capi_dyanmic_resampler_handle_data_port_op + DESCRIPTION: Function to handle data port operations. +===========================================================================*/ +capi_err_t capi_pcm_mf_cnv_handle_data_port_op(capi_pcm_mf_cnv_t *me_ptr, capi_buf_t *params_ptr) +{ + capi_err_t capi_result = CAPI_EOK; + + if (NULL == params_ptr->data_ptr) + { + CNV_MSG(me_ptr->miid, DBG_ERROR_PRIO, "Data port operation set param, received null buffer"); + return CAPI_EBADPARAM; + } + if (params_ptr->actual_data_len < sizeof(intf_extn_data_port_operation_t)) + { + CNV_MSG(me_ptr->miid, DBG_ERROR_PRIO, "Invalid payload size for port operation %d", params_ptr->actual_data_len); + return CAPI_ENEEDMORE; + } + intf_extn_data_port_operation_t *data_ptr = (intf_extn_data_port_operation_t *)(params_ptr->data_ptr); + + if (params_ptr->actual_data_len < + sizeof(intf_extn_data_port_operation_t) + (data_ptr->num_ports * sizeof(intf_extn_data_port_id_idx_map_t))) + { + CNV_MSG(me_ptr->miid, DBG_ERROR_PRIO, "Invalid payload size for port operation %d", params_ptr->actual_data_len); + return CAPI_ENEEDMORE; + } + + // validate num input ports. + if (data_ptr->num_ports > CAPI_PCM_CNV_MAX_IN_PORTS) + { + AR_MSG(DBG_ERROR_PRIO, "parameter, num_ports %d", data_ptr->num_ports); + return CAPI_EBADPARAM; + } + + for (uint32_t i = 0; i < data_ptr->num_ports; i++) + { + // get port state from the port operation + intf_extn_data_port_state_t port_state = intf_extn_data_port_op_to_port_state(data_ptr->opcode); + + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "Received Data Port operation %lx, is_input_port %lu, port state %lx", + data_ptr->opcode, + data_ptr->is_input_port, + port_state); + + // update the port state + if (data_ptr->is_input_port) + { + me_ptr->in_port_state = port_state; + } + else + { + me_ptr->out_port_state = port_state; + } + + // Handle port op specific things here. + switch (data_ptr->opcode) + { + case INTF_EXTN_DATA_PORT_OPEN: + { + // nothing to do + break; + } + case INTF_EXTN_DATA_PORT_START: + { + // unsuspend hw rs if it was suspended to + if (me_ptr->in_port_state == DATA_PORT_STATE_STARTED && me_ptr->out_port_state == DATA_PORT_STATE_STARTED) + { + // if hw resampler is being used un-suspend, not that hwsw_rs_lib_ptr is not freed + if (me_ptr->input_media_fmt_received && me_ptr->pc[PRI_IN_PORT_INDEX].hwsw_rs_lib_ptr) + { + CNV_MSG(me_ptr->miid, DBG_HIGH_PRIO, "Both inputs are started and rs library memory is initated"); + + bool_t IS_SUSPEND_FALSE = FALSE; + uint32_t need_to_init_hw_rs = FALSE; + hwsw_rs_lib_set_hwrs_suspend_resume(IS_SUSPEND_FALSE, + me_ptr->pc[PRI_IN_PORT_INDEX].hwsw_rs_lib_ptr, + &need_to_init_hw_rs); + if (TRUE == need_to_init_hw_rs) + { + hwsw_rs_lib_check_create_resampler_instance(me_ptr->pc[PRI_IN_PORT_INDEX].hwsw_rs_lib_ptr, + me_ptr->heap_info.heap_id); + } + } + } + + break; + } + case INTF_EXTN_DATA_PORT_SUSPEND: + case INTF_EXTN_DATA_PORT_STOP: + { + // Suspend if ip/op is not started + if (me_ptr->in_port_state != DATA_PORT_STATE_STARTED || me_ptr->out_port_state != DATA_PORT_STATE_STARTED) + { + // if hw resampler is being used suspend + if (me_ptr->input_media_fmt_received && me_ptr->pc[PRI_IN_PORT_INDEX].hwsw_rs_lib_ptr) + { + bool_t IS_SUSPEND_TRUE = TRUE; + uint32_t dummy = FALSE; + hwsw_rs_lib_set_hwrs_suspend_resume(IS_SUSPEND_TRUE, + me_ptr->pc[PRI_IN_PORT_INDEX].hwsw_rs_lib_ptr, + &dummy); + } + } + + break; + } + case INTF_EXTN_DATA_PORT_CLOSE: + { + // nothing to do + break; + } + default: + { + CNV_MSG(me_ptr->miid, DBG_ERROR_PRIO, "Port operation - Unsupported opcode: %lu", data_ptr->opcode); + CAPI_SET_ERROR(capi_result, CAPI_EUNSUPPORTED); + } + } + } + + return capi_result; +} + +/*_____________________________________________________________________________________________________________________ + + DESCRIPTION: + Checks if the channel type is valid + ____________________________________________________________________________________________________________________*/ +capi_err_t capi_pcm_mf_cnv_check_ch_type(capi_pcm_mf_cnv_t *me_ptr, + const uint16_t * channel_type, + const uint32_t array_size) +{ + for (uint8_t i = 0; (i < array_size) && (i < CAPI_MAX_CHANNELS_V2); i++) + { + if ((channel_type[i] < (uint16_t)PCM_CHANNEL_L) || (channel_type[i] > (uint16_t)PCM_MAX_CHANNEL_MAP_V2)) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] Unsupported channel type channel idx %d, channel type %d", + me_ptr->type, + (int)i, + (int)channel_type[i]); + return CAPI_EBADPARAM; + } + } + + return CAPI_EOK; +} + +/*______________________________________________________________________________________________________________________ + + DESCRIPTION: + Finds the Co-efficient ptr form the given input and output channel map and sets it to the library. + If the co-efficient ptr is available, it returns the valid co-efficient ptr +______________________________________________________________________________________________________________________*/ +static void *capi_pcm_mf_cnv_chmixer_get_coef_set_ptr(capi_pcm_mf_cnv_t *const me_ptr) +{ + uint32_t port = 0; + capi_media_fmt_v2_t *inp_media_fmt = &(me_ptr->in_media_fmt[port]); + capi_media_fmt_v2_t *out_media_fmt = &(me_ptr->out_media_fmt[port]); + + ChMixerChType in_ch_type[CH_MIXER_MAX_NUM_CH]; + for (uint32_t i = 0; i < inp_media_fmt->format.num_channels; i++) + { + in_ch_type[i] = (ChMixerChType)inp_media_fmt->channel_type[i]; + } + + ChMixerChType out_ch_type[CH_MIXER_MAX_NUM_CH]; + for (uint32_t i = 0; i < out_media_fmt->format.num_channels; i++) + { + out_ch_type[i] = (ChMixerChType)out_media_fmt->channel_type[i]; + } + + // Find the coef set which matches the current media format + capi_pcm_mf_cnv_chmixer_coef_set_t *coef_set_ptr = me_ptr->config.coef_sets_ptr; + int16_t * coef_ptr = NULL; + + for (int8_t i = me_ptr->config.num_coef_sets - 1; i >= 0; i--) + { + if ((me_ptr->config.num_coef_sets - 1) != i) // except the first iteration + { + coef_set_ptr++; + } + + if (coef_set_ptr->num_in_ch != inp_media_fmt->format.num_channels) + { + continue; + } + if (coef_set_ptr->num_out_ch != out_media_fmt->format.num_channels) + { + continue; + } + + uint32_t j = 0; + for (j = 0; j < inp_media_fmt->format.num_channels; j++) + { + if (coef_set_ptr->in_ch_map[j] != inp_media_fmt->channel_type[j]) + { + break; + } + } + + if (j < inp_media_fmt->format.num_channels) + { + continue; + } + + for (j = 0; j < out_media_fmt->format.num_channels; j++) + { + if (coef_set_ptr->out_ch_map[j] != out_media_fmt->channel_type[j]) + { + break; + } + } + + if (j == out_media_fmt->format.num_channels) + { + coef_ptr = coef_set_ptr->coef_ptr; + break; + } + } + + return coef_ptr; +} +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + 1. Checks if the media format sent is valid + 3. Copies the input media fmt variables + 2. If the configured param is 0, sets the output media fmt variables as well + ____________________________________________________________________________________________________________________*/ +capi_err_t capi_pcm_mf_cnv_handle_input_media_fmt(capi_pcm_mf_cnv_t * me_ptr, + capi_media_fmt_v2_t *temp_media_fmt_ptr, + uint32_t port) +{ + capi_err_t capi_result = CAPI_EOK; + +#ifdef PCM_CNV_DEBUG + CNV_MSG(me_ptr->miid, DBG_HIGH_PRIO, "[%u] Received Input media format", me_ptr->type); +#endif + + if (((CAPI_MFC == me_ptr->type) && (TRUE != pcm_mf_cnv_mfc_is_supported_media_type(temp_media_fmt_ptr))) || + ((CAPI_MFC != me_ptr->type) && (TRUE != pcm_mf_cnv_is_supported_media_type(temp_media_fmt_ptr)))) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "CAPI_INPUT_MEDIA_FORMAT: failed, invalid parameters, FORMAT = data_format = " + "%u, " + "bps = %u, bitstream_format = %u, data_interleaving = %u, data_is_signed = %u, num_channels = %u, " + "q_factor = %u, sampling_rate = %u", + temp_media_fmt_ptr->header.format_header.data_format, + temp_media_fmt_ptr->format.bits_per_sample, + temp_media_fmt_ptr->format.bitstream_format, + temp_media_fmt_ptr->format.data_interleaving, + temp_media_fmt_ptr->format.data_is_signed, + temp_media_fmt_ptr->format.num_channels, + temp_media_fmt_ptr->format.q_factor, + temp_media_fmt_ptr->format.sampling_rate); + CAPI_SET_ERROR(capi_result, CAPI_EFAILED); + return capi_result; + } + + // copy and save the input media fmt + me_ptr->in_media_fmt[port] = *temp_media_fmt_ptr; + + capi_standard_data_format_v2_t * in_std_ptr = &me_ptr->in_media_fmt[port].format; + capi_standard_data_format_v2_t * out_std_ptr = &me_ptr->out_media_fmt[port].format; + capi_pc_configured_mf_t * configured_media_fmt = &me_ptr->configured_media_fmt[port]; + fwk_extn_pcm_param_id_media_fmt_extn_t *in_extn_media_ptr = &me_ptr->extn_in_media_fmt[port]; + fwk_extn_pcm_param_id_media_fmt_extn_t *out_extn_media_ptr = &me_ptr->extn_out_media_fmt[port]; + + // configured media fmt is initialized to CAPI_MAX_FORMAT_TYPE + // If configured media fmt is not updated and is same as CAPI_MAX_FORMAT_TYPE then out_media_fmt would be same as + // in_media_fmt, else will copy the configured_media_fmt value to out_media_fmt. + if (CAPI_MAX_FORMAT_TYPE == configured_media_fmt->data_format) + { + me_ptr->out_media_fmt[port].header.format_header.data_format = + me_ptr->in_media_fmt[port].header.format_header.data_format; + } + else + { + me_ptr->out_media_fmt[port].header.format_header.data_format = configured_media_fmt->data_format; + } + + if (PARAM_VAL_NATIVE == configured_media_fmt->fmt.bit_width) // bit-width + { + out_extn_media_ptr->bit_width = in_extn_media_ptr->bit_width; + } + + if (PARAM_VAL_NATIVE == configured_media_fmt->fmt.bits_per_sample) + { + out_std_ptr->bits_per_sample = in_std_ptr->bits_per_sample; + } + + if (PARAM_VAL_NATIVE == configured_media_fmt->fmt.num_channels) + { + out_std_ptr->num_channels = in_std_ptr->num_channels; + } + + if (PARAM_VAL_NATIVE == configured_media_fmt->fmt.interleaved) + { + out_std_ptr->data_interleaving = in_std_ptr->data_interleaving; + } + + if (PARAM_VAL_NATIVE == configured_media_fmt->fmt.num_channels) + { + out_std_ptr->num_channels = in_std_ptr->num_channels; + memscpy(&out_std_ptr->channel_type, + sizeof(uint16_t) * in_std_ptr->num_channels, + &in_std_ptr->channel_type, + sizeof(uint16_t) * in_std_ptr->num_channels); + } + + if (PARAM_VAL_NATIVE == configured_media_fmt->fmt.q_factor) + { + out_std_ptr->q_factor = in_std_ptr->q_factor; + } + + if (PARAM_VAL_NATIVE == configured_media_fmt->sampling_rate) + { + out_std_ptr->sampling_rate = in_std_ptr->sampling_rate; + } + + out_std_ptr->bitstream_format = in_std_ptr->bitstream_format; + out_std_ptr->data_is_signed = in_std_ptr->data_is_signed; + + // To make sure lib checks pass + if (CAPI_MFC == me_ptr->type) + { + me_ptr->extn_in_media_fmt[port].alignment = me_ptr->extn_out_media_fmt[port].alignment = PC_LSB_ALIGNED; + me_ptr->extn_in_media_fmt[port].endianness = me_ptr->extn_out_media_fmt[port].endianness = PC_LITTLE_ENDIAN; + me_ptr->extn_in_media_fmt[port].bit_width = capi_pcm_mf_cnv_mfc_set_extn_inp_mf_bitwidth(in_std_ptr->q_factor); + if (PARAM_VAL_NATIVE == configured_media_fmt->fmt.bit_width) // bit-width + { + me_ptr->extn_out_media_fmt[port].bit_width = me_ptr->extn_in_media_fmt[port].bit_width; + } + } + + // resampler_reinit is set to FALSE + capi_result |= capi_pcm_mf_cnv_lib_init(me_ptr, FALSE); + capi_result |= capi_pcm_mf_cnv_check_and_raise_output_media_format_event(me_ptr); + + // CNV_MSG( me_ptr->miid,DBG_HIGH_PRIO, "[%u] Input media format - Done", me_ptr->type); + return capi_result; +} + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + Checks if the output media fmt is valid, if so raises the out put media fmt event + ____________________________________________________________________________________________________________________*/ +capi_err_t capi_pcm_mf_cnv_check_and_raise_output_media_format_event(capi_pcm_mf_cnv_t *me_ptr) +{ + uint32_t port = 0; + capi_err_t capi_result = CAPI_EOK; + + if (((CAPI_MFC == me_ptr->type) && (TRUE == pcm_mf_cnv_mfc_is_supported_media_type(&me_ptr->out_media_fmt[port]))) || + ((CAPI_MFC != me_ptr->type) && (TRUE == pcm_mf_cnv_is_supported_media_type(&me_ptr->out_media_fmt[port])))) + { + capi_result |= capi_cmn_output_media_fmt_event_v2(&me_ptr->cb_info, &me_ptr->out_media_fmt[port], FALSE, port); + } + return capi_result; +} + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + This function raises dm event to the fwk to inform the input samples required/output produced + ____________________________________________________________________________________________________________________*/ +static capi_err_t capi_pcm_mf_cnv_raise_dm_event(capi_pcm_mf_cnv_t * me_ptr, + fwk_extn_dm_param_id_req_samples_t *req_samples_ptr, + uint32_t event_id) +{ + capi_err_t capi_result = CAPI_EOK; + + capi_event_data_to_dsp_service_t event_payload; + capi_event_info_t event_info; + event_payload.param_id = event_id; + event_payload.token = 0; + event_payload.payload.data_ptr = (int8_t *)req_samples_ptr; + event_payload.payload.actual_data_len = sizeof(fwk_extn_dm_param_id_req_samples_t); + event_payload.payload.max_data_len = sizeof(fwk_extn_dm_param_id_req_samples_t); + event_info.payload.data_ptr = (int8_t *)&event_payload; + event_info.payload.actual_data_len = sizeof(event_payload); + event_info.payload.max_data_len = sizeof(event_payload); + event_info.port_info.is_valid = FALSE; + capi_result = me_ptr->cb_info.event_cb(me_ptr->cb_info.event_context, CAPI_EVENT_DATA_TO_DSP_SERVICE, &event_info); + if (CAPI_FAILED(capi_result)) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] Failed to send report samples event with result %d", + me_ptr->type, + capi_result); + } + + return capi_result; +} + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + This function is used to query the HW resampler for expected input samples to produce fixed out samples. + Update the return value at expected_in_samples_per_ch_ptr. + ____________________________________________________________________________________________________________________*/ +static void capi_pcm_cnv_hwrs_get_exp_in_samples(capi_pcm_mf_cnv_t *me_ptr, + uint32_t req_out_samples, + uint32_t * expected_in_samples_per_ch_ptr) +{ + uint32_t output_samples_per_ch = req_out_samples; + + // get frame size + uint32_t out_samples_per_ms = me_ptr->out_media_fmt[0].format.sampling_rate / 1000; + uint32_t in_samples_per_ms = me_ptr->in_media_fmt[0].format.sampling_rate / 1000; + uint32_t out_frame_size_in_ms = output_samples_per_ch / out_samples_per_ms; + + const uint32_t EXTRA_1MS_FOR_VARIABLE_INPUT = 1; + + // assuming input buffer has 1ms extra samples, this is just to indicate HW resampler that enough input is available + // to produce fixed output. In general HW resampler may need only at max one extra sample to fill the output. + uint32_t max_available_inp_samples = (out_frame_size_in_ms + EXTRA_1MS_FOR_VARIABLE_INPUT) * in_samples_per_ms; + + uint32_t expected_in_samples_per_ch = 0; + uint32_t expected_out_samples_per_ch = 0; + hwsw_rs_lib_process_get_hw_process_info(me_ptr->pc[0].hwsw_rs_lib_ptr, + max_available_inp_samples, + output_samples_per_ch, + &expected_in_samples_per_ch, + &expected_out_samples_per_ch); + + // this is the actual no of samples required to fill the expected output + *expected_in_samples_per_ch_ptr = expected_in_samples_per_ch; + +#ifdef PCM_CNV_DEBUG + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "[%u] HW resampler requested, available_in=%lu exp_in=%lu, exp_out=%lu", + me_ptr->type, + max_available_inp_samples, + expected_in_samples_per_ch, + expected_out_samples_per_ch); +#endif + return; +} + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + This function is used to query the resampler of req input samples to produce fixed out samples + ____________________________________________________________________________________________________________________*/ +capi_err_t capi_pcm_mf_cnv_get_fixed_out_samples(capi_pcm_mf_cnv_t *me_ptr) +{ + capi_err_t capi_result = CAPI_EOK; + + // if it is the software dyn resampler + if (NULL != me_ptr->pc[0].hwsw_rs_lib_ptr->sw_rs_mem_ptr[STAGE_ZERO] && + me_ptr->pc[0].hwsw_rs_lib_ptr->sw_rs_mem_ptr[STAGE_ZERO]->drs_mem_ptr.pStructMem) + { + // compute input samples required + uint32_t expected_in_samples = pc_get_fixed_out_samples(&me_ptr->pc[0], me_ptr->dm_info.req_out_samples); + + me_ptr->dm_info.expected_in_samples = expected_in_samples; + } + else // HW rs + { + capi_pcm_cnv_hwrs_get_exp_in_samples(me_ptr, + me_ptr->dm_info.req_out_samples, + &me_ptr->dm_info.expected_in_samples); + } + +#ifdef PCM_CNV_DEBUG + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "[%u] Fixed out samples %lu, in req %lu", + me_ptr->type, + me_ptr->dm_info.req_out_samples, + me_ptr->dm_info.expected_in_samples); +#endif + + if (me_ptr->dm_info.expected_in_samples) + { + // raise event + fwk_extn_dm_param_id_req_samples_t req_samples; + req_samples.is_input = TRUE; + req_samples.num_ports = 1; + req_samples.req_samples[0].port_index = 0; + req_samples.req_samples[0].samples_per_channel = me_ptr->dm_info.expected_in_samples; + capi_result |= capi_pcm_mf_cnv_raise_dm_event(me_ptr, &req_samples, FWK_EXTN_DM_EVENT_ID_REPORT_SAMPLES); + } + + return capi_result; +} + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + This function is used to calculate the max input samples req to produce max output samples + ____________________________________________________________________________________________________________________*/ +capi_err_t capi_pcm_mf_cnv_get_max_in_samples(capi_pcm_mf_cnv_t *me_ptr, uint32_t max_out_samples) +{ + capi_err_t capi_result = CAPI_EOK; + + // just return 1 ms larger for now + // check if input media format is known and output sampling rate is known + if ((!me_ptr->input_media_fmt_received) || + (CAPI_DATA_FORMAT_INVALID_VAL == me_ptr->in_media_fmt[0].format.sampling_rate) || + (CAPI_DATA_FORMAT_INVALID_VAL == me_ptr->out_media_fmt[0].format.sampling_rate)) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] DM get max samples cannot be returned, input/output media format not set", + me_ptr->type); + return capi_result; + } + // scale samples for input and add 1 ms to it + uint64_t duration_us = ((uint64_t)max_out_samples * 1000000) / me_ptr->out_media_fmt[0].format.sampling_rate; + uint32_t input_samples = (uint32_t)(((duration_us + 1000) * me_ptr->in_media_fmt[0].format.sampling_rate) / 1000000); + fwk_extn_dm_param_id_req_samples_t req_samples; + req_samples.is_input = TRUE; + req_samples.num_ports = 1; + req_samples.req_samples[0].port_index = 0; + req_samples.req_samples[0].samples_per_channel = input_samples; + capi_result |= capi_pcm_mf_cnv_raise_dm_event(me_ptr, &req_samples, FWK_EXTN_DM_EVENT_ID_REPORT_MAX_SAMPLES); + + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "[%u] set max out samples to %d, expected max in samples %d", + me_ptr->type, + max_out_samples, + input_samples); + return capi_result; +} + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + This function checks if it is a fractional resampling scenario, called from lib init after inp and out mf are known + ____________________________________________________________________________________________________________________*/ +bool_t capi_pcm_mf_cnv_is_fractional_media_type(const capi_pcm_mf_cnv_t *me_ptr) +{ + uint64_t in_sr = (uint64_t)me_ptr->in_media_fmt[0].format.sampling_rate; + uint64_t out_sr = (uint64_t)me_ptr->out_media_fmt[0].format.sampling_rate; + + if (me_ptr->frame_size_us) + { + uint64_t in_frame_size = (uint64_t)capi_cmn_us_to_samples(me_ptr->frame_size_us, in_sr); + uint64_t out_frame_size = (uint64_t)capi_cmn_us_to_samples(me_ptr->frame_size_us, out_sr); + + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "[%u] in_frame_size %lu, out_frame_size %lu", + me_ptr->type, + in_frame_size, + out_frame_size); + + return (in_frame_size * out_sr == out_frame_size * in_sr) ? FALSE : TRUE; + } + else + { + CNV_MSG(me_ptr->miid, DBG_HIGH_PRIO, "[%u] frame size is not set.", me_ptr->type); + return ((in_sr % 1000) || (out_sr % 1000)) ? TRUE : FALSE; + } +} + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + 1. check and update dm enable or disable to the fwk + 2. DM is disabled if frame-size ratio (in samples) is same as sampling rate ratio. + 3. DM is enabled if frame-size ratio (in samples) is not same as sampling rate ratio. +______________________________________________________________________________________________________________________*/ +void capi_pcm_mf_cnv_update_dm_disable(capi_pcm_mf_cnv_t *me_ptr) +{ + if ((CAPI_MFC == me_ptr->type) && me_ptr->input_media_fmt_received) + { + // Check should be for inp/out fractional media formats + if (capi_pcm_mf_cnv_is_fractional_media_type(me_ptr)) + { + me_ptr->dm_info.is_dm_disabled = FALSE; + capi_cmn_raise_dm_disable_event(&me_ptr->cb_info, me_ptr->miid, FWK_EXTN_DM_ENABLED_DM); + } + else // if it is not fractional disable dm + { + me_ptr->dm_info.is_dm_disabled = TRUE; + capi_cmn_raise_dm_disable_event(&me_ptr->cb_info, me_ptr->miid, FWK_EXTN_DM_DISABLED_DM); + } + } +} +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + 1. Copies the media format from capi structure to pcm lib media fmt + 2. Classifies the media format combo for both input and output + 3. Finds the channel mixer co-efficient ptr based on the media format + 4. Inits pcm library + 5. Raises kpps events based on the module order + 6. Resets max_data_len_per_ch, so that the scratch buffer allocation logic should be revisited during process + ____________________________________________________________________________________________________________________*/ +ar_result_t capi_pcm_mf_cnv_lib_init(capi_pcm_mf_cnv_t *me_ptr, bool_t fir_iir_rs_switch) +{ + ar_result_t result = AR_EOK; + uint32_t port = 0; + capi_standard_data_format_v2_t * in_std_ptr = &me_ptr->in_media_fmt[port].format; + capi_standard_data_format_v2_t * out_std_ptr = &me_ptr->out_media_fmt[port].format; + fwk_extn_pcm_param_id_media_fmt_extn_t *in_extn_media_ptr = &me_ptr->extn_in_media_fmt[port]; + fwk_extn_pcm_param_id_media_fmt_extn_t *out_extn_media_ptr = &me_ptr->extn_out_media_fmt[port]; + pc_media_fmt_t input_media_fmt; + pc_media_fmt_t output_media_fmt; + + if (CAPI_MFC != me_ptr->type) + { + if (!((pcm_mf_cnv_is_supported_media_type(&me_ptr->in_media_fmt[port])) && + (pcm_mf_cnv_is_supported_media_type(&me_ptr->out_media_fmt[port])) && + (CAPI_PCM_VALID == pcm_mf_cnv_is_supported_extn_media_type(in_extn_media_ptr)) && + (CAPI_PCM_VALID == pcm_mf_cnv_is_supported_extn_media_type(out_extn_media_ptr)))) + { + return AR_EOK; + } + } + else + { + if (!((pcm_mf_cnv_mfc_is_supported_media_type(&me_ptr->in_media_fmt[port])) && + (pcm_mf_cnv_mfc_is_supported_media_type(&me_ptr->out_media_fmt[port])) && + (CAPI_PCM_VALID == pcm_mf_cnv_is_supported_extn_media_type(in_extn_media_ptr)) && + (CAPI_PCM_VALID == pcm_mf_cnv_is_supported_extn_media_type(out_extn_media_ptr)))) + { + return AR_EOK; + } + } + + input_media_fmt.sampling_rate = in_std_ptr->sampling_rate; + input_media_fmt.bit_width = in_extn_media_ptr->bit_width; + input_media_fmt.word_size = in_std_ptr->bits_per_sample; + input_media_fmt.q_factor = in_std_ptr->q_factor; + input_media_fmt.interleaving = capi_interleaved_to_pc(in_std_ptr->data_interleaving); + input_media_fmt.endianness = (pc_endian_t)in_extn_media_ptr->endianness; + input_media_fmt.alignment = (pc_alignment_t)in_extn_media_ptr->alignment; + input_media_fmt.data_format = capi_dataformat_to_pc(me_ptr->in_media_fmt[port].header.format_header.data_format); + input_media_fmt.byte_combo = pc_classify_mf(&input_media_fmt); + input_media_fmt.num_channels = in_std_ptr->num_channels; + input_media_fmt.channel_type = &in_std_ptr->channel_type[0]; + + output_media_fmt.sampling_rate = out_std_ptr->sampling_rate; + output_media_fmt.bit_width = out_extn_media_ptr->bit_width; + output_media_fmt.word_size = out_std_ptr->bits_per_sample; + output_media_fmt.q_factor = out_std_ptr->q_factor; + output_media_fmt.interleaving = capi_interleaved_to_pc(out_std_ptr->data_interleaving); + output_media_fmt.endianness = (pc_endian_t)out_extn_media_ptr->endianness; + output_media_fmt.alignment = (pc_alignment_t)out_extn_media_ptr->alignment; + output_media_fmt.data_format = capi_dataformat_to_pc(me_ptr->out_media_fmt[port].header.format_header.data_format); + output_media_fmt.byte_combo = pc_classify_mf(&output_media_fmt); + output_media_fmt.num_channels = out_std_ptr->num_channels; + output_media_fmt.channel_type = &out_std_ptr->channel_type[0]; + + if ((PC_INVALID_CMBO == input_media_fmt.byte_combo) || (PC_INVALID_CMBO == output_media_fmt.byte_combo)) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] Invalid byte combo set for INPUT bw: %d, bps: %d and qfactor: %d or OUTPUT " + "bw: %d, bps: %d and qfactor: %d", + me_ptr->type, + input_media_fmt.bit_width, + input_media_fmt.word_size, + input_media_fmt.q_factor, + output_media_fmt.bit_width, + output_media_fmt.word_size, + output_media_fmt.q_factor); + return AR_EFAILED; + } + +#ifdef PCM_CNV_DEBUG + CNV_MSG(me_ptr->miid, DBG_HIGH_PRIO, "[%u] AFTER SETTING INPUT", me_ptr->type); + capi_pcm_mf_cnv_print_media_fmt(me_ptr, &input_media_fmt, port); + + CNV_MSG(me_ptr->miid, DBG_HIGH_PRIO, "[%u] AFTER SETTING OUTPUT", me_ptr->type); + capi_pcm_mf_cnv_print_media_fmt(me_ptr, &output_media_fmt, port); +#endif + + result = pc_init((pc_lib_t *)&me_ptr->pc[port], + (pc_media_fmt_t *)&input_media_fmt, + (pc_media_fmt_t *)&output_media_fmt, + (void *)capi_pcm_mf_cnv_chmixer_get_coef_set_ptr(me_ptr), + (uint32_t)me_ptr->heap_info.heap_id, + &me_ptr->lib_enable, + fir_iir_rs_switch, + me_ptr->miid, + me_ptr->type); + + if ((AR_EOK != result) || (me_ptr->lib_enable == FALSE)) + { + me_ptr->lib_enable = FALSE; + capi_pcm_mf_cnv_raise_process_check_event(me_ptr); + pc_deinit((pc_lib_t *)&me_ptr->pc[port]); + return result; + } + + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "[%u] PC Init Done, Pre modules -E1-%lu, Float_to_Fixed-%lu, I1-%lu, B1-%lu, R1-%lu, C-%lu", + me_ptr->type, + me_ptr->pc[port].core_lib.flags.ENDIANNESS_PRE, + me_ptr->pc[port].core_lib.flags.DATA_CNV_FLOAT_TO_FIXED, + me_ptr->pc[port].core_lib.flags.INT_DEINT_PRE, + me_ptr->pc[port].core_lib.flags.BYTE_CNV_PRE, + me_ptr->pc[port].core_lib.flags.RESAMPLER_PRE, + me_ptr->pc[port].core_lib.flags.CHANNEL_MIXER); + + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "[%u] PC Init Done , Post modules R2-%lu, B2-%lu, I2-%lu, Fixed_to_Float-%lu, E2-%lu", + me_ptr->type, + me_ptr->pc[port].core_lib.flags.RESAMPLER_POST, + me_ptr->pc[port].core_lib.flags.BYTE_CNV_POST, + me_ptr->pc[port].core_lib.flags.INT_DEINT_POST, + me_ptr->pc[port].core_lib.flags.DATA_CNV_FIXED_TO_FLOAT, + me_ptr->pc[port].core_lib.flags.ENDIANNESS_POST); + + // this flag should be set before dm mode setting. + me_ptr->input_media_fmt_received = TRUE; + + // raise TRUE or FALSE + capi_pcm_mf_cnv_raise_process_check_event(me_ptr); + capi_pcm_mf_cnv_update_and_raise_events(me_ptr); + + // if not fractional, and dm mode is configured, raise disabled event + capi_pcm_mf_cnv_update_dm_disable(me_ptr); + + // We keep allocated length 0 and actually recalculate the required length + // during the 1st process call. + // Also, this helps in realloc-ing the scratch buffer sizes if there is any change in media fmt dynamically + me_ptr->max_data_len_per_ch = 0; + + // CNV_MSG(me_ptr->miid, DBG_HIGH_PRIO, "[%u] lib init done", me_ptr->type); + + return AR_EOK; +} + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + Checks if the pcm cnv media fmt is has all the supported values + ____________________________________________________________________________________________________________________*/ +bool_t pcm_mf_cnv_is_supported_media_type(const capi_media_fmt_v2_t *format_ptr) +{ + if ((CAPI_FIXED_POINT != format_ptr->header.format_header.data_format) && + (CAPI_FLOATING_POINT != format_ptr->header.format_header.data_format)) + { + return FALSE; + } + + // Return if the format is invalid + if (CAPI_DATA_FORMAT_INVALID_VAL == format_ptr->format.bits_per_sample || + CAPI_DATA_FORMAT_INVALID_VAL == format_ptr->format.data_interleaving || + CAPI_DATA_FORMAT_INVALID_VAL == format_ptr->format.data_is_signed || + CAPI_DATA_FORMAT_INVALID_VAL == format_ptr->format.num_channels || + CAPI_DATA_FORMAT_INVALID_VAL == format_ptr->format.sampling_rate) + { + return FALSE; + } + + if (CAPI_FIXED_POINT == format_ptr->header.format_header.data_format) + { + if ((16 != format_ptr->format.bits_per_sample) && + (24 != format_ptr->format.bits_per_sample) && // for packed mode. + (32 != format_ptr->format.bits_per_sample)) + { + return FALSE; + } + } + else if ((CAPI_FLOATING_POINT == format_ptr->header.format_header.data_format)) + { + if ((32 != format_ptr->format.bits_per_sample) && (64 != format_ptr->format.bits_per_sample)) + { + return FALSE; + } + } + + if ((CAPI_INTERLEAVED != format_ptr->format.data_interleaving) && + (CAPI_DEINTERLEAVED_PACKED != format_ptr->format.data_interleaving) && + (CAPI_DEINTERLEAVED_UNPACKED_V2 != format_ptr->format.data_interleaving)) + { + return FALSE; + } + + if (TRUE != format_ptr->format.data_is_signed) + { + return FALSE; + } + + if ((0 == format_ptr->format.num_channels) || (CAPI_MAX_CHANNELS_V2 < format_ptr->format.num_channels)) + { + return FALSE; + } + + for (uint32_t i = 0; i < format_ptr->format.num_channels; i++) + { + if ((format_ptr->format.channel_type[i] < (uint16_t)PCM_CHANNEL_L) || + (format_ptr->format.channel_type[i] > (uint16_t)PCM_MAX_CHANNEL_MAP_V2)) + { + AR_MSG(DBG_ERROR_PRIO, + "Unsupported channel type channel idx %d, channel type %d received", + (int)i, + (int)format_ptr->format.channel_type[i]); + return FALSE; + } + } + + if ((384000 < format_ptr->format.sampling_rate) || (0 >= format_ptr->format.sampling_rate)) + { + return FALSE; + } + return TRUE; +} + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + Checks if the input mfc media fmt is supported + ____________________________________________________________________________________________________________________*/ +bool_t pcm_mf_cnv_mfc_is_supported_media_type(const capi_media_fmt_v2_t *fmt_ptr) +{ + if (NULL == fmt_ptr) + { + return FALSE; + } + + // The underlying APPIs can only support de-interleaved unpacked data. + if (CAPI_DEINTERLEAVED_UNPACKED_V2 != fmt_ptr->format.data_interleaving) + { + return FALSE; + } + + if (TRUE != fmt_ptr->format.data_is_signed) + { + return FALSE; + } + + if ((fmt_ptr->format.num_channels > CAPI_MAX_CHANNELS_V2) || (fmt_ptr->format.num_channels <= 0)) + { + return FALSE; + } + + for (uint32_t i = 0; i < fmt_ptr->format.num_channels; i++) + { + if ((fmt_ptr->format.channel_type[i] < (uint16_t)PCM_CHANNEL_L) || + (fmt_ptr->format.channel_type[i] > (uint16_t)PCM_MAX_CHANNEL_MAP_V2)) + { + AR_MSG(DBG_ERROR_PRIO, + "Unsupported channel type channel idx %d, channel type %d received", + (int)i, + (int)fmt_ptr->format.channel_type[i]); + return FALSE; + } + } + + if ((fmt_ptr->format.bits_per_sample != 16) && (fmt_ptr->format.bits_per_sample != 24) && + (fmt_ptr->format.bits_per_sample != 32)) + { + return FALSE; + } + + if ((fmt_ptr->format.sampling_rate > 384000) || (fmt_ptr->format.sampling_rate <= 0)) + { + return FALSE; + } + return TRUE; +} + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + Checks if the framework extension for media fmt structure has supported values + ____________________________________________________________________________________________________________________*/ +capi_pcm_mf_cnv_result_t pcm_mf_cnv_is_supported_extn_media_type(const fwk_extn_pcm_param_id_media_fmt_extn_t *extn_ptr) +{ + capi_pcm_mf_cnv_result_t result = CAPI_PCM_VALID; + + if ((16 != extn_ptr->bit_width) && (24 != extn_ptr->bit_width) && (32 != extn_ptr->bit_width) && + (64 != extn_ptr->bit_width)) + { + if (CAPI_DATA_FORMAT_INVALID_VAL != extn_ptr->bit_width) + { + return CAPI_PCM_ERROR; + } + else + { + result = CAPI_PCM_INVALID; + } + } + + if ((PC_LSB_ALIGNED != extn_ptr->alignment) && (PC_MSB_ALIGNED != extn_ptr->alignment)) + { + return CAPI_PCM_ERROR; + } + + if ((PC_LITTLE_ENDIAN != extn_ptr->endianness) && (PC_BIG_ENDIAN != extn_ptr->endianness)) + { + if (CAPI_DATA_FORMAT_INVALID_VAL != extn_ptr->endianness) + { + return CAPI_PCM_ERROR; + } + else + { + result = CAPI_PCM_INVALID; + } + } + + return result; +} + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + Checks if the output media fmt ptr has supported values + ____________________________________________________________________________________________________________________*/ +bool_t pcm_mf_cnv_is_supported_out_media_type(const payload_pcm_output_format_cfg_t *format_ptr, uint32_t data_format) +{ + capi_err_t result = CAPI_EOK; + if (DATA_FORMAT_FIXED_POINT == data_format) + { + if (CAPI_FAILED(result = capi_cmn_validate_client_pcm_output_cfg(format_ptr))) + { + return FALSE; + } + } + else if (DATA_FORMAT_FLOATING_POINT == data_format) + { + if (CAPI_FAILED(result = capi_cmn_validate_client_pcm_float_output_cfg(format_ptr))) + { + return FALSE; + } + } + else + { + return FALSE; + } + + return TRUE; +} + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + Function to raise various events of the pcm_mf_cnv module + ____________________________________________________________________________________________________________________*/ +capi_err_t capi_pcm_mf_cnv_raise_process_check_event(capi_pcm_mf_cnv_t *me_ptr) +{ + if ((CAPI_MFC == me_ptr->type) || (me_ptr->type == CAPI_PCM_CONVERTER)) + { + capi_cmn_update_process_check_event(&me_ptr->cb_info, me_ptr->lib_enable); + } + return CAPI_EOK; +} + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + Function to raise various events of the pcm_mf_cnv module + ____________________________________________________________________________________________________________________*/ +capi_err_t capi_pcm_mf_cnv_update_and_raise_events(capi_pcm_mf_cnv_t *me_ptr) +{ + capi_err_t capi_result = CAPI_EOK; + uint32_t port = 0; + uint32_t bw = 0; + uint32_t kpps = 0; + uint32_t new_algo_delay = 0; + + if (NULL == me_ptr->cb_info.event_cb) + { + CNV_MSG(me_ptr->miid, DBG_ERROR_PRIO, "[%u] Event callback is not set. Unable to raise events!", me_ptr->type); + CAPI_SET_ERROR(capi_result, CAPI_EUNSUPPORTED); + return capi_result; + } + + // This additional kpps is added to capi operations during process + pc_get_kpps_and_bw(&me_ptr->pc[port], &kpps, &bw); + kpps = kpps + PCM_CNV_CAPI_KPPS; + new_algo_delay = pc_get_algo_delay(&me_ptr->pc[port]); + + if (kpps != me_ptr->events_config.kpps) + { + me_ptr->events_config.kpps = kpps; + capi_result |= capi_cmn_update_kpps_event(&me_ptr->cb_info, me_ptr->events_config.kpps); + } + + if (bw != me_ptr->events_config.data_bw) + { + me_ptr->events_config.code_bw = 0; + me_ptr->events_config.data_bw = bw; + + capi_result |= capi_cmn_update_bandwidth_event(&me_ptr->cb_info, + me_ptr->events_config.code_bw, + me_ptr->events_config.data_bw); + } + + if (new_algo_delay != me_ptr->events_config.delay_in_us) + { + me_ptr->events_config.delay_in_us = new_algo_delay; + capi_result |= capi_cmn_update_algo_delay_event(&me_ptr->cb_info, me_ptr->events_config.delay_in_us); + } + return capi_result; +} + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + Set properties. Uses capi cmn functions + ____________________________________________________________________________________________________________________*/ +capi_err_t capi_pcm_mf_cnv_process_set_properties(capi_pcm_mf_cnv_t *me_ptr, capi_proplist_t *proplist_ptr) +{ + capi_err_t capi_result = CAPI_EOK; + capi_prop_t *prop_array = proplist_ptr->prop_ptr; + uint32_t i = 0; + uint32_t port = 0; + + if (NULL == prop_array) + { + CNV_MSG(me_ptr->miid, DBG_ERROR_PRIO, "[%u] Set property, received null property ptr.", me_ptr->type); + return CAPI_EBADPARAM; + } + + capi_result = capi_cmn_set_basic_properties(proplist_ptr, &me_ptr->heap_info, &me_ptr->cb_info, TRUE); + if (CAPI_EOK != capi_result) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] Set basic properties failed with result %lu", + me_ptr->type, + capi_result); + return capi_result; + } + + for (i = 0; i < proplist_ptr->props_num; i++) + { + capi_buf_t *payload_ptr = &(prop_array[i].payload); + + switch (prop_array[i].id) + { + case CAPI_HEAP_ID: + case CAPI_EVENT_CALLBACK_INFO: + case CAPI_PORT_NUM_INFO: + { + break; + } + case CAPI_ALGORITHMIC_RESET: + { + // If type is MFC and pre or post resamplers are enabled + if ((CAPI_MFC == me_ptr->type) && + ((me_ptr->pc[0].core_lib.flags.RESAMPLER_PRE) || (me_ptr->pc[0].core_lib.flags.RESAMPLER_POST))) + { + if ((IIR_RESAMPLER == me_ptr->pc[0].resampler_type) && (me_ptr->pc[0].iir_rs_lib_ptr)) + { + capi_result = iir_rs_lib_clear_algo_memory(me_ptr->pc[0].iir_rs_lib_ptr); + } + else if ((FIR_RESAMPLER == me_ptr->pc[0].resampler_type) && (me_ptr->pc[0].hwsw_rs_lib_ptr)) + { + capi_result |= hwsw_rs_lib_algo_reset(me_ptr->pc[0].hwsw_rs_lib_ptr); + } + } + break; + } + case CAPI_INPUT_MEDIA_FORMAT_V2: + { + if (payload_ptr->actual_data_len >= + sizeof(capi_set_get_media_format_t) + sizeof(capi_standard_data_format_v2_t)) + { + capi_set_get_media_format_t * main_ptr = (capi_set_get_media_format_t *)payload_ptr->data_ptr; + capi_standard_data_format_v2_t *data_ptr = (capi_standard_data_format_v2_t *)(main_ptr + 1); + capi_media_fmt_v2_t temp_media_fmt; + temp_media_fmt.header = *main_ptr; + temp_media_fmt.format.minor_version = data_ptr->minor_version; + temp_media_fmt.format.bitstream_format = data_ptr->bitstream_format; + temp_media_fmt.format.bits_per_sample = data_ptr->bits_per_sample; + temp_media_fmt.format.q_factor = data_ptr->q_factor; + temp_media_fmt.format.sampling_rate = data_ptr->sampling_rate; + temp_media_fmt.format.data_is_signed = data_ptr->data_is_signed; + temp_media_fmt.format.data_interleaving = data_ptr->data_interleaving; + temp_media_fmt.format.num_channels = data_ptr->num_channels; + for (uint32_t ch = 0; ch < temp_media_fmt.format.num_channels; ch++) + { + temp_media_fmt.format.channel_type[ch] = data_ptr->channel_type[ch]; + } + + if (CAPI_FLOATING_POINT == temp_media_fmt.header.format_header.data_format) + { + if (((CAPI_PCM_CONVERTER != me_ptr->type) && (CAPI_PCM_DECODER != me_ptr->type) && + (CAPI_PCM_ENCODER != me_ptr->type)) || + (!pc_is_floating_point_data_format_supported())) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] Floating point data format not supported by this module/chipset", + me_ptr->type); + return CAPI_EUNSUPPORTED; + } + } + + capi_result |= capi_pcm_mf_cnv_handle_input_media_fmt(me_ptr, &temp_media_fmt, port); + if (AR_EOK == capi_result) + { + if ((CAPI_PCM_ENCODER == me_ptr->type) && + ((PCM_ENCODER_CONFIG_FRAME_SIZE_IN_SAMPLES == me_ptr->frame_size.frame_size_type && + 0 != me_ptr->frame_size.frame_size_in_samples) || + (PCM_ENCODER_CONFIG_FRAME_SIZE_IN_MICROSECONDS == me_ptr->frame_size.frame_size_type && + 0 != me_ptr->frame_size.frame_size_in_us))) + { + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "[%u] Updating the output frame size for pcm encoder ", + me_ptr->type); + capi_result |= capi_pcm_encoder_update_frame_size(me_ptr); + } + else if ((CAPI_PCM_DECODER == me_ptr->type) || (CAPI_PCM_ENCODER == me_ptr->type)) + { + if (me_ptr->frame_size_us) + { + uint32_t frame_size_ms = me_ptr->frame_size_us / 1000; + uint32_t input_port_threshold = + frame_size_ms * CAPI_CMN_BITS_TO_BYTES(me_ptr->in_media_fmt[0].format.bits_per_sample) * + tu_get_unit_frame_size(me_ptr->in_media_fmt[0].format.sampling_rate) * + me_ptr->in_media_fmt[0].format.num_channels; + uint32_t output_port_threshold = + frame_size_ms * CAPI_CMN_BITS_TO_BYTES(me_ptr->out_media_fmt[0].format.bits_per_sample) * + tu_get_unit_frame_size(me_ptr->out_media_fmt[0].format.sampling_rate) * + me_ptr->out_media_fmt[0].format.num_channels; + if (input_port_threshold > 0) + { + // input threshold + if ((capi_result |= capi_cmn_update_port_data_threshold_event(&me_ptr->cb_info, + input_port_threshold, + TRUE, + 0) != CAPI_EOK)) + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] failed to set input port threshold value", + me_ptr->type); + return CAPI_EFAILED; + } + } + if (output_port_threshold > 0) + { + // output threshold + if ((capi_result |= capi_cmn_update_port_data_threshold_event(&me_ptr->cb_info, + output_port_threshold, + FALSE, + 0) != CAPI_EOK)) + + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] failed to set output port threshold value", + me_ptr->type); + return CAPI_EFAILED; + } + } + CNV_MSG(me_ptr->miid, + DBG_LOW_PRIO, + "[%u] Input port threshold is %d, " + "Output port threshold is %d", + me_ptr->type, + input_port_threshold, + output_port_threshold); + } + } + } + } + else + { + CNV_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "[%u] Set property id 0x%lx Bad param size %lu", + me_ptr->type, + (uint32_t)prop_array[i].id, + payload_ptr->actual_data_len); + CAPI_SET_ERROR(capi_result, CAPI_ENEEDMORE); + } + break; + } + case CAPI_MODULE_INSTANCE_ID: + { + if (payload_ptr->actual_data_len >= sizeof(capi_module_instance_id_t)) + { + capi_module_instance_id_t *data_ptr = (capi_module_instance_id_t *)payload_ptr->data_ptr; + me_ptr->miid = data_ptr->module_instance_id; + me_ptr->pc[0].miid = me_ptr->miid; +#if 0 + CNV_MSG(me_ptr->miid, + DBG_LOW_PRIO, + "MF_PCM_CNV: This module-id 0x%08lX, instance-id 0x%08lX", + data_ptr->module_id, + me_ptr->miid); +#endif + } + else + { + CNV_MSG(MIID_UNKNOWN, + DBG_ERROR_PRIO, + "MF_PCM_CNV: Set, Param id 0x%lx Bad param size %lu", + (uint32_t)prop_array[i].id, + payload_ptr->actual_data_len); + CAPI_SET_ERROR(capi_result, CAPI_ENEEDMORE); + } + break; + } + case CAPI_LOGGING_INFO: + { + // TODO: to store module logging info + break; + } + case CAPI_OUTPUT_MEDIA_FORMAT: + { + CAPI_SET_ERROR(capi_result, CAPI_EUNSUPPORTED); + break; + } + + case CAPI_OUTPUT_MEDIA_FORMAT_V2: + { + CAPI_SET_ERROR(capi_result, CAPI_EUNSUPPORTED); + break; + } + + case CAPI_CUSTOM_INIT_DATA: + { + break; + } + + default: + { +#if 0 + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "[%u] Set property id %#x. Not supported.", + me_ptr->type, + prop_array[i].id); +#endif + CAPI_SET_ERROR(capi_result, CAPI_EUNSUPPORTED); + break; + } + } + + if (CAPI_FAILED(capi_result)) + { + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "[%u] Set property for %#x failed with opcode %lu", + me_ptr->type, + prop_array[i].id, + capi_result); + } + } + + return capi_result; +} + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + get properties + ____________________________________________________________________________________________________________________*/ +capi_err_t capi_pcm_mf_cnv_process_get_properties(capi_pcm_mf_cnv_t * me_ptr, + capi_proplist_t * proplist_ptr, + capi_pcm_mf_cnv_type_t type) +{ + capi_err_t capi_result = CAPI_EOK; + capi_prop_t * prop_array = proplist_ptr->prop_ptr; + uint32_t port = 0; + uint32_t fwk_extn_ids_arr[1] = { FWK_EXTN_PCM }; + uint32_t fwk_extn_ids_arr_mfc[2] = { FWK_EXTN_DM, FWK_EXTN_CONTAINER_FRAME_DURATION }; + uint32_t fwk_extn_ids_arr_enc_dec[2] = { FWK_EXTN_PCM, FWK_EXTN_THRESHOLD_CONFIGURATION }; + capi_basic_prop_t mod_prop; + mod_prop.init_memory_req = align_to_8_byte(sizeof(capi_pcm_mf_cnv_t)); + mod_prop.stack_size = PCM_CNV_STACK_SIZE; + mod_prop.is_inplace = FALSE; + mod_prop.req_data_buffering = (CAPI_MFC == type); + mod_prop.max_metadata_size = 0; + + if (CAPI_MFC == type) + { + mod_prop.num_fwk_extns = 2; + mod_prop.fwk_extn_ids_arr = fwk_extn_ids_arr_mfc; + } + else if (CAPI_PCM_DECODER == type || CAPI_PCM_ENCODER == type) + { + mod_prop.num_fwk_extns = 2; + mod_prop.fwk_extn_ids_arr = fwk_extn_ids_arr_enc_dec; + } + else + { + mod_prop.num_fwk_extns = 1; + mod_prop.fwk_extn_ids_arr = fwk_extn_ids_arr; + } + + capi_result = capi_cmn_get_basic_properties(proplist_ptr, &mod_prop); + if (CAPI_EOK != capi_result) + { + AR_MSG(DBG_ERROR_PRIO, "Get common basic properties failed with capi_result %lu", capi_result); + + return capi_result; + } + + for (uint32_t i = 0; i < proplist_ptr->props_num; i++) + { + switch (prop_array[i].id) + { + case CAPI_INIT_MEMORY_REQUIREMENT: + case CAPI_STACK_SIZE: + case CAPI_IS_INPLACE: + case CAPI_REQUIRES_DATA_BUFFERING: + case CAPI_OUTPUT_MEDIA_FORMAT_SIZE: + case CAPI_NUM_NEEDED_FRAMEWORK_EXTENSIONS: + case CAPI_NEEDED_FRAMEWORK_EXTENSIONS: + { + break; + } + case CAPI_INTERFACE_EXTENSIONS: + { + capi_buf_t *payload_ptr = &(prop_array[i].payload); + if (payload_ptr->max_data_len < sizeof(capi_interface_extns_list_t)) + { + AR_MSG(DBG_ERROR_PRIO, "CAPI_INTERFACE_EXTENSIONS Bad param size %lu", payload_ptr->max_data_len); + CAPI_SET_ERROR(capi_result, CAPI_ENEEDMORE); + break; + } + + capi_interface_extns_list_t *intf_ext_list = (capi_interface_extns_list_t *)payload_ptr->data_ptr; + if (payload_ptr->max_data_len < (sizeof(capi_interface_extns_list_t) + + (intf_ext_list->num_extensions * sizeof(capi_interface_extn_desc_t)))) + { + AR_MSG(DBG_ERROR_PRIO, "CAPI_INTERFACE_EXTENSIONS invalid param size %lu", payload_ptr->max_data_len); + CAPI_SET_ERROR(capi_result, CAPI_ENEEDMORE); + break; + } + + capi_interface_extn_desc_t *curr_intf_extn_desc_ptr = + (capi_interface_extn_desc_t *)(payload_ptr->data_ptr + sizeof(capi_interface_extns_list_t)); + + for (uint32_t i = 0; i < intf_ext_list->num_extensions; i++) + { + switch (curr_intf_extn_desc_ptr->id) + { + case INTF_EXTN_DATA_PORT_OPERATION: + { + // Only MFC supports data port ops, MFC uses port state to release hw resources upon stop/suspend + if (CAPI_MFC == type) + { + curr_intf_extn_desc_ptr->is_supported = TRUE; + } + else + { + curr_intf_extn_desc_ptr->is_supported = FALSE; + } + break; + } + default: + { + curr_intf_extn_desc_ptr->is_supported = FALSE; + break; + } + } +#if 0 + AR_MSG(DBG_HIGH_PRIO, + "CAPI_INTERFACE_EXTENSIONS intf_ext = 0x%lx, is_supported = %d", + curr_intf_extn_desc_ptr->id, + (int)curr_intf_extn_desc_ptr->is_supported); +#endif + curr_intf_extn_desc_ptr++; + } + break; + } + + case CAPI_OUTPUT_MEDIA_FORMAT_V2: + { + if (NULL == me_ptr || ((prop_array[i].port_info.is_valid) && (0 != prop_array[i].port_info.port_index))) + { + AR_MSG(DBG_ERROR_PRIO, + "CAPI_MF_PCM_CNV: Get property id 0x%lx failed due to invalid/unexpected values", + (uint32_t)prop_array[i].id); + CAPI_SET_ERROR(capi_result, CAPI_EFAILED); + break; + } + + capi_result |= capi_cmn_handle_get_output_media_fmt_v2(&prop_array[i], &me_ptr->out_media_fmt[port]); + + break; + } + + case CAPI_PORT_DATA_THRESHOLD: + { + if (NULL == me_ptr) + { + CAPI_SET_ERROR(capi_result, CAPI_EFAILED); + AR_MSG(DBG_ERROR_PRIO, + "CAPI_MF_PCM_CNV: Get property for ID %#x. failed, NULL me_ptr.", + prop_array[i].id); + break; + } + + uint32_t threshold = 1; + + capi_result |= capi_cmn_handle_get_port_threshold(&prop_array[i], threshold); + + break; + } + + default: + { + // AR_MSG(DBG_HIGH_PRIO, "CAPI_MF_PCM_CNV: Get property for ID %#x. Not supported.", prop_array[i].id); + CAPI_SET_ERROR(capi_result, CAPI_EUNSUPPORTED); + break; + } + } + +#if 0 + if (CAPI_FAILED(capi_result)) + { + AR_MSG(DBG_HIGH_PRIO, + "CAPI_MF_PCM_CNV: Get property for %#x failed with opcode %lu", + prop_array[i].id, + capi_result); + } +#endif + } + + return capi_result; +} + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + The function sets the output bits per sample and output qfactor based on the output bitwidth received in the + setparam - PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT + ____________________________________________________________________________________________________________________*/ + +void capi_pcm_mf_cnv_mfc_set_output_bps_qf(capi_pc_configured_mf_t *media_fmt_ptr) +{ + uint32_t bit_width = media_fmt_ptr->fmt.bit_width; + + // 16 implies samples in 16-bit words with q15 format + if (16 == bit_width) + { + media_fmt_ptr->fmt.bits_per_sample = 16; + media_fmt_ptr->fmt.q_factor = PCM_Q_FACTOR_15; + } + + // 24 implies samples in 32-bit words with q27 format + else if (24 == bit_width) + { + media_fmt_ptr->fmt.bits_per_sample = 32; + media_fmt_ptr->fmt.q_factor = PCM_Q_FACTOR_27; + } + + // 32 implies samples in 32-bit words with q31 format + else if (32 == bit_width) + { + media_fmt_ptr->fmt.bits_per_sample = 32; + media_fmt_ptr->fmt.q_factor = PCM_Q_FACTOR_31; + } + else if (PARAM_VAL_NATIVE == bit_width) + { + AR_MSG(DBG_HIGH_PRIO, "MFC: native mode bit_width received"); + media_fmt_ptr->fmt.bits_per_sample = PARAM_VAL_NATIVE; + media_fmt_ptr->fmt.q_factor = PARAM_VAL_NATIVE; + } + else + { + AR_MSG(DBG_ERROR_PRIO, "MFC: unsupported bit_width received %lu, not setting q factor", bit_width); + } +} + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + Deinit the pcm mf cnv +____________________________________________________________________________________________________________________*/ + +void capi_pcm_mf_cnv_mfc_deinit(capi_pcm_mf_cnv_t *me_ptr) +{ + uint32_t port = 0; + + pc_deinit((pc_lib_t *)&me_ptr->pc[port]); + + if (NULL != me_ptr->config.coef_sets_ptr) + { + posal_memory_free(me_ptr->config.coef_sets_ptr); + me_ptr->config.coef_sets_ptr = NULL; + me_ptr->config.num_coef_sets = 0; + } + + if (NULL != me_ptr->scratch_buf_1[port].data_ptr) + { + posal_memory_free(me_ptr->scratch_buf_1[port].data_ptr); + me_ptr->scratch_buf_1->data_ptr = NULL; + } + if (NULL != me_ptr->scratch_buf_2[port].data_ptr) + { + posal_memory_free(me_ptr->scratch_buf_2[port].data_ptr); + me_ptr->scratch_buf_2->data_ptr = NULL; + } +} +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + The function sets the bitwidth based on qfactor received in input media format +____________________________________________________________________________________________________________________*/ + +uint32_t capi_pcm_mf_cnv_mfc_set_extn_inp_mf_bitwidth(uint32_t q_factor) +{ + uint32_t bitwidth = 0; + + if (PCM_Q_FACTOR_15 == q_factor) + { + bitwidth = 16; + } + else if ((PCM_Q_FACTOR_27 == q_factor) || (PCM_Q_FACTOR_23 == q_factor)) + { + bitwidth = 24; + } + else + { + bitwidth = 32; + } + + return bitwidth; +} + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + Function to set output buffer size of the pcm encoder + ____________________________________________________________________________________________________________________*/ + +capi_err_t capi_pcm_encoder_update_frame_size(capi_pcm_mf_cnv_t *me_ptr) +{ + capi_err_t capi_result = CAPI_EOK; + uint32_t frame_size_type = me_ptr->frame_size.frame_size_type; + uint32_t frame_size_in_samples = me_ptr->frame_size.frame_size_in_samples; + uint32_t frame_size_in_us = me_ptr->frame_size.frame_size_in_us; + uint32_t frame_size_in_bytes = 0; + uint32_t num_ch = me_ptr->in_media_fmt[0].format.num_channels; + uint32_t bps = (me_ptr->in_media_fmt[0].format.bits_per_sample) / 8; + uint32_t sr = me_ptr->in_media_fmt[0].format.sampling_rate; + + if (1 == frame_size_type) + { + frame_size_in_bytes = frame_size_in_samples * num_ch * bps; + } + else if (2 == frame_size_type) + { + frame_size_in_bytes = (uint32_t)(((uint64_t)(frame_size_in_us * sr) / 1000000) * bps * num_ch); + } + + CNV_MSG(me_ptr->miid, + DBG_LOW_PRIO, + "[%u]PARAM_ID_PCM_ENCODER_FRAME_SIZE frame_size_type is %d," + "frame_size in samples is %d, frame size in microsecond is %d", + me_ptr->type, + frame_size_type, + frame_size_in_samples, + frame_size_in_us); + CNV_MSG(me_ptr->miid, + DBG_LOW_PRIO, + "[%u]Number of channels is %d, sampling rate is %d," + "bytes per sample is %d,so calculated frame_size_in_bytes is %d", + me_ptr->type, + num_ch, + sr, + bps, + frame_size_in_bytes); + if (frame_size_in_bytes > 0) + { + // input threshold + if ((capi_result |= + capi_cmn_update_port_data_threshold_event(&me_ptr->cb_info, frame_size_in_bytes, TRUE, 0) != CAPI_EOK)) + { + CNV_MSG(me_ptr->miid, DBG_ERROR_PRIO, "[%u] failed to set input port threshold value", me_ptr->type); + return CAPI_EFAILED; + } + // output threshold + if ((capi_result |= + capi_cmn_update_port_data_threshold_event(&me_ptr->cb_info, frame_size_in_bytes, FALSE, 0) != CAPI_EOK)) + + { + CNV_MSG(me_ptr->miid, DBG_ERROR_PRIO, "[%u] failed to set output port threshold value", me_ptr->type); + return CAPI_EFAILED; + } + } + CNV_MSG(me_ptr->miid, DBG_HIGH_PRIO, "Successfully raised input and output threshold for the pcm encoder module"); + + return capi_result; +} diff --git a/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/src/capi_pcm_mf_cnv_utils.h b/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/src/capi_pcm_mf_cnv_utils.h new file mode 100644 index 0000000..3a17d24 --- /dev/null +++ b/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/src/capi_pcm_mf_cnv_utils.h @@ -0,0 +1,266 @@ +/* ======================================================================== */ +/** +@file capiv2_pcm_mf_cnv_utils.h + + Header file to implement the Common Audio Post Processor Interface + for Tx/Rx Tuning PCM_CONV block +*/ + +/* ========================================================================= + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause + ========================================================================== */ + +/*------------------------------------------------------------------------ + * Include files + * -----------------------------------------------------------------------*/ + +#ifndef CAPI_PCM_CNV_UTILS_H +#define CAPI_PCM_CNV_UTILS_H + +#ifndef CAPI_UNIT_TEST +/* For shared libraries. */ +#include "shared_lib_api.h" +#include "topo_utils.h" +#else +#include "capi_fwk_extns_pcm.h" +#include "capi_fwk_extns_frame_duration.h" +#endif +#include "capi_cmn.h" +#include "audpp_util.h" +#include "capi.h" +#include "audio_basic_op_ext.h" +#include "common_enc_dec_api.h" +#include "pcm_converter_api.h" +#include "pcm_encoder_api.h" +#include "pcm_decoder_api.h" +#include "mfc_api.h" +#include "capi_pcm_cnv.h" +#include "capi_pcm_dec.h" +#include "capi_pcm_enc.h" +#include "capi_mfc.h" +#include "pc_converter.h" + +#define CAPI_PCM_CNV_MAX_IN_PORTS 1 +#define CAPI_PCM_CNV_MAX_OUT_PORTS 1 +#define PCM_TRAILING_DELAY 0 +#define PCM_DECODER_DELAY 0 +#define PRI_IN_PORT_INDEX 0 +#define PRI_OUT_PORT_INDEX 0 +#define PCM_ENCODER_CONFIG_MAX_FRAME_SIZE_IN_SAMPLES 38400 +#define PCM_ENCODER_CONFIG_MAX_FRAME_SIZE_IN_MICROSECOND 100000 +#define PCM_ENCODER_CONFIG_FRAME_SIZE_IN_SAMPLES 1 +#define PCM_ENCODER_CONFIG_FRAME_SIZE_IN_MICROSECONDS 2 +#ifdef CAPI_UNIT_TEST +static inline uint32_t tu_get_unit_frame_size(uint32_t sample_rate) +{ + // Returns 1 sample as a minimum value + if (sample_rate < 1000) + { + return 1; + } + return (sample_rate / 1000); +} +#define PCM_CNV_DEBUG +#endif + +//#define PCM_CNV_DEBUG + + +#define ALIGN_4_BYTES(a) ((a + 3) & (0xFFFFFFFC)) + +static const uint32_t PCM_CNV_STACK_SIZE = 4096; +const uint32_t PCM_CNV_BW = 1 * 1024 * 1024; +const uint32_t PCM_CNV_CAPI_KPPS = 50; + +typedef enum capi_pcm_mf_cnv_result_t { CAPI_PCM_INVALID = 0, CAPI_PCM_VALID, CAPI_PCM_ERROR } capi_pcm_mf_cnv_result_t; + +typedef enum capi_pcm_mf_cnv_type_t { + CAPI_PCM_CONVERTER = 0, + CAPI_PCM_ENCODER, + CAPI_PCM_DECODER, + CAPI_MFC +} capi_pcm_mf_cnv_type_t; + +typedef enum capi_pcm_mf_cnv_perf_mode_t { + CAPI_PCM_PERF_MODE_INVALID = 0, + CAPI_PCM_PERF_MODE_LOW_POWER = 1, // should be same as #define APM_SG_PERF_MODE_LOW_POWER 0x1 + CAPI_PCM_PERF_MODE_LOW_LATENCY = 2 // should be same as #define APM_SG_PERF_MODE_LOW_LATENCY 0x2 +} capi_pcm_mf_cnv_perf_mode_t; + +typedef struct capi_pcm_mf_cnv_events_config_t +{ + uint32_t kpps; + uint32_t delay_in_us; + uint32_t code_bw; + uint32_t data_bw; +} capi_pcm_mf_cnv_events_config_t; + +typedef struct capi_pcm_mf_cnv_chmixer_coef_set_t +{ + uint16_t num_in_ch; + uint16_t *in_ch_map; + uint16_t num_out_ch; + uint16_t *out_ch_map; + int16_t * coef_ptr; +} capi_pcm_mf_cnv_chmixer_coef_set_t; + +typedef struct capi_pc_configured_mf_t +{ + data_format_t data_format; + uint32_t sampling_rate; + payload_pcm_output_format_cfg_t fmt; + uint16_t channel_type[CAPI_MAX_CHANNELS_V2]; +} capi_pc_configured_mf_t; + +typedef struct capi_pcm_mf_cnv_chmixer_config +{ + uint32_t num_coef_sets; + capi_pcm_mf_cnv_chmixer_coef_set_t *coef_sets_ptr; +} capi_pcm_mf_cnv_chmixer_config; + +typedef struct capi_pcm_mf_cnv_dm_info_t +{ + // mode + uint16_t dm_mode; + // flag to indicate if dm is enabled + uint16_t is_dm_disabled; + // for fixed output mode + uint32_t req_out_samples; + uint32_t expected_in_samples; + // for fixed input mode + uint32_t req_in_samples; + uint32_t expected_out_samples; + + uint32_t should_consume_partial_input; +} capi_pcm_mf_cnv_dm_info_t; + +typedef struct capi_pcm_mf_cnv_t +{ + capi_t vtbl; + capi_event_callback_info_t cb_info; + capi_heap_id_t heap_info; + capi_media_fmt_v2_t in_media_fmt[CAPI_PCM_CNV_MAX_IN_PORTS]; + capi_media_fmt_v2_t out_media_fmt[CAPI_PCM_CNV_MAX_OUT_PORTS]; + fwk_extn_pcm_param_id_media_fmt_extn_t extn_in_media_fmt[CAPI_PCM_CNV_MAX_IN_PORTS]; + fwk_extn_pcm_param_id_media_fmt_extn_t extn_out_media_fmt[CAPI_PCM_CNV_MAX_OUT_PORTS]; + capi_buf_t scratch_buf_1[CAPI_PCM_CNV_MAX_OUT_PORTS]; + capi_buf_t scratch_buf_2[CAPI_PCM_CNV_MAX_OUT_PORTS]; + capi_pc_configured_mf_t configured_media_fmt[CAPI_PCM_CNV_MAX_OUT_PORTS]; + capi_pcm_mf_cnv_chmixer_config config; + uint32_t coef_payload_size; + uint32_t inp_buf_size_per_ch; + uint32_t out_buf_size_per_ch; + uint32_t max_data_len_per_ch; + capi_pcm_mf_cnv_events_config_t events_config; + capi_pcm_mf_cnv_type_t type; + capi_pcm_mf_cnv_perf_mode_t perf_mode; + + // This variable will hold container based duration for MFC and SG perf mode based thresh for PCM decoders and + // encoders + uint32_t frame_size_us; + pc_lib_t pc[CAPI_PCM_CNV_MAX_IN_PORTS]; + bool_t lib_enable; + bool_t input_media_fmt_received; // indicates ip mf is received, lib init done and out mf is raised + uint32_t initial_silence_in_samples; + uint32_t trailing_silence_in_samples; + uint32_t miid; + param_id_pcm_encoder_frame_size_t frame_size; + + // port states + intf_extn_data_port_state_t in_port_state; + intf_extn_data_port_state_t out_port_state; + + // fixed input or output mode (if configured) and related data + capi_pcm_mf_cnv_dm_info_t dm_info; +} capi_pcm_mf_cnv_t; + +bool_t pcm_mf_cnv_is_supported_media_type(const capi_media_fmt_v2_t *format_ptr); + +bool_t pcm_mf_cnv_mfc_is_supported_media_type(const capi_media_fmt_v2_t *format_ptr); + +void capi_pcm_mf_cnv_mfc_set_output_bps_qf(capi_pc_configured_mf_t *media_fmt_ptr); + +uint32_t capi_pcm_mf_cnv_mfc_set_extn_inp_mf_bitwidth(uint32_t q_factor); +void capi_pcm_mf_cnv_mfc_deinit(capi_pcm_mf_cnv_t *me_ptr); +capi_err_t capi_pcm_mf_cnv_check_ch_type(capi_pcm_mf_cnv_t *me_ptr, + const uint16_t * channel_type, + const uint32_t array_size); + +capi_err_t capi_pcm_mf_cnv_handle_input_media_fmt(capi_pcm_mf_cnv_t * me_ptr, + capi_media_fmt_v2_t *temp_media_fmt_ptr, + uint32_t port); +capi_err_t capi_pcm_mf_cnv_check_and_raise_output_media_format_event(capi_pcm_mf_cnv_t *me_ptr); + +void capi_pcm_mf_cnv_print_media_fmt(capi_pcm_mf_cnv_t *me_ptr, pc_media_fmt_t *fmt_ptr, uint32_t port); +capi_err_t capi_pcm_mf_cnv_get_fixed_out_samples(capi_pcm_mf_cnv_t *me_ptr); +capi_err_t capi_pcm_mf_cnv_get_max_in_samples(capi_pcm_mf_cnv_t *me_ptr, uint32_t max_out_samples); + +ar_result_t capi_pcm_mf_cnv_lib_init(capi_pcm_mf_cnv_t *me_ptr, bool_t fir_iir_rs_switch); +capi_err_t capi_pcm_mf_cnv_allocate_scratch_buffer(capi_pcm_mf_cnv_t *me_ptr, uint32_t max_data_len_per_ch); +bool_t pcm_mf_cnv_is_supported_out_media_type(const payload_pcm_output_format_cfg_t *format_ptr, uint32 data_format); +capi_pcm_mf_cnv_result_t pcm_mf_cnv_is_supported_extn_media_type( + const fwk_extn_pcm_param_id_media_fmt_extn_t *format_ptr); +capi_err_t capi_pcm_mf_cnv_process_set_properties(capi_pcm_mf_cnv_t *me_ptr, capi_proplist_t *proplist_ptr); +capi_err_t capi_pcm_mf_cnv_process_get_properties(capi_pcm_mf_cnv_t * me_ptr, + capi_proplist_t * proplist_ptr, + capi_pcm_mf_cnv_type_t type); +void capi_pcm_mf_cnv_init_config(capi_pcm_mf_cnv_t *me_ptr); +capi_err_t capi_pcm_mf_cnv_update_and_raise_events(capi_pcm_mf_cnv_t *me_ptr); +capi_err_t capi_pcm_mf_cnv_raise_process_check_event(capi_pcm_mf_cnv_t *me_ptr); +capi_err_t capi_pcm_mf_cnv_raise_process_event(capi_pcm_mf_cnv_t *me_ptr); + +capi_err_t capi_pcm_mf_cnv_handle_data_port_op(capi_pcm_mf_cnv_t *me_ptr, capi_buf_t *params_ptr); + +capi_err_t capi_pcm_mf_cnv_realloc_scratch_buf(capi_pcm_mf_cnv_t *me_ptr, capi_buf_t *sb_ptr, uint32_t max_data_len); + +capi_err_t capi_pcm_mf_cnv_allocate_scratch_buffer(capi_pcm_mf_cnv_t *me_ptr, uint32_t max_data_len_per_ch); + +capi_err_t capi_pcm_encoder_update_frame_size(capi_pcm_mf_cnv_t *me_ptr); + + +void capi_pcm_mf_cnv_reset_buffer_len(capi_pcm_mf_cnv_t *me_ptr, + capi_buf_t * src_buf_ptr, + capi_buf_t * dest_buf_ptr, + uint32_t port); + +void capi_pcm_mf_cnv_update_dm_disable(capi_pcm_mf_cnv_t *me_ptr); + +static inline bool_t capi_pcm_mf_cnv_is_dm_enabled(capi_pcm_mf_cnv_t *me_ptr) +{ + return !me_ptr->dm_info.is_dm_disabled; +} + +static inline pc_interleaving_t capi_interleaved_to_pc(capi_interleaving_t capi_value) +{ + pc_interleaving_t pc_interleaved = PC_UNKNOWN_INTERLEAVING; + if (CAPI_INTERLEAVED == capi_value) + { + pc_interleaved = PC_INTERLEAVED; + } + else if (CAPI_DEINTERLEAVED_PACKED == capi_value) + { + pc_interleaved = PC_DEINTERLEAVED_PACKED; + } + else if(CAPI_DEINTERLEAVED_UNPACKED_V2 == capi_value) + { + // input media format will only be set with V2, hence no need to V1. + pc_interleaved = PC_DEINTERLEAVED_UNPACKED_V2; + } + return pc_interleaved; +} + +static inline pc_data_format_t capi_dataformat_to_pc(data_format_t capi_value) +{ + if (CAPI_FIXED_POINT == capi_value) + { + return PC_FIXED_FORMAT; + } + else if (CAPI_FLOATING_POINT == capi_value) + { + return PC_FLOATING_FORMAT; + } + return PC_INVALID_FORMAT; +} + +#endif // CAPI_PCM_CNV_UTILS_H diff --git a/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/src/capi_pcm_mf_cnv_utils_island.cpp b/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/src/capi_pcm_mf_cnv_utils_island.cpp new file mode 100644 index 0000000..5ea18fe --- /dev/null +++ b/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/src/capi_pcm_mf_cnv_utils_island.cpp @@ -0,0 +1,35 @@ +/* ========================================================================= + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause + * =========================================================================*/ + +/** + * @file capi_pcm_mf_cnv_utils_island.cpp + * + * C source file to implement the Audio Post Processor Interface for + * PCM_CNV Module + */ + +#include "capi_pcm_mf_cnv_utils.h" + +/*_____________________________________________________________________________________________________________________ + DESCRIPTION: + Prints media fmt + ____________________________________________________________________________________________________________________*/ +void capi_pcm_mf_cnv_print_media_fmt(capi_pcm_mf_cnv_t *me_ptr, pc_media_fmt_t *fmt_ptr, uint32_t port) +{ + CNV_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "bit_width : %u , word_size : %u, q_factor : %u, interleaving : %u, endianness : %u, " + "alignment : %u, num_channels : %u sampling rate %d", + fmt_ptr->bit_width, + fmt_ptr->word_size, + fmt_ptr->q_factor, + fmt_ptr->interleaving, + fmt_ptr->endianness, + fmt_ptr->alignment, + fmt_ptr->num_channels, + fmt_ptr->sampling_rate); +} + + diff --git a/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/stub_src/capi_pcm_cnv_stub.cpp b/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/stub_src/capi_pcm_cnv_stub.cpp new file mode 100644 index 0000000..5fd055e --- /dev/null +++ b/modules/cmn/pcm_mf_cnv/capi/pcm_cnv/stub_src/capi_pcm_cnv_stub.cpp @@ -0,0 +1,44 @@ +/* ========================================================================= + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + * =========================================================================*/ + +/** + * @file capi_pcm_cnv_stub.cpp + * + * Stub Interface for pcm_cnv. + */ + +#include "capi_pcm_cnv.h" + +capi_err_t capi_pcm_cnv_get_static_properties(capi_proplist_t *init_set_properties, + capi_proplist_t *static_properties) +{ + return CAPI_EUNSUPPORTED; +} + +capi_err_t capi_pcm_cnv_init(capi_t *_pif, capi_proplist_t *init_set_properties) +{ + return CAPI_EUNSUPPORTED; +} + +capi_err_t capi_pcm_enc_get_static_properties(capi_proplist_t *init_set_properties, + capi_proplist_t *static_properties) +{ + return CAPI_EUNSUPPORTED; +} +capi_err_t capi_pcm_enc_init(capi_t *_pif, capi_proplist_t *init_set_properties) +{ + return CAPI_EUNSUPPORTED; +} + +capi_err_t capi_pcm_dec_get_static_properties(capi_proplist_t *init_set_properties, + capi_proplist_t *static_properties) +{ + return CAPI_EUNSUPPORTED; +} + +capi_err_t capi_pcm_dec_init(capi_t *_pif, capi_proplist_t *init_set_properties) +{ + return CAPI_EUNSUPPORTED; +} diff --git a/modules/cmn/pcm_mf_cnv/lib/inc/pc_converter.h b/modules/cmn/pcm_mf_cnv/lib/inc/pc_converter.h new file mode 100644 index 0000000..b02f095 --- /dev/null +++ b/modules/cmn/pcm_mf_cnv/lib/inc/pc_converter.h @@ -0,0 +1,437 @@ +/*======================================================================== + +file pcm_converter.h +This file contains functions for compression-decompression container + + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause +======================================================================*/ + +/* ======================================================================= +INCLUDE FILES FOR MODULE +========================================================================== */ +#ifndef PCM_CONVERTER_H +#define PCM_CONVERTER_H +#include "audio_basic_op_ext.h" +#include "ar_error_codes.h" +#include "capi.h" +#include "capi_cmn.h" +#include "shared_lib_api.h" +#include "shared_aud_cmn_lib.h" +#include "common_enc_dec_api.h" +#include "pcm_converter_api.h" +#include "pcm_encoder_api.h" +#include "pcm_decoder_api.h" +#include "ChannelMixerLib.h" +#include "hwsw_rs_lib.h" +#include "iir_rs_lib.h" +#include "spf_interleaver.h" + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +/*--------------------------------------------------------------*/ +/* Macro definitions */ +/* -------------------------------------------------------------*/ +//#define USE_Q6_SPECIFIC_CODE +//#define PCM_CNV_LIB_DEBUG 1 +#define MIID_UNKNOWN 0 + +#define CNV_MSG_PREFIX "MFC_PCM_CNV:[%lX] " +#define CNV_MSG(ID, xx_ss_mask, xx_fmt, ...) AR_MSG(xx_ss_mask, CNV_MSG_PREFIX xx_fmt, ID, ##__VA_ARGS__) + +#define CNV_IIR_RS_MIN_FRAME_SIZE_US 1000 +#define MEM_ALIGN_EIGHT_BYTE 8 + +/*---------------------------------------------------------------------------- + * Global Data Definitions + * -------------------------------------------------------------------------*/ +static const uint32_t MAX_Q23 = ((1 << PCM_Q_FACTOR_23) - 1); +static const uint32_t MIN_Q23 = (-(1 << PCM_Q_FACTOR_23)); +static const uint32_t MAX_Q27 = ((1 << PCM_Q_FACTOR_27) - 1); +static const uint32_t MIN_Q27 = (-(1 << PCM_Q_FACTOR_27)); +static const uint32_t MAX_Q31 = 0x7FFFFFFF; +static const uint32_t MIN_Q31 = 0x80000000; +static const uint32_t NUMBER_OF_PROCESS = 11; +static const uint32_t MAX_NUMBER_OF_BUFFERING_PROCESS = 7; +static const uint32_t NUMBER_OF_CHANNEL_MAPS = 2; +static const uint32_t NUMBER_OF_REMAP_BUFFERS = 4; +static const uint32_t MAX_BW_IDX = 3; +// All the kpps values are scaled based on sample word size +// Indices to WS map is as follows, +// 0 - > 16 bit +// 1 - > 24 bit +// 2 - > 32 bit +static const uint32_t endianness_kpps_per_ch[MAX_BW_IDX] = { 230, 180, 130 }; +static const uint32_t intl_dintl_kpps_per_ch[MAX_BW_IDX] = { 110, 640, 110 }; +static const uint32_t byte_cnv_kpps_per_ch[MAX_BW_IDX] = { 100, 300, 120 }; +static const uint32_t ch_mixer_kpps_per_ch[MAX_BW_IDX] = { 450, 500, 500 }; +static const uint32_t memcpy_kpps_per_ch[MAX_BW_IDX] = { 100, 100, 100 }; +static const uint32_t fw_kpps = 100; // just a little extra kpps for logic + +static const uint32_t base_sample_rate = 48000; + +#define PC_CEIL(x, y) (((x) + (y)-1) / (y)) + +// Enlists possible combinations of media_fmt for a given buf +// BW- bit width, W - word size, Q - q-factor +typedef enum pc_mf_combo_t { + PC_INVALID_CMBO = 0, + PC_BW16_W16_Q15, + PC_BW24_W24_Q23, + PC_BW24_W32_Q23, + PC_BW24_W32_Q27, + PC_BW24_W32_Q31, + PC_BW32_W32_Q31, + PC_BW32_W32_FLOAT, + PC_BW64_W64_DOUBLE, +} pc_mf_combo_t; + +typedef enum pc_endian_t { + PC_UNKNOWN_ENDIAN = PARAM_VAL_INVALID, + PC_LITTLE_ENDIAN = PCM_LITTLE_ENDIAN, + PC_BIG_ENDIAN = PCM_BIG_ENDIAN +} pc_endian_t; + +typedef enum pc_alignment_t { + PC_UNKNOWN_ALIGNMENT = PARAM_VAL_INVALID, + PC_LSB_ALIGNED = PCM_LSB_ALIGNED, + PC_MSB_ALIGNED = PCM_MSB_ALIGNED +} pc_alignment_t; + +/* PC handles unpacked data, in the CAPI_DEINTERLEVED_UNPACKED_V2 format, + i.e it reads/updates only first chs lengths */ +typedef enum pc_interleaving_t +{ + PC_UNKNOWN_INTERLEAVING = PARAM_VAL_INVALID, + PC_INTERLEAVED = PCM_INTERLEAVED, + PC_DEINTERLEAVED_PACKED = PCM_DEINTERLEAVED_PACKED, + PC_DEINTERLEAVED_UNPACKED_V2 = PCM_DEINTERLEAVED_UNPACKED +} pc_interleaving_t; + +// Enlisting data format to the pc module +typedef enum pc_data_format_t { + PC_FIXED_FORMAT = 0, + PC_FLOATING_FORMAT, + PC_INVALID_FORMAT, +} pc_data_format_t; + +// Media Format used for +typedef struct pc_media_fmt_t +{ + uint32_t sampling_rate; + pc_endian_t endianness; + pc_interleaving_t interleaving; + pc_mf_combo_t byte_combo; + pc_alignment_t alignment; + pc_data_format_t data_format; + uint16_t bit_width; + uint16_t word_size; + uint16_t q_factor; + uint16_t num_channels; + uint16_t * channel_type; +} pc_media_fmt_t; + +// Process modules which can be present in the PCM pipeline +// UP - comes before channel map module +// Down - comes after channel map module +typedef union pc_process_flags_t +{ + struct + { + uint32_t ENDIANNESS_PRE : 1; + uint32_t DATA_CNV_FLOAT_TO_FIXED : 1; + uint32_t INT_DEINT_PRE : 1; + uint32_t BYTE_CNV_PRE : 1; + uint32_t RESAMPLER_PRE : 1; + uint32_t CHANNEL_MIXER : 1; + uint32_t RESAMPLER_POST : 1; + uint32_t BYTE_CNV_POST : 1; + uint32_t INT_DEINT_POST : 1; + uint32_t DATA_CNV_FIXED_TO_FLOAT : 1; + uint32_t ENDIANNESS_POST : 1; + }; + uint32_t word; +} pc_process_flags_t; + +typedef enum pc_proc_flags_t { + ENDIANNESS_PRE = 0, + DATA_CNV_FLOAT_TO_FIXED, + INT_DEINT_PRE, + BYTE_CNV_PRE, + RESAMPLER_PRE, + CHANNEL_MIXER, + RESAMPLER_POST, + BYTE_CNV_POST, + INT_DEINT_POST, + DATA_CNV_FIXED_TO_FLOAT, + ENDIANNESS_POST, +} pc_proc_flags_t; + +typedef enum pc_resampler_type_t { FIR_RESAMPLER = 0, IIR_RESAMPLER = 1, IIR_PREFERRED } pc_resampler_type_t; + +typedef enum pc_rs_position_flag_t { RESAMPLER_POSITION_PRE, RESAMPLER_POSITION_POST } pc_rs_position_flag_t; + +typedef enum pcm_mf_cnv_type_t { PCM_CNV = 0, PCM_ENC, PCM_DEC, MFC } pcm_mf_cnv_type_t; +// This structure is used as a v-table for process calls for modules in the pcm pipeline +typedef struct pc_proc_info_t +{ + // Pointer to the process function + ar_result_t (*process)(void * me_ptr, + capi_buf_t * input_buf_ptr, + capi_buf_t * output_buf_ptr, + pc_media_fmt_t *input_media_fmt_ptr, + pc_media_fmt_t *output_media_fmt_ptr); + + // Pointer to the output buffer. + // We point this to the de-interleaved-unpacked buffers part of the pc_lib_t struct, because + // 1.Actual buffers are not available in the init phase + // 2.Actual buffer ptr values may change + // 3.We might have to re-map the buffers run time since most of the process functions take deint-unpacked fmt + capi_buf_t *output_buffer_ptr; + + // Output media format for the module. Input media fmt is obtained form the previous process's output media fmt + pc_media_fmt_t output_media_fmt; + +} pc_proc_info_t; + +typedef struct pc_core_lib_t +{ + // Memory pointer to the memory required for channel maps and remap buffers + void *pc_mem_ptr; + + // Max of input and output number of channels + uint16_t max_num_channels; + + // Input Media format + pc_media_fmt_t input_media_fmt; + + // Output media format + pc_media_fmt_t output_media_fmt; + + // Flags to hold information on which module is enabled in the pipeline + pc_process_flags_t flags; + + // Number of modules with cannot do processing in-place + uint32_t no_of_buffering_stages; + + // Process info v-table required to call process of each module + pc_proc_info_t pc_proc_info[NUMBER_OF_PROCESS]; + + // Temporary de-interleaved-unpacked buffer ptr holder for fwk input + capi_buf_t *remap_input_buf_ptr; + + // Temporary de-interleaved-unpacked buffer ptr holder for scratch buffer 1 + capi_buf_t *remap_scratch_buf1_ptr; + + // Temporary de-interleaved-unpacked buffer ptr holder for scratch buffer 2 + capi_buf_t *remap_scratch_buf2_ptr; + + // Temporary de-interleaved-unpacked buffer ptr holder for fwk output buffer + capi_buf_t *remap_output_buf_ptr; +} pc_core_lib_t; + +// Main library structure for pcm converter +typedef struct pc_lib_t +{ + pc_core_lib_t core_lib; + + // log id used for printing + uint32_t miid; + + // Channel mixer library structure + ChMixerStateStruct *ch_lib_ptr; + + // either fir or iir resampler + pc_resampler_type_t resampler_type; + + // fir resampler config + uint32_t use_hw_rs; + + // fir resampler dyn mode + uint16_t dynamic_mode; + + // for resampler delay type + uint16_t delay_type; + + // hw-sw (fir) resampler lib + hwsw_resampler_lib_t *hwsw_rs_lib_ptr; + + // previous resampler position + pc_rs_position_flag_t resampler_used; + + // iir resampler lib + iir_rs_lib_t *iir_rs_lib_ptr; + + // For IIR_RS use cases, this is used to send the library the fixed frame size. It is also + // used to ensure we only consume one frame_size worth of data per process call. Capi client + // will send aggregated frame size (ex 20ms, or 1ms when threshold is disabed). + uint32_t cntr_frame_size_us; + uint32_t frame_size_us; + uint32_t in_frame_samples_per_ch; // To avoid process call divisions. + uint32_t out_frame_samples_per_ch; + uint32_t heap_id; + bool_t consume_partial_input; + bool_t iir_pref_set; +} pc_lib_t; + +// Process function + +ar_result_t pc_endianness_process(void * me_ptr, + capi_buf_t * input_buf_ptr, + capi_buf_t * output_buf_ptr, + pc_media_fmt_t *input_media_fmt_ptr, + pc_media_fmt_t *output_media_fmt_ptr); + +ar_result_t pc_interleaving_process(void * me_ptr, + capi_buf_t * input_buf_ptr, + capi_buf_t * output_buf_ptr, + pc_media_fmt_t *input_media_fmt_ptr, + pc_media_fmt_t *output_media_fmt_ptr); + +ar_result_t pc_byte_morph_process(void * me_ptr, + capi_buf_t * input_buf_ptr, + capi_buf_t * output_buf_ptr, + pc_media_fmt_t *input_media_fmt_ptr, + pc_media_fmt_t *output_media_fmt_ptr); + +ar_result_t pc_channel_mix_process(void * me_ptr, + capi_buf_t * input_buf_ptr, + capi_buf_t * output_buf_ptr, + pc_media_fmt_t *input_media_fmt_ptr, + pc_media_fmt_t *output_media_fmt_ptr); + +ar_result_t pc_dyn_resampler_process(void * me_ptr, + capi_buf_t * input_buf_ptr, + capi_buf_t * output_buf_ptr, + pc_media_fmt_t *input_media_fmt_ptr, + pc_media_fmt_t *output_media_fmt_ptr); + +ar_result_t pc_iir_resampler_process(void * me_ptr, + capi_buf_t * input_buf_ptr, + capi_buf_t * output_buf_ptr, + pc_media_fmt_t *input_media_fmt_ptr, + pc_media_fmt_t *output_media_fmt_ptr); + +ar_result_t pc_float_to_fixed_conv_process(void * me_ptr, + capi_buf_t * input_buf_ptr, + capi_buf_t * output_buf_ptr, + pc_media_fmt_t *input_media_fmt_ptr, + pc_media_fmt_t *output_media_fmt_ptr); + +ar_result_t pc_fixed_to_float_conv_process(void * me_ptr, + capi_buf_t * input_buf_ptr, + capi_buf_t * output_buf_ptr, + pc_media_fmt_t *input_media_fmt_ptr, + pc_media_fmt_t *output_media_fmt_ptr); + +ar_result_t pc_intlv_16_out(capi_buf_t *input_buf_ptr, + capi_buf_t *output_buf_ptr, + uint16_t word_size_in, + uint16_t q_factor_in); + +ar_result_t pc_intlv_24_out(capi_buf_t *input_buf_ptr, + capi_buf_t *output_buf_ptr, + uint16_t word_size_in, + uint16_t q_factor_in); + +ar_result_t pc_intlv_32_out(capi_buf_t *input_buf_ptr, + capi_buf_t *output_buf_ptr, + uint16_t word_size_in, + uint16_t q_factor_in, + uint16_t q_factor_out); + +/** Generates DEINTERLEAVED UNPACKED V1 output i.e updates all the chs data len's*/ +ar_result_t pc_deintlv_16_out(capi_buf_t *input_buf_ptr, + capi_buf_t *output_buf_ptr, + uint16_t num_channels, + uint16_t word_size_in, + uint16_t q_factor_in); + +/** Generates DEINTERLEAVED UNPACKED V1 output i.e updates all the chs data len's*/ +ar_result_t pc_deintlv_24_out(capi_buf_t *input_buf_ptr, + capi_buf_t *output_buf_ptr, + uint16_t num_channels, + uint16_t word_size_in, + uint16_t q_factor_in); + +/** Generates DEINTERLEAVED UNPACKED V1 output i.e updates all the chs data len's*/ +ar_result_t pc_deintlv_32_out(capi_buf_t *input_buf_ptr, + capi_buf_t *output_buf_ptr, + uint16_t num_channels, + uint16_t word_size_in, + uint16_t q_factor_in, + uint16_t q_factor_out); + +/** Generates DEINTERLEAVED UNPACKED V2 output i.e updates only first chs data len's*/ +ar_result_t pc_deintlv_unpacked_v2_16_out(capi_buf_t *input_buf_ptr, + capi_buf_t *output_buf_ptr, + uint16_t num_channels, + uint16_t word_size_in, + uint16_t q_factor_in); + +/** Generates DEINTERLEAVED UNPACKED V2 output i.e updates only first chs data len's*/ +ar_result_t pc_deintlv_unpacked_v2_24_out(capi_buf_t *input_buf_ptr, + capi_buf_t *output_buf_ptr, + uint16_t num_channels, + uint16_t word_size_in, + uint16_t q_factor_in); + +/** Generates DEINTERLEAVED UNPACKED V2 output i.e updates only first chs data len's*/ +ar_result_t pc_deintlv_unpacked_v2_32_out(capi_buf_t *input_buf_ptr, + capi_buf_t *output_buf_ptr, + uint16_t num_channels, + uint16_t word_size_in, + uint16_t q_factor_in, + uint16_t q_factor_out); + +ar_result_t pc_change_endianness(int8_t *src_ptr, + int8_t *dest_ptr, + uint32_t src_actual_len, + uint32_t *dest_actual_len, + uint32_t word_size); +/* ----------------------------------------------------------------------- + ** API Func + ** ----------------------------------------------------------------------- */ +bool_t pc_is_floating_point_data_format_supported(); +bool_t pc_is_ch_mixer_needed(pc_lib_t * pc_ptr, + int16_t * coef_set_ptr, + pc_media_fmt_t *input_media_fmt_ptr, + pc_media_fmt_t *output_media_fmt_ptr); + +ar_result_t pc_init(pc_lib_t * pc_ptr, + pc_media_fmt_t *input_media_fmt_ptr, + pc_media_fmt_t *output_media_fmt_ptr, + void * coef_set_ptr, + uint32_t heap_id, + bool_t * lib_enable, + bool_t fir_iir_rs_switch, + uint32_t miid, + uint8_t type); + +void pc_deinit(pc_lib_t *pc_ptr); + +void pc_get_kpps_and_bw(pc_lib_t *pc_ptr, uint32_t *kpps, uint32_t *bw); + +uint32_t pc_get_algo_delay(pc_lib_t *pc_ptr); + +pc_mf_combo_t pc_classify_mf(pc_media_fmt_t *mf_ptr); + +ar_result_t pc_process(pc_lib_t * me_ptr, + capi_buf_t *input_buf_ptr, + capi_buf_t *output_buf_ptr, + capi_buf_t *scratch_buf_ptr_1, + capi_buf_t *scratch_buf_ptr_2); + +uint32_t pc_get_fixed_out_samples(pc_lib_t *pc_ptr, uint32_t req_out_samples); + +void pc_set_frame_size(pc_lib_t *pc_ptr, uint32_t frame_size_us); + +void pc_set_cntr_frame_size(pc_lib_t *pc_ptr, uint32_t cntr_frame_size_us); +void pc_set_consume_partial_input(pc_lib_t *pc_ptr, bool_t consume_partial_input); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ +#endif // PCM_CONVERTER_H diff --git a/modules/cmn/pcm_mf_cnv/lib/src/pc_converter.cpp b/modules/cmn/pcm_mf_cnv/lib/src/pc_converter.cpp new file mode 100644 index 0000000..cbdf01f --- /dev/null +++ b/modules/cmn/pcm_mf_cnv/lib/src/pc_converter.cpp @@ -0,0 +1,75 @@ +/*======================================================================== + + file pcm_converter.cpp +This file contains functions for compression-decompression container + +Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. +SPDX-License-Identifier: BSD-3-Clause +======================================================================*/ + +/* ======================================================================= +INCLUDE FILES FOR MODULE +========================================================================== */ + +#include "pc_converter.h" + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + +______________________________________________________________________________________________________________________*/ + +/** changes endianess as it moves data into destination buffer, and returns the actual data length of the destination.*/ +ar_result_t pc_change_endianness(int8_t *in_ptr, + int8_t *out_ptr, + uint32_t src_actual_len, + uint32_t *dest_actual_len_ptr, + uint32_t word_size) +{ + uint32_t total_num_samples = src_actual_len / (word_size >> 3); + uint32_t i; + + if (16 == word_size) + { + int16_t *src_ptr_16 = (int16_t *)in_ptr; + int16_t *dst_ptr_16 = (int16_t *)out_ptr; + uint16_t val; + for (i = 0; i < total_num_samples; i++) + { + val = *src_ptr_16++; + *dst_ptr_16++ = (int16_t)(((val & 0xFF00) >> 8) | ((val & 0x00FF) << 8)); + } + } + else if (24 == word_size) + { + int8_t *src_ptr = in_ptr; + int8_t *dst_ptr = out_ptr; + uint8_t val1, val2, val3; + for (i = 0; i < total_num_samples; i++) + { + val1 = *src_ptr++; + val2 = *src_ptr++; + val3 = *src_ptr++; + *dst_ptr++ = val3; + *dst_ptr++ = val2; + *dst_ptr++ = val1; + } + } + else if (32 == word_size) + { + int32_t *dst_ptr = (int32_t *)out_ptr; + int32_t *src_ptr = (int32_t *)in_ptr; + uint32_t val; + for (i = 0; i < total_num_samples; i++) + { + val = *src_ptr++; +#if ((defined __hexagon__) || (defined __qdsp6__)) + *dst_ptr++ = Q6_R_swiz_R(val); +#else + *dst_ptr++ = (int32_t)((val & 0x000000FF) << 24 | (val & 0x0000FF00) << 8 | (val & 0x00FF0000) >> 8 | + (val & 0xFF000000) >> 24); +#endif + } + } + *dest_actual_len_ptr = src_actual_len; + return AR_EOK; +} diff --git a/modules/cmn/pcm_mf_cnv/lib/src/pc_converter_island.cpp b/modules/cmn/pcm_mf_cnv/lib/src/pc_converter_island.cpp new file mode 100644 index 0000000..bf0a8b8 --- /dev/null +++ b/modules/cmn/pcm_mf_cnv/lib/src/pc_converter_island.cpp @@ -0,0 +1,982 @@ +/*======================================================================== + + file pcm_converter.cpp +This file contains functions for compression-decompression container + +Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. +SPDX-License-Identifier: BSD-3-Clause +======================================================================*/ + +#include "pc_converter.h" + +/* ======================================================================= +INCLUDE FILES FOR MODULE +========================================================================== */ + +#if ((defined __hexagon__) || (defined __qdsp6__)) +#define USE_Q6_SPECIFIC_CODE +#endif +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + +______________________________________________________________________________________________________________________*/ +ar_result_t pc_intlv_16_out(capi_buf_t *input_buf_ptr, + capi_buf_t *output_buf_ptr, + uint16_t word_size_in, + uint16_t q_factor_in) +{ + + int32_t samp; + int32_t *buf_ptr_32; + int16_t *buf_ptr_16; +#ifdef USE_Q6_SPECIFIC_CODE + int64_t *buf_ptr_64; +#endif + uint32_t total_num_samples = input_buf_ptr->actual_data_len / (word_size_in >> 3); + // AR_MSG(DBG_ERROR_PRIO, "PCM_MF_CNV_LIB: ws - %d total_sample %d ", word_size_in, total_num_samples); + const uint32_t shift = (q_factor_in - 15); // (Qn - Q15) + + if (32 == word_size_in) + { +#ifdef USE_Q6_SPECIFIC_CODE + buf_ptr_64 = (int64_t *)(input_buf_ptr->data_ptr); /* Input buffer */ + buf_ptr_32 = (int32_t *)(output_buf_ptr->data_ptr); /* Output buffer */ + + /* If input buf addr is 8 byte aligned and out buf addr is 4 byte aligned + then only perform the vector operation + */ + if ((0 == ((uint32_t)buf_ptr_64 & 0x7)) && (0 == ((uint32_t)buf_ptr_32 & 0x3))) + { + for (samp = total_num_samples; samp >= 2; samp -= 2) + { + int64_t temp64 = *buf_ptr_64++; + /* Convert from Q31 to Q15*/ + + /* Right shift each of the 32 bit words into 16 bits and + store the two lower halfwords into destination + */ + *buf_ptr_32++ = Q6_R_vasrw_PR(temp64, shift); + } + + /* If the number of samples are odd, following loop will handle. */ + total_num_samples = samp; + } + /* if either of buf addr is not byte aligned as required for vectorization + * or for the remaining samples in case it's odd*/ + else + { + int32_t temp32; + + int16_t *buf_ptr_src_16 = (int16_t *)buf_ptr_32; + int32_t *buf_ptr_src2_32 = (int32_t *)buf_ptr_64; + + for (samp = 0; samp < total_num_samples; samp++) + { + temp32 = *buf_ptr_src2_32++; + /* Q28 to Q15 */ + /* First right shift followed by rounding operation */ + *buf_ptr_src_16++ = Q6_R_asr_RR(temp32, shift); + } + } + +#else + /*----------- Non Q6 Version ---------------*/ + buf_ptr_16 = (int16_t *)output_buf_ptr->data_ptr; + buf_ptr_32 = (int32_t *)input_buf_ptr->data_ptr; + + /* Qn to Q15 */ + // shift is 31-15 = 16 if q_factor_in is 31. + for (samp = 0; samp < total_num_samples; samp++) + { + int32_t temp32 = *buf_ptr_32++; + *buf_ptr_16++ = (int16_t)(temp32 >> shift); + } +#endif /* USE_Q6_SPECIFIC_CODE */ + } + else if (24 == word_size_in) + { + buf_ptr_16 = (int16_t *)output_buf_ptr->data_ptr; + uint8_t *src_ptr = (uint8_t *)input_buf_ptr->data_ptr; + /* Qn to Q15 */ + for (samp = 0; samp < total_num_samples; samp++) + { + int32_t num32; + uint32_t tem32; + + num32 = 0; + tem32 = *src_ptr++; + num32 = num32 | (tem32); + tem32 = *src_ptr++; + num32 = num32 | (tem32 << 8); + tem32 = *src_ptr++; + num32 = num32 | (tem32 << 16); + *buf_ptr_16++ = (int16_t)(num32 >> shift); + } + } + else + { + return AR_EUNSUPPORTED; + } + output_buf_ptr->actual_data_len = input_buf_ptr->actual_data_len * 2 / (word_size_in >> 3); + return AR_EOK; +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + +______________________________________________________________________________________________________________________*/ +ar_result_t pc_intlv_24_out(capi_buf_t *input_buf_ptr, + capi_buf_t *output_buf_ptr, + uint16_t word_size_in, + uint16_t q_factor_in) +{ + int32_t samp; + int32_t *buf_ptr_32; + int8_t * buf_ptr_8; + uint32_t total_num_samples = input_buf_ptr->actual_data_len / (word_size_in >> 3); + uint32_t shift; + + if (16 == word_size_in) + { + /*----------- Non Q6 Version ---------------*/ + + buf_ptr_8 = (int8_t *)output_buf_ptr->data_ptr; + uint8_t *src = (uint8_t *)input_buf_ptr->data_ptr; + + /* Q16 -> Q23 conversion */ + for (samp = 0; samp < total_num_samples; samp++) + { + *buf_ptr_8++ = 0x00; + *buf_ptr_8++ = *src++; + *buf_ptr_8++ = *src++; + } + } + else if (32 == word_size_in) + { + + shift = (q_factor_in - 23); // (Qn - Q23) + + uint8_t *dst = (uint8_t *)output_buf_ptr->data_ptr; + buf_ptr_32 = (int32_t *)input_buf_ptr->data_ptr; + int32_t num32 = 0; + uint8_t temp8 = 0; + + /* Q27/31 -> Q23 conversion */ + for (samp = 0; samp < total_num_samples; samp++) + { + num32 = *buf_ptr_32++ >> shift; + temp8 = (uint8_t)num32; + *dst++ = temp8; + + num32 = num32 >> 8; + temp8 = (uint8_t)num32; + *dst++ = temp8; + + num32 = num32 >> 8; + temp8 = (uint8_t)num32; + *dst++ = temp8; + } + } + else + { + return AR_EUNSUPPORTED; + } + + output_buf_ptr->actual_data_len = input_buf_ptr->actual_data_len * 3 / (word_size_in >> 3); + return AR_EOK; +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + +______________________________________________________________________________________________________________________*/ +ar_result_t pc_intlv_32_out(capi_buf_t *input_buf_ptr, + capi_buf_t *output_buf_ptr, + uint16_t word_size_in, + uint16_t q_factor_in, + uint16_t q_factor_out) +{ + + int32_t samp; + int32_t *buf_ptr_32; + int16_t *buf_ptr_16; + uint32_t total_num_samples = input_buf_ptr->actual_data_len / (word_size_in >> 3); +#ifdef USE_Q6_SPECIFIC_CODE + int64_t *buf_ptr_64; +#endif + uint32_t shift; + uint32_t min_value = 0; + uint32_t max_value = 0; + if (PCM_Q_FACTOR_23 == q_factor_in) + { + min_value = MIN_Q23; + max_value = MAX_Q23; + } + else if (PCM_Q_FACTOR_27 == q_factor_in) + { + min_value = MIN_Q27; + max_value = MAX_Q27; + } + else // when q_factor_in is other than 23 and 27 + { + min_value = (-(1 << q_factor_in)); + max_value = ((1 << q_factor_in) - 1); + } + if (16 == word_size_in) + { + // no in-place + if (input_buf_ptr == output_buf_ptr) + { + return AR_EBADPARAM; + } + + shift = (q_factor_out - q_factor_in); + +#ifdef USE_Q6_SPECIFIC_CODE + + buf_ptr_32 = (int32_t *)(input_buf_ptr->data_ptr); /* Input buffer */ + buf_ptr_64 = (int64_t *)(output_buf_ptr->data_ptr); /* Output buffer */ + + /* Convert from Q15 to Q31/27, conversion output in scratch buffer + */ + + /* If Output buf addr is 8 byte aligned and input buf addr is 4 byte aligned + then only perform the vector operation + */ + if ((0 == ((uint32_t)buf_ptr_64 & 0x7)) && (0 == ((uint32_t)buf_ptr_32 & 0x3))) + { + for (samp = total_num_samples; samp >= 2; samp -= 2) + { + /* Sign extend two 16-bit words in to two 32-bit words */ + *buf_ptr_64 = Q6_P_vsxthw_R(*buf_ptr_32++); + + /* Shift left the result to convert it to Q31/27 + */ + *buf_ptr_64 = Q6_P_vaslw_PR(*buf_ptr_64, shift); + buf_ptr_64++; + } + + /* If the number of samples per channel is odd */ + total_num_samples = samp; + } + /* if either of buf addr is not byte aligned as required for vectorization + * or for odd samples */ + else + { + buf_ptr_16 = (int16_t *)buf_ptr_32; + buf_ptr_32 = (int32_t *)buf_ptr_64; + + /* Q15 -> Q31/27 conversion */ + for (samp = 0; samp < total_num_samples; samp++) + { + (*buf_ptr_32++) = (int32_t)((*buf_ptr_16++) << shift); + } + } +#else + /*----------- Non Q6 Version ---------------*/ + + buf_ptr_16 = (int16_t *)input_buf_ptr->data_ptr; + buf_ptr_32 = (int32_t *)output_buf_ptr->data_ptr; + + /* Q15 -> Q31/27 conversion */ + for (samp = 0; samp < total_num_samples; samp++) + { + int32_t temp = *buf_ptr_16++; + (*buf_ptr_32++) = (int32_t)((temp) << shift); + } + +#endif /* USE_Q6_SPECIFIC_CODE */ + } + else if (24 == word_size_in) + { + /*----------- Non Q6 Version ---------------*/ + shift = (31 - q_factor_out); + buf_ptr_32 = (int32_t *)output_buf_ptr->data_ptr; + uint8_t *src = (uint8_t *)input_buf_ptr->data_ptr; + // POSAL_ASSERT(shift <= 8); + + /* Q23 -> Q27 conversion */ + for (samp = 0; samp < total_num_samples; samp++) + { + int32_t num32; + uint32_t tem32; + num32 = 0; + tem32 = *src++; + num32 = num32 | (tem32 << 8); + tem32 = *src++; + num32 = num32 | (tem32 << 16); + tem32 = *src++; + num32 = num32 | (tem32 << 24); + (*buf_ptr_32++) = (int32_t)((num32) >> (shift)); + } + } + else if (32 == word_size_in) + { +#ifdef USE_Q6_SPECIFIC_CODE + int64_t *in_buf_ptr_64 = (int64_t *)(input_buf_ptr->data_ptr); /* Input buffer */ + buf_ptr_64 = (int64_t *)(output_buf_ptr->data_ptr); /* Output buffer */ + + /* Convert from Q15 to Q27, conversion output in scratch buffer + */ + + /* If Output buf addr is 8 byte aligned and input buf addr is 8 byte aligned + then only perform the vector operation + */ + if ((q_factor_out < q_factor_in) && (0 == ((uint32_t)buf_ptr_64 & 0x7)) && (0 == ((uint32_t)in_buf_ptr_64 & 0x7))) + { + shift = q_factor_in - q_factor_out; + for (samp = total_num_samples; samp >= 2; samp -= 2) + { + /* Shift left the result to convert it to Q27 + */ + *buf_ptr_64 = Q6_P_vasrw_PR(*in_buf_ptr_64++, shift); + buf_ptr_64++; + } + + /* If the number of samples per channel is odd */ + total_num_samples = samp; + } + /* if either of buf addr is not byte aligned as required for vectorization + * or for odd samples */ + else + { + int32_t *in_buf_ptr_32 = (int32_t *)input_buf_ptr->data_ptr; + buf_ptr_32 = (int32_t *)output_buf_ptr->data_ptr; + if (q_factor_out >= q_factor_in) + { + shift = q_factor_out - q_factor_in; + } + else + { + shift = q_factor_in - q_factor_out; + } + + + if (q_factor_out >= q_factor_in) + { + for (samp = 0; samp < total_num_samples; samp++) + { + int32_t temp32 = (int32_t)(*in_buf_ptr_32++); + temp32 = (temp32 < (int32_t)min_value) ? (int32_t)min_value : temp32; + temp32 = (temp32 > (int32_t)max_value) ? (int32_t)max_value : temp32; + (*buf_ptr_32++) = temp32 << shift; + } + } + else + { + for (samp = 0; samp < total_num_samples; samp++) + { + (*buf_ptr_32++) = (int32_t)((*in_buf_ptr_32++) >> shift); + } + } + } +#else // USE_Q6_SPECIFIC_CODE + /*----------- Non Q6 Version ---------------*/ + int32_t *in_buf_ptr_32 = (int32_t *)input_buf_ptr->data_ptr; + buf_ptr_32 = (int32_t *)output_buf_ptr->data_ptr; + if (q_factor_out >= q_factor_in) + { + shift = q_factor_out - q_factor_in; + } + else + { + shift = q_factor_in - q_factor_out; + } + + if (q_factor_out >= q_factor_in) + { + for (samp = 0; samp < total_num_samples; samp++) + { + int32_t temp32 = (int32_t)(*in_buf_ptr_32++); + temp32 = (temp32 < (int32_t)min_value) ? (int32_t)min_value : temp32; + temp32 = (temp32 > (int32_t)max_value) ? (int32_t)max_value : temp32; + (*buf_ptr_32++) = temp32 << shift; + } + } + else + { + for (samp = 0; samp < total_num_samples; samp++) + { + (*buf_ptr_32++) = (int32_t)((*in_buf_ptr_32++) >> shift); + } + } +#endif + } + else + { + return AR_EUNSUPPORTED; + } + + output_buf_ptr->actual_data_len = input_buf_ptr->actual_data_len * 4 / (word_size_in >> 3); + return AR_EOK; +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + +______________________________________________________________________________________________________________________*/ +static ar_result_t pc_deintlv_16_out_util_(capi_buf_t *input_buf_ptr, + capi_buf_t *output_buf_ptr, + uint16_t num_channels, + uint16_t word_size_in, + uint16_t q_factor_in, + bool_t updates_only_first_ch_len) +{ + ar_result_t result = AR_EOK; + int32_t samp, i; + int32_t *buf_ptr_32; + int16_t *buf_ptr_16; + uint32_t shift_factor = 0; +#ifdef USE_Q6_SPECIFIC_CODE + int64_t *buf_ptr_64; +#endif + uint32_t num_samp_per_ch = input_buf_ptr->actual_data_len / (word_size_in >> 3); + + if (32 == word_size_in) + { + int64_t dw_MAX = 0; + int64_t dw_MIN = 0; + int32_t max_value = 0; + int32_t min_value = 0; + + if (PCM_Q_FACTOR_23 == q_factor_in) + { + dw_MAX = ((int64_t)MAX_Q23 << 32) | (int64_t)MAX_Q23; + dw_MIN = ((int64_t)MIN_Q23 << 32) | (int64_t)MIN_Q23; + shift_factor = PCM_Q_FACTOR_23 - PCM_Q_FACTOR_15; // q23 - q15 + max_value = (int32_t)MAX_Q23; + min_value = (int32_t)MIN_Q23; + } + else if (PCM_Q_FACTOR_27 == q_factor_in) + { + dw_MAX = ((int64_t)MAX_Q27 << 32) | (int64_t)MAX_Q27; + dw_MIN = ((int64_t)MIN_Q27 << 32) | (int64_t)MIN_Q27; + shift_factor = BYTE_UPDOWN_CONV_SHIFT_FACT; // q27 - q15 + max_value = (int32_t)MAX_Q27; + min_value = (int32_t)MIN_Q27; + } + // q31 to q15 + else if (PCM_Q_FACTOR_31 == q_factor_in) + { + dw_MAX = ((int64_t)MAX_Q31 << 32) | (int64_t)MAX_Q31; + dw_MIN = ((int64_t)MIN_Q31 << 32) | (int64_t)MIN_Q31; + shift_factor = PCM_Q_FACTOR_31 - PCM_Q_FACTOR_15; // q31 - q15 + max_value = (int32_t)MAX_Q31; + min_value = (int32_t)MIN_Q31; + } + +#ifdef USE_Q6_SPECIFIC_CODE + + for (i = 0; i < num_channels; i++) + { + buf_ptr_64 = (int64_t *)(input_buf_ptr[i].data_ptr); /* Input buffer */ + buf_ptr_32 = (int32_t *)(output_buf_ptr[i].data_ptr); /* Output buffer */ + + /* Convert from (Q27 or Q31) to Q15 */ + + /* If input buf addr is 8 byte aligned and out buf addr is 4 byte aligned + then only perform the vector operation */ + if ((0 == ((uint32_t)buf_ptr_64 & 0x7)) && (0 == ((uint32_t)buf_ptr_32 & 0x3))) + { + for (samp = num_samp_per_ch; samp >= 2; samp -= 2) + { + int64_t temp64; + + /* Saturate to 27 or 31 bits based on input q format */ + temp64 = Q6_P_vminw_PP(*buf_ptr_64++, dw_MAX); + temp64 = Q6_P_vmaxw_PP(temp64, dw_MIN); + + /* Right shift each of the 32 bit words into 16 bits and + store the two lower halfwords into destination */ + *buf_ptr_32++ = Q6_R_vasrw_PR(temp64, shift_factor); + } + + /* If the number of samples are odd */ + if (samp) + { + int32_t temp32; + + int16_t *buf_ptr_src_16 = (int16_t *)buf_ptr_32; + int32_t *buf_ptr_src2_32 = (int32_t *)buf_ptr_64; + + /* Saturate to 27 or 31 bits based on the input q format */ + temp32 = Q6_R_max_RR(*buf_ptr_src2_32, (int32_t)min_value); + temp32 = Q6_R_min_RR(temp32, (int32_t)max_value); + + /* Q27->Q15 or Q31->Q15 with rounding */ + *buf_ptr_src_16 = Q6_R_asr_RR(temp32, shift_factor); + } + } + else /* if either of buf addr is not byte aligned as required for vectorization */ + { + int32_t temp32; + + int16_t *buf_ptr_src_16 = (int16_t *)buf_ptr_32; + int32_t *buf_ptr_src2_32 = (int32_t *)buf_ptr_64; + + if (PCM_Q_FACTOR_27 == q_factor_in) + { + for (samp = 0; samp < num_samp_per_ch; samp++) + { + /* Saturate to 27 bits */ + temp32 = Q6_R_max_RR(*buf_ptr_src2_32++, (int32_t)min_value); + temp32 = Q6_R_min_RR(temp32, (int32_t)max_value); + + /* Q27 to Q15 */ + *buf_ptr_src_16++ = Q6_R_asr_RR(temp32, shift_factor); + } + } + else if (PCM_Q_FACTOR_31 == q_factor_in) + { + for (samp = 0; samp < num_samp_per_ch; samp++) + { + /* Saturate to 31 bits */ + temp32 = Q6_R_max_RR(*buf_ptr_src2_32++, (int32_t)min_value); + temp32 = Q6_R_min_RR(temp32, (int32_t)max_value); + + /* Q31 to Q15 */ + *buf_ptr_src_16++ = Q6_R_asr_RR(temp32, shift_factor); + } + } + } + } +#else + /*----------- Non Q6 Version ---------------*/ + for (i = 0; i < num_channels; i++) + { + buf_ptr_32 = (int32_t *)(input_buf_ptr[i].data_ptr); /* Input buffer */ + buf_ptr_16 = (int16_t *)(output_buf_ptr[i].data_ptr); /* Output buffer */ + + /* Convert from Q27 (or Q31) to Q15 , inplace conversion */ + for (samp = 0; samp < num_samp_per_ch; samp++) + { + int32_t temp32 = *buf_ptr_32++; + + /* Saturate to 27 or 31 bits */ + temp32 = (temp32 < (int32_t)min_value) ? (int32_t)min_value : temp32; + temp32 = (temp32 > (int32_t)max_value) ? (int32_t)max_value : temp32; + + /*shift to Q15 */ + *buf_ptr_16++ = (int16_t)(temp32 >> shift_factor); + } + } +#endif /* __q6_specific_code__ */ + } + else if (24 == word_size_in) + { + shift_factor = 23 - 15; + + for (i = 0; i < num_channels; i++) + { + uint8_t *src_ptr = (uint8_t *)(input_buf_ptr[i].data_ptr); /* Input buffer */ + buf_ptr_16 = (int16_t *)(output_buf_ptr[i].data_ptr); /* Output buffer */ + + /* Convert from Q27 (or Q31) to Q15 , inplace conversion */ + for (samp = 0; samp < num_samp_per_ch; samp++) + { + int32_t num32; + uint32_t tem32; + + num32 = 0; + tem32 = *src_ptr++; + num32 = num32 | (tem32); + tem32 = *src_ptr++; + num32 = num32 | (tem32 << 8); + tem32 = *src_ptr++; + num32 = num32 | (tem32 << 16); + /*Right shift to Q15*/ + *buf_ptr_16++ = (int16_t)(num32 >> shift_factor); + } + } + } + else + { + // optimization: write/read only first ch lens, and assume same lens for rest of the chs + output_buf_ptr[0].actual_data_len = 0; + result = AR_EUNSUPPORTED; + goto bailout_; + } + + // optimization: write/read only first ch lens, and assume same lens for rest of the chs + output_buf_ptr[0].actual_data_len = num_samp_per_ch * 2; + +bailout_: + // update rest of the channels as well + if (FALSE == updates_only_first_ch_len) + { + for (uint32_t i = 1; i < num_channels; i++) + { + output_buf_ptr[i].actual_data_len = output_buf_ptr[0].actual_data_len; + } + } + + return result; +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + +______________________________________________________________________________________________________________________*/ +static ar_result_t pc_deintlv_24_out_util_(capi_buf_t *input_buf_ptr, + capi_buf_t *output_buf_ptr, + uint16_t num_channels, + uint16_t word_size_in, + uint16_t q_factor_in, + bool_t updates_only_first_ch_len) +{ + ar_result_t result = AR_EOK; + int32_t samp, i; + int32_t *buf_ptr_32; + int8_t * buf_ptr_8; + uint32_t shift; + uint32_t num_samp_per_ch = input_buf_ptr->actual_data_len / (word_size_in >> 3); + + if (16 == word_size_in) + { + /*----------- Non Q6 Version ---------------*/ + + /* Q16 -> Q23 conversion */ + uint8_t *src; + for (i = 0; i < num_channels; i++) + { + buf_ptr_8 = (output_buf_ptr[i].data_ptr); /* Input buffer */ + src = (uint8_t *)(input_buf_ptr[i].data_ptr); /* Output buffer */ + + for (samp = 0; samp < num_samp_per_ch; samp++) + { + *buf_ptr_8++ = 0x00; + *buf_ptr_8++ = *src++; + *buf_ptr_8++ = *src++; + } + } + } + else if (32 == word_size_in) + { + + shift = (q_factor_in - 23); // (Qn - Q23) + + int32_t num32 = 0; + uint8_t temp8 = 0; + uint8_t *dst; + /* Q27/31 -> Q23 conversion */ + for (i = 0; i < num_channels; i++) + { + buf_ptr_32 = (int32_t *)(input_buf_ptr[i].data_ptr); /* Input buffer */ + dst = (uint8_t *)(output_buf_ptr[i].data_ptr); /* Output buffer */ + + for (samp = 0; samp < num_samp_per_ch; samp++) + { + num32 = *buf_ptr_32++ >> shift; + temp8 = (uint8_t)num32; + *dst++ = temp8; + + num32 = num32 >> 8; + temp8 = (uint8_t)num32; + *dst++ = temp8; + + num32 = num32 >> 8; + temp8 = (uint8_t)num32; + *dst++ = temp8; + } + } + } + else + { + // optimization: write/read only first ch lens, and assume same lens for rest of the chs + output_buf_ptr[0].actual_data_len = 0; + result = AR_EUNSUPPORTED; + goto bailout_; + } + // optimization: write/read only first ch lens, and assume same lens for rest of the chs + output_buf_ptr[0].actual_data_len = num_samp_per_ch * 3; + +bailout_: + // update rest of the channels as well + if (FALSE == updates_only_first_ch_len) + { + for (uint32_t i = 1; i < num_channels; i++) + { + output_buf_ptr[i].actual_data_len = output_buf_ptr[0].actual_data_len; + } + } + + return result; +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + +______________________________________________________________________________________________________________________*/ +static ar_result_t pc_deintlv_32_out_util_(capi_buf_t *input_buf_ptr, + capi_buf_t *output_buf_ptr, + uint16_t num_channels, + uint16_t word_size_in, + uint16_t q_factor_in, + uint16_t q_factor_out, + bool_t updates_only_first_ch_len) +{ + ar_result_t result = AR_EOK; + int32_t samp, i; + int32_t *buf_ptr_32; + uint8_t *buf_ptr_8; + uint32_t shift_factor = 0; + uint32_t num_samp_per_ch = input_buf_ptr->actual_data_len / (word_size_in >> 3); + uint32_t min_value = 0; + uint32_t max_value = 0; + if (PCM_Q_FACTOR_23 == q_factor_in) + { + min_value = MIN_Q23; + max_value = MAX_Q23; + } + else if (PCM_Q_FACTOR_27 == q_factor_in) + { + min_value = MIN_Q27; + max_value = MAX_Q27; + } + else // when q_factor_in is other than 23 and 27 + { + min_value = (-(1 << q_factor_in)); + max_value = ((1 << q_factor_in) - 1); + } + + if (16 == word_size_in) + { + shift_factor = q_factor_out - q_factor_in; +#ifdef USE_Q6_SPECIFIC_CODE + for (i = 0; i < num_channels; i++) + { + int32_t *buf_ptr_32 = (int32_t *)(input_buf_ptr[i].data_ptr); /* Input buffer */ + int64_t *buf_ptr_64 = (int64_t *)(output_buf_ptr[i].data_ptr); /* Output buffer */ + + + /* Convert from Q15 to Q28, conversion output in scratch buffer */ + + /* If Output buf addr is 8 byte aligned and input buf addr is 4 byte aligned + then only perform the vector operation */ + if ((0 == ((uint32_t)buf_ptr_64 & 0x7)) && (0 == ((uint32_t)buf_ptr_32 & 0x3))) + { + for (samp = num_samp_per_ch; samp >= 2; samp -= 2) + { + + /* Sign extend two 16-bit words in to two 32-bit words */ + *buf_ptr_64 = Q6_P_vsxthw_R(*buf_ptr_32++); + + + /* Shift left the result to convert it to Q27 or Q31 */ + *buf_ptr_64 = Q6_P_vaslw_PR(*buf_ptr_64, shift_factor); + buf_ptr_64++; + } + + /* If the number of samples per channel is odd */ + if (samp) + { + int16_t *buf_ptr_src_16 = (int16_t *)buf_ptr_32; + int32_t *buf_ptr_src2_32 = (int32_t *)buf_ptr_64; + *buf_ptr_src2_32 = (int32_t)(*buf_ptr_src_16 << shift_factor); + } + } + else /* if either of buf addr is not byte aligned as required for vectorization */ + { + int16_t *buf_ptr_src_16 = (int16_t *)buf_ptr_32; + int32_t *buf_ptr_src2_32 = (int32_t *)buf_ptr_64; + + /* Q15 -> Q27(Q31) conversion */ + for (samp = 0; samp < num_samp_per_ch; samp++) + { + (*buf_ptr_src2_32++) = (int32_t)((*buf_ptr_src_16++) << shift_factor); + } + } + } +#else + /*----------- Non Q6 Version ---------------*/ + for (i = 0; i < num_channels; i++) + { + int16_t *buf_ptr_16 = (int16_t *)(input_buf_ptr[i].data_ptr); /* Input buffer */ + buf_ptr_32 = (int32_t *)(output_buf_ptr[i].data_ptr); /* Output buffer */ + + /* Convert from Q15 to Q27(or Q31), conversion output in scratch buffer */ + for (samp = 0; samp < num_samp_per_ch; samp++) + { + *buf_ptr_32++ = ((int32_t)(*buf_ptr_16++)) << shift_factor; + } + } +#endif /* __qdsp6__ */ + } + else if (24 == word_size_in) + { + shift_factor = (31 - q_factor_out); + +#ifdef USE_Q6_SPECIFIC_CODE + for (i = 0; i < num_channels; i++) + { + buf_ptr_8 = (uint8_t *)(input_buf_ptr[i].data_ptr); /* Input buffer */ + buf_ptr_32 = (int32_t *)(output_buf_ptr[i].data_ptr); /* Output buffer */ + + int32_t num32 = 0; + int32_t tem32 = 0, tem32_1 = 0, tem32_2 = 0; + + /* Q23 -> Q27 conversion */ + for (samp = 0; samp < num_samp_per_ch; samp++) + { + num32 = 0; + tem32 = buf_ptr_8[0]; // load the 1st byte of a sample + tem32_1 = buf_ptr_8[1]; // load the 2nd byte of a sample + tem32_2 = buf_ptr_8[2]; // load the 3rd byte of a sample + + tem32 = Q6_R_aslor_RI(tem32, + tem32_1, + 8); // left shift 2nd byte by 8 and or with first byte -- later left shift total by 8 + num32 = Q6_R_aslor_RI(num32, tem32_2, 24); // left shift 3rd byte by 24 + buf_ptr_8 += 3; // increment the input buf ptr + + num32 = + Q6_R_aslor_RI(num32, tem32, 8); // effective shifts will be (1st - 8, 2nd - 16, 3rd - 24) for each byte + (*buf_ptr_32++) = (int32_t)((num32) >> (shift_factor)); + } + } +#else + for (i = 0; i < num_channels; i++) + { + buf_ptr_8 = (uint8_t *)(input_buf_ptr[i].data_ptr); /* Input buffer */ + buf_ptr_32 = (int32_t *)(output_buf_ptr[i].data_ptr); /* Output buffer */ + + /* Q23 -> Q27 conversion */ + for (samp = 0; samp < num_samp_per_ch; samp++) + { + int32_t num32; + uint32_t tem32; + num32 = 0; + + tem32 = *buf_ptr_8++; + num32 = num32 | (tem32 << 8); + tem32 = *buf_ptr_8++; + num32 = num32 | (tem32 << 16); + tem32 = *buf_ptr_8++; + num32 = num32 | (tem32 << 24); + + (*buf_ptr_32++) = (int32_t)((num32) >> (shift_factor)); + } + } +#endif + } + else + { + shift_factor = (q_factor_out - q_factor_in); // (Q31/27 - Qn) + + if (q_factor_out >= q_factor_in) + { + shift_factor = q_factor_out - q_factor_in; + } + else + { + shift_factor = q_factor_in - q_factor_out; + } + for (i = 0; i < num_channels; i++) + { + int32_t *in_buf_ptr_32 = (int32_t *)(input_buf_ptr[i].data_ptr); + buf_ptr_32 = (int32_t *)(output_buf_ptr[i].data_ptr); + + if (q_factor_out >= q_factor_in) + { + for (samp = 0; samp < num_samp_per_ch; samp++) + { + int32_t temp32 = (int32_t)(*in_buf_ptr_32++); + temp32 = (temp32 < (int32_t)min_value) ? (int32_t)min_value : temp32; + temp32 = (temp32 > (int32_t)max_value) ? (int32_t)max_value : temp32; + (*buf_ptr_32++) = temp32 << shift_factor; + } + } + else + { + for (samp = 0; samp < num_samp_per_ch; samp++) + { + (*buf_ptr_32++) = (int32_t)((*in_buf_ptr_32++) >> shift_factor); + } + } + } + } + + + // optimization: write/read only first ch lens, and assume same lens for rest of the chs + output_buf_ptr[0].actual_data_len = num_samp_per_ch * 4; + + // update rest of the channels as well + if (FALSE == updates_only_first_ch_len) + { + for (uint32_t i = 1; i < num_channels; i++) + { + output_buf_ptr[i].actual_data_len = output_buf_ptr[0].actual_data_len; + } + } + + + return result; +} + +ar_result_t pc_deintlv_16_out(capi_buf_t *input_buf_ptr, + capi_buf_t *output_buf_ptr, + uint16_t num_channels, + uint16_t word_size_in, + uint16_t q_factor_in) +{ + return pc_deintlv_16_out_util_(input_buf_ptr, output_buf_ptr, num_channels, word_size_in, q_factor_in, FALSE); +} + +ar_result_t pc_deintlv_24_out(capi_buf_t *input_buf_ptr, + capi_buf_t *output_buf_ptr, + uint16_t num_channels, + uint16_t word_size_in, + uint16_t q_factor_in) +{ + return pc_deintlv_24_out_util_(input_buf_ptr, output_buf_ptr, num_channels, word_size_in, q_factor_in, FALSE); +} + +ar_result_t pc_deintlv_32_out(capi_buf_t *input_buf_ptr, + capi_buf_t *output_buf_ptr, + uint16_t num_channels, + uint16_t word_size_in, + uint16_t q_factor_in, + uint16_t q_factor_out) +{ + return pc_deintlv_32_out_util_(input_buf_ptr, + output_buf_ptr, + num_channels, + word_size_in, + q_factor_in, + q_factor_out, + FALSE); +} + +ar_result_t pc_deintlv_unpacked_v2_16_out(capi_buf_t *input_buf_ptr, + capi_buf_t *output_buf_ptr, + uint16_t num_channels, + uint16_t word_size_in, + uint16_t q_factor_in) +{ + return pc_deintlv_16_out_util_(input_buf_ptr, output_buf_ptr, num_channels, word_size_in, q_factor_in, TRUE); +} + +ar_result_t pc_deintlv_unpacked_v2_24_out(capi_buf_t *input_buf_ptr, + capi_buf_t *output_buf_ptr, + uint16_t num_channels, + uint16_t word_size_in, + uint16_t q_factor_in) +{ + return pc_deintlv_24_out_util_(input_buf_ptr, output_buf_ptr, num_channels, word_size_in, q_factor_in, TRUE); +} + +ar_result_t pc_deintlv_unpacked_v2_32_out(capi_buf_t *input_buf_ptr, + capi_buf_t *output_buf_ptr, + uint16_t num_channels, + uint16_t word_size_in, + uint16_t q_factor_in, + uint16_t q_factor_out) +{ + return pc_deintlv_32_out_util_(input_buf_ptr, + output_buf_ptr, + num_channels, + word_size_in, + q_factor_in, + q_factor_out, + TRUE); +} diff --git a/modules/cmn/pcm_mf_cnv/lib/src/pc_float/pc_float.cpp b/modules/cmn/pcm_mf_cnv/lib/src/pc_float/pc_float.cpp new file mode 100644 index 0000000..cbc8153 --- /dev/null +++ b/modules/cmn/pcm_mf_cnv/lib/src/pc_float/pc_float.cpp @@ -0,0 +1,278 @@ +/*======================================================================== + + file pc_float.cpp +This file contains functions for data format conversions. + +Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. +SPDX-License-Identifier: BSD-3-Clause +======================================================================*/ + +/* ======================================================================= +INCLUDE FILES FOR MODULE +========================================================================== */ + +#include "pc_converter.h" + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: +Convert floating point input data to 32 bit (Q31) fixed point data, conversion to other bit widths is subsequently taken +care by byte morph process. +______________________________________________________________________________________________________________________*/ + +ar_result_t pc_float_to_fixed_conv_process(void * me_ptr, + capi_buf_t * input_buf_ptr, + capi_buf_t * output_buf_ptr, + pc_media_fmt_t *input_media_fmt_ptr, + pc_media_fmt_t *output_media_fmt_ptr) +{ + ar_result_t result = AR_EOK; + uint16_t bit_width_in = input_media_fmt_ptr->bit_width; + uint16_t word_size_in = input_media_fmt_ptr->word_size; + uint16_t word_size_out = output_media_fmt_ptr->word_size; + uint32_t samp = 0; + + // num_samples_in represents total samples for interleaved data, and samples per ch for deint data + uint32_t num_samples_in = + input_buf_ptr->actual_data_len / (word_size_in >> 3); // Calculate number of samples of input data + +#ifdef PCM_CNV_LIB_DEBUG + pc_lib_t *pc_ptr = (pc_lib_t *)me_ptr; + CNV_MSG(pc_ptr->miid, + DBG_HIGH_PRIO, + "Float to fixed: Word_size_in %d num_samples_in %d word_size_out %d, interleaving " + "%d", + word_size_in, + num_samples_in, + word_size_out, + input_media_fmt_ptr->interleaving); +#endif + + const uint32_t iq31 = (1 << PCM_Q_FACTOR_31); + const uint64_t iq31_1 = (1ll << PCM_Q_FACTOR_31); + + if (BIT_WIDTH_32 == bit_width_in) + { + int32_t num32 = 0; + float32_t * buf_ptr_32 = NULL; // Type cast input buffer to float data format + int32_t * dst_ptr_32 = NULL; + const float32_t fq31 = (float32_t)(iq31); + + if (PC_INTERLEAVED == input_media_fmt_ptr->interleaving) + { + buf_ptr_32 = (float32_t *)(input_buf_ptr->data_ptr); /* Input buffer */ + dst_ptr_32 = (int32_t *)(output_buf_ptr->data_ptr); /* Output buffer */ + + for (samp = 0; samp < num_samples_in; samp++) + { + // Operate on each input sample and converting to fixed point Q31 format + num32 = (int32_t)(*buf_ptr_32++ * fq31); + // Write converted sample to output buffer + *dst_ptr_32++ = num32; + } + output_buf_ptr->actual_data_len = num_samples_in * (word_size_out >> 3); + } + else + { + for (uint32_t i = 0; i < input_media_fmt_ptr->num_channels; i++) + { + buf_ptr_32 = (float32_t *)(input_buf_ptr[i].data_ptr); /* Input buffer */ + dst_ptr_32 = (int32_t *)(output_buf_ptr[i].data_ptr); /* Output buffer */ + + for (samp = 0; samp < num_samples_in; samp++) + { + // Operate on each input sample and converting to fixed point Q31 format + num32 = (int32_t)(*buf_ptr_32++ * fq31); + // Write converted sample to output buffer + *dst_ptr_32++ = num32; + } + } + + // optimization: write/read only first ch lens, and assume same lens for rest of the chs in the PC library. + output_buf_ptr[0].actual_data_len = num_samples_in * (word_size_out >> 3); + } + } + else if (BIT_WIDTH_64 == bit_width_in) + { + int32_t num32 = 0; + float64_t * buf_ptr_64 = NULL; + int32_t * dst_ptr_32 = NULL; + const float64_t dq31 = (float64_t)(iq31_1); + + if (PC_INTERLEAVED == input_media_fmt_ptr->interleaving) + { + buf_ptr_64 = (float64_t *)(input_buf_ptr->data_ptr); /* Input buffer */ + dst_ptr_32 = (int32_t *)(output_buf_ptr->data_ptr); /* Output buffer */ + + for (samp = 0; samp < num_samples_in; samp++) + { + // Operate on each input sample and converting to fixed point Q31 format + num32 = (int32_t)(*buf_ptr_64++ * dq31); + // Write converted sample to output buffer + *dst_ptr_32++ = num32; + } + output_buf_ptr->actual_data_len = num_samples_in * (word_size_out >> 3); + } + else + { + for (uint32_t i = 0; i < input_media_fmt_ptr->num_channels; i++) + { + buf_ptr_64 = (float64_t *)(input_buf_ptr[i].data_ptr); /* Input buffer */ + dst_ptr_32 = (int32_t *)(output_buf_ptr[i].data_ptr); /* Output buffer */ + + for (samp = 0; samp < num_samples_in; samp++) + { + // Operate on each input sample and converting to fixed point Q31 format + num32 = (int32_t)(*buf_ptr_64++ * dq31); + // Write converted sample to output buffer + *dst_ptr_32++ = num32; + } + } + // optimization: write/read only first ch lens, and assume same lens for rest of the chs in the PC library. + output_buf_ptr[0].actual_data_len = num_samples_in * (word_size_out >> 3); + } + } + else + { + result = AR_EUNSUPPORTED; + return result; + } + + return result; +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: +Convert 32 bit (Q31) Fixed point input data to Floating point, conversion from any other bit width to 32 bit fixed point +data is done by byte morph process before this function is called. +______________________________________________________________________________________________________________________*/ + +ar_result_t pc_fixed_to_float_conv_process(void * me_ptr, + capi_buf_t * input_buf_ptr, + capi_buf_t * output_buf_ptr, + pc_media_fmt_t *input_media_fmt_ptr, + pc_media_fmt_t *output_media_fmt_ptr) +{ + ar_result_t result = AR_EOK; + + uint16_t word_size_in = input_media_fmt_ptr->word_size; + uint16_t word_size_out = output_media_fmt_ptr->word_size; + uint16_t bit_width_out = output_media_fmt_ptr->bit_width; + uint32_t samp = 0; + int32_t *buf_ptr_32 = NULL; + + // num_samples_in represents total samples for interleaved data, and samples per ch for deint data + uint32_t num_samples_in = input_buf_ptr->actual_data_len / (word_size_in >> 3); + +#ifdef PCM_CNV_LIB_DEBUG + pc_lib_t *pc_ptr = (pc_lib_t *)me_ptr; + CNV_MSG(pc_ptr->miid, + DBG_HIGH_PRIO, + "Fixed to float: Word_size_in %d num_samples_in %d word_size_out %d, interleaving " + "%d", + word_size_in, + num_samples_in, + word_size_out, + input_media_fmt_ptr->interleaving); +#endif + + const uint32_t iq31 = (1 << PCM_Q_FACTOR_31); + const uint64_t iq31_1 = (1ll << PCM_Q_FACTOR_31); + + if (BIT_WIDTH_32 == bit_width_out) + { + float32_t num32 = 0; + float32_t * dst_ptr_32 = NULL; + const float32_t fq31 = (float32_t)(iq31); + + if (PC_INTERLEAVED == input_media_fmt_ptr->interleaving) + { + buf_ptr_32 = (int32_t *)(input_buf_ptr->data_ptr); /* Input buffer */ + dst_ptr_32 = (float32_t *)(output_buf_ptr->data_ptr); /* Output buffer */ + + for (samp = 0; samp < num_samples_in; samp++) + { + // Convert fixed point input sample of data to floating point + num32 = ((float32_t)*buf_ptr_32++) / fq31; + // Writing to output buffer + *dst_ptr_32++ = num32; + } + output_buf_ptr->actual_data_len = num_samples_in * (word_size_out >> 3); + } + else + { + for (uint32_t i = 0; i < input_media_fmt_ptr->num_channels; i++) + { + buf_ptr_32 = (int32_t *)(input_buf_ptr[i].data_ptr); /* Input buffer */ + dst_ptr_32 = (float32_t *)(output_buf_ptr[i].data_ptr); /* Output buffer */ + + for (samp = 0; samp < num_samples_in; samp++) + { + // Convert fixed point input sample of data to floating point + num32 = ((float32_t)*buf_ptr_32++) / fq31; + // Writing to output buffer + *dst_ptr_32++ = num32; + } + } + + // optimization: write/read only first ch lens, and assume same lens for rest of the chs in the PC library. + output_buf_ptr[0].actual_data_len = num_samples_in * (word_size_out >> 3); + } + } + else if (BIT_WIDTH_64 == bit_width_out) + { + float64_t num64 = 0; + float64_t * dst_ptr_64 = NULL; + const float64_t dq31 = (float64_t)(iq31_1); + + if (PC_INTERLEAVED == input_media_fmt_ptr->interleaving) + { + buf_ptr_32 = (int32_t *)(input_buf_ptr->data_ptr); /* Input buffer */ + dst_ptr_64 = (float64_t *)(output_buf_ptr->data_ptr); /* Output buffer */ + + for (samp = 0; samp < num_samples_in; samp++) + { + // Convert fixed point input sample of data to floating point + num64 = ((float64_t)*buf_ptr_32++) / dq31; + // Writing to output buffer + *dst_ptr_64++ = num64; + } + output_buf_ptr->actual_data_len = num_samples_in * (word_size_out >> 3); + } + else + { + for (uint32_t i = 0; i < input_media_fmt_ptr->num_channels; i++) + { + buf_ptr_32 = (int32_t *)(input_buf_ptr[i].data_ptr); /* Input buffer */ + dst_ptr_64 = (float64_t *)(output_buf_ptr[i].data_ptr); /* Output buffer */ + + for (samp = 0; samp < num_samples_in; samp++) + { + // Convert fixed point input sample of data to floating point + num64 = ((float64_t)*buf_ptr_32++) / dq31; + // Writing to output buffer + *dst_ptr_64++ = num64; + } + } + + // optimization: write/read only first ch lens, and assume same lens for rest of the chs in the PC library. + output_buf_ptr[0].actual_data_len = num_samples_in * (word_size_out >> 3); + } + } + else + { + result = AR_EUNSUPPORTED; + return result; + } + + return result; +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: +Function to check if floating point data format is supported by platform +______________________________________________________________________________________________________________________*/ + +bool_t pc_is_floating_point_data_format_supported() +{ + return TRUE; +} diff --git a/modules/cmn/pcm_mf_cnv/lib/src/pc_init.cpp b/modules/cmn/pcm_mf_cnv/lib/src/pc_init.cpp new file mode 100644 index 0000000..3bafca1 --- /dev/null +++ b/modules/cmn/pcm_mf_cnv/lib/src/pc_init.cpp @@ -0,0 +1,1681 @@ +/*______________________________________________________________________________________________________________________ + + file pc_process.cpp +This file contains functions for compression-decompression container + + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause +______________________________________________________________________________________________________________________*/ + +/*______________________________________________________________________________________________________________________ +INCLUDE FILES FOR MODULE +______________________________________________________________________________________________________________________*/ + +#include "pc_converter.h" + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + 1. Calculates the memory required for storing + a. Channel map data for input and output media formats + b. storing remap capi buffers * number of channels for input output scratch buffer 1 and scratch buffer 2 + c. Channel mixer library + 2. Frees if any memory is allocated already. It is better to free and relloc instead of tracking. + 3. Assign memory to the corresponding pointers +______________________________________________________________________________________________________________________*/ +ar_result_t pc_realloc_chmixer_memory(pc_lib_t *pc_ptr, + uint32_t *ch_mixer_lib_size_ptr, + uint16_t max_num_of_channels, + uint16_t input_num_of_channels, + uint16_t output_num_of_channels, + uint32_t heap_id) +{ + ar_result_t result = AR_EOK; + uint16_t * channel_type_ptr = NULL; + ChMixerStateStruct *ch_lib_ptr = NULL; + capi_buf_t * buf_ptr = NULL; + uint8_t * mem_ptr = NULL; + + // Just need to allocate for the input and output, the proc media fmt can copy the channel map + // from the input ptr or output ptr + uint32_t lib_size = 0; + ChMixerGetInstanceSize(&lib_size, input_num_of_channels, output_num_of_channels); + uint32_t size_to_malloc = sizeof(uint16_t) * max_num_of_channels * (NUMBER_OF_CHANNEL_MAPS); + size_to_malloc += sizeof(capi_buf_t) * max_num_of_channels * NUMBER_OF_REMAP_BUFFERS; + size_to_malloc += CAPI_ALIGN_8_BYTE(lib_size); + + if (NULL != pc_ptr->core_lib.pc_mem_ptr) + { + posal_memory_aligned_free((void *)pc_ptr->core_lib.pc_mem_ptr); + pc_ptr->core_lib.pc_mem_ptr = NULL; + } + + pc_ptr->core_lib.pc_mem_ptr = + (void *)posal_memory_aligned_malloc(size_to_malloc, MEM_ALIGN_EIGHT_BYTE, (POSAL_HEAP_ID)heap_id); + if (NULL == pc_ptr->core_lib.pc_mem_ptr) + { + return AR_ENOMEMORY; + } + + memset(pc_ptr->core_lib.pc_mem_ptr, 0, size_to_malloc); + + ch_lib_ptr = (ChMixerStateStruct *)pc_ptr->core_lib.pc_mem_ptr; + mem_ptr = (uint8_t *)pc_ptr->core_lib.pc_mem_ptr; + pc_ptr->ch_lib_ptr = ch_lib_ptr; + + *ch_mixer_lib_size_ptr = lib_size; + mem_ptr += CAPI_ALIGN_8_BYTE(lib_size); + channel_type_ptr = (uint16_t *)mem_ptr; + + pc_ptr->core_lib.input_media_fmt.channel_type = channel_type_ptr; + channel_type_ptr += max_num_of_channels; + + pc_ptr->core_lib.output_media_fmt.channel_type = channel_type_ptr; + channel_type_ptr += max_num_of_channels; + + buf_ptr = (capi_buf_t *)channel_type_ptr; + + pc_ptr->core_lib.remap_input_buf_ptr = buf_ptr; + buf_ptr += max_num_of_channels; + + pc_ptr->core_lib.remap_scratch_buf1_ptr = buf_ptr; + buf_ptr += max_num_of_channels; + + pc_ptr->core_lib.remap_scratch_buf2_ptr = buf_ptr; + buf_ptr += max_num_of_channels; + + pc_ptr->core_lib.remap_output_buf_ptr = buf_ptr; + + pc_ptr->core_lib.max_num_channels = max_num_of_channels; + return result; +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + 1. Check if sample rate is supported by iir resampler +______________________________________________________________________________________________________________________*/ +ar_result_t pc_iir_rs_validate_sample_rate(pc_lib_t *pc_ptr, uint32_t sample_rate) +{ + if (NULL == pc_ptr) + { + return AR_EBADPARAM; + } + + if ((IIR_RS_8K_SAMPLING_RATE != sample_rate) && (IIR_RS_16K_SAMPLING_RATE != sample_rate) && + (IIR_RS_24K_SAMPLING_RATE != sample_rate) && (IIR_RS_32K_SAMPLING_RATE != sample_rate) && + (IIR_RS_48K_SAMPLING_RATE != sample_rate) && (IIR_RS_96K_SAMPLING_RATE != sample_rate) && + (IIR_RS_192K_SAMPLING_RATE != sample_rate) && (IIR_RS_384K_SAMPLING_RATE != sample_rate)) + { + return AR_EBADPARAM; + } + + return AR_EOK; +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + 1. Check if frame size and input media format are set. If so, set frame size in samples per channel. +______________________________________________________________________________________________________________________*/ +void pc_iir_rs_check_set_frame_size_samples(pc_lib_t *pc_ptr) +{ + // As frame_size is only needed for IIR resampler, we can use this iir_rs input media format + // validity check to verify that input media format was initialized by this time. + uint32_t in_sr = pc_ptr->core_lib.input_media_fmt.sampling_rate; + uint32_t out_sr = pc_ptr->core_lib.output_media_fmt.sampling_rate; + if ((0 != pc_ptr->frame_size_us) && (AR_EOK == pc_iir_rs_validate_sample_rate(pc_ptr, in_sr)) && + (AR_EOK == pc_iir_rs_validate_sample_rate(pc_ptr, out_sr))) + { + pc_ptr->in_frame_samples_per_ch = capi_cmn_us_to_samples(pc_ptr->frame_size_us, in_sr); + pc_ptr->out_frame_samples_per_ch = (pc_ptr->in_frame_samples_per_ch * out_sr) / in_sr; + + CNV_MSG(pc_ptr->miid, + DBG_HIGH_PRIO, + "Frame size samples set to %ld input, %ld output", + pc_ptr->in_frame_samples_per_ch, + pc_ptr->out_frame_samples_per_ch); + } +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + 1. API call for capi to send the iir resampler library the frame size. +______________________________________________________________________________________________________________________*/ +void pc_set_frame_size(pc_lib_t *pc_ptr, uint32_t frame_size_us) +{ + pc_ptr->frame_size_us = frame_size_us; + pc_iir_rs_check_set_frame_size_samples(pc_ptr); + + CNV_MSG(pc_ptr->miid, + DBG_HIGH_PRIO, + "Cntr frame size set to %ld us, lib frame size %ld us", + pc_ptr->cntr_frame_size_us, + pc_ptr->frame_size_us); +} + +void pc_set_cntr_frame_size(pc_lib_t *pc_ptr, uint32_t cntr_frame_size_us) +{ + pc_ptr->cntr_frame_size_us = cntr_frame_size_us; + + CNV_MSG(pc_ptr->miid, + DBG_HIGH_PRIO, + "Cntr frame size set to %ld us, lib frame size %ld us", + pc_ptr->cntr_frame_size_us, + pc_ptr->frame_size_us); + + // If operating at min frame size, then we return to cntr frame size at process call context. + if (CNV_IIR_RS_MIN_FRAME_SIZE_US != pc_ptr->frame_size_us) + { + pc_set_frame_size(pc_ptr, pc_ptr->cntr_frame_size_us); + } +} + +void pc_set_consume_partial_input(pc_lib_t *pc_ptr, bool_t consume_partial_input) +{ + pc_ptr->consume_partial_input = consume_partial_input; + + // If exiting consume partial input, we return to cntr frame size at process call context. + if (consume_partial_input) + { + pc_set_frame_size(pc_ptr, CNV_IIR_RS_MIN_FRAME_SIZE_US); + } +} + +ar_result_t pc_check_alloc_fir_rs(pc_lib_t *pc_ptr, uint32_t heap_id, bool_t *fir_rs_reinit_ptr) +{ +#ifdef PCM_CNV_LIB_DEBUG + CNV_MSG(pc_ptr->miid, DBG_HIGH_PRIO, "FIR resampler to be used, allocating memory"); +#endif + if (NULL == pc_ptr->hwsw_rs_lib_ptr) + { + pc_ptr->hwsw_rs_lib_ptr = + (hwsw_resampler_lib_t *)posal_memory_malloc(sizeof(hwsw_resampler_lib_t), (POSAL_HEAP_ID)heap_id); + if (NULL == pc_ptr->hwsw_rs_lib_ptr) + { + return AR_ENOMEMORY; + } + memset(pc_ptr->hwsw_rs_lib_ptr, 0, sizeof(hwsw_resampler_lib_t)); + hwsw_rs_lib_init(pc_ptr->hwsw_rs_lib_ptr); + } + + // if it is fir resampler cache and check if any related config has changed + hwsw_rs_lib_set_config(pc_ptr->use_hw_rs, + pc_ptr->dynamic_mode, + pc_ptr->delay_type, + pc_ptr->hwsw_rs_lib_ptr, + fir_rs_reinit_ptr); + + // free iir resampler if allocated + if (pc_ptr->iir_rs_lib_ptr) + { + iir_rs_lib_deinit(pc_ptr->iir_rs_lib_ptr); + posal_memory_free(pc_ptr->iir_rs_lib_ptr); + pc_ptr->iir_rs_lib_ptr = NULL; + } + return AR_EOK; +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + 1. Allocates resampler lib ptr memory if resamplers are needed depending on resampler type + 2. If the other type of resampler exists, de-allocate it + 3. Does lib struct initialization + 4. When allocating for iir resampler, checks if current input output sample rates are supported + 5. Errors out, if iir resampler has unsupported sampling rates, + does not fall back on dynamic resampler + 6. Falls back on fir resampler if iir preferred is set + 7. If resampling is not needed, frees memory of resampler +______________________________________________________________________________________________________________________*/ +ar_result_t pc_check_alloc_resampler_memory(pc_lib_t * pc_ptr, + uint32_t heap_id, + bool_t * fir_rs_reinit_ptr, + bool_t * fir_iir_rs_switch_ptr, + pc_media_fmt_t *input_media_fmt_ptr, + pc_media_fmt_t *output_media_fmt_ptr, + bool_t need_for_resampler) + +{ + ar_result_t result = AR_EOK; + // resampler is needed + if (need_for_resampler) + { + // If FIR Resampler has been configured via api PARAM_ID_MFC_RESAMPLER_CFG + if ((FIR_RESAMPLER == pc_ptr->resampler_type) && (!pc_ptr->iir_pref_set)) + { + // Function will check if current is also sw fir and wont reinit if so + pc_check_alloc_fir_rs(pc_ptr, heap_id, fir_rs_reinit_ptr); + } + /* IF IIR Pref or IIR resampler has been configured via api PARAM_ID_MFC_RESAMPLER_CFG*/ + else + { + result |= pc_iir_rs_validate_sample_rate(pc_ptr, input_media_fmt_ptr->sampling_rate); + result |= pc_iir_rs_validate_sample_rate(pc_ptr, output_media_fmt_ptr->sampling_rate); + + // If SR validation fails + if (AR_EOK != result) + { + if (!pc_ptr->iir_pref_set) // If IIR resampler was configured via api PARAM_ID_MFC_RESAMPLER_CFG + { + CNV_MSG(pc_ptr->miid, + DBG_ERROR_PRIO, + "Error! Disabling module, IIR resampler cannot support either input SR %d or output SR %d, IIR " + "pref flag %d ", + input_media_fmt_ptr->sampling_rate, + output_media_fmt_ptr->sampling_rate, + pc_ptr->iir_pref_set); + + // This will disable module in capi layer if result is FAILED + return AR_EFAILED; + } + else // If iir pref rs was configured, since sample rate validation failed final rs should be fir + { + CNV_MSG(pc_ptr->miid, + DBG_HIGH_PRIO, + "IIR resampler cannot support input SR %d or output SR %d, changing resampler type from current " + "type:" + "%d(0: FIR, 1: IIR) to FIR rs, IIR pref flag %d", + input_media_fmt_ptr->sampling_rate, + output_media_fmt_ptr->sampling_rate, + pc_ptr->resampler_type, + pc_ptr->iir_pref_set); + + if (IIR_RESAMPLER == pc_ptr->resampler_type) + { + // Need to set switch flag now, if it was wrongly determined as IIR in set param context + *fir_iir_rs_switch_ptr = TRUE; + } + + // Set final rs type and init + pc_ptr->resampler_type = FIR_RESAMPLER; + return pc_check_alloc_fir_rs(pc_ptr, heap_id, fir_rs_reinit_ptr); + } + } + + // Sr validation didnt fail, need to set final rs to IIR + if (FIR_RESAMPLER == pc_ptr->resampler_type) + { + CNV_MSG(pc_ptr->miid, + DBG_HIGH_PRIO, + "IIR resampler can be used for current sample rates. Changing from current " + "type %d(0: FIR, 1: IIR) to IIR, IIR pref flag %d", + pc_ptr->resampler_type, + pc_ptr->iir_pref_set); + + // Need to set switch flag now, if it was wrongly determined as IIR in set param context + *fir_iir_rs_switch_ptr = TRUE; + } + // Then change the current type and init + pc_ptr->resampler_type = IIR_RESAMPLER; + + if (NULL == pc_ptr->iir_rs_lib_ptr) + { +#ifdef PCM_CNV_LIB_DEBUG + CNV_MSG(pc_ptr->miid, DBG_HIGH_PRIO, "IIR resampler to be used, allocating memory"); +#endif + pc_ptr->iir_rs_lib_ptr = (iir_rs_lib_t *)posal_memory_malloc(sizeof(iir_rs_lib_t), (POSAL_HEAP_ID)heap_id); + if (NULL == pc_ptr->iir_rs_lib_ptr) + { + return AR_ENOMEMORY; + } + + memset(pc_ptr->iir_rs_lib_ptr, 0, sizeof(iir_rs_lib_t)); + + // free fir resampler if allocated + if (pc_ptr->hwsw_rs_lib_ptr) + { + hwsw_rs_lib_deinit(pc_ptr->hwsw_rs_lib_ptr); + posal_memory_free(pc_ptr->hwsw_rs_lib_ptr); + pc_ptr->hwsw_rs_lib_ptr = NULL; + } + } + } + } + else // do not need a resampler + { + // free fir resampler if allocated + if (pc_ptr->hwsw_rs_lib_ptr) + { + hwsw_rs_lib_deinit(pc_ptr->hwsw_rs_lib_ptr); + posal_memory_free(pc_ptr->hwsw_rs_lib_ptr); + pc_ptr->hwsw_rs_lib_ptr = NULL; + } + + // free iir resampler if allocated + if (pc_ptr->iir_rs_lib_ptr) + { + iir_rs_lib_deinit(pc_ptr->iir_rs_lib_ptr); + posal_memory_free(pc_ptr->iir_rs_lib_ptr); + pc_ptr->iir_rs_lib_ptr = NULL; + } + } + + return result; +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + Creates or reinits resamplers depending on config, sampling rates, num channels and bitwidth + Can be sw fir, hw fir or iir +______________________________________________________________________________________________________________________*/ +ar_result_t pc_check_create_resampler_instance(pc_lib_t *pc_ptr, uint32_t heap_id, bool_t fir_rs_reinit) +{ + ar_result_t result = AR_EOK; + if ((pc_ptr->core_lib.flags.RESAMPLER_POST) || (pc_ptr->core_lib.flags.RESAMPLER_PRE)) + { + if (FIR_RESAMPLER == pc_ptr->resampler_type) + { + // mf checks already done + result = hwsw_rs_lib_check_create_resampler_instance(pc_ptr->hwsw_rs_lib_ptr, heap_id); + if (result != AR_EOK) + { + CNV_MSG(pc_ptr->miid, DBG_ERROR_PRIO, "Resampler check create failed"); + } + } + else if (IIR_RESAMPLER == pc_ptr->resampler_type) + { + pc_media_fmt_t *rs_imf_ptr = NULL; + if (TRUE == pc_ptr->core_lib.flags.RESAMPLER_PRE) + { + if (TRUE == pc_ptr->core_lib.flags.BYTE_CNV_PRE) + { + rs_imf_ptr = &pc_ptr->core_lib.pc_proc_info[BYTE_CNV_PRE].output_media_fmt; + } + else + { + rs_imf_ptr = &pc_ptr->core_lib.input_media_fmt; + } + } + else // if (TRUE == pc_ptr->core_lib.flags.RESAMPLER_POST) -> omitted for static analysis + { + if (TRUE == pc_ptr->core_lib.flags.CHANNEL_MIXER) + { + rs_imf_ptr = &pc_ptr->core_lib.pc_proc_info[CHANNEL_MIXER].output_media_fmt; + } + else if (TRUE == pc_ptr->core_lib.flags.BYTE_CNV_PRE) + { + rs_imf_ptr = &pc_ptr->core_lib.pc_proc_info[BYTE_CNV_PRE].output_media_fmt; + } + else + { + rs_imf_ptr = &pc_ptr->core_lib.input_media_fmt; + } + } + + CNV_MSG(pc_ptr->miid, + DBG_HIGH_PRIO, + "IIR IN sampling_rate %lu, out sampling_rate %lu, ch = %lu", + rs_imf_ptr->sampling_rate, + pc_ptr->core_lib.output_media_fmt.sampling_rate, + rs_imf_ptr->num_channels); + + // Setting channel malloc flag since iir resampler needs to re-alloc memory + result = iir_rs_lib_allocate_memory(pc_ptr->iir_rs_lib_ptr, + rs_imf_ptr->sampling_rate, + pc_ptr->core_lib.output_media_fmt.sampling_rate, + rs_imf_ptr->num_channels, + rs_imf_ptr->bit_width, + IIR_RS_FRAME_LEN_MAX_MS, + heap_id); + if (AR_EOK == result) + { + result = iir_rs_lib_clear_algo_memory(pc_ptr->iir_rs_lib_ptr); + if (AR_EOK != result) + { + CNV_MSG(pc_ptr->miid, DBG_ERROR_PRIO, "Failed to init IIR resampler"); + } + } + else + { + CNV_MSG(pc_ptr->miid, DBG_ERROR_PRIO, "Failed to allocate memory for IIR resampler"); + } + } + else + { + // By this time we should already figure out whether to use FIR or IIR resampler for IIR preferred option + CNV_MSG(pc_ptr->miid, DBG_ERROR_PRIO, "Unsupported resampler type %d", pc_ptr->resampler_type); + return AR_EUNSUPPORTED; + } + } + + return result; +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + Calculates expected input samples for a given number of out samples +______________________________________________________________________________________________________________________*/ + +uint32_t pc_get_fixed_out_samples(pc_lib_t *pc_ptr, uint32_t req_out_samples) +{ + if ((NULL != pc_ptr->hwsw_rs_lib_ptr->sw_rs_mem_ptr[STAGE_ZERO]) && + (!pc_ptr->hwsw_rs_lib_ptr->sw_rs_mem_ptr[STAGE_ZERO]->drs_mem_ptr.pStructMem)) + { + CNV_MSG(pc_ptr->miid, DBG_ERROR_PRIO, "sw resampler struct is NULL"); + return 0; + } + + uint32_t expected_in_samples; + if (pc_ptr->hwsw_rs_lib_ptr->is_multi_stage_process) + { + uint32_t expected_in_samples_intm = + resamp_calc_fixedout(pc_ptr->hwsw_rs_lib_ptr->sw_rs_mem_ptr[STAGE_ONE]->drs_mem_ptr.pStructMem, + req_out_samples); + expected_in_samples = + resamp_calc_fixedout(pc_ptr->hwsw_rs_lib_ptr->sw_rs_mem_ptr[STAGE_ZERO]->drs_mem_ptr.pStructMem, + expected_in_samples_intm); + } + else + { + expected_in_samples = + resamp_calc_fixedout(pc_ptr->hwsw_rs_lib_ptr->sw_rs_mem_ptr[STAGE_ZERO]->drs_mem_ptr.pStructMem, + req_out_samples); + } + + return expected_in_samples; +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + Checks if reordering is needed + 1. Whenever update_rs_config is set, resampler will reinit and cause glitch, hence reordering is allowed + 2. Else + a. If sw resampler is being used in dynamic mode and sampling rate changes, we don't want to reorder + and cause a glitch + b. If pre resampler is used and input num ch remains same, don't reorder since out num ch for resampler won't +change + c. If post resampler is used and output num ch remains same, don't reorder since inp num ch for resampler won't +change +______________________________________________________________________________________________________________________*/ +bool_t pc_is_reorder_allowed(pc_lib_t * pc_ptr, + pc_media_fmt_t *input_media_fmt_ptr, + pc_media_fmt_t *output_media_fmt_ptr, + bool_t fir_iir_rs_switch, + bool_t fir_rs_reinit) +{ + if (fir_iir_rs_switch || fir_rs_reinit) + { + return TRUE; + } + else if ((((FIR_RESAMPLER == pc_ptr->resampler_type) && + ((pc_ptr->hwsw_rs_lib_ptr) && (SW_RESAMPLER == pc_ptr->hwsw_rs_lib_ptr->this_resampler_instance_using) && + (pc_ptr->hwsw_rs_lib_ptr->sw_rs_config_param.dynamic_mode) && + (pc_ptr->core_lib.input_media_fmt.sampling_rate != input_media_fmt_ptr->sampling_rate)))) || + ((RESAMPLER_POSITION_PRE == pc_ptr->resampler_used) && + (input_media_fmt_ptr->num_channels == pc_ptr->core_lib.input_media_fmt.num_channels)) || + ((RESAMPLER_POSITION_POST == pc_ptr->resampler_used) && + (output_media_fmt_ptr->num_channels == pc_ptr->core_lib.output_media_fmt.num_channels))) + { + return FALSE; + } + + return TRUE; +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + Copies the media format and checks if reorder is allowed + If resampler config is updated there will be a re-init so can skip the reorder check +______________________________________________________________________________________________________________________*/ +void pc_copy_media_fmt(pc_lib_t *pc_ptr, pc_media_fmt_t *input_media_fmt_ptr, pc_media_fmt_t *output_media_fmt_ptr) +{ + pc_ptr->core_lib.input_media_fmt.alignment = input_media_fmt_ptr->alignment; + pc_ptr->core_lib.input_media_fmt.bit_width = input_media_fmt_ptr->bit_width; + pc_ptr->core_lib.input_media_fmt.byte_combo = input_media_fmt_ptr->byte_combo; + pc_ptr->core_lib.input_media_fmt.endianness = input_media_fmt_ptr->endianness; + pc_ptr->core_lib.input_media_fmt.interleaving = input_media_fmt_ptr->interleaving; + pc_ptr->core_lib.input_media_fmt.num_channels = input_media_fmt_ptr->num_channels; + pc_ptr->core_lib.input_media_fmt.q_factor = input_media_fmt_ptr->q_factor; + pc_ptr->core_lib.input_media_fmt.word_size = input_media_fmt_ptr->word_size; + pc_ptr->core_lib.input_media_fmt.sampling_rate = input_media_fmt_ptr->sampling_rate; + pc_ptr->core_lib.input_media_fmt.data_format = input_media_fmt_ptr->data_format; + + for (uint32_t ch = 0; ch < pc_ptr->core_lib.input_media_fmt.num_channels; ch++) + { + pc_ptr->core_lib.input_media_fmt.channel_type[ch] = input_media_fmt_ptr->channel_type[ch]; + } + + pc_ptr->core_lib.output_media_fmt.alignment = output_media_fmt_ptr->alignment; + pc_ptr->core_lib.output_media_fmt.bit_width = output_media_fmt_ptr->bit_width; + pc_ptr->core_lib.output_media_fmt.byte_combo = output_media_fmt_ptr->byte_combo; + pc_ptr->core_lib.output_media_fmt.endianness = output_media_fmt_ptr->endianness; + pc_ptr->core_lib.output_media_fmt.interleaving = output_media_fmt_ptr->interleaving; + pc_ptr->core_lib.output_media_fmt.num_channels = output_media_fmt_ptr->num_channels; + pc_ptr->core_lib.output_media_fmt.q_factor = output_media_fmt_ptr->q_factor; + pc_ptr->core_lib.output_media_fmt.word_size = output_media_fmt_ptr->word_size; + pc_ptr->core_lib.output_media_fmt.sampling_rate = output_media_fmt_ptr->sampling_rate; + pc_ptr->core_lib.output_media_fmt.data_format = output_media_fmt_ptr->data_format; + + for (uint32_t ch = 0; ch < pc_ptr->core_lib.output_media_fmt.num_channels; ch++) + { + pc_ptr->core_lib.output_media_fmt.channel_type[ch] = output_media_fmt_ptr->channel_type[ch]; + } + + pc_iir_rs_check_set_frame_size_samples(pc_ptr); +} + +/* When the inp/out data format is different and either one of the input or out byte combo is PC_BW32_W32_Q31, +it translates to a data format conversion ONLY. In such cases byte conv should not be enabled. */ +bool_t pc_byte_combo_change_handled_by_dfc(pc_mf_combo_t input_byte_combo, pc_mf_combo_t output_byte_combo) +{ + if (((PC_BW32_W32_FLOAT == input_byte_combo) && (PC_BW32_W32_Q31 == output_byte_combo)) || + ((PC_BW32_W32_FLOAT == output_byte_combo) && (PC_BW32_W32_Q31 == input_byte_combo)) || + ((PC_BW64_W64_DOUBLE == input_byte_combo) && (PC_BW32_W32_Q31 == output_byte_combo)) || + ((PC_BW64_W64_DOUBLE == output_byte_combo) && (PC_BW32_W32_Q31 == input_byte_combo))) + { + return TRUE; + } + + return FALSE; +} +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + ALGORITHM to create the process pipeline + CH - channel mixer, B1/B2 - byte converters, I1/I2 - interleaving/deinterleaving, E - endianness + I - Interleaved + D - Deinterleaved + Vchi - Valid ch-mixer byte format at the input + Vcho- Valid ch-mixer byte format at the output + Chi - number channels at input + Cho - number of channels at output + + 1. Enable/disable E1/E2 + a. If Inp is BE -> enable E1 + b. If out is BE -> enable E2 + 2. Find if CH is needed + 3. Based on CH and I info, find if I1 or/and I2 are needed + a. If CH is not needed + i. if input is I and output is D - > use I1 to convert from I to D + ii. else, if input is D and output is I - > use I2 to convert from D to I + iii. for I to I, do nothing, for D to D do nothing + b. If CH is needed + i. for I to I, enable both I1(does I to D) and I2(does D to I) + ii. For D to D, do nothing + iii. for I to D and D to I follow same approach as id CH is not present + 4. If IIR resampler is needed and BW is not 16, use B1 to convert to BW16 and B2 to convert to out BW. + 5. If Ch is not needed + a. enable B1 based on B + 6. If Ch is needed + a. Enable B1 and B2 If !Vchi and !Vcho + b. If just Vchi + i. Don’t care if Byte conversion is not needed + ii. B2 if needed + c. If just Vcho + i. Don’t care if byte conversion is not needed + ii. B1 if needed + d. If both Vcho and Vchi + i. If byte conversion is TRUE + 1) B1 if Chi < Cho + 2) B2 if Chi >= Cho + 7. If resampler is needed + a. reorder is allowed + i. channel in >= channel out covers no ch mix needed case also + ii. channel in < channel out + b. reorder not allowed, use previous position + 8. Now we know the values of I1, B1, R1, Ch, R2, B2, I2 . Make sure to record the correct values of conversion during +the +above phase +______________________________________________________________________________________________________________________*/ +static void pc_identify_required_processes(pc_lib_t * pc_ptr, + pc_media_fmt_t *input_media_fmt_ptr, + pc_media_fmt_t *output_media_fmt_ptr, + bool_t * need_for_resampler_ptr, + bool_t * enable_for_deint_packed_unpacked_cnv, + int16_t * coef_set_ptr, + uint8_t type) +{ + *need_for_resampler_ptr = FALSE; + *enable_for_deint_packed_unpacked_cnv = FALSE; + // set the flags to zero before assigning values to them. This should not be done in pc_realloc_memory as flags will + // already be set + pc_ptr->core_lib.flags.word = 0; + if ((input_media_fmt_ptr->sampling_rate != output_media_fmt_ptr->sampling_rate) || + ((pc_ptr->dynamic_mode) && (pc_ptr->resampler_type == FIR_RESAMPLER))) + { + *need_for_resampler_ptr = TRUE; + } + + if (PC_BIG_ENDIAN == input_media_fmt_ptr->endianness) + { + pc_ptr->core_lib.flags.ENDIANNESS_PRE = TRUE; + } + else + { + if (PC_BIG_ENDIAN == output_media_fmt_ptr->endianness) + { + pc_ptr->core_lib.flags.ENDIANNESS_POST = TRUE; + } + } + + pc_ptr->core_lib.flags.CHANNEL_MIXER = + pc_is_ch_mixer_needed(pc_ptr, coef_set_ptr, input_media_fmt_ptr, output_media_fmt_ptr); + if (pc_ptr->core_lib.flags.CHANNEL_MIXER) + { + if (PC_INTERLEAVED == input_media_fmt_ptr->interleaving) + { + pc_ptr->core_lib.flags.INT_DEINT_PRE = TRUE; + } + if (PC_INTERLEAVED == output_media_fmt_ptr->interleaving) + { + pc_ptr->core_lib.flags.INT_DEINT_POST = TRUE; + } + } + // if the num of channels is 1 and type is mfc and pcm converter we need to skip enablng the interleaving flags + // needed when we do not need ch mixer + else if (!((input_media_fmt_ptr->num_channels == 1) && ((type == MFC) || (type == PCM_CNV)))) + { + if ((PC_INTERLEAVED == input_media_fmt_ptr->interleaving) && + (PC_INTERLEAVED != output_media_fmt_ptr->interleaving)) + { + pc_ptr->core_lib.flags.INT_DEINT_PRE = TRUE; + } + if ((PC_INTERLEAVED != input_media_fmt_ptr->interleaving) && + (PC_INTERLEAVED == output_media_fmt_ptr->interleaving)) + { + pc_ptr->core_lib.flags.INT_DEINT_POST = TRUE; + } + // If num channels are not equal module will anyway be enabled due to channel mixer check + // If num channels are equal and inp and out interleaving conv is needed for deint packed to deint unpacked or + // vice versa + // we need to keep the module enabled. Pack-unpack conv happens through buffer sizing and not by enabling the I1 + // and I2 processes in the pcm cnv + // If num ch is 1, fwk takes care of propagating interleaving flag to downstream modules and pcm cnv/mfc can be + // bypassed + if ((input_media_fmt_ptr->interleaving != output_media_fmt_ptr->interleaving) && + (PC_INTERLEAVED != input_media_fmt_ptr->interleaving) && + (PC_INTERLEAVED != output_media_fmt_ptr->interleaving)) + { + *enable_for_deint_packed_unpacked_cnv = TRUE; + } + } + if (input_media_fmt_ptr->byte_combo == output_media_fmt_ptr->byte_combo) + { + if (((TRUE == pc_ptr->core_lib.flags.CHANNEL_MIXER) || (TRUE == *need_for_resampler_ptr))) + { + // Up conversion is needed + if (PC_BW24_W24_Q23 == input_media_fmt_ptr->byte_combo) + { + pc_ptr->core_lib.flags.BYTE_CNV_PRE = TRUE; + pc_ptr->core_lib.flags.BYTE_CNV_POST = TRUE; + } + // Down conversion is needed + if ((IIR_RESAMPLER == pc_ptr->resampler_type) && (PC_BW16_W16_Q15 != input_media_fmt_ptr->byte_combo)) + { + pc_ptr->core_lib.flags.BYTE_CNV_PRE = TRUE; + pc_ptr->core_lib.flags.BYTE_CNV_POST = TRUE; + } + } + } + /* else if case translates to byte combo being different. + * When we convert for certain fixed to float formats or vice versa, "byte_combo" notation in pcm converter is + * different + * but it actually translates to a data format conversion ONLY. In such cases byte conv should not be enabled. */ + else if (!pc_byte_combo_change_handled_by_dfc(input_media_fmt_ptr->byte_combo, output_media_fmt_ptr->byte_combo)) + { + if ((*need_for_resampler_ptr) && (IIR_RESAMPLER == pc_ptr->resampler_type)) + { + if (PC_BW16_W16_Q15 != input_media_fmt_ptr->byte_combo) + { + pc_ptr->core_lib.flags.BYTE_CNV_PRE = TRUE; + } + + if (PC_BW16_W16_Q15 != output_media_fmt_ptr->byte_combo) + { + pc_ptr->core_lib.flags.BYTE_CNV_POST = TRUE; + } + } + else if (FALSE == pc_ptr->core_lib.flags.CHANNEL_MIXER) + { + if (input_media_fmt_ptr->sampling_rate >= output_media_fmt_ptr->sampling_rate) + { + pc_ptr->core_lib.flags.BYTE_CNV_POST = TRUE; + } + else + { + pc_ptr->core_lib.flags.BYTE_CNV_PRE = TRUE; + } + } + else + { + if (input_media_fmt_ptr->num_channels >= output_media_fmt_ptr->num_channels) + { + if ((PC_BW24_W24_Q23 != input_media_fmt_ptr->byte_combo) && + (PC_BW24_W24_Q23 != output_media_fmt_ptr->byte_combo)) + { + pc_ptr->core_lib.flags.BYTE_CNV_POST = TRUE; + } + else if ((PC_BW24_W24_Q23 != input_media_fmt_ptr->byte_combo) && + (PC_BW24_W24_Q23 == output_media_fmt_ptr->byte_combo)) + { + pc_ptr->core_lib.flags.BYTE_CNV_POST = TRUE; + } + else if ((PC_BW24_W24_Q23 == input_media_fmt_ptr->byte_combo) && + (PC_BW24_W24_Q23 != output_media_fmt_ptr->byte_combo)) + { + pc_ptr->core_lib.flags.BYTE_CNV_PRE = TRUE; + } + else + { + pc_ptr->core_lib.flags.BYTE_CNV_PRE = TRUE; + pc_ptr->core_lib.flags.BYTE_CNV_POST = TRUE; + } + } + else + { + if ((PC_BW24_W24_Q23 != input_media_fmt_ptr->byte_combo) && + (PC_BW24_W24_Q23 != output_media_fmt_ptr->byte_combo)) + { + pc_ptr->core_lib.flags.BYTE_CNV_PRE = TRUE; + } + else if ((PC_BW24_W24_Q23 != input_media_fmt_ptr->byte_combo) && + (PC_BW24_W24_Q23 == output_media_fmt_ptr->byte_combo)) + { + pc_ptr->core_lib.flags.BYTE_CNV_POST = TRUE; + } + else if ((PC_BW24_W24_Q23 == input_media_fmt_ptr->byte_combo) && + (PC_BW24_W24_Q23 != output_media_fmt_ptr->byte_combo)) + { + pc_ptr->core_lib.flags.BYTE_CNV_PRE = TRUE; + } + else + { + pc_ptr->core_lib.flags.BYTE_CNV_PRE = TRUE; + pc_ptr->core_lib.flags.BYTE_CNV_POST = TRUE; + } + } + } + } + + if (input_media_fmt_ptr->data_format != output_media_fmt_ptr->data_format) + { + if (PC_FLOATING_FORMAT == input_media_fmt_ptr->data_format) + { + pc_ptr->core_lib.flags.DATA_CNV_FLOAT_TO_FIXED = TRUE; + } + else if (PC_FIXED_FORMAT == input_media_fmt_ptr->data_format) + { + pc_ptr->core_lib.flags.DATA_CNV_FIXED_TO_FLOAT = TRUE; + } + } + return; +} + +void pc_update_resampler_pos_and_buff_stages(pc_lib_t *pc_ptr, bool_t need_for_resampler, bool_t reorder_allowed) +{ + if ((need_for_resampler) && (reorder_allowed)) + { + CNV_MSG(pc_ptr->miid, DBG_HIGH_PRIO, "MFC can get reordered"); + if (pc_ptr->core_lib.input_media_fmt.num_channels >= pc_ptr->core_lib.output_media_fmt.num_channels) + { + pc_ptr->core_lib.flags.RESAMPLER_POST = TRUE; + pc_ptr->resampler_used = RESAMPLER_POSITION_POST; + } + else + { + pc_ptr->core_lib.flags.RESAMPLER_PRE = TRUE; + pc_ptr->resampler_used = RESAMPLER_POSITION_PRE; + } + } + else if ((need_for_resampler) && (!reorder_allowed)) // no re-ordering + { + if (RESAMPLER_POSITION_PRE == pc_ptr->resampler_used) + { + pc_ptr->core_lib.flags.RESAMPLER_PRE = TRUE; + } + else + { + pc_ptr->core_lib.flags.RESAMPLER_POST = TRUE; + } + } + + pc_ptr->core_lib.no_of_buffering_stages = + pc_ptr->core_lib.flags.DATA_CNV_FLOAT_TO_FIXED + pc_ptr->core_lib.flags.DATA_CNV_FIXED_TO_FLOAT + + pc_ptr->core_lib.flags.INT_DEINT_PRE + pc_ptr->core_lib.flags.INT_DEINT_POST + + pc_ptr->core_lib.flags.BYTE_CNV_PRE + pc_ptr->core_lib.flags.BYTE_CNV_POST + + pc_ptr->core_lib.flags.RESAMPLER_PRE + pc_ptr->core_lib.flags.RESAMPLER_POST + + pc_ptr->core_lib.flags.CHANNEL_MIXER; + +#ifdef PCM_CNV_LIB_DEBUG + CNV_MSG(pc_ptr->miid, + DBG_HIGH_PRIO, + "Required Pre-Processes, E1-%lu, I1-%lu, B1-%lu, R1-%lu, C-%lu", + pc_ptr->core_lib.flags.ENDIANNESS_PRE, + pc_ptr->core_lib.flags.INT_DEINT_PRE, + pc_ptr->core_lib.flags.BYTE_CNV_PRE, + pc_ptr->core_lib.flags.RESAMPLER_PRE, + pc_ptr->core_lib.flags.CHANNEL_MIXER); + + CNV_MSG(pc_ptr->miid, + DBG_HIGH_PRIO, + "Required Post-Processes , R2-%lu, B2-%lu, I2-%lu, E2-%lu ", + pc_ptr->core_lib.flags.RESAMPLER_POST, + pc_ptr->core_lib.flags.BYTE_CNV_POST, + pc_ptr->core_lib.flags.INT_DEINT_POST, + pc_ptr->core_lib.flags.ENDIANNESS_POST); +#endif +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + Sets lib mf for hw sw resampler lib +______________________________________________________________________________________________________________________*/ +void pc_fill_fir_resampler_lib_mf(pc_lib_t *pc_ptr, pc_media_fmt_t *inp_mf, uint32_t out_sample_rate) +{ + hwsw_rs_media_fmt_t rs_lib_media_fmt; + rs_lib_media_fmt.bits_per_sample = inp_mf->bit_width; + rs_lib_media_fmt.inp_sample_rate = inp_mf->sampling_rate; + rs_lib_media_fmt.num_channels = inp_mf->num_channels; + rs_lib_media_fmt.output_sample_rate = out_sample_rate; + rs_lib_media_fmt.q_factor = inp_mf->q_factor; + if (NULL == pc_ptr->hwsw_rs_lib_ptr) + { + CNV_MSG(pc_ptr->miid, DBG_ERROR_PRIO, "hwsw_rs_lib_ptr is NULL"); + } + else + { + hwsw_rs_set_lib_mf(pc_ptr->hwsw_rs_lib_ptr, &rs_lib_media_fmt); + } +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + Check pc_identify_required_processes function before reading this + NBS -> number of buffering stages + 1. For E1. if E1 is enabled, and NBS = 0, make output buffer = fw output buffer else, do in-place + 2. For I1. If it enabled, it is converting from D to I, so hard code the value. + 3. For B1. if both B1 and B2 are enabled -> B1 should up-convert to support ch-mixer bit-width, + Else, take the out media fmt from the main output media format + 4. For R1, update output sampling rate and depending on type of resampler call process function + 5. For CH mixer, take media fmt from main output media fmt + 6. For R2, update output sampling rate and depending on type of resampler call process function + 7. For B1, take media fmt from main output media fmt + 8. For I2, if enabled, it does D to I. get the media fmt from output anyway + 9. For E2, output buffer is always fw output buffer +______________________________________________________________________________________________________________________*/ +void pc_fill_proc_info(pc_lib_t *pc_ptr) +{ + capi_buf_t *buffers[MAX_NUMBER_OF_BUFFERING_PROCESS] = { &pc_ptr->core_lib.remap_scratch_buf1_ptr[0], + &pc_ptr->core_lib.remap_scratch_buf2_ptr[0], + &pc_ptr->core_lib.remap_scratch_buf1_ptr[0], + &pc_ptr->core_lib.remap_scratch_buf2_ptr[0], + &pc_ptr->core_lib.remap_scratch_buf1_ptr[0], + &pc_ptr->core_lib.remap_scratch_buf2_ptr[0], + &pc_ptr->core_lib.remap_output_buf_ptr[0] }; + + uint32_t current_index = (MAX_NUMBER_OF_BUFFERING_PROCESS)-pc_ptr->core_lib.no_of_buffering_stages; + + if (MAX_NUMBER_OF_BUFFERING_PROCESS <= current_index) + { + CNV_MSG(pc_ptr->miid, DBG_ERROR_PRIO, "Invalid current index %d", current_index); + return; + } + + if ((pc_ptr->core_lib.flags.RESAMPLER_POST) && (pc_ptr->core_lib.flags.RESAMPLER_PRE)) + { + CNV_MSG(pc_ptr->miid, DBG_ERROR_PRIO, "Both resamplers cannot be enabled"); + return; + } + + pc_media_fmt_t *current_output_media_fmt_ptr = &pc_ptr->core_lib.input_media_fmt; + + if (pc_ptr->core_lib.flags.ENDIANNESS_PRE) + { + pc_ptr->core_lib.pc_proc_info[ENDIANNESS_PRE].process = pc_endianness_process; + if (0 == pc_ptr->core_lib.no_of_buffering_stages) + { + pc_ptr->core_lib.pc_proc_info[ENDIANNESS_PRE].output_buffer_ptr = pc_ptr->core_lib.remap_output_buf_ptr; + } + else + { + pc_ptr->core_lib.pc_proc_info[ENDIANNESS_PRE].output_buffer_ptr = pc_ptr->core_lib.remap_input_buf_ptr; + } + + pc_ptr->core_lib.pc_proc_info[ENDIANNESS_PRE].output_media_fmt = pc_ptr->core_lib.input_media_fmt; + pc_ptr->core_lib.pc_proc_info[ENDIANNESS_PRE].output_media_fmt.endianness = PC_LITTLE_ENDIAN; + current_output_media_fmt_ptr = &pc_ptr->core_lib.pc_proc_info[ENDIANNESS_PRE].output_media_fmt; + } + + /* The output Q factor for float to fixed conversion is assumed to be 31 word size =32 and if the required output q + factor of pc converter is different, byte morph process would take care of that*/ + if ((pc_ptr->core_lib.flags.DATA_CNV_FLOAT_TO_FIXED) && (MAX_NUMBER_OF_BUFFERING_PROCESS > current_index)) + { + pc_ptr->core_lib.pc_proc_info[DATA_CNV_FLOAT_TO_FIXED].process = pc_float_to_fixed_conv_process; + pc_ptr->core_lib.pc_proc_info[DATA_CNV_FLOAT_TO_FIXED].output_buffer_ptr = buffers[current_index++]; + pc_ptr->core_lib.pc_proc_info[DATA_CNV_FLOAT_TO_FIXED].output_media_fmt = *current_output_media_fmt_ptr; + pc_ptr->core_lib.pc_proc_info[DATA_CNV_FLOAT_TO_FIXED].output_media_fmt.data_format = PC_FIXED_FORMAT; + + pc_ptr->core_lib.pc_proc_info[DATA_CNV_FLOAT_TO_FIXED].output_media_fmt.q_factor = 31; + pc_ptr->core_lib.pc_proc_info[DATA_CNV_FLOAT_TO_FIXED].output_media_fmt.word_size = 32; + pc_ptr->core_lib.pc_proc_info[DATA_CNV_FLOAT_TO_FIXED].output_media_fmt.bit_width = 32; + pc_ptr->core_lib.pc_proc_info[DATA_CNV_FLOAT_TO_FIXED].output_media_fmt.byte_combo = PC_BW32_W32_Q31; + current_output_media_fmt_ptr = &pc_ptr->core_lib.pc_proc_info[DATA_CNV_FLOAT_TO_FIXED].output_media_fmt; + } + + if ((pc_ptr->core_lib.flags.INT_DEINT_PRE) && (MAX_NUMBER_OF_BUFFERING_PROCESS > current_index)) + { + pc_ptr->core_lib.pc_proc_info[INT_DEINT_PRE].process = pc_interleaving_process; + pc_ptr->core_lib.pc_proc_info[INT_DEINT_PRE].output_buffer_ptr = buffers[current_index++]; + pc_ptr->core_lib.pc_proc_info[INT_DEINT_PRE].output_media_fmt = *current_output_media_fmt_ptr; + pc_ptr->core_lib.pc_proc_info[INT_DEINT_PRE].output_media_fmt.interleaving = PC_DEINTERLEAVED_UNPACKED_V2; + current_output_media_fmt_ptr = &pc_ptr->core_lib.pc_proc_info[INT_DEINT_PRE].output_media_fmt; + } + + if ((pc_ptr->core_lib.flags.BYTE_CNV_PRE) && (MAX_NUMBER_OF_BUFFERING_PROCESS > current_index)) + { + pc_ptr->core_lib.pc_proc_info[BYTE_CNV_PRE].process = pc_byte_morph_process; + pc_ptr->core_lib.pc_proc_info[BYTE_CNV_PRE].output_buffer_ptr = buffers[current_index++]; + pc_ptr->core_lib.pc_proc_info[BYTE_CNV_PRE].output_media_fmt = *current_output_media_fmt_ptr; + + // If IIR resampler is enabled, byte converter pre should convert to 16bit format + if ((pc_ptr->core_lib.flags.RESAMPLER_PRE || pc_ptr->core_lib.flags.RESAMPLER_POST) && + (IIR_RESAMPLER == pc_ptr->resampler_type)) + { + pc_ptr->core_lib.pc_proc_info[BYTE_CNV_PRE].output_media_fmt.bit_width = 16; + pc_ptr->core_lib.pc_proc_info[BYTE_CNV_PRE].output_media_fmt.word_size = 16; + pc_ptr->core_lib.pc_proc_info[BYTE_CNV_PRE].output_media_fmt.q_factor = 15; + pc_ptr->core_lib.pc_proc_info[BYTE_CNV_PRE].output_media_fmt.byte_combo = PC_BW16_W16_Q15; + } + else + { + if (pc_ptr->core_lib.flags.BYTE_CNV_POST) + { + pc_ptr->core_lib.pc_proc_info[BYTE_CNV_PRE].output_media_fmt.bit_width = 24; + pc_ptr->core_lib.pc_proc_info[BYTE_CNV_PRE].output_media_fmt.word_size = 32; + pc_ptr->core_lib.pc_proc_info[BYTE_CNV_PRE].output_media_fmt.q_factor = 23; + pc_ptr->core_lib.pc_proc_info[BYTE_CNV_PRE].output_media_fmt.byte_combo = PC_BW24_W32_Q23; + } + else + { + pc_ptr->core_lib.pc_proc_info[BYTE_CNV_PRE].output_media_fmt.bit_width = + pc_ptr->core_lib.output_media_fmt.bit_width; + pc_ptr->core_lib.pc_proc_info[BYTE_CNV_PRE].output_media_fmt.word_size = + pc_ptr->core_lib.output_media_fmt.word_size; + pc_ptr->core_lib.pc_proc_info[BYTE_CNV_PRE].output_media_fmt.q_factor = + pc_ptr->core_lib.output_media_fmt.q_factor; + pc_ptr->core_lib.pc_proc_info[BYTE_CNV_PRE].output_media_fmt.byte_combo = + pc_ptr->core_lib.output_media_fmt.byte_combo; + } + } + current_output_media_fmt_ptr = &pc_ptr->core_lib.pc_proc_info[BYTE_CNV_PRE].output_media_fmt; + } + + if ((pc_ptr->core_lib.flags.RESAMPLER_PRE) && (MAX_NUMBER_OF_BUFFERING_PROCESS > current_index)) + { + pc_ptr->core_lib.pc_proc_info[RESAMPLER_PRE].output_buffer_ptr = buffers[current_index++]; + pc_ptr->core_lib.pc_proc_info[RESAMPLER_PRE].output_media_fmt = *current_output_media_fmt_ptr; + pc_ptr->core_lib.pc_proc_info[RESAMPLER_PRE].output_media_fmt.sampling_rate = + pc_ptr->core_lib.output_media_fmt.sampling_rate; + + if (FIR_RESAMPLER == pc_ptr->resampler_type) + { + pc_ptr->core_lib.pc_proc_info[RESAMPLER_PRE].process = pc_dyn_resampler_process; + pc_fill_fir_resampler_lib_mf(pc_ptr, + current_output_media_fmt_ptr, + pc_ptr->core_lib.output_media_fmt.sampling_rate); + } + else + { + pc_ptr->core_lib.pc_proc_info[RESAMPLER_PRE].process = pc_iir_resampler_process; + } + + current_output_media_fmt_ptr = &pc_ptr->core_lib.pc_proc_info[RESAMPLER_PRE].output_media_fmt; + } + + if ((pc_ptr->core_lib.flags.CHANNEL_MIXER) && (MAX_NUMBER_OF_BUFFERING_PROCESS > current_index)) + { + pc_ptr->core_lib.pc_proc_info[CHANNEL_MIXER].process = pc_channel_mix_process; + pc_ptr->core_lib.pc_proc_info[CHANNEL_MIXER].output_buffer_ptr = buffers[current_index++]; + pc_ptr->core_lib.pc_proc_info[CHANNEL_MIXER].output_media_fmt = *current_output_media_fmt_ptr; + pc_ptr->core_lib.pc_proc_info[CHANNEL_MIXER].output_media_fmt.num_channels = + pc_ptr->core_lib.output_media_fmt.num_channels; + pc_ptr->core_lib.pc_proc_info[CHANNEL_MIXER].output_media_fmt.channel_type = + pc_ptr->core_lib.output_media_fmt.channel_type; + current_output_media_fmt_ptr = &pc_ptr->core_lib.pc_proc_info[CHANNEL_MIXER].output_media_fmt; + } + if ((pc_ptr->core_lib.flags.RESAMPLER_POST) && (MAX_NUMBER_OF_BUFFERING_PROCESS > current_index)) + { + pc_ptr->core_lib.pc_proc_info[RESAMPLER_POST].output_buffer_ptr = buffers[current_index++]; + pc_ptr->core_lib.pc_proc_info[RESAMPLER_POST].output_media_fmt = *current_output_media_fmt_ptr; + pc_ptr->core_lib.pc_proc_info[RESAMPLER_POST].output_media_fmt.sampling_rate = + pc_ptr->core_lib.output_media_fmt.sampling_rate; + + if (FIR_RESAMPLER == pc_ptr->resampler_type) + { + pc_ptr->core_lib.pc_proc_info[RESAMPLER_POST].process = pc_dyn_resampler_process; + pc_fill_fir_resampler_lib_mf(pc_ptr, + current_output_media_fmt_ptr, + pc_ptr->core_lib.output_media_fmt.sampling_rate); + } + else + { + pc_ptr->core_lib.pc_proc_info[RESAMPLER_POST].process = pc_iir_resampler_process; + } + + current_output_media_fmt_ptr = &pc_ptr->core_lib.pc_proc_info[RESAMPLER_POST].output_media_fmt; + } + + if ((pc_ptr->core_lib.flags.BYTE_CNV_POST) && (MAX_NUMBER_OF_BUFFERING_PROCESS > current_index)) + { + pc_ptr->core_lib.pc_proc_info[BYTE_CNV_POST].process = pc_byte_morph_process; + pc_ptr->core_lib.pc_proc_info[BYTE_CNV_POST].output_buffer_ptr = buffers[current_index++]; + pc_ptr->core_lib.pc_proc_info[BYTE_CNV_POST].output_media_fmt = *current_output_media_fmt_ptr; + + // Since floating point supports only 32 and 64 bit data, Data_cnv_fixed_to_float always converts from 32 bit, + // hence byte conv is needed to get the input data ready in 32 bit format + if (pc_ptr->core_lib.flags.DATA_CNV_FIXED_TO_FLOAT) + { + pc_ptr->core_lib.pc_proc_info[BYTE_CNV_POST].output_media_fmt.bit_width = 32; + pc_ptr->core_lib.pc_proc_info[BYTE_CNV_POST].output_media_fmt.word_size = 32; + pc_ptr->core_lib.pc_proc_info[BYTE_CNV_POST].output_media_fmt.q_factor = 31; + pc_ptr->core_lib.pc_proc_info[BYTE_CNV_POST].output_media_fmt.byte_combo = PC_BW32_W32_Q31; + } + else + { + pc_ptr->core_lib.pc_proc_info[BYTE_CNV_POST].output_media_fmt.bit_width = + pc_ptr->core_lib.output_media_fmt.bit_width; + pc_ptr->core_lib.pc_proc_info[BYTE_CNV_POST].output_media_fmt.word_size = + pc_ptr->core_lib.output_media_fmt.word_size; + pc_ptr->core_lib.pc_proc_info[BYTE_CNV_POST].output_media_fmt.q_factor = + pc_ptr->core_lib.output_media_fmt.q_factor; + pc_ptr->core_lib.pc_proc_info[BYTE_CNV_POST].output_media_fmt.byte_combo = + pc_ptr->core_lib.output_media_fmt.byte_combo; + } + current_output_media_fmt_ptr = &pc_ptr->core_lib.pc_proc_info[BYTE_CNV_POST].output_media_fmt; + } + + if ((pc_ptr->core_lib.flags.INT_DEINT_POST) && (MAX_NUMBER_OF_BUFFERING_PROCESS > current_index)) + { + pc_ptr->core_lib.pc_proc_info[INT_DEINT_POST].process = pc_interleaving_process; + pc_ptr->core_lib.pc_proc_info[INT_DEINT_POST].output_buffer_ptr = buffers[current_index++]; + pc_ptr->core_lib.pc_proc_info[INT_DEINT_POST].output_media_fmt = *current_output_media_fmt_ptr; + pc_ptr->core_lib.pc_proc_info[INT_DEINT_POST].output_media_fmt.interleaving = + pc_ptr->core_lib.output_media_fmt.interleaving; + current_output_media_fmt_ptr = &pc_ptr->core_lib.pc_proc_info[INT_DEINT_POST].output_media_fmt; + } + + if ((pc_ptr->core_lib.flags.DATA_CNV_FIXED_TO_FLOAT) && (MAX_NUMBER_OF_BUFFERING_PROCESS > current_index)) + { + pc_ptr->core_lib.pc_proc_info[DATA_CNV_FIXED_TO_FLOAT].process = pc_fixed_to_float_conv_process; + pc_ptr->core_lib.pc_proc_info[DATA_CNV_FIXED_TO_FLOAT].output_buffer_ptr = buffers[current_index]; + pc_ptr->core_lib.pc_proc_info[DATA_CNV_FIXED_TO_FLOAT].output_media_fmt = *current_output_media_fmt_ptr; + + pc_ptr->core_lib.pc_proc_info[DATA_CNV_FIXED_TO_FLOAT].output_media_fmt.data_format = PC_FLOATING_FORMAT; + pc_ptr->core_lib.pc_proc_info[DATA_CNV_FIXED_TO_FLOAT].output_media_fmt.word_size = + pc_ptr->core_lib.output_media_fmt.word_size; + pc_ptr->core_lib.pc_proc_info[DATA_CNV_FIXED_TO_FLOAT].output_media_fmt.bit_width = + pc_ptr->core_lib.output_media_fmt.bit_width; + pc_ptr->core_lib.pc_proc_info[DATA_CNV_FIXED_TO_FLOAT].output_media_fmt.q_factor = + pc_ptr->core_lib.output_media_fmt.q_factor; + pc_ptr->core_lib.pc_proc_info[DATA_CNV_FIXED_TO_FLOAT].output_media_fmt.byte_combo = + pc_ptr->core_lib.output_media_fmt.byte_combo; + + current_output_media_fmt_ptr = &pc_ptr->core_lib.pc_proc_info[DATA_CNV_FIXED_TO_FLOAT].output_media_fmt; + } + + if (pc_ptr->core_lib.flags.ENDIANNESS_POST) + { + pc_ptr->core_lib.pc_proc_info[ENDIANNESS_POST].process = pc_endianness_process; + pc_ptr->core_lib.pc_proc_info[ENDIANNESS_POST].output_buffer_ptr = pc_ptr->core_lib.remap_output_buf_ptr; + pc_ptr->core_lib.pc_proc_info[ENDIANNESS_POST].output_media_fmt = *current_output_media_fmt_ptr; + pc_ptr->core_lib.pc_proc_info[ENDIANNESS_POST].output_media_fmt.endianness = + pc_ptr->core_lib.output_media_fmt.endianness; + } + + // Assign proper interleaving to the last process + // This is to handle deinterleaved unpacked to deinterleaved packed case + for (uint32_t i = NUMBER_OF_PROCESS - 1; i > 0; i--) + { + if (pc_ptr->core_lib.pc_proc_info[i - 1].process) + { + // Ignore the ENDIANNESS_POST since it will have correct output interleaving + // If only ENDIANNESS_POST is enabled, it will not be in place, it will do DUP to DP or DP to DUP conversions + // If only ENDIANNESS_PRE is enabled, this logic will provide correct interleaving to ENDIANNESS_PRE + // If one of the buffering modules is enabled, this logic will provide correct out interleaving info to it + pc_ptr->core_lib.pc_proc_info[i - 1].output_media_fmt.interleaving = + pc_ptr->core_lib.output_media_fmt.interleaving; + break; + } + } +} +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + Checks if the channel mixer is needed or not. + Enable it if, + 1. If the co-efficient pointer is not null, that means there is a Client configuration for the given ch-map + 2. If the number of channels or ch-map from input to output doesn't match. This means ch-mixer should be enabled + with default co-efficient values +______________________________________________________________________________________________________________________*/ +bool_t pc_is_ch_mixer_needed(pc_lib_t * pc_ptr, + int16_t * coef_set_ptr, + pc_media_fmt_t *input_media_fmt_ptr, + pc_media_fmt_t *output_media_fmt_ptr) +{ + bool_t ch_enable_flag = FALSE; + uint32_t in_num_ch = input_media_fmt_ptr->num_channels; + uint32_t out_num_ch = output_media_fmt_ptr->num_channels; + if (in_num_ch == out_num_ch) + { + for (uint16_t i = 0; i < in_num_ch; i++) + { + if (input_media_fmt_ptr->channel_type[i] != output_media_fmt_ptr->channel_type[i]) + { + ch_enable_flag = TRUE; + break; + } + } + + // inp chmap == out chmap and coef ptr is not null, check if we need to enable + if ((FALSE == ch_enable_flag) && (NULL != coef_set_ptr)) + { + bool_t is_identity_matrix = TRUE; + for (uint32_t row = 0; row < out_num_ch; row++) + { + for (uint32_t col = 0; col < in_num_ch; col++) + { + if ((row == col) && (*(coef_set_ptr + (row * in_num_ch + col)) != 0x4000)) + { + // If elements of main diagonal is not equal to 1 + is_identity_matrix = FALSE; + break; + } + else if ((row != col) && *(coef_set_ptr + (row * in_num_ch + col)) != 0) + { + // If other elements than main diagonal is not equal to 0 + is_identity_matrix = FALSE; + break; + } + } + if (!is_identity_matrix) + { + ch_enable_flag = TRUE; + break; // break out of second for loop + } + } + } + } + else + { + ch_enable_flag = TRUE; + } + + return ch_enable_flag; +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + Does a set param to the channel mixer with the proper co-efficient ptr. + IF the ptr is null, default value will be picked inside the ch-mixer library +______________________________________________________________________________________________________________________*/ +ar_result_t pc_reinit_ch_mixer(pc_lib_t *pc_ptr, uint32_t channel_mixer_lib_size, void *coef_set_ptr) +{ + ChMixerChType in_ch_type[CAPI_MAX_CHANNELS_V2]; + for (uint32_t i = 0; i < pc_ptr->core_lib.input_media_fmt.num_channels; i++) + { + in_ch_type[i] = (ChMixerChType)pc_ptr->core_lib.input_media_fmt.channel_type[i]; + } + + ChMixerChType out_ch_type[CAPI_MAX_CHANNELS_V2]; + for (uint32_t i = 0; i < pc_ptr->core_lib.output_media_fmt.num_channels; i++) + { + out_ch_type[i] = (ChMixerChType)pc_ptr->core_lib.output_media_fmt.channel_type[i]; + } + + // Initialize channel mixer lib. + ChMixerResultType result = + ChMixerSetParam(pc_ptr->ch_lib_ptr, + channel_mixer_lib_size, + (uint32)pc_ptr->core_lib.input_media_fmt.num_channels, + in_ch_type, + (uint32)pc_ptr->core_lib.output_media_fmt.num_channels, + out_ch_type, + (uint32)pc_ptr->core_lib.pc_proc_info[CHANNEL_MIXER].output_media_fmt.word_size, + coef_set_ptr); + if (CH_MIXER_SUCCESS != result) + { + CNV_MSG(pc_ptr->miid, DBG_ERROR_PRIO, "CH-mixer reint failed"); + return AR_EFAILED; + } + return AR_EOK; +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + Does a set param to the channel mixer with the proper co-efficient ptr. + IF the ptr is null, default value will be picked inside the ch-mixer library +______________________________________________________________________________________________________________________*/ +ar_result_t pc_check_lib_enable(pc_lib_t * pc_ptr, + bool_t * enable, + bool_t need_for_resampler, + bool_t enable_for_deint_packed_unpacked_cnv, + pc_media_fmt_t *input_media_fmt_ptr) +{ + // If input data format is floating point and float_to_fixed conversion process is not enabled and channel mixer and + // resampler flags are true, then disable the module since these processes do not support floating point data. + if ((PC_FLOATING_FORMAT == input_media_fmt_ptr->data_format) && + (TRUE != pc_ptr->core_lib.flags.DATA_CNV_FLOAT_TO_FIXED) && + (pc_ptr->core_lib.flags.CHANNEL_MIXER || need_for_resampler)) + { + CNV_MSG(pc_ptr->miid, + DBG_ERROR_PRIO, + "Floating point data format is not supported by channel mixer %d or resampler %d", + pc_ptr->core_lib.flags.CHANNEL_MIXER, + need_for_resampler); + *enable = FALSE; + return AR_EOK; + } + + // if any of the flags such as ENDIANNESS_PRE,INT_DEINT_PRE,BYTE_CNV_PRE,CHANNEL_MIXER,BYTE_CNV_POST,INT_DEINT_POST + // ,ENDIANNESS_POST or if we require a resampler then the module has to be enabled + if (pc_ptr->core_lib.flags.word || (need_for_resampler == TRUE) || (enable_for_deint_packed_unpacked_cnv == TRUE)) + { + *enable = TRUE; + } + else + { + *enable = FALSE; + } + return AR_EOK; +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + Does PCM CONVERTER library init. + 1. Reallocated the memory needed. + 2. Checks if resampler reordering is allowed + 2. Copies the media format. + 3. Based on the media formats, identifies the modules to be enabled. + 4. If resampler is needed, allocates lib ptr memory for either of the resamplers, frees other if present + 6. Based on enabled modules and media fmt, fill in the module process info table. + 5. Creates or re-inits resampler if needed + 7. Reints ch-mixer library if needed +______________________________________________________________________________________________________________________*/ + +ar_result_t pc_init(pc_lib_t * pc_ptr, + pc_media_fmt_t *input_media_fmt_ptr, + pc_media_fmt_t *output_media_fmt_ptr, + void * coef_set_ptr, + uint32_t heap_id, + bool_t * lib_enable_ptr, + bool_t fir_iir_rs_switch, + uint32_t miid, + uint8_t type) +{ + + ar_result_t result = AR_EOK; + bool_t fir_rs_reinit = FALSE; + bool_t need_for_resampler = FALSE; + bool_t enable_for_deint_packed_unpacked_cnv = FALSE; + uint32_t channel_mixer_lib_size = 0; + + uint16_t max_num_of_channels = input_media_fmt_ptr->num_channels >= output_media_fmt_ptr->num_channels + ? input_media_fmt_ptr->num_channels + : output_media_fmt_ptr->num_channels; + // free the allocated mem and memset pc_ptr->core_lib to 0 so that the pointers do not hold information about + // previous instance + if (NULL != pc_ptr->core_lib.pc_mem_ptr) + { + posal_memory_aligned_free((void *)pc_ptr->core_lib.pc_mem_ptr); + pc_ptr->core_lib.pc_mem_ptr = NULL; + } + memset(&(pc_ptr->core_lib), 0, sizeof(pc_core_lib_t)); + + pc_identify_required_processes(pc_ptr, + input_media_fmt_ptr, + output_media_fmt_ptr, + &need_for_resampler, + &enable_for_deint_packed_unpacked_cnv, + (int16_t *)coef_set_ptr, + type); + + pc_check_lib_enable(pc_ptr, + lib_enable_ptr, + need_for_resampler, + enable_for_deint_packed_unpacked_cnv, + input_media_fmt_ptr); + if (*lib_enable_ptr == FALSE) + { + // if module is not enabled exit pc_init + return result; + } + + result = pc_realloc_chmixer_memory(pc_ptr, + &channel_mixer_lib_size, + max_num_of_channels, + input_media_fmt_ptr->num_channels, + output_media_fmt_ptr->num_channels, + heap_id); + if (result != AR_EOK) + { + return result; + } + + result = pc_check_alloc_resampler_memory(pc_ptr, + heap_id, + &fir_rs_reinit, + &fir_iir_rs_switch, + input_media_fmt_ptr, + output_media_fmt_ptr, + need_for_resampler); + + if (result != AR_EOK) + { + return result; + } + + bool_t reorder_allowed = + pc_is_reorder_allowed(pc_ptr, input_media_fmt_ptr, output_media_fmt_ptr, fir_iir_rs_switch, fir_rs_reinit); + + pc_copy_media_fmt(pc_ptr, input_media_fmt_ptr, output_media_fmt_ptr); + + /* update resampler pre and post flags based the need for resampler and reorder allowed. Num of bufferring stages is + also calculated.*/ + pc_update_resampler_pos_and_buff_stages(pc_ptr, need_for_resampler, reorder_allowed); + + pc_fill_proc_info(pc_ptr); + + result = pc_check_create_resampler_instance(pc_ptr, heap_id, fir_rs_reinit); + + pc_reinit_ch_mixer(pc_ptr, channel_mixer_lib_size, coef_set_ptr); + + return result; +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + Does PCM CONVERTER library deinit. +______________________________________________________________________________________________________________________*/ + +void pc_deinit(pc_lib_t *pc_ptr) +{ + if (NULL != pc_ptr->core_lib.pc_mem_ptr) + { + posal_memory_aligned_free(pc_ptr->core_lib.pc_mem_ptr); + pc_ptr->core_lib.pc_mem_ptr = NULL; + } + + if (NULL != pc_ptr->hwsw_rs_lib_ptr) + { + hwsw_rs_lib_deinit(pc_ptr->hwsw_rs_lib_ptr); + posal_memory_free(pc_ptr->hwsw_rs_lib_ptr); + pc_ptr->hwsw_rs_lib_ptr = NULL; + } + + if (NULL != pc_ptr->iir_rs_lib_ptr) + { + iir_rs_lib_deinit(pc_ptr->iir_rs_lib_ptr); + posal_memory_free(pc_ptr->iir_rs_lib_ptr); + pc_ptr->iir_rs_lib_ptr = NULL; + } +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + Aggregates KPPS based on number of enabled modules. + 1. KPPS tables are calculated per channel at 48KHz. + 2. So KPPS scaling is needed before the aggregation. + 3. Added fw kpps for re-mapping buffer as well. This is scaled based on number of active modules in the chain +______________________________________________________________________________________________________________________*/ +void pc_get_kpps_and_bw(pc_lib_t *pc_ptr, uint32_t *kpps, uint32_t *bw) +{ + pc_media_fmt_t *current_input_media_fmt_ptr = &pc_ptr->core_lib.input_media_fmt; + uint64_t new_kpps = 0; + uint64_t new_bw = 0; + *kpps = 0; + *bw = 0; + + if (pc_ptr->core_lib.flags.ENDIANNESS_PRE) + { + uint32_t word_size = pc_ptr->core_lib.pc_proc_info[ENDIANNESS_PRE].output_media_fmt.word_size; + uint32_t num_channels = pc_ptr->core_lib.pc_proc_info[ENDIANNESS_PRE].output_media_fmt.num_channels; + uint32_t sr = pc_ptr->core_lib.pc_proc_info[ENDIANNESS_PRE].output_media_fmt.sampling_rate; + uint32_t num_bytes = (pc_ptr->core_lib.input_media_fmt.bit_width >> 3); + + new_kpps += ((endianness_kpps_per_ch[(word_size >> 3) - 2] * num_channels) * sr) / base_sample_rate; + // for endinaess and interleaving we calculate bandwidth based on num of channels, num of bytes, sampling rate + // Bw calculation is multiplied by 2 because for every data copy we do a read+write operation + new_bw += (2 * sr * num_channels * num_bytes); + + current_input_media_fmt_ptr = &pc_ptr->core_lib.pc_proc_info[ENDIANNESS_PRE].output_media_fmt; + } + + if (pc_ptr->core_lib.flags.INT_DEINT_PRE) + { + uint32_t word_size = pc_ptr->core_lib.pc_proc_info[INT_DEINT_PRE].output_media_fmt.word_size; + uint32_t num_channels = pc_ptr->core_lib.pc_proc_info[INT_DEINT_PRE].output_media_fmt.num_channels; + uint32_t sr = pc_ptr->core_lib.pc_proc_info[INT_DEINT_PRE].output_media_fmt.sampling_rate; + uint32_t num_bytes = (pc_ptr->core_lib.input_media_fmt.bit_width >> 3); + + new_kpps += ((intl_dintl_kpps_per_ch[(word_size >> 3) - 2] * num_channels) * sr) / base_sample_rate; + new_bw += (2 * sr * num_channels * num_bytes); + + current_input_media_fmt_ptr = &pc_ptr->core_lib.pc_proc_info[INT_DEINT_PRE].output_media_fmt; + } + + if (pc_ptr->core_lib.flags.BYTE_CNV_PRE) + { + uint32_t word_size = pc_ptr->core_lib.pc_proc_info[BYTE_CNV_PRE].output_media_fmt.word_size; + uint32_t num_channels = pc_ptr->core_lib.pc_proc_info[BYTE_CNV_PRE].output_media_fmt.num_channels; + uint32_t sr = pc_ptr->core_lib.pc_proc_info[BYTE_CNV_PRE].output_media_fmt.sampling_rate; + + if ((24 == current_input_media_fmt_ptr->word_size) || (24 == word_size)) + { + new_kpps += ((byte_cnv_kpps_per_ch[(24 >> 3) - 2] * num_channels) * sr) / base_sample_rate; + } + else + { + new_kpps += ((byte_cnv_kpps_per_ch[(word_size >> 3) - 2] * num_channels) * sr) / base_sample_rate; + } + // for byte conversion bandwidth based on num of channels, sampling rate and sum of input and output num of bytes + new_bw += (sr * num_channels * + ((pc_ptr->core_lib.input_media_fmt.bit_width + pc_ptr->core_lib.output_media_fmt.bit_width) >> 3)); + + current_input_media_fmt_ptr = &pc_ptr->core_lib.pc_proc_info[BYTE_CNV_PRE].output_media_fmt; + } + + if (pc_ptr->core_lib.flags.RESAMPLER_PRE) + { + if (FIR_RESAMPLER == pc_ptr->resampler_type) + { + new_kpps += hwsw_rs_lib_get_kpps(pc_ptr->hwsw_rs_lib_ptr); + new_bw += hwsw_rs_lib_get_bw(pc_ptr->hwsw_rs_lib_ptr); + } + else + { + new_kpps += iir_rs_lib_get_kpps(pc_ptr->iir_rs_lib_ptr, + pc_ptr->core_lib.input_media_fmt.sampling_rate, + pc_ptr->core_lib.output_media_fmt.sampling_rate); + new_bw += iir_rs_lib_get_bw(pc_ptr->iir_rs_lib_ptr, pc_ptr->core_lib.input_media_fmt.sampling_rate); + } + + current_input_media_fmt_ptr = &pc_ptr->core_lib.pc_proc_info[RESAMPLER_PRE].output_media_fmt; + } + + if (pc_ptr->core_lib.flags.CHANNEL_MIXER) + { + uint32_t sr = pc_ptr->core_lib.pc_proc_info[CHANNEL_MIXER].output_media_fmt.sampling_rate; + uint32_t num_active_coeff = ChMixerGetNumActiveCoefficients(pc_ptr->ch_lib_ptr); + uint32_t out_ch = pc_ptr->core_lib.output_media_fmt.num_channels; + uint32_t num_bytes = (pc_ptr->core_lib.input_media_fmt.bit_width >> 3); + // Using constants -8, 4, 5 through profiling, + 225 for capi kpps + // Opening up the brackets for better div/mult: + // out ch * (-8 + num_samples(4 + (5* in_ch* num_active_coeff/(in_ch * out_ch)))) + c + // => out_ch * (-8 + (num_samples * 4) + (num_samples * 5* num_coeff/out_ch)) + c + // sr/1000 translates to num samples + uint64_t chmixer_kpps = (out_ch * (-8 + ((sr * 4) + (sr * 5 * num_active_coeff / out_ch)) / 1000)) + 225; + + new_kpps += chmixer_kpps; + // for ch mixer, bandwidth based on num of bytes, sampling rate and sum of input and output channels + new_bw += (sr * num_bytes * + (pc_ptr->core_lib.output_media_fmt.num_channels + pc_ptr->core_lib.input_media_fmt.num_channels)); + + current_input_media_fmt_ptr = &pc_ptr->core_lib.pc_proc_info[CHANNEL_MIXER].output_media_fmt; + } + + if (pc_ptr->core_lib.flags.RESAMPLER_POST) + { + if (FIR_RESAMPLER == pc_ptr->resampler_type) + { + new_kpps += hwsw_rs_lib_get_kpps(pc_ptr->hwsw_rs_lib_ptr); + new_bw += hwsw_rs_lib_get_bw(pc_ptr->hwsw_rs_lib_ptr); + } + else + { + new_kpps += iir_rs_lib_get_kpps(pc_ptr->iir_rs_lib_ptr, + pc_ptr->core_lib.input_media_fmt.sampling_rate, + pc_ptr->core_lib.output_media_fmt.sampling_rate); + new_bw += iir_rs_lib_get_bw(pc_ptr->iir_rs_lib_ptr, pc_ptr->core_lib.input_media_fmt.sampling_rate); + } + current_input_media_fmt_ptr = &pc_ptr->core_lib.pc_proc_info[RESAMPLER_POST].output_media_fmt; + } + + if (pc_ptr->core_lib.flags.BYTE_CNV_POST) + { + uint32_t word_size = pc_ptr->core_lib.pc_proc_info[BYTE_CNV_POST].output_media_fmt.word_size; + uint32_t num_channels = pc_ptr->core_lib.pc_proc_info[BYTE_CNV_POST].output_media_fmt.num_channels; + uint32_t sr = pc_ptr->core_lib.pc_proc_info[BYTE_CNV_PRE].output_media_fmt.sampling_rate; + + if ((24 == current_input_media_fmt_ptr->word_size) || (24 == word_size)) + { + new_kpps += ((byte_cnv_kpps_per_ch[(24 >> 3) - 2] * num_channels) * sr) / base_sample_rate; + } + else + { + new_kpps += ((byte_cnv_kpps_per_ch[(word_size >> 3) - 2] * num_channels) * sr) / base_sample_rate; + } + + new_bw += (sr * num_channels * + ((pc_ptr->core_lib.input_media_fmt.bit_width + pc_ptr->core_lib.output_media_fmt.bit_width) >> 3)); + current_input_media_fmt_ptr = &pc_ptr->core_lib.pc_proc_info[BYTE_CNV_POST].output_media_fmt; + } + + if (pc_ptr->core_lib.flags.INT_DEINT_POST) + { + uint32_t word_size = pc_ptr->core_lib.pc_proc_info[INT_DEINT_POST].output_media_fmt.word_size; + uint32_t num_channels = pc_ptr->core_lib.pc_proc_info[INT_DEINT_POST].output_media_fmt.num_channels; + uint32_t sr = pc_ptr->core_lib.pc_proc_info[BYTE_CNV_PRE].output_media_fmt.sampling_rate; + uint32_t num_bytes = (pc_ptr->core_lib.input_media_fmt.bit_width >> 3); + + new_kpps += ((intl_dintl_kpps_per_ch[(word_size >> 3) - 2] * num_channels) * sr) / base_sample_rate; + new_bw += (2 * sr * num_channels * num_bytes); + + current_input_media_fmt_ptr = &pc_ptr->core_lib.pc_proc_info[INT_DEINT_POST].output_media_fmt; + } + + if (pc_ptr->core_lib.flags.ENDIANNESS_POST) + { + uint32_t word_size = pc_ptr->core_lib.pc_proc_info[ENDIANNESS_POST].output_media_fmt.word_size; + uint32_t num_channels = pc_ptr->core_lib.pc_proc_info[ENDIANNESS_POST].output_media_fmt.num_channels; + uint32_t sr = pc_ptr->core_lib.pc_proc_info[BYTE_CNV_PRE].output_media_fmt.sampling_rate; + uint32_t num_bytes = (pc_ptr->core_lib.input_media_fmt.bit_width >> 3); + + new_kpps += ((endianness_kpps_per_ch[(word_size >> 3) - 2] * num_channels) * sr) / base_sample_rate; + new_bw += (2 * sr * num_channels * num_bytes); + + current_input_media_fmt_ptr = &pc_ptr->core_lib.pc_proc_info[ENDIANNESS_POST].output_media_fmt; + } + + uint32_t no_of_stages = pc_ptr->core_lib.flags.ENDIANNESS_PRE + pc_ptr->core_lib.flags.ENDIANNESS_POST + + pc_ptr->core_lib.flags.INT_DEINT_PRE + pc_ptr->core_lib.flags.INT_DEINT_POST + + pc_ptr->core_lib.flags.BYTE_CNV_PRE + pc_ptr->core_lib.flags.BYTE_CNV_POST + + pc_ptr->core_lib.flags.CHANNEL_MIXER; + + if (0 == no_of_stages) + { + uint32_t index = (pc_ptr->core_lib.input_media_fmt.word_size >> 3) - 2; + if (MAX_BW_IDX > index) + { + new_kpps += memcpy_kpps_per_ch[index] * pc_ptr->core_lib.input_media_fmt.num_channels; + } + } + + new_kpps += (fw_kpps * no_of_stages); + *kpps = (uint32_t)new_kpps; + *bw = (uint32_t)new_bw; +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + Aggregates algo delay based on number of enabled modules. +______________________________________________________________________________________________________________________*/ +uint32_t pc_get_algo_delay(pc_lib_t *pc_ptr) +{ + uint32_t algo_delay = 0; + if ((pc_ptr->core_lib.flags.RESAMPLER_PRE) || (pc_ptr->core_lib.flags.RESAMPLER_POST)) + { + if (FIR_RESAMPLER == pc_ptr->resampler_type) + { + algo_delay += hwsw_rs_lib_get_alg_delay(pc_ptr->hwsw_rs_lib_ptr); + } + else + { + algo_delay += iir_rs_lib_get_delay(pc_ptr->iir_rs_lib_ptr, + pc_ptr->core_lib.input_media_fmt.sampling_rate, + pc_ptr->core_lib.output_media_fmt.sampling_rate); + } + } + return algo_delay; +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + Classifies the bit-width, word-size and q-factor to an enum +______________________________________________________________________________________________________________________*/ +pc_mf_combo_t pc_classify_mf(pc_media_fmt_t *mf_ptr) +{ + if (NULL == mf_ptr) + { + return PC_INVALID_CMBO; + } + uint16_t word_size = mf_ptr->word_size; + uint16_t bit_width = mf_ptr->bit_width; + uint16_t q_factor = mf_ptr->q_factor; + uint16_t data_format = mf_ptr->data_format; + + if (PC_FLOATING_FORMAT == data_format) + { + if ((32 == word_size) && (32 == bit_width)) + { + return PC_BW32_W32_FLOAT; + } + else if ((64 == word_size) && (64 == bit_width)) + { + return PC_BW64_W64_DOUBLE; + } + } + + if ((16 == word_size) && (PCM_Q_FACTOR_15 == q_factor) && (16 == bit_width)) + { + return PC_BW16_W16_Q15; + } + else if ((24 == word_size) && (PCM_Q_FACTOR_23 == q_factor) && (24 == bit_width)) + { + return PC_BW24_W24_Q23; + } + else if (32 == word_size) + { + if (PCM_Q_FACTOR_23 == q_factor) + { + if (24 == bit_width) + { + return PC_BW24_W32_Q23; + } + } + + if (PCM_Q_FACTOR_27 == q_factor) + { + if (24 == bit_width) + { + return PC_BW24_W32_Q27; + } + } + else if (PCM_Q_FACTOR_31 == q_factor) + { + if (32 == bit_width) + { + return PC_BW32_W32_Q31; + } + else if (24 == bit_width) + { + return PC_BW24_W32_Q31; + } + } + } + return PC_INVALID_CMBO; +} diff --git a/modules/cmn/pcm_mf_cnv/lib/src/pc_process.cpp b/modules/cmn/pcm_mf_cnv/lib/src/pc_process.cpp new file mode 100644 index 0000000..4dac487 --- /dev/null +++ b/modules/cmn/pcm_mf_cnv/lib/src/pc_process.cpp @@ -0,0 +1,101 @@ +/*______________________________________________________________________________________________________________________ + + file pc_process.cpp +This file contains functions for compression-decompression container + +Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. +SPDX-License-Identifier: BSD-3-Clause +______________________________________________________________________________________________________________________*/ + +/*______________________________________________________________________________________________________________________ +INCLUDE FILES FOR MODULE +______________________________________________________________________________________________________________________*/ + +#include "pc_converter.h" + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + +______________________________________________________________________________________________________________________*/ +ar_result_t pc_endianness_process(void *me_ptr, + capi_buf_t *input_buf_ptr, + capi_buf_t *output_buf_ptr, + pc_media_fmt_t *input_media_fmt_ptr, + pc_media_fmt_t *output_media_fmt_ptr) +{ + ar_result_t result = AR_EOK; + // No need to check for the same endianness, since it is done in the init phase + if (PC_INTERLEAVED != input_media_fmt_ptr->interleaving) + { + + + uint32_t num_bytes_to_process = input_buf_ptr[0].actual_data_len; + uint32_t num_bytes_processed = 0; + for (uint16_t ch = 0; ch < input_media_fmt_ptr->num_channels; ch++) + { + // Buffer length remains the same since we use the same buffer + pc_change_endianness(input_buf_ptr[ch].data_ptr, + output_buf_ptr[ch].data_ptr, + num_bytes_to_process, + &num_bytes_processed, + input_media_fmt_ptr->word_size); + } + output_buf_ptr[0].actual_data_len = num_bytes_processed; + } + else + { + // Buffer length remains the same since we use the same buffer + pc_change_endianness(input_buf_ptr->data_ptr, + output_buf_ptr->data_ptr, + input_buf_ptr->actual_data_len, + &output_buf_ptr->actual_data_len, + input_media_fmt_ptr->word_size); + } + + return result; +} + + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + +______________________________________________________________________________________________________________________*/ +ar_result_t pc_dyn_resampler_process(void *_pif, + capi_buf_t *input_buf_ptr, + capi_buf_t *output_buf_ptr, + pc_media_fmt_t *input_media_fmt_ptr, + pc_media_fmt_t *output_media_fmt_ptr) +{ + + ar_result_t result = AR_EOK; + pc_lib_t *me_ptr = (pc_lib_t *)_pif; + uint32_t heap_id = me_ptr->heap_id; + + // TODO: Currently resampler doesnt support unpacked V2, need changes in multiple places in dyn resampler module, + // mp2_dec, resampler library, hwrs driver. + for (uint32_t i = 1; i < input_media_fmt_ptr->num_channels; i++) + { + input_buf_ptr[i].actual_data_len = input_buf_ptr[0].actual_data_len; + input_buf_ptr[i].max_data_len = input_buf_ptr[0].max_data_len; + } + + for (uint32_t i = 1; i < output_media_fmt_ptr->num_channels; i++) + { + output_buf_ptr[i].actual_data_len = output_buf_ptr[0].actual_data_len; + output_buf_ptr[i].max_data_len = output_buf_ptr[0].max_data_len; + } + + result = hwsw_rs_lib_process(me_ptr->hwsw_rs_lib_ptr, + input_buf_ptr, + output_buf_ptr, + input_media_fmt_ptr->num_channels, + output_media_fmt_ptr->num_channels, + heap_id); + return result; +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + +______________________________________________________________________________________________________________________*/ + diff --git a/modules/cmn/pcm_mf_cnv/lib/src/pc_process_island.cpp b/modules/cmn/pcm_mf_cnv/lib/src/pc_process_island.cpp new file mode 100644 index 0000000..19341eb --- /dev/null +++ b/modules/cmn/pcm_mf_cnv/lib/src/pc_process_island.cpp @@ -0,0 +1,697 @@ +/*______________________________________________________________________________________________________________________ + +file pc_process.cpp +This file contains functions for compression-decompression container + +Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. +SPDX-License-Identifier: BSD-3-Clause +______________________________________________________________________________________________________________________*/ + +/*______________________________________________________________________________________________________________________ +INCLUDE FILES FOR MODULE +______________________________________________________________________________________________________________________*/ + +#include "pc_converter.h" + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + Re-mapps the main buffer data pointer to the remap-buffer by using proper media fmt + Also, calculated the proper length and separation while remapping output buffer if there is any byte-scale involved +______________________________________________________________________________________________________________________*/ +void pc_remap_from_main_buf(capi_buf_t * main_buf_ptr, + capi_buf_t * remapped_buf_ptr, + capi_buf_t * remapped_input_ref_buf_ptr, + pc_media_fmt_t *media_fmt_ptr, + pc_media_fmt_t *ref_input_media_fmt_ptr, + bool_t is_input_side, + bool_t is_in_or_out_buf) +{ + uint32_t ch_spacing = 0; + uint32_t alignement_offset = 0; + + if (PC_INTERLEAVED == media_fmt_ptr->interleaving) + { + remapped_buf_ptr->data_ptr = main_buf_ptr->data_ptr; + remapped_buf_ptr->actual_data_len = main_buf_ptr->actual_data_len; + remapped_buf_ptr->max_data_len = main_buf_ptr->max_data_len; + } + else if (PC_DEINTERLEAVED_PACKED == media_fmt_ptr->interleaving) + { + if (is_input_side) + { + ch_spacing = main_buf_ptr->actual_data_len / media_fmt_ptr->num_channels; + } + else + { + if (PC_INTERLEAVED == ref_input_media_fmt_ptr->interleaving) + { + ch_spacing = (remapped_input_ref_buf_ptr->actual_data_len * (media_fmt_ptr->word_size >> 3) * + (media_fmt_ptr->num_channels)) / + ((ref_input_media_fmt_ptr->word_size >> 3) * (ref_input_media_fmt_ptr->num_channels)); + } + else + { + ch_spacing = remapped_input_ref_buf_ptr->actual_data_len * (media_fmt_ptr->word_size >> 3) / + (ref_input_media_fmt_ptr->word_size >> 3); + } + } + alignement_offset = ch_spacing % 4; + ch_spacing = ch_spacing - alignement_offset; + + for (uint32_t ch = 0; ch < media_fmt_ptr->num_channels; ch++) + { + remapped_buf_ptr[ch].data_ptr = main_buf_ptr->data_ptr + (ch_spacing * ch); + } + remapped_buf_ptr[0].actual_data_len = is_input_side ? ch_spacing : 0; + remapped_buf_ptr[0].max_data_len = ch_spacing; + } + else if (PC_DEINTERLEAVED_UNPACKED_V2 == media_fmt_ptr->interleaving) + { + if (is_in_or_out_buf) + { + for (uint32_t ch = 0; ch < media_fmt_ptr->num_channels; ch++) + { + remapped_buf_ptr[ch].data_ptr = main_buf_ptr[ch].data_ptr; + } + remapped_buf_ptr[0].actual_data_len = main_buf_ptr[0].actual_data_len; + remapped_buf_ptr[0].max_data_len = main_buf_ptr[0].max_data_len; + } + else + { + ch_spacing = is_input_side ? main_buf_ptr->actual_data_len / media_fmt_ptr->num_channels + : main_buf_ptr->max_data_len / media_fmt_ptr->num_channels; + + alignement_offset = ch_spacing % 4; + ch_spacing = ch_spacing - alignement_offset; + for (uint32_t ch = 0; ch < media_fmt_ptr->num_channels; ch++) + { + remapped_buf_ptr[ch].data_ptr = main_buf_ptr->data_ptr + (ch_spacing * ch); + } + remapped_buf_ptr[0].actual_data_len = is_input_side ? ch_spacing : 0; + remapped_buf_ptr[0].max_data_len = ch_spacing; + } + } +} +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + 1. Finds out which actual buffer (process input, scratch buffer 1, scratch buffer 2 or output) + to use to remap from the remapped buffer pointer + 2. Similarly updated needed media fmt and a flag to indicate if the main buffer is from framework or if it scratch +______________________________________________________________________________________________________________________*/ +void pc_remap_buffer(pc_lib_t * pc_ptr, + capi_buf_t * input_buf_ptr, + capi_buf_t * output_buf_ptr, + capi_buf_t * scratch_buf_ptr_1, + capi_buf_t * scratch_buf_ptr_2, + capi_buf_t * remapped_ib_ptr, + capi_buf_t * remapped_ob_ptr, + pc_media_fmt_t *input_media_fmt_ptr, + pc_media_fmt_t *output_media_fmt_ptr, + bool_t is_input) +{ + capi_buf_t * main_buf_ptr = NULL; + bool_t is_in_out_buf = FALSE; + capi_buf_t * remapped_buf_ptr = NULL; + pc_media_fmt_t *media_fmt_ptr = NULL; + if (is_input) + { + remapped_buf_ptr = remapped_ib_ptr; + media_fmt_ptr = input_media_fmt_ptr; + } + else + { + remapped_buf_ptr = remapped_ob_ptr; + media_fmt_ptr = output_media_fmt_ptr; + } + + if (pc_ptr->core_lib.remap_input_buf_ptr == remapped_buf_ptr) + { + is_in_out_buf = TRUE; + main_buf_ptr = input_buf_ptr; + } + else if (pc_ptr->core_lib.remap_scratch_buf1_ptr == remapped_buf_ptr) + { + main_buf_ptr = scratch_buf_ptr_1; + } + else if (pc_ptr->core_lib.remap_scratch_buf2_ptr == remapped_buf_ptr) + { + main_buf_ptr = scratch_buf_ptr_2; + } + else if (pc_ptr->core_lib.remap_output_buf_ptr == remapped_buf_ptr) + { + is_in_out_buf = TRUE; + main_buf_ptr = output_buf_ptr; + } + else + { + CNV_MSG(pc_ptr->miid, DBG_ERROR_PRIO, "WARNING! BUFFER NOT MATCHED"); + return; + } + + pc_remap_from_main_buf(main_buf_ptr, + remapped_buf_ptr, + remapped_ib_ptr, + media_fmt_ptr, + input_media_fmt_ptr, + is_input, + is_in_out_buf); +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + +______________________________________________________________________________________________________________________*/ +ar_result_t pc_interleaving_process(void *me_ptr, + capi_buf_t *input_buf_ptr, + capi_buf_t *output_buf_ptr, + pc_media_fmt_t *input_media_fmt_ptr, + pc_media_fmt_t *output_media_fmt_ptr) +{ + ar_result_t result = AR_EOK; + + if (PC_INTERLEAVED == input_media_fmt_ptr->interleaving) + { + uint32_t num_samp_per_ch = + input_buf_ptr->actual_data_len / (input_media_fmt_ptr->num_channels * input_media_fmt_ptr->word_size >> 3); + uint32_t bytes_per_samp = input_media_fmt_ptr->word_size >> 3; + + result = spf_intlv_to_deintlv_unpacked_v2(input_buf_ptr, + output_buf_ptr, + input_media_fmt_ptr->num_channels, + bytes_per_samp, + num_samp_per_ch); + } + else + { + result = spf_deintlv_to_intlv(input_buf_ptr, + output_buf_ptr, + input_media_fmt_ptr->num_channels, + input_media_fmt_ptr->word_size); + } + + return result; +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + +______________________________________________________________________________________________________________________*/ +ar_result_t pc_byte_morph_process(void * me_ptr, + capi_buf_t * input_buf_ptr, + capi_buf_t * output_buf_ptr, + pc_media_fmt_t *input_media_fmt_ptr, + pc_media_fmt_t *output_media_fmt_ptr) +{ + ar_result_t result = AR_EOK; + pc_lib_t * pc_ptr = (pc_lib_t *)me_ptr; + if (PC_INTERLEAVED == input_media_fmt_ptr->interleaving) + { + if (PC_BW16_W16_Q15 == output_media_fmt_ptr->byte_combo) + { + result = pc_intlv_16_out(input_buf_ptr, + output_buf_ptr, + input_media_fmt_ptr->word_size, + input_media_fmt_ptr->q_factor); + } + else if (PC_BW24_W24_Q23 == output_media_fmt_ptr->byte_combo) + { + result = pc_intlv_24_out(input_buf_ptr, + output_buf_ptr, + input_media_fmt_ptr->word_size, + input_media_fmt_ptr->q_factor); + } + else if ((PC_BW24_W32_Q23 == output_media_fmt_ptr->byte_combo) || + (PC_BW24_W32_Q27 == output_media_fmt_ptr->byte_combo) || + (PC_BW24_W32_Q31 == output_media_fmt_ptr->byte_combo) || + (PC_BW32_W32_Q31 == output_media_fmt_ptr->byte_combo) || + (PC_BW32_W32_FLOAT == output_media_fmt_ptr->byte_combo) || + (PC_BW64_W64_DOUBLE == output_media_fmt_ptr->byte_combo)) + { + result = pc_intlv_32_out(input_buf_ptr, + output_buf_ptr, + input_media_fmt_ptr->word_size, + input_media_fmt_ptr->q_factor, + output_media_fmt_ptr->q_factor); + } + else + { + CNV_MSG(pc_ptr->miid, DBG_ERROR_PRIO, "Unsupported byte combo "); + } + } + else + { + if (PC_BW16_W16_Q15 == output_media_fmt_ptr->byte_combo) + { + result = pc_deintlv_unpacked_v2_16_out(input_buf_ptr, + output_buf_ptr, + input_media_fmt_ptr->num_channels, + input_media_fmt_ptr->word_size, + input_media_fmt_ptr->q_factor); + } + else if (PC_BW24_W24_Q23 == output_media_fmt_ptr->byte_combo) + { + result = pc_deintlv_unpacked_v2_24_out(input_buf_ptr, + output_buf_ptr, + input_media_fmt_ptr->num_channels, + input_media_fmt_ptr->word_size, + input_media_fmt_ptr->q_factor); + } + else if ((PC_BW24_W32_Q23 == output_media_fmt_ptr->byte_combo) || + (PC_BW24_W32_Q27 == output_media_fmt_ptr->byte_combo) || + (PC_BW24_W32_Q31 == output_media_fmt_ptr->byte_combo) || + (PC_BW32_W32_Q31 == output_media_fmt_ptr->byte_combo) || + (PC_BW32_W32_FLOAT == output_media_fmt_ptr->byte_combo) || + (PC_BW64_W64_DOUBLE == output_media_fmt_ptr->byte_combo)) + { + result = pc_deintlv_unpacked_v2_32_out(input_buf_ptr, + output_buf_ptr, + input_media_fmt_ptr->num_channels, + input_media_fmt_ptr->word_size, + input_media_fmt_ptr->q_factor, + output_media_fmt_ptr->q_factor); + } + else + { + CNV_MSG(pc_ptr->miid, DBG_ERROR_PRIO, "Unsupported byte combo "); + } + } + + return result; +} +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + +______________________________________________________________________________________________________________________*/ +ar_result_t pc_channel_mix_process(void * me_ptr, + capi_buf_t * input_buf_ptr, + capi_buf_t * output_buf_ptr, + pc_media_fmt_t *input_media_fmt_ptr, + pc_media_fmt_t *output_media_fmt_ptr) +{ + ar_result_t result = AR_EOK; + pc_lib_t * pc_ptr = (pc_lib_t *)me_ptr; + void * local_input_buf_ptr[CAPI_MAX_CHANNELS_V2] = { NULL }; + void * local_output_buf_ptr[CAPI_MAX_CHANNELS_V2] = { NULL }; + uint32_t bytes_to_process = 0; + uint32_t samples_to_process = 0; + uint32_t byte_sample_convert = 0; + for (uint32_t i = 0; i < input_media_fmt_ptr->num_channels; i++) + { + local_input_buf_ptr[i] = (void *)input_buf_ptr[i].data_ptr; + } + + for (uint32_t ch = 0; ch < output_media_fmt_ptr->num_channels; ch++) + { + local_output_buf_ptr[ch] = (void *)output_buf_ptr[ch].data_ptr; + } + + bytes_to_process = input_buf_ptr[0].actual_data_len; + byte_sample_convert = (input_media_fmt_ptr->word_size == 16) ? 1 : 2; + samples_to_process = bytes_to_process >> (byte_sample_convert); + + ChMixerProcess(pc_ptr->ch_lib_ptr, local_output_buf_ptr, local_input_buf_ptr, samples_to_process); + // optimization: write/read only first ch lens, and assume same lens for rest of the chs + input_buf_ptr[0].actual_data_len = samples_to_process << byte_sample_convert; + output_buf_ptr[0].actual_data_len = samples_to_process << byte_sample_convert; + + return result; +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + +______________________________________________________________________________________________________________________*/ +ar_result_t pc_iir_resampler_process(void * me_ptr, + capi_buf_t * input_buf_ptr, + capi_buf_t * output_buf_ptr, + pc_media_fmt_t *input_media_fmt_ptr, + pc_media_fmt_t *output_media_fmt_ptr) +{ + + capi_err_t result = CAPI_EOK; + pc_lib_t * pc_ptr = (pc_lib_t *)me_ptr; + uint32_t num_input_samples, ch; + uint32_t processed_input_samples, generated_output_samples; + + iir_rs_lib_instance_t *port_instance_ptr = &pc_ptr->iir_rs_lib_ptr->lib_instance_per_port_ptr[0]; + num_input_samples = input_buf_ptr->actual_data_len >> 1; + // num_output_samples = (num_input_samples * output_media_fmt_ptr->sampling_rate) / + // input_media_fmt_ptr->sampling_rate; + + int8_t *input_ptr[CAPI_MAX_CHANNELS_V2] = { 0 }; + int8_t *output_ptr[CAPI_MAX_CHANNELS_V2] = { 0 }; + + processed_input_samples = generated_output_samples = 0; + while (num_input_samples > processed_input_samples) + { + for (ch = 0; ch < port_instance_ptr->lib_io_config.in_channels; ch++) + { + input_ptr[ch] = input_buf_ptr[ch].data_ptr + (processed_input_samples << 1); + output_ptr[ch] = output_buf_ptr[ch].data_ptr + (generated_output_samples << 1); + } + + // resampler process takes case of memscpy in passthrough case with different input and output pointers. + result = iir_rs_process(pc_ptr->iir_rs_lib_ptr, + input_ptr, + output_ptr, + pc_ptr->in_frame_samples_per_ch, + pc_ptr->out_frame_samples_per_ch); + + if (AR_EOK != result) + { + result = CAPI_EFAILED; + } + + processed_input_samples += pc_ptr->in_frame_samples_per_ch; + generated_output_samples += pc_ptr->out_frame_samples_per_ch; // amith - For now. + } + + // optimization: write/read only first ch lens, and assume same lens for rest of the chs + output_buf_ptr[0].actual_data_len = generated_output_samples << 1; + return result; +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + +______________________________________________________________________________________________________________________*/ +ar_result_t pc_calc_and_update_max_in_len(pc_lib_t * pc_ptr, + capi_buf_t *input_buf_ptr, + capi_buf_t *output_buf_ptr, + capi_buf_t *remapped_ib_ptr) +{ + ar_result_t result = AR_EOK; + uint32_t in_len_per_ch = 0; + uint32_t out_len_per_ch = 0; + uint32_t max_in_len_to_consume_per_ch = 0; + pc_media_fmt_t *imf_ptr = &pc_ptr->core_lib.input_media_fmt; + pc_media_fmt_t *omf_ptr = &pc_ptr->core_lib.output_media_fmt; + + if ((PC_INTERLEAVED == imf_ptr->interleaving) || (PC_DEINTERLEAVED_PACKED == imf_ptr->interleaving)) + { + in_len_per_ch = input_buf_ptr->actual_data_len / imf_ptr->num_channels; + } + else + { + in_len_per_ch = input_buf_ptr->actual_data_len; + } + + if ((PC_INTERLEAVED == omf_ptr->interleaving) || (PC_DEINTERLEAVED_PACKED == omf_ptr->interleaving)) + { + out_len_per_ch = output_buf_ptr->max_data_len / omf_ptr->num_channels; + } + else + { + out_len_per_ch = output_buf_ptr->max_data_len; + } + + // get output samples based on output buffer + uint32_t output_samples_per_ch = out_len_per_ch / (omf_ptr->word_size >> 3); + + // scale max required input samples to fill output max buffer length. + uint32_t max_req_in_per_ch = in_len_per_ch; + if ((pc_ptr->core_lib.flags.RESAMPLER_PRE || pc_ptr->core_lib.flags.RESAMPLER_POST)) + { + uint32_t req_in_samples_per_ch = 0; + // If IIR resampler is used, fixed input frame size is required. The following checks are needed: + // 1. Input length meets frame size. + // 2. Enough output was provided to fit the input. + if (NULL != pc_ptr->iir_rs_lib_ptr) + { + + if ((0 == pc_ptr->in_frame_samples_per_ch) || (0 == pc_ptr->out_frame_samples_per_ch)) + { + CNV_MSG(pc_ptr->miid, DBG_ERROR_PRIO, "pc_process: Error: Frame size not set."); + return AR_EFAILED; + } + + uint32_t in_frame_size_bpc = pc_ptr->in_frame_samples_per_ch * (imf_ptr->word_size >> 3); + + // Check if input length meets frame size. + if ((in_len_per_ch < in_frame_size_bpc) || (output_samples_per_ch < pc_ptr->out_frame_samples_per_ch)) + { +#ifdef PCM_CNV_DEBUG + CNV_MSG(pc_ptr->miid, + DBG_ERROR_PRIO, + "pc_process: Input given: bytes per ch: %ld, output samples per ch %ld, frame size bytes per ch: " + "%ld", + in_len_per_ch, + output_samples_per_ch, + in_frame_size_bpc); +#endif + return AR_ENEEDMORE; + } + req_in_samples_per_ch = pc_ptr->in_frame_samples_per_ch; + + // If more input was given, allow loops over iir resampler. IIR resampler pc process function allows for + // looping. + if (in_len_per_ch > in_frame_size_bpc) + { + + uint32_t in_proc_multiplier = in_len_per_ch / in_frame_size_bpc; + uint32_t out_proc_multiplier = output_samples_per_ch / pc_ptr->out_frame_samples_per_ch; + uint32_t proc_multiplier = MIN(in_proc_multiplier, out_proc_multiplier); + req_in_samples_per_ch *= proc_multiplier; +#ifdef PCM_CNV_DEBUG + CNV_MSG(pc_ptr->miid, + DBG_HIGH_PRIO, + "pc_process: Input given: bytes per ch: %ld, output bytes per ch %ld, consuming frame size bytes " + "per ch: %ld, in proc mult %ld, out proc mult %ld", + in_len_per_ch, + out_len_per_ch, + req_in_samples_per_ch * (imf_ptr->word_size >> 3), + in_proc_multiplier, + out_proc_multiplier); +#endif + + // Revert lib frame size back to cntr frame size when no longer in consume_partial_input mode, and + // we are able to process a full cntr frame size worth of data. + if ((!pc_ptr->consume_partial_input) && (pc_ptr->frame_size_us != pc_ptr->cntr_frame_size_us)) + { + uint64_t *FRACT_TIME_PTR_NULL = NULL; + + // To convert samples per ch to us, we can use bits_per_sample = 8, num_channels = 1. + uint32_t input_size_us = + capi_cmn_bytes_to_us(req_in_samples_per_ch, imf_ptr->sampling_rate, 8, 1, FRACT_TIME_PTR_NULL); + + if (input_size_us == pc_ptr->cntr_frame_size_us) + { + pc_set_frame_size(pc_ptr, pc_ptr->cntr_frame_size_us); + } + } + } + } + else if (NULL != pc_ptr->hwsw_rs_lib_ptr) // either hw or sw resampler is used. + { + // For software resampler, use the function to get in samples from out + if ((NULL != pc_ptr->hwsw_rs_lib_ptr->sw_rs_mem_ptr[STAGE_ZERO]) && + (pc_ptr->hwsw_rs_lib_ptr->sw_rs_mem_ptr[STAGE_ZERO]->drs_mem_ptr.pStructMem)) + { + req_in_samples_per_ch = pc_get_fixed_out_samples(pc_ptr, output_samples_per_ch); + } + else // For hardware resampler use differnt function to get in samples to be consumed + { + uint32_t local_out_sample = output_samples_per_ch; + hwsw_rs_lib_process_get_hw_process_info(pc_ptr->hwsw_rs_lib_ptr, + in_len_per_ch / (imf_ptr->word_size >> 3), + output_samples_per_ch, + &req_in_samples_per_ch, + &local_out_sample); + } + } + else + { + CNV_MSG(pc_ptr->miid, DBG_ERROR_PRIO, "pc_process: WARNING: Neither IIR not hwsw resamplers were enabled"); + req_in_samples_per_ch = (output_samples_per_ch * imf_ptr->sampling_rate) / omf_ptr->sampling_rate; + } + max_req_in_per_ch = req_in_samples_per_ch * (imf_ptr->word_size >> 3); + } + else // for case where resampler is not present calculate max input length from max samples output can hold + { + max_req_in_per_ch = output_samples_per_ch * (imf_ptr->word_size >> 3); + } + + // Update input buffer actual data legnth based on the len thats consumed by pc process, + // 1. If INPUT actual data length is greater than required, update actual data length + // based one expected input samples. + // 2. If INPUT actual data length is lesser than expected, this is an error case. Library + // will operate in fixed input mode and consume all the input, so no need to update + // input actual data length. + // 3. If OUTPUT max length is not enough to hold the converted data from input, + // calculate the corresponding input length which should be consumed + max_in_len_to_consume_per_ch = MIN(max_req_in_per_ch, in_len_per_ch); + +#ifdef PCM_CNV_LIB_DEBUG + CNV_MSG(pc_ptr->miid, + DBG_HIGH_PRIO, + "pc_process: max_in_len_to_consume_per_ch %lu in_len_per_ch %lu output_samples_per_ch %lu " + "max_req_in_per_ch %lu", + max_in_len_to_consume_per_ch, + in_len_per_ch, + output_samples_per_ch, + max_req_in_per_ch); +#endif + + if (PC_INTERLEAVED == imf_ptr->interleaving || PC_DEINTERLEAVED_PACKED == imf_ptr->interleaving) + { + input_buf_ptr->actual_data_len = max_in_len_to_consume_per_ch * imf_ptr->num_channels; + } + else + { + input_buf_ptr[0].actual_data_len = max_in_len_to_consume_per_ch; + } + + if (PC_INTERLEAVED == imf_ptr->interleaving) + { + remapped_ib_ptr->actual_data_len = input_buf_ptr->actual_data_len; + } + else + { + remapped_ib_ptr[0].actual_data_len = max_in_len_to_consume_per_ch; + } + + return result; +} + +/*______________________________________________________________________________________________________________________ + DESCRIPTION: + +______________________________________________________________________________________________________________________*/ +ar_result_t pc_process(pc_lib_t * pc_ptr, + capi_buf_t *input_buf_ptr, + capi_buf_t *output_buf_ptr, + capi_buf_t *scratch_buf_ptr_1, + capi_buf_t *scratch_buf_ptr_2) +{ + ar_result_t result = AR_EOK; + capi_buf_t * next_input_buf_ptr = pc_ptr->core_lib.remap_input_buf_ptr; + pc_media_fmt_t *next_input_media_fmt_ptr = &pc_ptr->core_lib.input_media_fmt; + bool_t any_process_done = FALSE; + + + + pc_remap_buffer(pc_ptr, + input_buf_ptr, + output_buf_ptr, + scratch_buf_ptr_1, + scratch_buf_ptr_2, + next_input_buf_ptr, + NULL, + next_input_media_fmt_ptr, + NULL, + TRUE); + + if (AR_ENEEDMORE == pc_calc_and_update_max_in_len(pc_ptr, input_buf_ptr, output_buf_ptr, next_input_buf_ptr)) + { + return AR_ENEEDMORE; + } + + for (uint32_t i = 0; i < NUMBER_OF_PROCESS; i++) + { if (NULL != pc_ptr->core_lib.pc_proc_info[i].process) + { + any_process_done = TRUE; + capi_buf_t * current_input_buf_ptr = next_input_buf_ptr; + capi_buf_t * current_output_buf_ptr = pc_ptr->core_lib.pc_proc_info[i].output_buffer_ptr; + pc_media_fmt_t *output_media_fmt_ptr = &pc_ptr->core_lib.pc_proc_info[i].output_media_fmt; + pc_remap_buffer(pc_ptr, + input_buf_ptr, + output_buf_ptr, + scratch_buf_ptr_1, + scratch_buf_ptr_2, + current_input_buf_ptr, + current_output_buf_ptr, + next_input_media_fmt_ptr, + output_media_fmt_ptr, + FALSE); + + result = pc_ptr->core_lib.pc_proc_info[i].process((void *)pc_ptr, + current_input_buf_ptr, + current_output_buf_ptr, + next_input_media_fmt_ptr, + output_media_fmt_ptr); + + if (current_output_buf_ptr == pc_ptr->core_lib.remap_output_buf_ptr) + { + output_buf_ptr->actual_data_len = current_output_buf_ptr->actual_data_len; + } + + next_input_media_fmt_ptr = &pc_ptr->core_lib.pc_proc_info[i].output_media_fmt; + next_input_buf_ptr = current_output_buf_ptr; + } + } + if (FALSE == any_process_done) + { + + if (pc_ptr->core_lib.input_media_fmt.interleaving == pc_ptr->core_lib.output_media_fmt.interleaving) + { + + if ((PC_INTERLEAVED == pc_ptr->core_lib.input_media_fmt.interleaving) || + (PC_DEINTERLEAVED_PACKED == pc_ptr->core_lib.input_media_fmt.interleaving)) + { + + if (input_buf_ptr->data_ptr != output_buf_ptr->data_ptr) + { + output_buf_ptr->actual_data_len = memscpy(output_buf_ptr->data_ptr, + output_buf_ptr->max_data_len, + input_buf_ptr->data_ptr, + input_buf_ptr->actual_data_len); + } + } + else + { + + uint32_t num_bytes_to_copy = MIN(output_buf_ptr[0].max_data_len, input_buf_ptr[0].actual_data_len); + for (int32_t ch = 0; ch < pc_ptr->core_lib.input_media_fmt.num_channels; ch++) + { + memscpy(output_buf_ptr[ch].data_ptr, num_bytes_to_copy, input_buf_ptr[ch].data_ptr, num_bytes_to_copy); + } + output_buf_ptr[0].actual_data_len = num_bytes_to_copy; + } + + } + else + { + + uint32_t bytes_per_ch = 0; + + // Enters here only for DEINTERLEAVED_PACKED <-> DEINTERLEAVED_UNPACKED conversion + // Note: Interleaved <-> deinterleaved packed/deinterleaved unpacked case doesn't happen + // since the interleaver process will be triggered if that happens + if (PCM_DEINTERLEAVED_PACKED == pc_ptr->core_lib.input_media_fmt.interleaving) + { + + bytes_per_ch = (input_buf_ptr->actual_data_len) / (pc_ptr->core_lib.input_media_fmt.num_channels); + uint32_t num_bytes_to_copy = MIN(bytes_per_ch, output_buf_ptr[0].max_data_len); + for (int32_t ch = 0; ch < pc_ptr->core_lib.input_media_fmt.num_channels; ch++) + { + memscpy(output_buf_ptr[ch].data_ptr, + num_bytes_to_copy, + input_buf_ptr->data_ptr + (bytes_per_ch * ch), + num_bytes_to_copy); + } + output_buf_ptr[0].actual_data_len = num_bytes_to_copy; + } + else + { + + // We can't use output buffer lengths because, actual length will be 0 and if we use max len, it will not be + // packed + // Since input is unpacked, first channels length will suffice for the bytes per ch + bytes_per_ch = (input_buf_ptr[0].actual_data_len); + output_buf_ptr->actual_data_len = 0; + for (int32_t ch = 0; ch < pc_ptr->core_lib.input_media_fmt.num_channels; ch++) + { + output_buf_ptr->actual_data_len += memscpy(output_buf_ptr->data_ptr + (bytes_per_ch * ch), + output_buf_ptr->max_data_len, + input_buf_ptr[ch].data_ptr, + bytes_per_ch); + } + } + + } + + } + + + return result; +} diff --git a/modules/cmn/pcm_mf_cnv/lib/stub_src/pc_float/pc_float_stub.cpp b/modules/cmn/pcm_mf_cnv/lib/stub_src/pc_float/pc_float_stub.cpp new file mode 100644 index 0000000..8252fa4 --- /dev/null +++ b/modules/cmn/pcm_mf_cnv/lib/stub_src/pc_float/pc_float_stub.cpp @@ -0,0 +1,41 @@ + +/*======================================================================== + + file pc_float_stub.cpp +This file contains stub functions for data format conversions + +Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. +SPDX-License-Identifier: BSD-3-Clause +======================================================================*/ + +/* ======================================================================= +INCLUDE FILES FOR MODULE +========================================================================== */ + +#include "pc_converter.h" + +ar_result_t pc_float_to_fixed_conv_process(void * me_ptr, + capi_buf_t * input_buf_ptr, + capi_buf_t * output_buf_ptr, + pc_media_fmt_t *input_media_fmt_ptr, + pc_media_fmt_t *output_media_fmt_ptr) +{ + ar_result_t result = AR_EUNSUPPORTED; + return result; +} + +ar_result_t pc_fixed_to_float_conv_process(void * me_ptr, + capi_buf_t * input_buf_ptr, + capi_buf_t * output_buf_ptr, + pc_media_fmt_t *input_media_fmt_ptr, + pc_media_fmt_t *output_media_fmt_ptr) + +{ + ar_result_t result = AR_EUNSUPPORTED; + return result; +} + +bool_t pc_is_floating_point_data_format_supported() +{ + return FALSE; +} diff --git a/modules/cmn/pcm_mf_cnv/lib/tst/main.cpp b/modules/cmn/pcm_mf_cnv/lib/tst/main.cpp new file mode 100644 index 0000000..68091a4 --- /dev/null +++ b/modules/cmn/pcm_mf_cnv/lib/tst/main.cpp @@ -0,0 +1,239 @@ +/*============================================================================== + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause + ==============================================================================*/ + +/*============================================================================ + FILE: main.c + + OVERVIEW: Entry point for PCM_converter unit test + + DEPENDENCIES: None + + ============================================================================*/ +#include +#include +#include +#include +#include +#include + +#include "wavefile.h" +#include "pcm_converter.h" +using namespace std; +uint8_t posal_debugmsg_lowest_prio = 4; + +/* ----------------------------------------------------------------------- +** Global Data +** ----------------------------------------------------------------------- */ +extern WavHeader wh; +extern WavHeader wh_write; +void get_interleaved_info(pc_main_mf_t *media_fmt_ptr, int in_value) +{ + switch (in_value) + { + case 0: + media_fmt_ptr->interleaving = CAPI_DEINTERLEAVED_PACKED; + break; + case 1: + media_fmt_ptr->interleaving = CAPI_INTERLEAVED; + break; + default: + media_fmt_ptr->interleaving = CAPI_INVALID_INTERLEAVING; + break; + } +} + +void get_endian_info(pc_main_mf_t *media_fmt_ptr, int in_value) +{ + switch (in_value) + { + case 0: + media_fmt_ptr->endianness = PCM_CNV_LITTLE_ENDIAN; + break; + case 1: + media_fmt_ptr->endianness = PCM_CNV_BIG_ENDIAN; + break; + default: + media_fmt_ptr->endianness = PCM_CNV_UNKNOWN_ENDIAN; + break; + } +} + +void print_media_format(pc_main_mf_t *media_fmt_ptr) +{ + cout << endl; + cout << "Media format is" << endl; + cout << "Bit width :" << media_fmt_ptr->bit_width << endl; + cout << "Word size :" << media_fmt_ptr->word_size << endl; + cout << "Q format :" << media_fmt_ptr->q_factor << endl; + cout << "Interleaving :" << media_fmt_ptr->interleaving << endl; + cout << "Endianness :" << media_fmt_ptr->endianness << endl << endl; +} + +int main(int argc, char *argv[]) +{ + ar_result_t result = AR_EOK; + unsigned long long cycles = 0; + int sample_count = 0; + int bytes_to_read = 0; + int bytes_to_write = 0; + int bytes_read = 0; + int bytes_written = 0; + int frame_size_bytes = 0; + int frame_size_bytes_out = 0; + int frame_size_samples = 0; + pc_main_mf_t inp_media_fmt = { 0 }; + pc_main_mf_t out_media_fmt = { 0 }; + int8_t * input_buf_ptr = NULL; + int8_t * output_buf_ptr = NULL; + int8_t * scratch_buf_ptr = NULL; + pc_lib_t me; + pc_lib_t * me_ptr = &me; + + // Check the number of arguments + if (argc < 11) + { + fprintf(stderr, "Qualcomm's new PCM converter:\n"); + fprintf(stderr, + "Usage: tst_pcm_cnv " + " \n\n"); + fprintf(stderr, "Input Multi-channel Audio wave file. \n"); + fprintf(stderr, "Output Multi-channel Audio output from PCM converter. \n"); + fprintf(stderr, "Q_format of input file - 15, 23, 27 or 31 \n"); + fprintf(stderr, "Whether input file is to be interleaved - set 1 to interleave\n"); + fprintf(stderr, "Endianness of input file - 0 or 1, 0 for little\n"); + fprintf(stderr, "Bitwidth of output file - 16, 24 or 32 \n"); + fprintf(stderr, "Word size of output file - 16, 24 or 32 \n"); + fprintf(stderr, "Q_format of output file - 15, 23, 27 or 31 \n"); + fprintf(stderr, "Whether output file is to be interleaved - set 1 to interleave\n"); + fprintf(stderr, "Endianness of output file - 0 or 1, 0 for little\n"); + fprintf(stderr, "The input multi-channel audio file can be in interleaved/deinterleaved format. \n"); + return -1; + } + + if (0 != WaveFileReadInit(argv[1], 1)) + return -1; + + inp_media_fmt.bit_width = wh.bitsPerSample; + inp_media_fmt.word_size = wh.bytesPerSample * 8 / wh.numChannels; + inp_media_fmt.q_factor = atoi(argv[3]); + get_interleaved_info(&inp_media_fmt, atoi(argv[4])); + get_endian_info(&inp_media_fmt, atoi(argv[5])); + + out_media_fmt.bit_width = atoi(argv[6]); + out_media_fmt.word_size = atoi(argv[7]); + out_media_fmt.q_factor = atoi(argv[8]); + get_interleaved_info(&out_media_fmt, atoi(argv[9])); + get_endian_info(&out_media_fmt, atoi(argv[10])); + + memcpy(&wh_write, &wh, sizeof(WavHeader)); + wh_write.bitsPerSample = out_media_fmt.bit_width; + wh_write.bytesPerSample = out_media_fmt.word_size * wh_write.numChannels / 8; + wh_write.bytesPerSecond = wh_write.bytesPerSample * wh_write.sampleRate; + wh_write.dataLength = wh.dataLength * wh_write.bytesPerSample / wh.bytesPerSample; + print_media_format(&inp_media_fmt); + print_media_format(&out_media_fmt); + + if (0 != WaveFileWriteInit(argv[2])) + { + WaveFileIOEnd(); + return -1; + } + + frame_size_samples = (wh.sampleRate / wh.sampleRate * 2) * wh.numChannels; // 2 samples per channel + frame_size_bytes = frame_size_samples * inp_media_fmt.word_size >> 3; + frame_size_bytes_out = frame_size_samples * out_media_fmt.word_size >> 3; + + + input_buf_ptr = (int8_t *)calloc(frame_size_bytes, sizeof(int8_t)); + output_buf_ptr = (int8_t *)calloc(frame_size_bytes_out, sizeof(int8_t)); + scratch_buf_ptr = (int8_t *)calloc(frame_size_bytes_out, sizeof(int8_t)); + + sample_count = wh.dataLength / wh.bytesPerSample; + + me.input_media_fmt = inp_media_fmt; + me.output_media_fmt = out_media_fmt; + me.inp_media_fmt_combo = pc_classify_mf(&inp_media_fmt); + me.out_media_fmt_combo = pc_classify_mf(&out_media_fmt); + + cout << "frame_size_samples :" << frame_size_samples << endl; + cout << "frame_size_bytes :" << frame_size_bytes << endl; + cout << "frame_size_bytes_out :" << frame_size_bytes_out << endl; + do + { + if (frame_size_bytes > wh.dataLength) + { + bytes_to_read = wh.dataLength; + bytes_to_write = wh_write.dataLength; + } + else + { + bytes_to_read = frame_size_bytes; + bytes_to_write = frame_size_bytes_out; + } + + // Read data from the input wave file + if (WaveFileRead((char *)input_buf_ptr, bytes_to_read) < 0) + { + break; + } + bytes_read += bytes_to_read; + sample_count -= bytes_to_read / wh.bytesPerSample; + + capi_buf_t inp_buffer = { 0 }; + capi_buf_t out_buffer = { 0 }; + capi_buf_t scratch_buffer = { 0 }; + inp_buffer.data_ptr = (int8_t *)input_buf_ptr; + inp_buffer.actual_data_len = bytes_to_read; + inp_buffer.max_data_len = frame_size_bytes * sizeof(int8_t); + out_buffer.data_ptr = (int8_t *)output_buf_ptr; + out_buffer.actual_data_len = 0; + out_buffer.max_data_len = frame_size_bytes_out * sizeof(int8_t); + scratch_buffer.data_ptr = (int8_t *)scratch_buf_ptr; + scratch_buffer.actual_data_len = 0; + scratch_buffer.max_data_len = frame_size_bytes_out * sizeof(int8_t); + pc_ch_info_t temp = { 0 }; + temp.num_samp_per_ch = bytes_to_read / ((inp_media_fmt.word_size >> 3) * wh.numChannels); + temp.num_channels = wh.numChannels; + temp.chan_spacing_in = bytes_to_read / wh.numChannels; + temp.chan_spacing_out = bytes_to_write / wh_write.numChannels; + + static int flag = 0; + if (0 == flag) + { + cout << "num_samp_per_ch :" << temp.num_samp_per_ch << endl; + cout << "num_channels :" << temp.num_channels << endl; + cout << "chan_spacing_in :" << temp.chan_spacing_in << endl; + cout << "chan_spacing_out :" << temp.chan_spacing_out << endl; + flag = 1; + } + result = pc_process(me_ptr, + &inp_buffer, + &out_buffer, + &scratch_buffer); + if (AR_EOK != result) + { + AR_MSG(DBG_ERROR_PRIO, "Failed to PCM convert, lol"); + break; + } + //printf("%x %x %x\n", output_buf_ptr, input_buf_ptr, scratch_buf_ptr); + WaveFileWrite((char *)output_buf_ptr, bytes_to_write); + + bytes_written += bytes_to_write; + + } while (sample_count > 0); // End main processing loop + + printf("cycles %llu\t", cycles); + /* printf("MIPS %f\n", */ + /* (((float)cycles * fparams[SAMPLE_RATE] * (float)numChannels)) / ((float)samplesWritten * 1000000)); */ + + free(input_buf_ptr); + free(output_buf_ptr); + free(scratch_buf_ptr); + printf("Bytes read from input file: %d\n", bytes_read); + printf("Bytes written to output file: %d\n", bytes_written); + + return 0; +} diff --git a/modules/processing/channel_mixer/api/chmixer_api.h b/modules/processing/channel_mixer/api/chmixer_api.h new file mode 100644 index 0000000..34462f7 --- /dev/null +++ b/modules/processing/channel_mixer/api/chmixer_api.h @@ -0,0 +1,108 @@ +#ifndef CHMIXER_API_H +#define CHMIXER_API_H + +/*============================================================================== + @file chmixer_api.h + @brief This file contains CHMIXER API +==============================================================================*/ + +/*=========================================================================== +Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. +SPDX-License-Identifier: BSD-3-Clause +======================================================================== */ + +/*------------------------------------------------------------------------------ + Includes +------------------------------------------------------------------------------*/ +#include "chmixer_common_api.h" +#include "module_cmn_api.h" + +/** @h2xml_title1 {Channel Mixer Module API} + @h2xml_title_agile_rev {Channel Mixer Module API} + @h2xml_title_date {February 9, 2018} */ + +/*------------------------------------------------------------------------------ + Defines +------------------------------------------------------------------------------*/ +/* Input port ID of chmixer module */ +#define CHMIXER_DATA_INPUT_PORT 0x2 + +/* Output port ID of chmixer module */ +#define CHMIXER_DATA_OUTPUT_PORT 0x1 + +/*------------------------------------------------------------------------------ + Parameters +------------------------------------------------------------------------------*/ +/* ID of the Coefficient parameter used by MODULE_ID_CHMIXER to configure the channel mixer weighting coefficients. */ +#define PARAM_ID_CHMIXER_OUT_CH_CFG 0x0800103B + +/** @h2xmlp_parameter {"PARAM_ID_CHMIXER_OUT_CH_CFG", PARAM_ID_CHMIXER_OUT_CH_CFG} + @h2xmlp_description {Configures the channel mixer output config} + @h2xmlp_toolPolicy {RTC; Calibration} + @h2xmlp_maxSize {68}*/ + +#include "spf_begin_pack.h" +#include "spf_begin_pragma.h" +struct param_id_chmixer_out_ch_cfg_t +{ + int16_t num_channels; + /**< @h2xmle_description { Specifies number of channels \n + - Sender will have to pad accordingly to make sure it is 4 byte aligned} + @h2xmle_range {-2..MODULE_CMN_MAX_CHANNEL} + @h2xmle_default {-1} */ + uint16_t channel_map[0]; + /**< @h2xmle_description { Payload consisting of channel mapping information for all the channels \n + ->If num_channels field is set to PARAM_VAL_NATIVE (-1) or PARAM_VAL_UNSET(-2) + this field will be ignored} + @h2xmle_variableArraySize {num_channels} + @h2xmle_rangeEnum {pcm_channel_map} + @h2xmle_default {1}*/ +} +#include "spf_end_pragma.h" +#include "spf_end_pack.h" +; + +/* Structure type def for above payload. */ +typedef struct param_id_chmixer_out_ch_cfg_t param_id_chmixer_out_ch_cfg_t; + +/*------------------------------------------------------------------------------ + Module +------------------------------------------------------------------------------*/ + +#define MODULE_ID_CHMIXER 0x07001013 +/** + @h2xmlm_module {"MODULE_ID_CHMIXER", + MODULE_ID_CHMIXER} + @h2xmlm_displayName {"Channel Mixer"} + @h2xmlm_modSearchKeys{channel mixer,Audio} + @h2xmlm_description {- ID of the Channel Mixer module. This module upmixes or downmixes + audio channels based on configured coefficients\n + - Supported Input Media Format: \n + - Data Format : FIXED_POINT \n + - fmt_id : Don't care\n + - Sample Rates : >0 \n + - Number of channels : 1 to 128\n + - Channel type : 0 to 128\n + - Bits per sample : 16, 32\n + - Q format : Don't care\n + - Interleaving : de-interleaved unpacked\n + - Signed/unsigned : Signed} + @h2xmlm_dataInputPorts { IN = CHMIXER_DATA_INPUT_PORT} + @h2xmlm_dataOutputPorts { OUT = CHMIXER_DATA_OUTPUT_PORT} + @h2xmlm_dataMaxInputPorts { 1 } + @h2xmlm_dataMaxOutputPorts { 1 } + @h2xmlm_supportedContTypes { APM_CONTAINER_TYPE_SC, APM_CONTAINER_TYPE_GC} + @h2xmlm_isOffloadable {true} + @h2xmlm_stackSize { 2048 } + @{ <-- Start of the Module --> + + @h2xml_Select {param_id_chmixer_out_ch_cfg_t} + @h2xmlm_InsertParameter + + @h2xml_Select {param_id_chmixer_coeff_t} + @h2xmlm_InsertParameter + + @h2xml_Select {chmixer_coeff_t} + @h2xmlm_InsertStructure + @} <-- End of the Module -->*/ +#endif // CHMIXER_API_H diff --git a/modules/processing/channel_mixer/api/chmixer_common_api.h b/modules/processing/channel_mixer/api/chmixer_common_api.h new file mode 100644 index 0000000..612d42f --- /dev/null +++ b/modules/processing/channel_mixer/api/chmixer_common_api.h @@ -0,0 +1,190 @@ +#ifndef CHMIXER_COMMON_API +#define CHMIXER_COMMON_API + +/*============================================================================== + @file chmixer_common_api.h + @brief This file contains common parameters +==============================================================================*/ + +/*======================================================================= +Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. +SPDX-License-Identifier: BSD-3-Clause +=========================================================================*/ + +#include "ar_defs.h" +#include "media_fmt_api.h" +#include "module_cmn_api.h" + +/*============================================================================== + Constants +==============================================================================*/ + +/*# @h2xmlp_subStruct */ + +/** @ingroup ar_spf_mod_chmixer_mod + Part of the payload of #PARAM_ID_CHMIXER_COEFF. +*/ +#include "spf_begin_pack.h" +struct chmixer_coeff_t +{ + uint16_t num_output_channels; + /**< Number of output channels in the array. + + @values 1 through 128 (for some products it is 32) (Default = 1) */ + + /*#< @h2xmle_description {Number of output channels.} + @h2xmle_range {1..MODULE_CMN_MAX_CHANNEL} + @h2xmle_default {1} */ + + uint16_t num_input_channels; + /**< Number of input channels in the array. + + @values 1 through 128 (for some products it is 32) (Default = 1) + + This value is followed by the payload consisting of the output channel + map, input channel map, and coefficients. */ + + /*#< @h2xmle_description {Number of input channels. This value is followed + by the payload consisting of the output channel + map, input channel map, and coefficients.} + @h2xmle_default {1} + @h2xmle_range {1..MODULE_CMN_MAX_CHANNEL} */ + +#ifdef __H2XML__ + uint16_t out_chmap[0]; + /**< Array of channel maps for the output channels. */ + + /*#< @h2xmle_description {Array of channel maps for the output + channels.} + @h2xmle_variableArraySize {num_output_channels} + @h2xmle_rangeEnum {pcm_channel_map} + @h2xmle_default {1} */ + + uint16_t in_chmap[0]; + /**< Array of channel maps for the input channels. */ + + /*#< @h2xmle_description {Array of channel maps for the input + channels.} + @h2xmle_variableArraySize {num_input_channels} + @h2xmle_rangeEnum {pcm_channel_map} + @h2xmle_default {1} */ + + uint16_t coeff[0]; + /**< Array of channel mixer coefficients: [num_output_channels] + [num_input_channels]. */ + + /*#< @h2xmle_description {Array of channel mixer coefficients: + [num_output_channels][num_input_channels].} + @h2xmle_variableArraySize {"num_output_channels*num_input_channels"} */ +#endif +} +#include "spf_end_pack.h" +; +typedef struct chmixer_coeff_t chmixer_coeff_t; + + +/*============================================================================== + API definitions +==============================================================================*/ + +/** @ingroup ar_spf_mod_chmixer_mod + Identifier for the coefficient parameter that #MODULE_ID_MFC uses to + configure the channel mixer weighting coefficients. + + @msgpayload + param_id_chmixer_coeff_t \n + @indent{12pt} Immediately following this structure is a payload with the + (number of coefficients) * (variable-sized \n + @indent{12pt} chmixer coefficient arrays). \n + @indent{12pt} chmixer_coeff_t + @par + The payload with (number of coefficients) * (variable-sized chmixer + coefficient arrays) comprises the following: + - num_output_channels (permitted values: 1..128) (for certain products this module supports only 32 channels) + - num_input_channels (permitted values: 1..128) (for certain products this module supports only 32 channels) + - num_output_channels entries of output channel mapping (permitted values: + 1 to 128) (for certain products it is 1 to 32) + - num_input_channels entries of input channel mapping (permitted values: + 1 to 128) (for certain products it is 1 to 32) + - num_output_channels (rows) * num_input_channels (columns) entries of + channel mixer weighting coefficients in Q14 format (permitted values: + 0 to 16384) @lb{2} + For example: @vertspace{-3} + - Unity (1) - 0x4000 -> 16384 @lstsp2 + - 3 dB (0.707) - 0x2D44 -> 11588 @lstsp2 + - 6 dB (0.5) - 0x2000 -> 8192 @lstsp2 + - 0 - 0x0 -> 0 + - Optional 16-bit zero padding if the entire combination of 1, 2, 3, 4, + and 5 is not a multiple of 32 bits. @vertspace{-3} + - Padding is required so the entire payload is aligned to 32 bits + (permitted value: 0) @lstsp2 + - Number of coefficients + + @subhead4{Rules for using Set parameters} + -# Through a single Set parameter, a client can configure any number of + coefficient sets. For example, the client can configure eight set + parameters as follows: + - num_coeff_tbls = 8 @lstsp1 + - num_output_channels = 2 @lb{0.5} + num_input_channels = 2 @lb{0.5} + out_chmap[] = FL, FR @lb{0.5} + in_chmap[] = FL, FR @lb{0.5} + coeff[0] set #0 [2][2] @lstsp1 + - num_output_channels = 2 @lb{0.5} + num_input_channels = 5 @lb{0.5} + out_chmap[] = 2, FL, FR @lb{0.5} + in_chmap[] = L, FR, FC, LS, RS @lb{0.5} + coeff[0] set #1 [2][5] @lstsp1 + - .. @lstsp1 + - num_output_channels = 1 @lb{0.5} + num_input_channels = 2 @lb{0.5} + out_chmap[] = FL @lb{0.5} + in_chmap[] = FL, FR @lb{0.5} + coeff[0] set #7 [1][2] @lstsp1 + -# When the client sends a new coefficient mapping, the previous values are + overwritten. + -# If multiple rows contain duplicate entries, the higher index rule (which + is to be set later) is selected and applied, if applicable. + -# If the client does not explicitly provide the rule, the module reverts + to default coefficients (built-in coefficients). + - For example, if the input or output media types do not match the Set + parameter rule provided. @lstsp1 + - The default coefficients are based on the Recommendation ITU-R BS.775 + specification. +*/ +#define PARAM_ID_CHMIXER_COEFF 0x0800101F + +/*# @h2xmlp_parameter {"PARAM_ID_CHMIXER_COEFF", PARAM_ID_CHMIXER_COEFF} + @h2xmlp_description {ID for the parameter that configures the channel + mixer weighting coefficients. For more details, + including rules for using Set parameters, see AudioReach Signal Processing Framework (SPF) API Reference.} + @h2xmlp_toolPolicy {Calibration} */ + +/** @ingroup ar_spf_mod_chmixer_mod + Payload for #PARAM_ID_CHMIXER_COEFF. +*/ +#include "spf_begin_pack.h" +#include "spf_begin_pragma.h" +struct param_id_chmixer_coeff_t +{ + uint32_t num_coeff_tbls; + /**< Number of coefficient tables in the chmixer_coeff_tbl array. */ + + /*#< @h2xmle_description {Number of coefficient tables in the array.} + @h2xmle_range {0..255} + @h2xmle_default {0} */ + + chmixer_coeff_t chmixer_coeff_tbl[0]; + /**< Array of channel mixer coefficient tables (of size num_coeff_tbls). */ + + /*#< @h2xmle_description {Array of channel mixer coefficient tables + (of size num_coeff_tbls).} + @h2xmle_variableArraySize {num_coeff_tbls} */ +} +#include "spf_end_pragma.h" +#include "spf_end_pack.h" +; +typedef struct param_id_chmixer_coeff_t param_id_chmixer_coeff_t; + + +#endif //CHMIXER_COMMON_API diff --git a/modules/processing/channel_mixer/build/CMakeLists.txt b/modules/processing/channel_mixer/build/CMakeLists.txt new file mode 100644 index 0000000..253157e --- /dev/null +++ b/modules/processing/channel_mixer/build/CMakeLists.txt @@ -0,0 +1,43 @@ +#[[ + @file CMakeLists.txt + + @brief + + @copyright + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause +]] +cmake_minimum_required(VERSION 3.10) + +set(chmixer_sources + ${LIB_ROOT}/capi/src/capi_chmixer.cpp + ${LIB_ROOT}/capi/src/capi_chmixer_utils.cpp + ${LIB_ROOT}/lib/src/ChannelMixerLib.c + ${LIB_ROOT}/lib/src/ChannelMixerLib_island.c + ${LIB_ROOT}/lib/src/ChannelMixerRemapRules.c +) + +set(chmixer_includes + ${LIB_ROOT}/api + ${LIB_ROOT}/capi/inc + ${LIB_ROOT}/capi/src + ${LIB_ROOT}/lib/inc + ${LIB_ROOT}/lib/src + ../../../cmn/common/utils/inc +) + +spf_module_sources( + KCONFIG CONFIG_CHMIXER + NAME chmixer + MAJOR_VER 1 + MINOR_VER 0 + AMDB_ITYPE "capi" + AMDB_MTYPE "PP" + AMDB_MID "0x07001013" + AMDB_TAG "capi_chmixer" + AMDB_MOD_NAME "MODULE_ID_CHMIXER" + SRCS ${chmixer_sources} + INCLUDES ${chmixer_includes} + H2XML_HEADERS "${LIB_ROOT}/api/chmixer_api.h" + CFLAGS "" +) diff --git a/modules/processing/channel_mixer/capi/inc/capi_chmixer.h b/modules/processing/channel_mixer/capi/inc/capi_chmixer.h new file mode 100644 index 0000000..8731257 --- /dev/null +++ b/modules/processing/channel_mixer/capi/inc/capi_chmixer.h @@ -0,0 +1,54 @@ +/* ======================================================================== */ +/** + @file capi_chmixer.h + + Header file to implement the Audio Post Processor Interface for + Channel Mixer. +*/ + +/* ========================================================================= + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause + ========================================================================= */ + +/*------------------------------------------------------------------------ + * Include files and Macro definitions + * -----------------------------------------------------------------------*/ +#ifndef CAPI_CHMIXER_H +#define CAPI_CHMIXER_H + +#include "capi.h" +#include "ar_defs.h" + +/*------------------------------------------------------------------------ +* Macros, Defines, Type declarations +* -----------------------------------------------------------------------*/ +#define CAPI_CHMIXER_PARAM_ID_ENABLE (0) + +/*------------------------------------------------------------------------ + * Structure definitions + * -----------------------------------------------------------------------*/ +/** Payload for CHANNELMIXER_PARAM_ID_ENABLE. */ +typedef struct capi_chmixer_enable_payload +{ + uint32_t enable; /**< Defined the state of the module. */ +} capi_chmixer_enable_payload_t; + +/*------------------------------------------------------------------------ +* Function declarations +* -----------------------------------------------------------------------*/ + +#ifdef __cplusplus +extern "C" { +#endif + +capi_err_t capi_chmixer_get_static_properties (capi_proplist_t *init_set_properties, capi_proplist_t *static_properties); + +capi_err_t capi_chmixer_init (capi_t *_pif, capi_proplist_t *init_set_properties); + +#ifdef __cplusplus +} +#endif + +#endif //CAPI_CHMIXER_H + diff --git a/modules/processing/channel_mixer/capi/src/capi_chmixer.cpp b/modules/processing/channel_mixer/capi/src/capi_chmixer.cpp new file mode 100644 index 0000000..9fe2393 --- /dev/null +++ b/modules/processing/channel_mixer/capi/src/capi_chmixer.cpp @@ -0,0 +1,839 @@ +/* ========================================================================= + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause + * =========================================================================*/ + +/* ======================================================================== */ +/** + @file capi_chmixer.cpp + + C source file to implement the Audio Post Processor Interface for + Channel Mixer. + */ + +/*------------------------------------------------------------------------ + * Include files and Macro definitions + * -----------------------------------------------------------------------*/ +#include "posal.h" +#include "capi_chmixer.h" +#include "capi_chmixer_utils.h" + +#include "chmixer_api.h" + +#define ALIGN_4_BYTES(a) ((a + 3) & (0xFFFFFFFC)) + +#ifdef CAPI_CHMIXER_KPPS_PROFILING +#include +#endif + +/*------------------------------------------------------------------------ + * Static declarations + * -----------------------------------------------------------------------*/ + +static capi_err_t capi_chmixer_process(capi_t *_pif, capi_stream_data_t *input[], capi_stream_data_t *output[]); + +static capi_err_t capi_chmixer_end(capi_t *_pif); + +static capi_err_t capi_chmixer_set_param(capi_t * _pif, + const uint32_t param_id, + const capi_port_info_t *const port_info_ptr, + capi_buf_t *const params_ptr); + +static capi_err_t capi_chmixer_get_param(capi_t * _pif, + const uint32_t param_id, + const capi_port_info_t *const port_info_ptr, + capi_buf_t *const params_ptr); + +static capi_err_t capi_chmixer_set_properties(capi_t *_pif, capi_proplist_t *props_ptr); + +static capi_err_t capi_chmixer_get_properties(capi_t *_pif, capi_proplist_t *props_ptr); + +static const capi_vtbl_t vtbl = { capi_chmixer_process, capi_chmixer_end, + capi_chmixer_set_param, capi_chmixer_get_param, + capi_chmixer_set_properties, capi_chmixer_get_properties }; + +/*------------------------------------------------------------------------ + Function name: capi_chmixer_get_static_properties + DESCRIPTION: Function to get the static properties of CHMIXER module + -----------------------------------------------------------------------*/ +capi_err_t capi_chmixer_get_static_properties(capi_proplist_t *const init_set_properties, + capi_proplist_t *const static_properties) +{ + capi_err_t capi_result = CAPI_EOK; + + if (NULL != static_properties) + { + capi_result |= capi_chmixer_process_get_properties((capi_chmixer_t *)NULL, static_properties); + if (CAPI_FAILED(capi_result)) + { + return capi_result; + } + } + return capi_result; +} + +/*------------------------------------------------------------------------ + Function name: capi_chmixer_init + DESCRIPTION: Initialize the CAPI V2 CHMIXER module. + -----------------------------------------------------------------------*/ +capi_err_t capi_chmixer_init(capi_t *_pif, capi_proplist_t *init_set_properties) +{ + capi_err_t capi_result = CAPI_EOK; + + if (NULL == _pif) + { + CHMIXER_MSG(MIID_UNKNOWN, DBG_ERROR_PRIO, "Init received NULL pointer"); + return CAPI_EBADPARAM; + } + + const uint32_t mem_size = capi_get_init_mem_req(); + memset(_pif, 0, mem_size); + + uintptr_t ptr = (uintptr_t)_pif; + + capi_chmixer_t *me_ptr = (capi_chmixer_t *)ptr; + + me_ptr->heap_mem.heap_id = POSAL_HEAP_DEFAULT; + me_ptr->vtbl.vtbl_ptr = &vtbl; + me_ptr->client_enable = CAPI_CHMIXER_ENABLE; + + // setting invalid media format + capi_result = capi_cmn_init_media_fmt_v2(&me_ptr->input_media_fmt[CAPI_CHMIXER_DEFAULT_PORT]); + me_ptr->output_media_fmt[CAPI_CHMIXER_DEFAULT_PORT] = me_ptr->input_media_fmt[CAPI_CHMIXER_DEFAULT_PORT]; + + me_ptr->use_default_channel_info[CAPI_CHMIXER_DEFAULT_PORT] = TRUE; + + // No coefficients have been received yet + me_ptr->config.lib_enable = FALSE; + me_ptr->config.num_coef_sets = 0; + me_ptr->config.coef_sets_ptr = NULL; + + // Set flags + me_ptr->inp_media_fmt_received = FALSE; + me_ptr->is_native_mode = TRUE; + me_ptr->coef_payload_size = 0; + + // sets invalid events + capi_chmixer_init_events(me_ptr); + + if (NULL != init_set_properties) + { + // should contain EVENT_CALLBACK_INFO, PORT_INFO + capi_result |= capi_chmixer_process_set_properties(me_ptr, init_set_properties); + + // Ignoring non-fatal error code. + capi_result ^= (capi_result & CAPI_EUNSUPPORTED); + } + uint32_t miid = me_ptr ? me_ptr->miid : MIID_UNKNOWN; + + if (CAPI_FAILED(capi_result)) + { + CHMIXER_MSG(miid, DBG_ERROR_PRIO, "Init failed!"); + } + else + { + capi_chmixer_raise_events(me_ptr, false); +#ifdef CAPI_CHMIXER_DEBUG_MSG + CHMIXER_MSG(miid, DBG_ERROR_PRIO, "CAPI CHMIXER: Init Done!"); +#endif + } + return capi_result; +} + +/*------------------------------------------------------------------------ + Function name: capi_chmixer_set_properties + DESCRIPTION: Function to set properties to the CHMIXER module + -----------------------------------------------------------------------*/ +static capi_err_t capi_chmixer_set_properties(capi_t *_pif, capi_proplist_t *props_ptr) +{ + capi_err_t capi_result = CAPI_EOK; + if (NULL == _pif || NULL == props_ptr) + { + CHMIXER_MSG(MIID_UNKNOWN, DBG_ERROR_PRIO, "Set properties received NULL pointer"); + return CAPI_EBADPARAM; + } + capi_chmixer_t *me_ptr = (capi_chmixer_t *)_pif; + capi_result |= capi_chmixer_process_set_properties(me_ptr, props_ptr); + + return capi_result; +} + +/*------------------------------------------------------------------------ + Function name: capi_chmixer_get_properties + DESCRIPTION: Function to get the properties from the CHMIXER module + -----------------------------------------------------------------------*/ +static capi_err_t capi_chmixer_get_properties(capi_t *_pif, capi_proplist_t *props_ptr) +{ + capi_err_t capi_result = CAPI_EOK; + if (NULL == props_ptr) + { + CHMIXER_MSG(MIID_UNKNOWN, DBG_ERROR_PRIO, "Get properties received NULL pointer"); + return CAPI_EBADPARAM; + } + capi_chmixer_t *me_ptr = (capi_chmixer_t *)_pif; + capi_result |= capi_chmixer_process_get_properties(me_ptr, props_ptr); + + return capi_result; +} + +/*------------------------------------------------------------------------ + Function name: capi_chmixer_set_param + DESCRIPTION: Function to clear coef sets value\ptr. + -----------------------------------------------------------------------*/ +static void capi_free_coef_sets(capi_chmixer_t *const me_ptr) +{ + // Freeing older memory + if (NULL != me_ptr->config.coef_sets_ptr) + { + posal_memory_free(me_ptr->config.coef_sets_ptr); + me_ptr->config.coef_sets_ptr = NULL; + me_ptr->config.num_coef_sets = 0; + me_ptr->coef_payload_size = 0; + } +} + + +/*------------------------------------------------------------------------ + Function name: capi_chmixer_set_param + DESCRIPTION: Function to set parameter value\structure. + -----------------------------------------------------------------------*/ +static capi_err_t capi_chmixer_set_param(capi_t * _pif, + const uint32_t param_id, + const capi_port_info_t *const port_info_ptr, + capi_buf_t *const params_ptr) + +{ + if (NULL == _pif || NULL == params_ptr) + { + CHMIXER_MSG(MIID_UNKNOWN, DBG_ERROR_PRIO, "Set param received NULL pointer"); + return CAPI_EBADPARAM; + } + + capi_err_t capi_result = CAPI_EOK; + capi_chmixer_t *const me_ptr = (capi_chmixer_t *)(_pif); + const void *const param_ptr = (void *)params_ptr->data_ptr; + uint32_t param_size = params_ptr->actual_data_len; + + if (NULL == param_ptr) + { + CHMIXER_MSG(me_ptr->miid, DBG_ERROR_PRIO, "Set param received NULL param pointer"); + return CAPI_EBADPARAM; + } + + switch (param_id) + { + case CAPI_CHMIXER_PARAM_ID_ENABLE: + { + if (param_size >= sizeof(capi_chmixer_enable_payload_t)) + { + const capi_chmixer_enable_payload_t *const enable_ptr = (const capi_chmixer_enable_payload_t *)(param_ptr); + me_ptr->client_enable = enable_ptr->enable; + + CHMIXER_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "CAPI_CHMIXER_PARAM_ID_ENABLE received with enable %d", + enable_ptr->enable); + + capi_chmixer_raise_events(me_ptr, false); + } + else + { + CHMIXER_MSG(me_ptr->miid, DBG_ERROR_PRIO, "Set Enable/Disable, Bad param size %lu", param_size); + CAPI_SET_ERROR(capi_result, CAPI_ENEEDMORE); + break; + } + break; + } + case PARAM_ID_CHMIXER_COEFF: + { + CHMIXER_MSG(me_ptr->miid, DBG_HIGH_PRIO, "PARAM_ID_CHMIXER_COEFF received"); + if (param_size >= sizeof(param_id_chmixer_coeff_t)) + { + size_t req_param_size = 0, req_param_size_al = 0; + uint32_t index = 0, total_payload_size = 0, total_payload_aligned_size = 0; + + const param_id_chmixer_coeff_t *const p_param = (param_id_chmixer_coeff_t *)param_ptr; + + param_size = param_size - sizeof(param_id_chmixer_coeff_t); + + // error check for coefficients index + if (0 == p_param->num_coeff_tbls) + { + CHMIXER_MSG(me_ptr->miid, DBG_ERROR_PRIO, "Set param received zero coefficients"); + + // Freeing older memory + capi_free_coef_sets(me_ptr); + + me_ptr->config.lib_enable = FALSE; + break; + } + + // pointer to the first array + uint8_t *first_data_ptr = (uint8_t *)(sizeof(param_id_chmixer_coeff_t) + params_ptr->data_ptr); + uint8_t *new_coef_arr_ptr = first_data_ptr; + + if (param_size < sizeof(chmixer_coeff_t)) + { + CHMIXER_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "Set coeff, Bad param size %lu, required param size %lu", + param_size, + sizeof(chmixer_coeff_t)); + return CAPI_ENEEDMORE; + } + chmixer_coeff_t *coeff_param_ptr = (chmixer_coeff_t *)first_data_ptr; + + // Size and error check loop + for (index = 0; index < p_param->num_coeff_tbls; index++) + { + // Size of each tbl + req_param_size = + sizeof(chmixer_coeff_t) + (sizeof(uint16_t) * coeff_param_ptr->num_output_channels) + + (sizeof(uint16_t) * coeff_param_ptr->num_input_channels) + + (sizeof(int16_t) * coeff_param_ptr->num_output_channels * coeff_param_ptr->num_input_channels); + + // if req param size is not 4byte align add padding len and store in req_param_size_aligned + req_param_size_al = ALIGN_4_BYTES(req_param_size); + CHMIXER_MSG(me_ptr->miid, DBG_HIGH_PRIO, "al size %d size %d", req_param_size_al, req_param_size); + + if (param_size < req_param_size_al) + { + CHMIXER_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "Set coeff, Bad param size %lu, required param size %lu", + param_size, + req_param_size_al); + CAPI_SET_ERROR(capi_result, CAPI_ENEEDMORE); + return capi_result; + } + + // error check for num channels + if ((coeff_param_ptr->num_input_channels > CAPI_CHMIXER_MAX_CHANNELS) || + (0 == coeff_param_ptr->num_input_channels) || + (coeff_param_ptr->num_output_channels > CAPI_CHMIXER_MAX_CHANNELS) || + (0 == coeff_param_ptr->num_output_channels)) + { + CHMIXER_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "Set coeff, invalid number of channels. inp ch %hu, out ch %hu", + coeff_param_ptr->num_input_channels, + coeff_param_ptr->num_output_channels); + return CAPI_EBADPARAM; + } + + const uint16_t *out_ch_map = (uint16_t *)((uint8_t *)coeff_param_ptr + sizeof(chmixer_coeff_t)); + const uint16_t *in_ch_map = out_ch_map + coeff_param_ptr->num_output_channels; + + // error check for channel type + capi_result |= capi_chmixer_check_ch_type(in_ch_map, coeff_param_ptr->num_input_channels); + capi_result |= capi_chmixer_check_ch_type(out_ch_map, coeff_param_ptr->num_output_channels); + if (CAPI_FAILED(capi_result)) + { + CHMIXER_MSG(me_ptr->miid, DBG_ERROR_PRIO, "Set coeff, invalid channel type"); + return capi_result; + } + + // Update size + param_size = param_size - req_param_size_al; + + // Update param size and ptrs to point to the next coeff array provided its not last coefficient payload + if (index != (p_param->num_coeff_tbls - 1)) + { + if (param_size < sizeof(chmixer_coeff_t)) + { + CHMIXER_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "Set coeff, Bad param size %lu, required param size %lu", + param_size, + sizeof(chmixer_coeff_t)); + return CAPI_EBADPARAM; + } + + // update to move to the next array + new_coef_arr_ptr = (uint8_t *)(new_coef_arr_ptr + req_param_size_al); + coeff_param_ptr = (chmixer_coeff_t *)(new_coef_arr_ptr); + } + + // Update total payload size to be used when we store the params in chmixer + total_payload_size += req_param_size; + total_payload_aligned_size += req_param_size_al; + CHMIXER_MSG(me_ptr->miid, DBG_HIGH_PRIO, "Total payload size %d", total_payload_size); + } + + if (me_ptr->coef_payload_size != total_payload_size) + { + // Freeing older memory + capi_free_coef_sets(me_ptr); + + uint32_t size_to_malloc = + total_payload_size + + (p_param->num_coeff_tbls * (sizeof(capi_chmixer_coef_set) - sizeof(chmixer_coeff_t))); + + me_ptr->config.coef_sets_ptr = + (capi_chmixer_coef_set *)posal_memory_malloc(size_to_malloc, (POSAL_HEAP_ID)me_ptr->heap_mem.heap_id); + if (NULL == me_ptr->config.coef_sets_ptr) + { + CHMIXER_MSG(me_ptr->miid, DBG_ERROR_PRIO, "Set param failed to allocate memory"); + return CAPI_ENOMEMORY; + } + } + + // For first iteration + // Start of chmixer channel arrays + uint8_t *new_coeff_arr = (uint8_t *)me_ptr->config.coef_sets_ptr; + // start of chmixer channel maps + uint8_t *new_ch_map_arr = new_coeff_arr + (p_param->num_coeff_tbls * sizeof(capi_chmixer_coef_set)); + + // reset coeff param ptr to point to start of first array input cfg + coeff_param_ptr = (chmixer_coeff_t *)first_data_ptr; + + // Processing loop + for (index = 0; index < p_param->num_coeff_tbls; index++) + { + uint32_t padding = 0; + // Size of each tbl + uint32_t tbl_size = + (sizeof(uint16_t) * coeff_param_ptr->num_output_channels) + + (sizeof(uint16_t) * coeff_param_ptr->num_input_channels) + + (sizeof(int16_t) * coeff_param_ptr->num_output_channels * coeff_param_ptr->num_input_channels); + + if (0 != (tbl_size % 4)) + { + padding = 1; // it can only be 2bytes less since every entry is 2bytes + } + + // input ptrs + const uint16_t *out_ch_map = (uint16_t *)((uint8_t *)coeff_param_ptr + sizeof(chmixer_coeff_t)); + const uint16_t *in_ch_map = out_ch_map + coeff_param_ptr->num_output_channels; + const int16_t * coef_ptr = (int16_t *)(in_ch_map + coeff_param_ptr->num_input_channels); + + // Assign pointers after malloc + capi_chmixer_coef_set *coef_set = (capi_chmixer_coef_set *)new_coeff_arr; + coef_set->num_in_ch = coeff_param_ptr->num_input_channels; + coef_set->num_out_ch = coeff_param_ptr->num_output_channels; + + coef_set->out_ch_map = (uint16_t *)(new_ch_map_arr); + coef_set->in_ch_map = (uint16_t *)(coef_set->out_ch_map + coef_set->num_out_ch); + coef_set->coef_ptr = (int16_t *)(coef_set->in_ch_map + coef_set->num_in_ch); + +#ifdef CAPI_CHMIXER_DEBUG_MSG + + uint8_t i = 0; + uint8_t j = 0; + + CHMIXER_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "SET PARAM FOR COEFF: Inp Number of Channels %lu", + coeff_param_ptr->num_input_channels); + + for (i = 0; i < coef_set->num_in_ch; i++) + { + CHMIXER_MSG(me_ptr->miid, DBG_HIGH_PRIO, "Inp Channel_Type[%hhu]: %d", i, in_ch_map[i]); + } + + CHMIXER_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "Out Number of Channels %lu", + coeff_param_ptr->num_output_channels); + for (j = 0; j < coef_set->num_out_ch; j++) + { + CHMIXER_MSG(me_ptr->miid, DBG_HIGH_PRIO, "Out Channel_Type[%hhu]: %d", j, out_ch_map[j]); + } + + uint8_t a = i * j; + for (j = 0; j < a; j++) + { + CHMIXER_MSG(me_ptr->miid, DBG_HIGH_PRIO, "coefficients: %x", *(coef_ptr + j)); + } +#endif + // Copy values + uint32_t size_to_cpy = + ((coef_set->num_in_ch * sizeof(uint16_t)) + (coef_set->num_out_ch * sizeof(uint16_t)) + + (coef_set->num_in_ch * coef_set->num_out_ch * sizeof(int16_t))); + + memscpy(coef_set->out_ch_map, size_to_cpy, out_ch_map, size_to_cpy); + + // Update in and out params to point to the next coeff array provided its not last coefficient payload + if (index != (p_param->num_coeff_tbls - 1)) + { + // Increment with padding to read next sub-structure correctly + coeff_param_ptr = (chmixer_coeff_t *)(coef_ptr + (coeff_param_ptr->num_output_channels * + coeff_param_ptr->num_input_channels) + + padding); + // point to next array + new_coeff_arr = new_coeff_arr + sizeof(capi_chmixer_coef_set); + // point to next map + new_ch_map_arr = (uint8_t *)(coef_set->coef_ptr + (coef_set->num_out_ch * coef_set->num_in_ch)); + } + } // end of for loop + + // Update number of coeff sets + me_ptr->config.num_coef_sets = p_param->num_coeff_tbls; + + // Update data length + params_ptr->actual_data_len = total_payload_aligned_size; + me_ptr->coef_payload_size = total_payload_size; + + if ((me_ptr->input_media_fmt[CAPI_CHMIXER_DEFAULT_PORT].format.num_channels == + CAPI_DATA_FORMAT_INVALID_VAL) || + (me_ptr->output_media_fmt[CAPI_CHMIXER_DEFAULT_PORT].format.num_channels == + CAPI_DATA_FORMAT_INVALID_VAL)) + { + // media format is not received. + break; + } + + capi_result |= capi_chmixer_check_init_lib_instance(me_ptr); + if (CAPI_FAILED(capi_result)) + { + CHMIXER_MSG(me_ptr->miid, DBG_ERROR_PRIO, "Set coeff, Reinit failed"); + break; + } + capi_chmixer_raise_events(me_ptr, FALSE); + } + else + { + CHMIXER_MSG(me_ptr->miid, DBG_ERROR_PRIO, "Set coeff, Bad param size %lu", param_size); + CAPI_SET_ERROR(capi_result, CAPI_ENEEDMORE); + break; + } + break; + } + case PARAM_ID_CHMIXER_OUT_CH_CFG: + { + // sanity check for valid and output port + if ((port_info_ptr->is_valid) && (port_info_ptr->is_input_port)) + { + CHMIXER_MSG(me_ptr->miid, DBG_ERROR_PRIO, "Set param id 0x%lx, invalid port info.", param_id); + CAPI_SET_ERROR(capi_result, CAPI_EBADPARAM); + break; + } + + CHMIXER_MSG(me_ptr->miid, DBG_HIGH_PRIO, "Set param id PARAM_ID_CHMIXER_OUT_CH_CFG"); + + // sanity check for valid output port index + if ((port_info_ptr->is_valid) && (0 != port_info_ptr->port_index)) + { + CHMIXER_MSG(me_ptr->miid, DBG_ERROR_PRIO, "Set param id 0x%lx, invalid output port index.", param_id); + CAPI_SET_ERROR(capi_result, CAPI_EBADPARAM); + break; + } + + if (params_ptr->actual_data_len >= sizeof(param_id_chmixer_out_ch_cfg_t)) + { + param_id_chmixer_out_ch_cfg_t *data_ptr = (param_id_chmixer_out_ch_cfg_t *)params_ptr->data_ptr; + capi_media_fmt_v2_t out_media_fmt = me_ptr->input_media_fmt[CAPI_CHMIXER_DEFAULT_PORT]; + + if (PARAM_VAL_UNSET == data_ptr->num_channels) + { +#ifdef CAPI_CHMIXER_DEBUG_MSG + CHMIXER_MSG(me_ptr->miid, DBG_HIGH_PRIO, "Set param received unset num channels"); +#endif + break; + } + else if (PARAM_VAL_NATIVE == data_ptr->num_channels) + { +#ifdef CAPI_CHMIXER_DEBUG_MSG + CHMIXER_MSG(me_ptr->miid, DBG_HIGH_PRIO, "Set param received native num channels"); +#endif + me_ptr->is_native_mode = TRUE; + } + else if ((CAPI_CHMIXER_MAX_CHANNELS < data_ptr->num_channels) || + (data_ptr->num_channels == PARAM_VAL_INVALID)) + { + CHMIXER_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "Invalid num channels %d received, Max supported = %d", + data_ptr->num_channels, + CAPI_CHMIXER_MAX_CHANNELS); + CAPI_SET_ERROR(capi_result, CAPI_EBADPARAM); + break; + } + else // VALID + { + uint32_t required_size = + sizeof(param_id_chmixer_out_ch_cfg_t) + (data_ptr->num_channels * sizeof(uint16_t)); + if (params_ptr->actual_data_len < required_size) + { + CHMIXER_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "Set param id 0x%lx, Bad param size %lu, required_size = %lu", + param_id, + params_ptr->actual_data_len, + required_size); + CAPI_SET_ERROR(capi_result, CAPI_ENEEDMORE); + break; + } + + me_ptr->is_native_mode = FALSE; + uint16_t *channel_map = &data_ptr->channel_map[0]; + + // free older configured chmap being used + if (me_ptr->configured_ch_map) + { + posal_memory_free(me_ptr->configured_ch_map); + me_ptr->configured_ch_map = NULL; + } + // save new config + me_ptr->configured_num_channels = data_ptr->num_channels; + + me_ptr->configured_ch_map = (uint16_t *)posal_memory_malloc((sizeof(uint16_t) * data_ptr->num_channels), + (POSAL_HEAP_ID)me_ptr->heap_mem.heap_id); + if (NULL == me_ptr->configured_ch_map) + { + CHMIXER_MSG(me_ptr->miid, DBG_ERROR_PRIO, "Set param failed to allocate memory"); + return CAPI_ENOMEMORY; + } + + memscpy(&me_ptr->configured_ch_map[0], + sizeof(out_media_fmt.channel_type), + channel_map, + sizeof(uint16_t) * data_ptr->num_channels); + +#ifdef CAPI_CHMIXER_DEBUG_MSG + CHMIXER_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "Set param for out mf: Storing num channels %d because valid config", + data_ptr->num_channels); + for (uint8_t j = 0; j < data_ptr->num_channels; j++) + { + CHMIXER_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "Set param for out mf: chmap received[%d]: %x", + j, + data_ptr->channel_map[j]); + } +#endif + + if (TRUE == me_ptr->inp_media_fmt_received) // copy to out media fmt and raise event + { + out_media_fmt.format.num_channels = data_ptr->num_channels; + memscpy(out_media_fmt.channel_type, + sizeof(out_media_fmt.channel_type), + channel_map, + sizeof(uint16_t) * data_ptr->num_channels); + } + } + + if (TRUE == me_ptr->inp_media_fmt_received) + { + capi_result = capi_chmixer_set_output_media_fmt(me_ptr, &out_media_fmt); + } + } + else + { + CHMIXER_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "Set param id 0x%lx, Bad param size %lu", + param_id, + params_ptr->actual_data_len); + CAPI_SET_ERROR(capi_result, CAPI_ENEEDMORE); + } + break; + } + default: + { + CHMIXER_MSG(me_ptr->miid, DBG_ERROR_PRIO, "Unsupported param id %lu", param_id); + CAPI_SET_ERROR(capi_result, CAPI_EUNSUPPORTED); + break; + } + } + return capi_result; +} + +/*------------------------------------------------------------------------ + Function name: capi_chmixer_get_param + DESCRIPTION: Function to get parameter value\structure. + -----------------------------------------------------------------------*/ +static capi_err_t capi_chmixer_get_param(capi_t * _pif, + const uint32_t param_id, + const capi_port_info_t *const port_info_ptr, + capi_buf_t *const params_ptr) +{ + if (NULL == _pif || NULL == params_ptr) + { + CHMIXER_MSG(MIID_UNKNOWN, DBG_ERROR_PRIO, "Get param received NULL pointer"); + return CAPI_EBADPARAM; + } + + capi_err_t capi_result = CAPI_EOK; + const capi_chmixer_t *const me_ptr = (capi_chmixer_t *)(_pif); + + if (NULL == params_ptr->data_ptr) + { + CHMIXER_MSG(me_ptr->miid, DBG_ERROR_PRIO, "Get param received NULL param pointer"); + return CAPI_EBADPARAM; + } + + switch (param_id) + { + case PARAM_ID_CHMIXER_OUT_CH_CFG: + { + capi_media_fmt_v2_t out_media_fmt = me_ptr->output_media_fmt[CAPI_CHMIXER_DEFAULT_PORT]; + uint32_t num_ch = + ((CAPI_DATA_FORMAT_INVALID_VAL != out_media_fmt.format.num_channels) ? out_media_fmt.format.num_channels + : 0); + uint32_t required_size = sizeof(param_id_chmixer_out_ch_cfg_t) + (num_ch * sizeof(uint16_t)); + + if (params_ptr->max_data_len < required_size) + { + CHMIXER_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "Get param id 0x%lx, Bad param size %lu", + param_id, + params_ptr->max_data_len); + params_ptr->actual_data_len = required_size; + CAPI_SET_ERROR(capi_result, CAPI_ENEEDMORE); + break; + } + + param_id_chmixer_out_ch_cfg_t *data_ptr = (param_id_chmixer_out_ch_cfg_t *)params_ptr->data_ptr; + data_ptr->num_channels = out_media_fmt.format.num_channels; + uint16_t *channel_map = (uint16_t *)(data_ptr + 1); + memscpy(channel_map, + params_ptr->max_data_len - sizeof(param_id_chmixer_out_ch_cfg_t), + out_media_fmt.channel_type, + (num_ch * sizeof(uint16_t))); + + params_ptr->actual_data_len = required_size; + break; + } + default: + { + CHMIXER_MSG(me_ptr->miid, DBG_ERROR_PRIO, "Unsupported param id %lu", param_id); + CAPI_SET_ERROR(capi_result, CAPI_EUNSUPPORTED); + break; + } + } + return capi_result; +} + +/*------------------------------------------------------------------------ + Function name: capi_chmixer_process + DESCRIPTION: Processes an input buffer and generates an output buffer. + -----------------------------------------------------------------------*/ +static capi_err_t capi_chmixer_process(capi_t *_pif, capi_stream_data_t *input[], capi_stream_data_t *output[]) +{ + capi_chmixer_t *const me_ptr = (capi_chmixer_t *)_pif; + + POSAL_ASSERT(me_ptr); + POSAL_ASSERT(input[CAPI_CHMIXER_DEFAULT_PORT]); + POSAL_ASSERT(output[CAPI_CHMIXER_DEFAULT_PORT]); + +#ifdef CAPI_CHMIXER_KPPS_PROFILING + me_ptr->profiler.start_cycles = q6sim_read_cycles(); +#endif + + void *input_bufs[CAPI_CHMIXER_MAX_CHANNELS] = { NULL }; + void *output_bufs[CAPI_CHMIXER_MAX_CHANNELS] = { NULL }; + + uint32_t bytes_to_process = input[CAPI_CHMIXER_DEFAULT_PORT]->buf_ptr[0].actual_data_len; + + for (uint32_t i = 0; i < input[0]->bufs_num && i < CAPI_CHMIXER_MAX_CHANNELS; i++) + { + input_bufs[i] = (void *)input[CAPI_CHMIXER_DEFAULT_PORT]->buf_ptr[i].data_ptr; + bytes_to_process = + s32_min_s32_s32(bytes_to_process, input[CAPI_CHMIXER_DEFAULT_PORT]->buf_ptr[i].actual_data_len); + } + + for (uint32_t i = 0; i < output[0]->bufs_num && i < CAPI_CHMIXER_MAX_CHANNELS; i++) + { + output_bufs[i] = (void *)output[CAPI_CHMIXER_DEFAULT_PORT]->buf_ptr[i].data_ptr; + bytes_to_process = s32_min_s32_s32(bytes_to_process, output[CAPI_CHMIXER_DEFAULT_PORT]->buf_ptr[i].max_data_len); + } + + if (me_ptr->config.lib_enable) + { + uint16_t byte_sample_convert = + (me_ptr->input_media_fmt[CAPI_CHMIXER_DEFAULT_PORT].format.bits_per_sample == 16) ? 1 : 2; + + uint32_t samples_to_process = bytes_to_process >> byte_sample_convert; + + ChMixerProcess(me_ptr->lib_ptr, output_bufs, input_bufs, samples_to_process); + + for (uint32_t i = 0; i < me_ptr->input_media_fmt[CAPI_CHMIXER_DEFAULT_PORT].format.num_channels; i++) + { + input[CAPI_CHMIXER_DEFAULT_PORT]->buf_ptr[i].actual_data_len = samples_to_process << byte_sample_convert; + } + + for (uint32_t i = 0; i < me_ptr->output_media_fmt[CAPI_CHMIXER_DEFAULT_PORT].format.num_channels; i++) + { + output[CAPI_CHMIXER_DEFAULT_PORT]->buf_ptr[i].actual_data_len = samples_to_process << byte_sample_convert; + } + } + else + { + // if process check is disabled then directly copy input to output. + // This is needed because pspd-mtmx does not have process check event handling for chmixer. + if (input[CAPI_CHMIXER_DEFAULT_PORT]->bufs_num == output[CAPI_CHMIXER_DEFAULT_PORT]->bufs_num) + { + for (uint16_t i = 0; i < input[CAPI_CHMIXER_DEFAULT_PORT]->bufs_num; i++) + { + memscpy(output[CAPI_CHMIXER_DEFAULT_PORT]->buf_ptr[i].data_ptr, + bytes_to_process, + input[CAPI_CHMIXER_DEFAULT_PORT]->buf_ptr[i].data_ptr, + bytes_to_process); + + input[CAPI_CHMIXER_DEFAULT_PORT]->buf_ptr[i].actual_data_len = bytes_to_process; + output[CAPI_CHMIXER_DEFAULT_PORT]->buf_ptr[i].actual_data_len = bytes_to_process; + } + } + else + { + return CAPI_EFAILED; + } + } + + output[0]->flags = input[0]->flags; + if (input[0]->flags.is_timestamp_valid) + { + output[0]->timestamp = input[0]->timestamp - me_ptr->event_config.chmixer_delay_in_us; + } + +#ifdef CAPI_CHMIXER_KPPS_PROFILING + me_ptr->profiler.end_cycles = q6sim_read_cycles(); + me_ptr->profiler.frame_sample_count = samples_to_process; + capi_chmixer_kpps_profiler(me_ptr); +#endif + +#ifdef CAPI_CHMIXER_DATA_LOG + capi_chmixer_data_logger(me_ptr, input[CAPI_CHMIXER_DEFAULT_PORT], output[CAPI_CHMIXER_DEFAULT_PORT]); +#endif + + return CAPI_EOK; +} + +/*------------------------------------------------------------------------ + Function name: capi_chmixer_end + DESCRIPTION: Returns the library to the uninitialized state and frees the + memory that was allocated by module. This function also frees the virtual + function table. + -----------------------------------------------------------------------*/ +static capi_err_t capi_chmixer_end(capi_t *_pif) +{ + capi_chmixer_t *const me_ptr = (capi_chmixer_t * const)(_pif); + +#ifdef CAPI_CHMIXER_KPPS_PROFILING + capi_chmixer_kpps_print(me_ptr); +#endif + + capi_free_coef_sets(me_ptr); + + if (NULL != me_ptr->configured_ch_map) + { + posal_memory_free(me_ptr->configured_ch_map); + me_ptr->configured_ch_map = NULL; + } + if (NULL != me_ptr->lib_ptr) + { + posal_memory_aligned_free(me_ptr->lib_ptr); + me_ptr->lib_ptr = NULL; + } + uint32_t miid = me_ptr->miid; + memset((void *)me_ptr, 0, sizeof(me_ptr)); + + me_ptr->vtbl.vtbl_ptr = NULL; + + CHMIXER_MSG(miid, DBG_HIGH_PRIO, "End done"); + return CAPI_EOK; +} diff --git a/modules/processing/channel_mixer/capi/src/capi_chmixer_utils.cpp b/modules/processing/channel_mixer/capi/src/capi_chmixer_utils.cpp new file mode 100644 index 0000000..ea9f730 --- /dev/null +++ b/modules/processing/channel_mixer/capi/src/capi_chmixer_utils.cpp @@ -0,0 +1,1094 @@ +/* ======================================================================== */ +/** + @file capi_chmixer_utils.cpp + + C source file to implement the Audio Post Processor Interface utilities for + Channel Mixer. +*/ + +/* ========================================================================= + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause + ========================================================================= */ + +/*------------------------------------------------------------------------ + * Include files and Macro definitions + * -----------------------------------------------------------------------*/ +#include "capi_chmixer_utils.h" +#include "posal.h" +#ifdef CAPI_CHMIXER_DATA_LOG +#include +#endif + +/*------------------------------------------------------------------------ + * Function definitions + * -----------------------------------------------------------------------*/ + +/*=========================================================================== + FUNCTION : capi_init_mem_req + DESCRIPTION: Function to get init memory requirement for capi v2 module. +===========================================================================*/ +uint32_t capi_get_init_mem_req() +{ + return (align_to_8_byte(sizeof(capi_chmixer_t))); +} + +/*=========================================================================== + FUNCTION : copy_media_fmt_v2 + DESCRIPTION: Function to copy v2 version of media format to another v2 structure. +===========================================================================*/ +static void copy_media_fmt_v2(capi_media_fmt_v2_t *media_fmt_v2_dst, const capi_media_fmt_v2_t *const media_fmt_v2_src) +{ + media_fmt_v2_dst->header = media_fmt_v2_src->header; + media_fmt_v2_dst->format = media_fmt_v2_src->format; + media_fmt_v2_dst->format.minor_version = CAPI_MEDIA_FORMAT_MINOR_VERSION; + + memscpy(media_fmt_v2_dst->channel_type, + sizeof(media_fmt_v2_dst->channel_type), + media_fmt_v2_src->channel_type, + media_fmt_v2_src->format.num_channels * sizeof(media_fmt_v2_src->channel_type[0])); + + return; +} + +/*=========================================================================== + FUNCTION : capi_chmixer_init_events + DESCRIPTION: Function to init all the events with invalid values. +===========================================================================*/ +void capi_chmixer_init_events(capi_chmixer_t *const me_ptr) +{ + me_ptr->event_config.chmixer_enable = TRUE; + + // Setting events to maximum(invalid) value. + me_ptr->event_config.chmixer_KPPS = 0x7FFFFFFF; + me_ptr->event_config.chmixer_bandwidth = 0x7FFFFFFF; + me_ptr->event_config.chmixer_delay_in_us = 0x7FFFFFFF; +} + +/*=========================================================================== + FUNCTION : capi_chmixer_check_ch_type + DESCRIPTION: Function to check valid channel type. +===========================================================================*/ +capi_err_t capi_chmixer_check_ch_type(const uint16_t *channel_type, const uint32_t array_size) +{ + for (uint8_t i = 0; (i < array_size) && (i < CAPI_CHMIXER_MAX_CHANNELS); i++) + { + if ((channel_type[i] < (uint16_t)PCM_CHANNEL_L) || (channel_type[i] > (uint16_t)PCM_MAX_CHANNEL_MAP_V2)) + { + AR_MSG(DBG_ERROR_PRIO, + "CAPI CHMIXER: Unsupported channel type channel idx %d, channel type %d", + (int)i, + (int)channel_type[i]); + return CAPI_EBADPARAM; + } + } + + return CAPI_EOK; +} + +/*=========================================================================== + FUNCTION : capi_chmixer_check_output_media_change_v2 + DESCRIPTION: Function to check if output media format is changed using media_fmt_v2. +===========================================================================*/ +static bool_t capi_chmixer_check_output_media_change(const capi_media_fmt_v2_t *const output_media_fmt1, + const capi_media_fmt_v2_t *const output_media_fmt2) +{ + if (output_media_fmt1->format.num_channels != output_media_fmt2->format.num_channels) + { + return TRUE; + } + + for (uint8_t i = 0; (i < output_media_fmt1->format.num_channels) && (i < CAPI_CHMIXER_MAX_CHANNELS); i++) + { + if (output_media_fmt1->channel_type[i] != output_media_fmt2->channel_type[i]) + { + return TRUE; + } + } + + return FALSE; +} + +/*=========================================================================== + FUNCTION : capi_chmixer_is_supported_input_media_fmt + DESCRIPTION: Function to check if input media format is supported by module +===========================================================================*/ +static capi_err_t capi_chmixer_is_supported_input_media_fmt(const capi_media_fmt_v2_t *const input_media_fmt) +{ + if (CAPI_FIXED_POINT != input_media_fmt->header.format_header.data_format) + { + AR_MSG(DBG_ERROR_PRIO, "CAPI CHMIXER: Unsupported data format"); + return CAPI_EUNSUPPORTED; + } + + if ((16 != input_media_fmt->format.bits_per_sample) && (32 != input_media_fmt->format.bits_per_sample)) + { + AR_MSG(DBG_ERROR_PRIO, + "CAPI CHMIXER: Only supports 16 and 32 bit data. Received %lu.", + input_media_fmt->format.bits_per_sample); + return CAPI_EUNSUPPORTED; + } + + if (CAPI_DEINTERLEAVED_UNPACKED != input_media_fmt->format.data_interleaving) + { + AR_MSG(DBG_ERROR_PRIO, "CAPI CHMIXER: Only deinterleaved unpacked data supported."); + return CAPI_EUNSUPPORTED; + } + + if (!input_media_fmt->format.data_is_signed) + { + AR_MSG(DBG_ERROR_PRIO, "CAPI CHMIXER: Unsigned data not supported."); + return CAPI_EUNSUPPORTED; + } + + if ((0 == input_media_fmt->format.num_channels) || + (CAPI_CHMIXER_MAX_CHANNELS < input_media_fmt->format.num_channels)) + { + AR_MSG(DBG_ERROR_PRIO, + "CAPI CHMIXER: Only upto %lu channels supported. Received %lu.", + CAPI_CHMIXER_MAX_CHANNELS, + input_media_fmt->format.num_channels); + return CAPI_EUNSUPPORTED; + } + + if (0 == input_media_fmt->format.sampling_rate) + { + AR_MSG(DBG_ERROR_PRIO, + "CAPI CHMIXER: Unsupported sampling rate. Received %lu.", + input_media_fmt->format.sampling_rate); + return CAPI_EUNSUPPORTED; + } + + if (CAPI_EOK != capi_chmixer_check_ch_type(input_media_fmt->channel_type, input_media_fmt->format.num_channels)) + { + AR_MSG(DBG_ERROR_PRIO, "CAPI CHMIXER: Unsupported channel type"); + return CAPI_EUNSUPPORTED; + } + return CAPI_EOK; +} + +/*=========================================================================== + FUNCTION : capi_chmixer_is_supported_output_media_fmt + DESCRIPTION: Function to check if output media format is supported by module using media_fmt_v2 +===========================================================================*/ +static capi_err_t capi_chmixer_is_supported_output_media_fmt(const capi_media_fmt_v2_t *const output_media_fmt) +{ + if ((0 == output_media_fmt->format.num_channels) || + (CAPI_CHMIXER_MAX_CHANNELS < output_media_fmt->format.num_channels)) + { + AR_MSG(DBG_ERROR_PRIO, + "CAPI CHMIXER: Only upto %lu channels supported. Received %lu.", + CAPI_CHMIXER_MAX_CHANNELS, + output_media_fmt->format.num_channels); + return CAPI_EUNSUPPORTED; + } + + if (CAPI_EOK != capi_chmixer_check_ch_type(output_media_fmt->channel_type, output_media_fmt->format.num_channels)) + { + AR_MSG(DBG_ERROR_PRIO, "CAPI CHMIXER: Unsupported channel type"); + return CAPI_EUNSUPPORTED; + } + return CAPI_EOK; +} + +static capi_err_t capi_chmixer_is_supported_media_type(const capi_media_fmt_v2_t *const input_media_fmt, + const capi_media_fmt_v2_t *const output_media_fmt) +{ + capi_err_t out_result = capi_chmixer_is_supported_output_media_fmt(output_media_fmt); + capi_err_t inp_result = capi_chmixer_is_supported_input_media_fmt(input_media_fmt); + return (inp_result | out_result); +} + +capi_err_t capi_chmixer_set_output_media_fmt(capi_chmixer_t *const me_ptr, + const capi_media_fmt_v2_t *const out_media_fmt) +{ + capi_err_t capi_result = CAPI_EOK; + + if (capi_chmixer_check_output_media_change(out_media_fmt, &me_ptr->output_media_fmt[CAPI_CHMIXER_DEFAULT_PORT])) + { + copy_media_fmt_v2(&me_ptr->output_media_fmt[CAPI_CHMIXER_DEFAULT_PORT], out_media_fmt); + + capi_result |= capi_chmixer_is_supported_output_media_fmt(&me_ptr->output_media_fmt[CAPI_CHMIXER_DEFAULT_PORT]); + if (CAPI_SUCCEEDED(capi_result)) + { +#ifdef CAPI_CHMIXER_DEBUG_MSG + CHMIXER_MSG(me_ptr->miid, DBG_HIGH_PRIO, "Valid output media format."); +#endif + if (me_ptr->input_media_fmt[CAPI_CHMIXER_DEFAULT_PORT].format.num_channels != CAPI_DATA_FORMAT_INVALID_VAL) + { + capi_result |= capi_chmixer_check_init_lib_instance(me_ptr); + if (CAPI_SUCCEEDED(capi_result)) + { + capi_chmixer_raise_events(me_ptr, TRUE); + } + else + { + return capi_result; + } + } + } + else + { + me_ptr->config.lib_enable = FALSE; + capi_chmixer_raise_events(me_ptr, FALSE); + } + } + return capi_result; +} + +/*=========================================================================== + FUNCTION : capi_chmixer_raise_events + DESCRIPTION: Function to raise all the events using the callback function +===========================================================================*/ +capi_err_t capi_chmixer_raise_events(capi_chmixer_t *const me_ptr, bool_t media_fmt_update) +{ + capi_err_t result = CAPI_EOK; + if (NULL == me_ptr->cb_info.event_cb) + { +#ifdef CAPI_CHMIXER_DEBUG_MSG + CHMIXER_MSG(me_ptr->miid, DBG_ERROR_PRIO, "Event callback is not set. Unable to raise events!"); +#endif + return result; + } + + if (media_fmt_update) + { + // raise events which are only media format dependent + const uint32_t sample_rate = me_ptr->input_media_fmt[CAPI_CHMIXER_DEFAULT_PORT].format.sampling_rate; + if (sample_rate == CAPI_DATA_FORMAT_INVALID_VAL) + { + return CAPI_EFAILED; + } + const uint32_t bw = 0; + const uint32_t delay_in_us = 0; + + uint32_t out_ch = me_ptr->output_media_fmt[0].format.num_channels; + uint32_t num_active_coeff = ChMixerGetNumActiveCoefficients((ChMixerStateStruct *)me_ptr->lib_ptr); + + // Using constants -8, 4, 5 through profiling, + 225 for capi kpps + // Opening up the brackets for better div/mult: + // out ch * (-8 + num_samples(4 + (5* in_ch* num_active_coeff/(in_ch * out_ch)))) + c + // => out_ch * (-8 + (num_samples * 4) + (num_samples * 5* num_coeff/out_ch)) + c + // sr/1000 translates to num samples + uint32_t kpps = + (out_ch * (-8 + ((sample_rate * 4) + (sample_rate * 5 * num_active_coeff / out_ch)) / 1000)) + 225; + + CHMIXER_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "Raising output media fmt with kpps %d num_active_coeff %d", + kpps, + num_active_coeff); + + // Raise event if kpps is changed. + if (kpps != me_ptr->event_config.chmixer_KPPS) + { + me_ptr->event_config.chmixer_KPPS = kpps; + result = capi_cmn_update_kpps_event(&me_ptr->cb_info, kpps); + } + + // Raise event if bw is changed. + if (bw != me_ptr->event_config.chmixer_bandwidth) + { + me_ptr->event_config.chmixer_bandwidth = bw; + result = capi_cmn_update_bandwidth_event(&me_ptr->cb_info, 0, 0); + } + + // Raise event if algo delay is changed. + if (delay_in_us != me_ptr->event_config.chmixer_delay_in_us) + { + me_ptr->event_config.chmixer_delay_in_us = delay_in_us; + result = capi_cmn_update_algo_delay_event(&me_ptr->cb_info, 0); + } + +#ifdef CAPI_CHMIXER_DEBUG_MSG + CHMIXER_MSG(me_ptr->miid, + DBG_HIGH_PRIO, + "Raising output media fmt with num ch %d", + me_ptr->output_media_fmt[0].format.num_channels); +#endif + + result = capi_cmn_output_media_fmt_event_v2(&me_ptr->cb_info, + &me_ptr->output_media_fmt[CAPI_CHMIXER_DEFAULT_PORT], + FALSE, + 0); + } + + const bool_t process_check = (me_ptr->config.lib_enable) && (me_ptr->client_enable != 0); + if (me_ptr->event_config.chmixer_enable != process_check) + { + me_ptr->event_config.chmixer_enable = process_check; + result = capi_cmn_update_process_check_event(&me_ptr->cb_info, process_check); + } + + return result; +} + +/*=========================================================================== + FUNCTION : capi_chmixer_reinit + DESCRIPTION: Function to initialize chmixer libraries. +===========================================================================*/ +static capi_err_t capi_chmixer_reinit(capi_chmixer_t *const me_ptr) +{ + capi_media_fmt_v2_t *inp_media_fmt = &(me_ptr->input_media_fmt[CAPI_CHMIXER_DEFAULT_PORT]); + capi_media_fmt_v2_t *out_media_fmt = &(me_ptr->output_media_fmt[CAPI_CHMIXER_DEFAULT_PORT]); + + ChMixerChType in_ch_type[CH_MIXER_MAX_NUM_CH]; + for (uint32_t i = 0; i < inp_media_fmt->format.num_channels; i++) + { + in_ch_type[i] = (ChMixerChType)inp_media_fmt->channel_type[i]; + } + + ChMixerChType out_ch_type[CH_MIXER_MAX_NUM_CH]; + for (uint32_t i = 0; i < out_media_fmt->format.num_channels; i++) + { + out_ch_type[i] = (ChMixerChType)out_media_fmt->channel_type[i]; + } + + // Find the coef set which matches the current media format + capi_chmixer_coef_set *coef_set_ptr = me_ptr->config.coef_sets_ptr; + int16_t * coef_ptr = NULL; + + for (int8_t i = me_ptr->config.num_coef_sets - 1; i >= 0; i--) + { + if ((me_ptr->config.num_coef_sets - 1) != i) // except the first iteration + { + coef_set_ptr++; + } + + if (coef_set_ptr->num_in_ch != inp_media_fmt->format.num_channels) + { + continue; + } + if (coef_set_ptr->num_out_ch != out_media_fmt->format.num_channels) + { + continue; + } + + uint32_t j = 0; + for (j = 0; j < inp_media_fmt->format.num_channels; j++) + { + if (coef_set_ptr->in_ch_map[j] != inp_media_fmt->channel_type[j]) + { + break; + } + } + + if (j < inp_media_fmt->format.num_channels) + { + continue; + } + + for (j = 0; j < out_media_fmt->format.num_channels; j++) + { + if (coef_set_ptr->out_ch_map[j] != out_media_fmt->channel_type[j]) + { + break; + } + } + + if (j == out_media_fmt->format.num_channels) + { + coef_ptr = coef_set_ptr->coef_ptr; + break; + } + } + + // Initialize channel mixer lib. + ChMixerResultType result = ChMixerSetParam(me_ptr->lib_ptr, + me_ptr->lib_instance_size, + (uint32) inp_media_fmt->format.num_channels, + in_ch_type, + (uint32)out_media_fmt->format.num_channels, + out_ch_type, + (uint32)inp_media_fmt->format.bits_per_sample, + coef_ptr); + if (CH_MIXER_SUCCESS != result) + { + CHMIXER_MSG(me_ptr->miid, DBG_ERROR_PRIO, "Reinit failed."); + return CAPI_EFAILED; + } + + me_ptr->config.lib_enable = TRUE; // chmixer is successfully initialized. + + // if input and output media format is exactly same and + // there is no custom coefficients then raise process check as FALSE. + bool_t is_chmixer_req = FALSE; // disabled by default + + if (inp_media_fmt->format.num_channels == out_media_fmt->format.num_channels) + { + for (uint16_t i = 0; i < inp_media_fmt->format.num_channels; i++) + { + if (inp_media_fmt->channel_type[i] != out_media_fmt->channel_type[i]) + { + is_chmixer_req = TRUE; // If num channels are same but chmaps are different -> enable + break; + } + } + + // inp chmap == out chmap and coef ptr is not null, check if we need to enable + if ((FALSE == is_chmixer_req) && (NULL != coef_ptr)) + { + bool_t is_identity_matrix = TRUE; + for (uint32_t row = 0; row < out_media_fmt->format.num_channels; row++) + { + for (uint32_t col = 0; col < inp_media_fmt->format.num_channels; col++) + { + if ((row == col) && (*(coef_ptr + (row * inp_media_fmt->format.num_channels + col)) != 0x4000)) + { + // If elements of main diagonal is not equal to 1 + is_identity_matrix = FALSE; + break; + } + else if ((row != col) && *(coef_ptr + (row * inp_media_fmt->format.num_channels + col)) != 0) + { + // If other elements than main diagonal is not equal to 0 + is_identity_matrix = FALSE; + break; + } + } + if (!is_identity_matrix) + { + is_chmixer_req = TRUE; + break; // break out of second for loop + } + } + } + } + else + { + is_chmixer_req = TRUE; // If num channels are diff -> enable + } + + me_ptr->config.lib_enable = is_chmixer_req; + + return CAPI_EOK; +} + +/*=========================================================================== + FUNCTION : capi_chmixer_check_create_lib_instance + DESCRIPTION: Function to check the media fmt and create the chmixer instance. +===========================================================================*/ +capi_err_t capi_chmixer_check_init_lib_instance(capi_chmixer_t *const me_ptr) +{ + capi_err_t capi_result = CAPI_EOK; + + me_ptr->config.lib_enable = FALSE; + + capi_media_fmt_v2_t *inp_media_fmt = &(me_ptr->input_media_fmt[CAPI_CHMIXER_DEFAULT_PORT]); + capi_media_fmt_v2_t *out_media_fmt = &(me_ptr->output_media_fmt[CAPI_CHMIXER_DEFAULT_PORT]); + + // Sanity check for valid media format + capi_result |= capi_chmixer_is_supported_media_type(inp_media_fmt, out_media_fmt); + if (CAPI_FAILED(capi_result)) + { + // Raise process check; FALSE + capi_chmixer_raise_events(me_ptr, FALSE); + return capi_result; + } + else + { + out_media_fmt->header = inp_media_fmt->header; + out_media_fmt->format.minor_version = CAPI_MEDIA_FORMAT_MINOR_VERSION; + out_media_fmt->format.bitstream_format = inp_media_fmt->format.bitstream_format; + out_media_fmt->format.bits_per_sample = inp_media_fmt->format.bits_per_sample; + out_media_fmt->format.q_factor = inp_media_fmt->format.q_factor; + out_media_fmt->format.data_is_signed = inp_media_fmt->format.data_is_signed; + out_media_fmt->format.data_interleaving = inp_media_fmt->format.data_interleaving; + out_media_fmt->format.sampling_rate = inp_media_fmt->format.sampling_rate; + } + + uint32_t lib_size = 0; + ChMixerGetInstanceSize(&lib_size, inp_media_fmt->format.num_channels, out_media_fmt->format.num_channels); + if ((me_ptr->lib_ptr) && (me_ptr->lib_instance_size != lib_size)) + { + posal_memory_aligned_free(me_ptr->lib_ptr); + me_ptr->lib_instance_size = 0; + me_ptr->lib_ptr = NULL; + } + + if (!me_ptr->lib_ptr) + { + me_ptr->lib_ptr = + posal_memory_aligned_malloc(lib_size, MEM_ALIGN_EIGHT_BYTE, (POSAL_HEAP_ID)me_ptr->heap_mem.heap_id); + if (NULL == me_ptr->lib_ptr) + { + CHMIXER_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "failed to allocate the memory to create channel mixer library %lu", + capi_result); + me_ptr->lib_instance_size = 0; + return CAPI_ENOMEMORY; + } + me_ptr->lib_instance_size = lib_size; + } + + capi_result |= capi_chmixer_reinit(me_ptr); + if (CAPI_FAILED(capi_result)) + { + // Raise process check; FALSE + capi_chmixer_raise_events(me_ptr, FALSE); + return capi_result; + } + +#ifdef CAPI_CHMIXER_DEBUG_MSG + CHMIXER_MSG(me_ptr->miid, DBG_HIGH_PRIO, "Input Media Fmt:"); + CHMIXER_MSG(me_ptr->miid, DBG_HIGH_PRIO, "Number of Channels %lu", inp_media_fmt->format.num_channels); + for (uint8_t i = 0; i < inp_media_fmt->format.num_channels; i++) + { + CHMIXER_MSG(me_ptr->miid, DBG_HIGH_PRIO, "Channel_Type[%hhu]: %hu", i, inp_media_fmt->format.channel_type[i]); + } + + CHMIXER_MSG(me_ptr->miid, DBG_HIGH_PRIO, "Output Media Fmt:"); + CHMIXER_MSG(me_ptr->miid, DBG_HIGH_PRIO, "Number of Channels %lu", out_media_fmt->format.num_channels); + for (uint8_t i = 0; i < out_media_fmt->format.num_channels; i++) + { + CHMIXER_MSG(me_ptr->miid, DBG_HIGH_PRIO, "Channel_Type[%hhu]: %hu", i, out_media_fmt->format.channel_type[i]); + } +#endif + + return capi_result; +} + +/*=========================================================================== + FUNCTION : capi_chmixer_process_set_properties + DESCRIPTION: Function to set properties +===========================================================================*/ +capi_err_t capi_chmixer_process_set_properties(capi_chmixer_t *me_ptr, capi_proplist_t *proplist_ptr) +{ + capi_err_t capi_result = CAPI_EOK; + if (NULL == me_ptr) + { + CHMIXER_MSG(MIID_UNKNOWN, DBG_ERROR_PRIO, "Set common property received null ptr"); + return CAPI_EBADPARAM; + } + + capi_result |= capi_cmn_set_basic_properties(proplist_ptr, &me_ptr->heap_mem, &me_ptr->cb_info, TRUE); + if (CAPI_EOK != capi_result) + { + CHMIXER_MSG(me_ptr->miid, DBG_ERROR_PRIO, "Set basic properties failed with result %lu", capi_result); + } + + capi_prop_t *prop_array = proplist_ptr->prop_ptr; + for (uint8_t i = 0; i < proplist_ptr->props_num; i++) + { + const capi_buf_t *const payload_ptr = &(prop_array[i].payload); + const uint32_t payload_actual_data_len = payload_ptr->actual_data_len; + const uint32_t param_id = prop_array[i].id; + + capi_result = CAPI_EOK; + switch (param_id) + { + case CAPI_HEAP_ID: + case CAPI_EVENT_CALLBACK_INFO: + case CAPI_ALGORITHMIC_RESET: + case CAPI_CUSTOM_INIT_DATA: + case CAPI_PORT_NUM_INFO: + case CAPI_INTERFACE_EXTENSIONS: + { + break; + } + case CAPI_INPUT_MEDIA_FORMAT_V2: + { + if (NULL == payload_ptr->data_ptr) + { + CHMIXER_MSG(me_ptr->miid, DBG_ERROR_PRIO, "Set property id 0x%lx, received null buffer.", param_id); + CAPI_SET_ERROR(capi_result, CAPI_EBADPARAM); + break; + } + uint32_t required_size = CAPI_CHMIXER_V2_MIN_SIZE; + if (payload_actual_data_len >= required_size) + { +#ifdef CAPI_CHMIXER_DEBUG_MSG + CHMIXER_MSG(me_ptr->miid, DBG_HIGH_PRIO, "received input media fmt"); +#endif + + // sanity check for valid and input port + if ((!prop_array[i].port_info.is_valid) || (!prop_array[i].port_info.is_input_port)) + { + CHMIXER_MSG(me_ptr->miid, DBG_ERROR_PRIO, "Set property id 0x%lx, invalid port info.", param_id); + CAPI_SET_ERROR(capi_result, CAPI_EBADPARAM); + break; + } + + // sanity check for valid input port index + if (CAPI_CHMIXER_DEFAULT_PORT != prop_array[i].port_info.port_index) + { + CHMIXER_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "Set property id 0x%lx, invalid input port index %lu", + param_id, + prop_array[i].port_info.port_index); + CAPI_SET_ERROR(capi_result, CAPI_EBADPARAM); + break; + } + + const capi_media_fmt_v2_t *const in_data_ptr = (capi_media_fmt_v2_t *)(payload_ptr->data_ptr); + if (in_data_ptr->format.minor_version >= CAPI_MEDIA_FORMAT_MINOR_VERSION) + { +#ifdef CAPI_CHMIXER_KPPS_PROFILING + if (in_data_ptr->format.sampling_rate != + me_ptr->input_media_fmt[CAPI_CHMIXER_DEFAULT_PORT].format.sampling_rate) + { + capi_chmixer_kpps_print(me_ptr); + capi_chmixer_kpps_profiler_reset(me_ptr); + } +#endif + required_size += in_data_ptr->format.num_channels * sizeof(in_data_ptr->channel_type[0]); + if (payload_actual_data_len < required_size) + { + CHMIXER_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "Set property id 0x%lx, Bad param size %lu", + param_id, + payload_actual_data_len); + CAPI_SET_ERROR(capi_result, CAPI_ENEEDMORE); + break; + } + copy_media_fmt_v2(&me_ptr->input_media_fmt[CAPI_CHMIXER_DEFAULT_PORT], in_data_ptr); + + capi_result |= + capi_chmixer_is_supported_input_media_fmt(&me_ptr->input_media_fmt[CAPI_CHMIXER_DEFAULT_PORT]); + if (CAPI_SUCCEEDED(capi_result)) + { +#ifdef CAPI_CHMIXER_DEBUG_MSG + CHMIXER_MSG(me_ptr->miid, DBG_HIGH_PRIO, "Valid input media format."); +#endif + // Set flag + me_ptr->inp_media_fmt_received = TRUE; + + if (me_ptr->use_default_channel_info[CAPI_CHMIXER_DEFAULT_PORT]) + { + me_ptr->output_media_fmt[CAPI_CHMIXER_DEFAULT_PORT].format = + me_ptr->input_media_fmt[CAPI_CHMIXER_DEFAULT_PORT].format; + memscpy(me_ptr->output_media_fmt[CAPI_CHMIXER_DEFAULT_PORT].channel_type, + sizeof(me_ptr->output_media_fmt[CAPI_CHMIXER_DEFAULT_PORT].channel_type), + me_ptr->input_media_fmt[CAPI_CHMIXER_DEFAULT_PORT].channel_type, + me_ptr->input_media_fmt[CAPI_CHMIXER_DEFAULT_PORT].format.num_channels * + sizeof(me_ptr->input_media_fmt[CAPI_CHMIXER_DEFAULT_PORT].channel_type[0])); + } + + if (FALSE == me_ptr->is_native_mode) // overwrite if its a valid value + { +#ifdef CAPI_CHMIXER_DEBUG_MSG + CHMIXER_MSG(me_ptr->miid, DBG_HIGH_PRIO, "Received valid output mf"); +#endif + me_ptr->output_media_fmt[CAPI_CHMIXER_DEFAULT_PORT].format.num_channels = + me_ptr->configured_num_channels; + memscpy(me_ptr->output_media_fmt[CAPI_CHMIXER_DEFAULT_PORT].channel_type, + sizeof(me_ptr->output_media_fmt[CAPI_CHMIXER_DEFAULT_PORT].channel_type), + me_ptr->configured_ch_map, + (sizeof(uint16_t) * me_ptr->configured_num_channels)); + } + + if (me_ptr->output_media_fmt[CAPI_CHMIXER_DEFAULT_PORT].format.num_channels != + CAPI_DATA_FORMAT_INVALID_VAL) + { + capi_result |= capi_chmixer_check_init_lib_instance(me_ptr); + if (CAPI_SUCCEEDED(capi_result)) + { + capi_chmixer_raise_events(me_ptr, TRUE); + } + else + { + return capi_result; + } + } + } + else + { + me_ptr->config.lib_enable = FALSE; + capi_chmixer_raise_events(me_ptr, FALSE); + } + } + else + { + CHMIXER_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "Set property id 0x%lx, Bad version %lu", + param_id, + in_data_ptr->format.minor_version); + CAPI_SET_ERROR(capi_result, CAPI_EUNSUPPORTED); + break; + } + } + else + { + CHMIXER_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "Set property id 0x%lx, Bad param size %lu", + param_id, + payload_actual_data_len); + CAPI_SET_ERROR(capi_result, CAPI_ENEEDMORE); + break; + } + break; + } + case CAPI_OUTPUT_MEDIA_FORMAT_V2: + { + if (NULL == payload_ptr->data_ptr) + { + CHMIXER_MSG(me_ptr->miid, DBG_ERROR_PRIO, "Set property id 0x%lx, received null buffer.", param_id); + CAPI_SET_ERROR(capi_result, CAPI_EBADPARAM); + break; + } + + uint32_t required_size = CAPI_CHMIXER_V2_MIN_SIZE; + if (payload_actual_data_len >= required_size) + { +#ifdef CAPI_CHMIXER_DEBUG_MSG + CHMIXER_MSG(me_ptr->miid, DBG_HIGH_PRIO, "received output media fmt"); +#endif + + // sanity check for valid and input port + if ((!prop_array[i].port_info.is_valid) || (prop_array[i].port_info.is_input_port)) + { + CHMIXER_MSG(me_ptr->miid, DBG_ERROR_PRIO, "Set property id 0x%lx, invalid port info.", param_id); + CAPI_SET_ERROR(capi_result, CAPI_EBADPARAM); + break; + } + + // sanity check for valid input port index + if (CAPI_CHMIXER_DEFAULT_PORT != prop_array[i].port_info.port_index) + { + CHMIXER_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "Set property id 0x%lx, invalid output port index %lu", + param_id, + prop_array[i].port_info.port_index); + CAPI_SET_ERROR(capi_result, CAPI_EBADPARAM); + break; + } + + const capi_media_fmt_v2_t *const out_data_ptr = (capi_media_fmt_v2_t *)(payload_ptr->data_ptr); + if (out_data_ptr->format.minor_version >= CAPI_MEDIA_FORMAT_MINOR_VERSION) + { + required_size += out_data_ptr->format.num_channels * sizeof(out_data_ptr->channel_type[0]); + if (payload_actual_data_len < required_size) + { + CHMIXER_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "Set property id 0x%lx, Bad param size %lu", + param_id, + payload_actual_data_len); + CAPI_SET_ERROR(capi_result, CAPI_ENEEDMORE); + break; + } + me_ptr->use_default_channel_info[CAPI_CHMIXER_DEFAULT_PORT] = FALSE; + + capi_result = capi_chmixer_set_output_media_fmt(me_ptr, out_data_ptr); + } + else + { + CHMIXER_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "Set property id 0x%lx, Bad version %lu", + param_id, + out_data_ptr->format.minor_version); + CAPI_SET_ERROR(capi_result, CAPI_EUNSUPPORTED); + break; + } + } + else + { + CHMIXER_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "Set property id 0x%lx, Bad param size %lu", + param_id, + payload_actual_data_len); + CAPI_SET_ERROR(capi_result, CAPI_ENEEDMORE); + break; + } + break; + } + case CAPI_MODULE_INSTANCE_ID: + { + if (payload_ptr->actual_data_len >= sizeof(capi_module_instance_id_t)) + { + capi_module_instance_id_t *data_ptr = (capi_module_instance_id_t *)payload_ptr->data_ptr; + me_ptr->miid = data_ptr->module_instance_id; + CHMIXER_MSG(me_ptr->miid, + DBG_LOW_PRIO, + "This module-id 0x%08lX, instance-id 0x%08lX", + data_ptr->module_id, + me_ptr->miid); + } + else + { + CHMIXER_MSG(me_ptr->miid, + DBG_ERROR_PRIO, + "Set property id 0x%lx, Bad param size %lu", + param_id, + payload_ptr->max_data_len); + capi_result |= CAPI_ENEEDMORE; + } + break; + } // CAPI_MODULE_INSTANCE_ID + default: + { + CHMIXER_MSG(me_ptr->miid, DBG_ERROR_PRIO, "Set property id 0x%lx, Not supported.", param_id); + continue; + } + } + } + return capi_result; +} + +/*=========================================================================== + FUNCTION : capi_chmixer_process_get_properties + DESCRIPTION: chmixer get properties +===========================================================================*/ +capi_err_t capi_chmixer_process_get_properties(capi_chmixer_t *me_ptr, capi_proplist_t *proplist_ptr) +{ + capi_err_t capi_result = CAPI_EOK; + + capi_basic_prop_t mod_prop; + mod_prop.init_memory_req = capi_get_init_mem_req(); + mod_prop.stack_size = CAPI_CHMIXER_STACK_SIZE; + mod_prop.num_fwk_extns = 0; + mod_prop.fwk_extn_ids_arr = NULL; + mod_prop.is_inplace = FALSE; + mod_prop.req_data_buffering = FALSE; + mod_prop.max_metadata_size = 0; // NA + + uint32_t miid = me_ptr ? me_ptr->miid : MIID_UNKNOWN; + capi_result |= capi_cmn_get_basic_properties(proplist_ptr, &mod_prop); + if (CAPI_EOK != capi_result) + { + CHMIXER_MSG(miid, DBG_ERROR_PRIO, "Get common basic properties failed with result %lu", capi_result); + } + + capi_prop_t *const prop_array = proplist_ptr->prop_ptr; + + for (uint32_t i = 0; i < proplist_ptr->props_num; i++) + { + capi_buf_t *const payload_ptr = &(prop_array[i].payload); + miid = me_ptr ? me_ptr->miid : MIID_UNKNOWN; + + if (NULL == payload_ptr->data_ptr) + { + CHMIXER_MSG(miid, DBG_ERROR_PRIO, "Get property id 0x%lx, received null buffer.", prop_array[i].id); + continue; + } + + switch (prop_array[i].id) + { + case CAPI_INIT_MEMORY_REQUIREMENT: + case CAPI_STACK_SIZE: + case CAPI_IS_INPLACE: + case CAPI_REQUIRES_DATA_BUFFERING: + case CAPI_NUM_NEEDED_FRAMEWORK_EXTENSIONS: + case CAPI_OUTPUT_MEDIA_FORMAT_SIZE: + case CAPI_MAX_METADATA_SIZE: + case CAPI_INTERFACE_EXTENSIONS: + { + break; + } + case CAPI_PORT_DATA_THRESHOLD: + { + if (NULL == me_ptr) + { + CHMIXER_MSG(MIID_UNKNOWN, DBG_ERROR_PRIO, "null ptr while querying threshold"); + return CAPI_EBADPARAM; + } + + uint32_t threshold_in_bytes = 1; // default + capi_result = capi_cmn_handle_get_port_threshold(&prop_array[i], threshold_in_bytes); + break; + } + case CAPI_OUTPUT_MEDIA_FORMAT_V2: + { + if (NULL == me_ptr) + { + CHMIXER_MSG(MIID_UNKNOWN, + DBG_ERROR_PRIO, + "Get property id 0x%lx, module is not allocated", + prop_array[i].id); + CAPI_SET_ERROR(capi_result, CAPI_EBADPARAM); + break; + } + + // sanity check for valid output port index + if (CAPI_CHMIXER_DEFAULT_PORT != prop_array[i].port_info.port_index) + { + CHMIXER_MSG(miid, DBG_ERROR_PRIO, "Get property id 0x%lx, invalid outptu port index.", prop_array[i].id); + CAPI_SET_ERROR(capi_result, CAPI_EBADPARAM); + break; + } + + capi_result = capi_cmn_handle_get_output_media_fmt_v2(&prop_array[i], + &me_ptr->output_media_fmt[CAPI_CHMIXER_DEFAULT_PORT]); + break; + } + default: + { + CHMIXER_MSG(miid, DBG_ERROR_PRIO, "Get property id 0x%lx, Not Supported.", prop_array[i].id); + continue; + } + } + } + return capi_result; +} + +#ifdef CAPI_CHMIXER_KPPS_PROFILING +/*=========================================================================== + FUNCTION : capi_chmixer_kpps_profiler_reset + DESCRIPTION: reset profiler structure (should be called if media fmt changed) +===========================================================================*/ +void capi_chmixer_kpps_profiler_reset(capi_chmixer_t *me_ptr) +{ + memset(&me_ptr->profiler, 0, sizeof(me_ptr->profiler)); +} + +/*=========================================================================== + FUNCTION : capi_chmixer_kpps_profiler + DESCRIPTION: generates profiling data +===========================================================================*/ +void capi_chmixer_kpps_profiler(capi_chmixer_t *me_ptr) +{ + uint64 frame_cycles; + uint32_t sample_rate = me_ptr->input_media_fmt[CAPI_CHMIXER_DEFAULT_PORT].format.sampling_rate; + + frame_cycles = me_ptr->profiler.end_cycles - me_ptr->profiler.start_cycles; + me_ptr->profiler.total_cycles += frame_cycles; + + me_ptr->profiler.total_sample_count += me_ptr->profiler.frame_sample_count; + + me_ptr->profiler.frame_kpps = (frame_cycles * sample_rate) / (me_ptr->profiler.frame_sample_count * 1000); + me_ptr->profiler.average_kpps = + (me_ptr->profiler.total_cycles * sample_rate) / (me_ptr->profiler.total_sample_count * 1000); + + if (me_ptr->profiler.frame_kpps > me_ptr->profiler.peak_kpps) + { + me_ptr->profiler.peak_kpps = me_ptr->profiler.frame_kpps; + } +} + +/*=========================================================================== + FUNCTION : capi_chmixer_kpps_print + DESCRIPTION: print profiling data +===========================================================================*/ +void capi_chmixer_kpps_print(capi_chmixer_t *me_ptr) +{ + if (0 != me_ptr->profiler.total_sample_count) + { + CHMIXER_MSG(me_ptr->miid, + DBG_MED_PRIO, + "\nProfiling result:-\n" + "1.\t Number of Input Channels %lu\n" + "2.\t Number of Output Channels %lu\n" + "3.\t Sampling Rate %lu\n" + "4.\t Average KPPS %llu\n" + "5.\t Peak KPPS %llu\n" + "====================================================================================", + me_ptr->input_media_fmt[CAPI_CHMIXER_DEFAULT_PORT].format.num_channels, + me_ptr->output_media_fmt[CAPI_CHMIXER_DEFAULT_PORT].format.num_channels, + me_ptr->input_media_fmt[CAPI_CHMIXER_DEFAULT_PORT].format.sampling_rate, + me_ptr->profiler.average_kpps, + me_ptr->profiler.peak_kpps); + } +} +#endif + +#ifdef CAPI_CHMIXER_DATA_LOG +/*=========================================================================== + FUNCTION : capi_chmixer_data_logger + DESCRIPTION: log input and output data to and from chmixer capi v2 +===========================================================================*/ +void capi_chmixer_data_logger(capi_chmixer_t *me_ptr, const capi_stream_data_t *input, const capi_stream_data_t *output) +{ + static posal_atomic_word_t instance_id = { 0 }; + + char_t in_file_name[100]; + char_t out_file_name[100]; + + void * in_ptrs, *out_ptrs; + uint16 i = 0; + + bool_t is_file_opened = TRUE; + + FILE *fp_in = NULL; + FILE *fp_out = NULL; + + if (0 == me_ptr->logger.instance_id) + { + me_ptr->logger.instance_id = (uint32_t)posal_atomic_increment(&instance_id); + } + + const uint32_t sample_rate = me_ptr->input_media_fmt[CAPI_CHMIXER_DEFAULT_PORT].format.sampling_rate; + const uint16_t num_in_channel = me_ptr->input_media_fmt[CAPI_CHMIXER_DEFAULT_PORT].format.num_channels; + const uint16_t num_out_channel = me_ptr->output_media_fmt[CAPI_CHMIXER_DEFAULT_PORT].format.num_channels; + + if ((me_ptr->logger.samp_rate != sample_rate) || (me_ptr->logger.num_in_channel != num_in_channel) || + (me_ptr->logger.num_out_channel != num_out_channel)) + { + me_ptr->logger.samp_rate = sample_rate; + me_ptr->logger.num_in_channel = num_in_channel; + me_ptr->logger.num_out_channel = num_out_channel; + me_ptr->logger.split_timestamp = me_ptr->logger.curr_timestamp; + is_file_opened = FALSE; + } + + for (i = 0; i < num_in_channel; i++) + { + in_ptrs = input->buf_ptr[i].data_ptr; + + snprintf(in_file_name, + 100, + "chmixer_id%lu_in_ts%llu_sr%lu_ch%d.pcm", + me_ptr->logger.instance_id, + me_ptr->logger.split_timestamp, + me_ptr->logger.samp_rate, + i); + + if (!is_file_opened) + { + fp_in = fopen(in_file_name, "wb"); + } + else + { + fp_in = fopen(in_file_name, "a+b"); + } + + if (NULL != fp_in) + { + fwrite(in_ptrs, 1, input->buf_ptr[0].actual_data_len, fp_in); + fclose(fp_in); + fp_in = NULL; + } + } + + for (i = 0; i < num_out_channel; i++) + { + out_ptrs = output->buf_ptr[i].data_ptr; + + snprintf(out_file_name, + 100, + "chmixer_id%lu_out_ts%llu_sr%lu_ch%d.pcm", + me_ptr->logger.instance_id, + me_ptr->logger.split_timestamp, + me_ptr->logger.samp_rate, + i); + + if (!is_file_opened) + { + fp_out = fopen(out_file_name, "wb"); + } + else + { + fp_out = fopen(out_file_name, "a+b"); + } + + if (NULL != fp_out) + { + fwrite(out_ptrs, 1, output->buf_ptr[0].actual_data_len, fp_out); + fclose(fp_out); + fp_out = NULL; + } + } + + uint8_t bit_width = me_ptr->input_media_fmt[CAPI_CHMIXER_DEFAULT_PORT].format.bits_per_sample; + uint8_t byte_sample_convert = (bit_width == 16) ? 1 : 2; + me_ptr->logger.curr_timestamp += ((uint64)(input->buf_ptr[0].actual_data_len >> byte_sample_convert) * 1000000) / + ((uint64)(me_ptr->logger.samp_rate)); +} +#endif diff --git a/modules/processing/channel_mixer/capi/src/capi_chmixer_utils.h b/modules/processing/channel_mixer/capi/src/capi_chmixer_utils.h new file mode 100644 index 0000000..d5dd7e5 --- /dev/null +++ b/modules/processing/channel_mixer/capi/src/capi_chmixer_utils.h @@ -0,0 +1,175 @@ +/* ======================================================================== */ +/** + @file capi_chmixer_utils.h + + Header file to implement utilities for Channel Mixer audio Post + Processing module +*/ + +/* ========================================================================= + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause + ========================================================================== */ + +#ifndef CAPI_CHMIXER_UTILS_H +#define CAPI_CHMIXER_UTILS_H + +/*------------------------------------------------------------------------ + * Include files + * -----------------------------------------------------------------------*/ +#include "capi.h" + +#include "audpp_util.h" +#include "audio_dsp32.h" + +#ifndef CAPI_STANDALONE +#include "shared_lib_api.h" +#endif + +#include "ChannelMixerLib.h" +#include "capi_cmn.h" +/*------------------------------------------------------------------------ + * Macros, Defines, Type declarations + * -----------------------------------------------------------------------*/ +//Enable this flag for average and peak kpps analysis (only on sim) +//#define CAPI_CHMIXER_KPPS_PROFILING 1 + +//Enable this flag for input and output buffer logging (only on sim) +//#define CAPI_CHMIXER_DATA_LOG 1 + +//Enable this flag for debug messages +//#define CAPI_CHMIXER_DEBUG_MSG 1 + +/* debug message */ +#define MIID_UNKNOWN 0 +#define CHMIXER_MSG_PREFIX "CAPI CHMIXER:[%lX] " +#define CHMIXER_MSG(ID, xx_ss_mask, xx_fmt, ...)\ + AR_MSG(xx_ss_mask, CHMIXER_MSG_PREFIX xx_fmt, ID, ##__VA_ARGS__) + + +static const uint16_t CAPI_CHMIXER_MAX_IN_PORTS = 1; +static const uint16_t CAPI_CHMIXER_MAX_OUT_PORTS = 1; +static const uint16_t CAPI_CHMIXER_DEFAULT_PORT = 0; + +static const uint32_t CAPI_CHMIXER_STACK_SIZE = 2048; + +static const uint32_t CAPI_CHMIXER_MAX_CHANNELS = CH_MIXER_MAX_NUM_CH; + +static const int16_t CAPI_CHMIXER_UNITY_Q14 = (1<<14); + +static const uint32_t CAPI_CHMIXER_ENABLE = 1; + +#define CAPI_CHMIXER_ALIGN_4_BYTE(x) (((x) + 3) & (0xFFFFFFFC)) + +#define CAPI_CHMIXER_V2_MIN_SIZE (sizeof(capi_standard_data_format_v2_t) + sizeof(capi_set_get_media_format_t)) + +#define CH_MIXER_16BIT_Q_FORMAT 15 + +#define MEM_ALIGN_EIGHT_BYTE 8 +/*------------------------------------------------------------------------ + * Structure definitions + * -----------------------------------------------------------------------*/ +typedef struct capi_chmixer_events_config +{ + bool_t chmixer_enable; + uint32_t chmixer_KPPS; + uint32_t chmixer_delay_in_us; + uint32_t chmixer_bandwidth; +} capi_chmixer_events_config_t; + +typedef struct capi_chmixer_coef_set_t +{ + uint16_t num_in_ch; + uint16_t *in_ch_map; + uint16_t num_out_ch; + uint16_t *out_ch_map; + int16_t *coef_ptr; +} capi_chmixer_coef_set; + +typedef struct capi_chmixer_config +{ + bool_t lib_enable; + uint32_t num_coef_sets; + capi_chmixer_coef_set *coef_sets_ptr; +}capi_chmixer_config_t; + +typedef struct capi_chmixer_profiler +{ + uint64_t start_cycles; + uint64_t end_cycles; + uint64_t total_cycles; + uint64_t total_sample_count; + uint64_t frame_sample_count; + uint64_t frame_kpps; + uint64_t average_kpps; + uint64_t peak_kpps; +} capi_chmixer_profiler_t; + +typedef struct capi_chmixer_data_log_info +{ + uint16_t num_in_channel; + uint16_t num_out_channel; + uint32_t samp_rate; + uint64_t curr_timestamp; + uint64_t split_timestamp; + uint32_t instance_id; +} capi_chmixer_data_log_info_t; + +typedef struct capi_chmixer +{ + capi_t vtbl; + capi_event_callback_info_t cb_info; + capi_heap_id_t heap_mem; + capi_chmixer_events_config event_config; + capi_media_fmt_v2_t input_media_fmt[CAPI_CHMIXER_MAX_IN_PORTS]; + capi_media_fmt_v2_t output_media_fmt[CAPI_CHMIXER_MAX_OUT_PORTS]; + bool_t use_default_channel_info[CAPI_CHMIXER_MAX_OUT_PORTS]; + + capi_chmixer_config_t config; + void *lib_ptr; + uint32_t lib_instance_size; + uint32_t client_enable; + + bool_t is_native_mode; + bool_t inp_media_fmt_received; + uint16_t configured_num_channels; + uint16_t * configured_ch_map; + uint32_t coef_payload_size; +#ifdef CAPI_CHMIXER_KPPS_PROFILING + capi_chmixer_profiler_t profiler; +#endif +#ifdef CAPI_CHMIXER_DATA_LOG + capi_chmixer_data_log_info_t logger; +#endif + uint32_t miid; +} capi_chmixer_t; + +/*------------------------------------------------------------------------ + * Function declarations + * -----------------------------------------------------------------------*/ +uint32_t capi_get_init_mem_req(); +void capi_chmixer_init_media_fmt(capi_chmixer_t *const me_ptr); +void capi_chmixer_init_config(capi_chmixer_t *const me_ptr); +void capi_chmixer_init_events(capi_chmixer_t *const me_ptr); + +capi_err_t capi_chmixer_check_ch_type(const uint16_t *channel_type,const uint32_t array_size); +capi_err_t capi_chmixer_set_output_media_fmt(capi_chmixer_t *const me_ptr, const capi_media_fmt_v2_t *const out_media_fmt); + +capi_err_t capi_chmixer_process_set_properties(capi_chmixer_t * me_ptr, capi_proplist_t * proplist_ptr); +capi_err_t capi_chmixer_process_get_properties(capi_chmixer_t * me_ptr, capi_proplist_t * proplist_ptr); + +capi_err_t capi_chmixer_check_init_lib_instance(capi_chmixer_t *const me_ptr); + +capi_err_t capi_chmixer_raise_events(capi_chmixer_t *const me_ptr, bool_t media_fmt_update); + +#ifdef CAPI_CHMIXER_KPPS_PROFILING +void capi_chmixer_kpps_profiler_reset(capi_chmixer_t* me_ptr); +void capi_chmixer_kpps_profiler(capi_chmixer_t *me_ptr); +void capi_chmixer_kpps_print(capi_chmixer_t* me_ptr); +#endif + +#ifdef CAPI_CHMIXER_DATA_LOG +void capi_chmixer_data_logger(capi_chmixer_t *me_ptr, const capi_stream_data_t *input, const capi_stream_data_t *output); +#endif + +#endif // CAPI_CHMIXER_UTILS_H diff --git a/modules/processing/channel_mixer/capi/stub_src/capi_chmixer.cpp b/modules/processing/channel_mixer/capi/stub_src/capi_chmixer.cpp new file mode 100644 index 0000000..9e1c9ca --- /dev/null +++ b/modules/processing/channel_mixer/capi/stub_src/capi_chmixer.cpp @@ -0,0 +1,38 @@ +/* ======================================================================== */ +/** + @file capi_chmixer.cpp + + C stub source file for Channel Mixer. +*/ + +/* ========================================================================= + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause + ========================================================================= */ + +/*------------------------------------------------------------------------ + * Include files and Macro definitions + * -----------------------------------------------------------------------*/ +#include "capi.h" +#include "capi_chmixer.h" + + +/*------------------------------------------------------------------------ + Function name: capi_chmixer_get_static_properties + DESCRIPTION: Function to get the static properties for CHMIXER module + -----------------------------------------------------------------------*/ +capi_err_t capi_chmixer_get_static_properties (capi_proplist_t *init_set_properties, capi_proplist_t *static_properties) +{ + return CAPI_EUNSUPPORTED; +} + + +/*------------------------------------------------------------------------ + Function name: capi_chmixer_init + DESCRIPTION: Initialize the CHMIXER module and library. + -----------------------------------------------------------------------*/ +capi_err_t capi_chmixer_init (capi_t* _pif, capi_proplist_t *init_set_properties) +{ + return CAPI_EUNSUPPORTED; +} + diff --git a/modules/processing/channel_mixer/lib/inc/ChannelMixerLib.h b/modules/processing/channel_mixer/lib/inc/ChannelMixerLib.h new file mode 100644 index 0000000..40492ce --- /dev/null +++ b/modules/processing/channel_mixer/lib/inc/ChannelMixerLib.h @@ -0,0 +1,209 @@ +#ifndef CHANNEL_MIXER_LIB_H +#define CHANNEL_MIXER_LIB_H +/*============================================================================ + @file ChannelMixerLib.h + + Public header file for the Channel Mixer algorithm. */ + +/*========================================================================= +Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. +SPDX-License-Identifier: BSD-3-Clause +========================================================================= */ + +#include "AudioComdef.h" +#include "ar_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifdef PROD_SPECIFIC_MAX_CH +#define CH_MIXER_MAX_NUM_CH PROD_SPECIFIC_MAX_CH // Maximum number of channels. +#else +#define CH_MIXER_MAX_NUM_CH 32 // Maximum number of channels. +#endif + +#define CH_MIXER_ALIGN_8_BYTE(x) (((x) + 7) & (0xFFFFFFF8)) +#define CH_MIXER_BIT_MASK_SIZE 64 +#define CH_MIXER_MAX_BITMASK_GROUPS 3 +#define CH_MIXER_BIT_MASK_SIZE_MINUS_ONE (CH_MIXER_BIT_MASK_SIZE - 1) + +typedef enum _ChMixerBitMaskGroup +{ + ChMixerBitMaskGroup_1to63 = 0, + ChMixerBitMaskGroup_64to127 = 1, + ChMixerBitMaskGroup_128to191 = 2, +} ChMixerBitMaskGroup; + +// ID for each channel +typedef enum _ChMixerChType +{ + CH_MIXER_PCM_CH_NONE = 0, // None + CH_MIXER_PCM_CH_L = 1, // Front-left (FL) + CH_MIXER_PCM_CH_R = 2, // Front-right (FR) + CH_MIXER_PCM_CH_C = 3, // Front-center (FC) + CH_MIXER_PCM_CH_LS = 4, // Left-surround (LS) + CH_MIXER_PCM_CH_RS = 5, // Right-surround (RS) + CH_MIXER_PCM_CH_LFE_1 =6, // Low frequency effects (LFE-1) + CH_MIXER_PCM_CH_CS = 7, // Center-surround (CS, RC) + CH_MIXER_PCM_CH_LB = 8, // Left-back (LB, RL) + CH_MIXER_PCM_CH_RB = 9, // Right-back (RB, RR) + CH_MIXER_PCM_CH_TS = 10, // Top-surround (TS) + CH_MIXER_PCM_CH_CVH = 11, // Center-vertical-height (CVH) + CH_MIXER_PCM_CH_TFC = CH_MIXER_PCM_CH_CVH, // Top Front Center (TFC) + CH_MIXER_PCM_CH_MS = 12, // Mono-surround (MS) + CH_MIXER_PCM_CH_FLC = 13, // Front left of center (FLC) + CH_MIXER_PCM_CH_FRC = 14, // Front Right of center (FRC) + CH_MIXER_PCM_CH_RLC = 15, // Rear left of center (RLC) + CH_MIXER_PCM_CH_RRC = 16, // Rear right of center (RRC) + CH_MIXER_PCM_CH_CB = CH_MIXER_PCM_CH_CS, // Center-back (CB) + CH_MIXER_PCM_CH_LFE_2 =17, // Low frequency effects (LFE-2) + CH_MIXER_PCM_CH_SL = 18, // Side-Left (SL) + CH_MIXER_PCM_CH_SR = 19, // Side-Right(SR) + CH_MIXER_PCM_CH_TFL = 20, // Top Front Left (TFL) + CH_MIXER_PCM_CH_LVH = CH_MIXER_PCM_CH_TFL, // Left Vertical Height (LVH) + CH_MIXER_PCM_CH_TFR = 21, // Top Front Right (TFR) + CH_MIXER_PCM_CH_RVH = CH_MIXER_PCM_CH_TFR, // Right Vertical Height (RVH) + CH_MIXER_PCM_CH_TC = 22, // Top Center (TC) + CH_MIXER_PCM_CH_TBL = 23, // Top Back Left (TBL) + CH_MIXER_PCM_CH_TBR = 24, // Top Back Right (TBR) + CH_MIXER_PCM_CH_TSL = 25, // Top Side Left (TSL) + CH_MIXER_PCM_CH_TSR = 26, // Top Side Right (TSR) + CH_MIXER_PCM_CH_TBC = 27, // Top Back Center (TBC) + CH_MIXER_PCM_CH_BFC = 28, // Bottom Front Center (BFC) + CH_MIXER_PCM_CH_BFL = 29, // Bottom Front Left (BFL) + CH_MIXER_PCM_CH_BFR = 30, // Bottom Front Right (BFR) + CH_MIXER_PCM_CH_LW = 31, // Left Wide (LW) + CH_MIXER_PCM_CH_RW = 32, // Right Wide (RW) + CH_MIXER_PCM_CH_LSD = 33, // Left Side Direct (LSD) + CH_MIXER_PCM_CH_RSD = 34, // Right Side Direct (RSD) + CH_MIXER_PCM_CH_MAX_TYPE = 128 +} ChMixerChType; + +// Possible return values to indicate status of library. +typedef enum _ChMixerResultType +{ + CH_MIXER_SUCCESS = 0, + CH_MIXER_ENOT_SUPPORTED = -1, // Return this if the specific combination of + // input and output channel conversion is not supported + CH_MIXER_EBAD_PARAM = -2 +} ChMixerResultType; + +// Maintain internal state +typedef struct _ChMixerdynStateStruct +{ + // input channel map array inputChannels[num_input_channels] + ChMixerChType *pInputChannels; + // output channel map array outputChannels[num_output_channels] + ChMixerChType *pOutputChannels; + // The mixer matrix. + // linear matrix array mixerMatrixL16Q14[num_output_channels * num_input_channels] + int16 *pMixerMatrixL16Q14; + // To identify which matrix coefficients are zero, define the following array + // row array dimension is defined with one more than number of channels for keeping the end marker value + // 2-D step matrix represented in linear array inputStepMatrix[num_output_channels][num_input_channels + 1]; + int8 *pInputStepMatrix; + } ChMixerdynStateStruct; + +// Maintain internal state + typedef struct _ChMixerStateStruct { + uint32 numInputCh; // Number of input channels + uint32 numOutputCh; // Number of output channels + uint32 num_active_coeff; + // Number of coefficients refers to numbber of input channels that contribute + // to all the output channels + uint32 dataBitWidth; // Data bit width of input and output. + // If the input channels and output channels are same, then + // it is a trivial copy (with some reordering). + bool_t isTrivialCopy; + // flag to indicate if custom mixer coefficients are applicable. + // 0 indicates default coefficient set is used. + bool_t isCustomCoeffValid; + // ptr to custom coefficient memory + int16 *ptrCustomCoeffset; + // ptr to coefficients + int16 *ptrCoeff; + // dynamic channel mixer structure + ChMixerdynStateStruct dynState; + } ChMixerStateStruct; + +/* +@brief Return the size of state structure required to operate one instance of +Channel mixer. The size is in bytes (at least 4-byte aligned). + +@param pSize: [out] Pointer to memory location where size of structure must be +updated. +Return values: None +*/ +void ChMixerGetInstanceSize(uint32 *pSize, uint32 numInputChannels, uint32 numOutputChannels); + +/* +@brief Initialize Channel mixing algorithm +Based on the input and output channels, identify the input & output channel +configurations respectively + +@param pCMState : [out] Pointer to the state structure that must be initialized. +@param numInputChannels : [in] Number of input channels. +@param inputChannels : [in] Pointer to array that indicates what each input channel is +@param numOutputChannels : [in] Number of output channels. +@param inputChannels : [in] Pointer to array that indicates what each output channel is +@param dataBitWidth : [in] Bit-width of data + +Return values: Return CH_MIXER_SUCCESS if this conversion can be performed and +return CH_MIXER_ENOT_SUPPORTED if conversion is not supported. +*/ + +ChMixerResultType ChMixerInitialize(void * pCMState, + uint32 lib_mem_size, + uint32 numInputChannels, + ChMixerChType *inputChannels, + uint32 numOutputChannels, + ChMixerChType *outputChannels, + uint32 dataBitWidth); + +/* +@brief Apply the Channel mixing algorithm + +@param pCMState : [in] Pointer to the state structure +@param output : [out] Multi channel output pointer +@param input : [in] Multi channel input pointer +@param numSamples : [in] Number of samples in the input +*/ +void ChMixerProcess(void *pCMState, void **output, void **input, uint32 numSamples); + +/* +@brief set param interface for Channel mixing algorithm +Based on the parameters set by client, update the channel mixer library accordingly. + +@param pCMState : [in] Pointer to the state structure that must be initialized. +@param numInputChannels : [in] Number of input channels. +@param inputChannels : [in] Pointer to array that indicates what each input channel is +@param numOutputChannels : [in] Number of output channels. +@param outputChannels : [in] Pointer to array that indicates what each output channel is +@param dataBitWidth : [in] Bit-width of data +@param pCustomCoeffSet : [in] Pointer to array of custom coefficients sent through set param + +Return values: A value indicate success or failure ( a different value for each + failure) +*/ +ChMixerResultType ChMixerSetParam(void * pCMState, + uint32 lib_mem_size, + uint32 numInputChannels, + ChMixerChType *inputChannels, + uint32 numOutputChannels, + ChMixerChType *outputChannels, + uint32 dataBitWidth, + void * pCustomCoeffSet); + +/* +@brief Query for num active coefficients +*/ +uint32 ChMixerGetNumActiveCoefficients(ChMixerStateStruct *pState); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif // #ifndef CHANNEL_MIXER_LIB_H + diff --git a/modules/processing/channel_mixer/lib/src/ChannelMixerLib.c b/modules/processing/channel_mixer/lib/src/ChannelMixerLib.c new file mode 100644 index 0000000..774c044 --- /dev/null +++ b/modules/processing/channel_mixer/lib/src/ChannelMixerLib.c @@ -0,0 +1,642 @@ +/*============================================================================ + @file ChannelMixerLib.c + + Impelementation of the Channel Mixer algorithm. */ + +/*========================================================================= +Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. +SPDX-License-Identifier: BSD-3-Clause +========================================================================= */ + +#include "audio_basic_op.h" +#include "audio_basic_op_ext.h" +#include "ChannelMixerLib.h" +#include "ChannelMixerRemapRules.h" +#include "audio_divide_qx.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +static void copy_in_out_chan_params(ChMixerStateStruct *pState, + uint32 numInputChannels, + ChMixerChType * inputChannels, + uint32 numOutputChannels, + ChMixerChType * outputChannels, + uint32 dataBitWidth); + +/* +@brief Normalize the Mixer matrix + +@param pState : [in/out] Pointer to the state structure +*/ +void ChMixerMatrixNormalize(ChMixerStateStruct *pState) +{ + uint32 outputChIndex, inputChIndex; + int32 rowTotal[CH_MIXER_MAX_NUM_CH]; + int32 rowTotalMax; + int32 coefficient; + int16 shift; + + /* Calculate the total contribution by all input channels for every output channel.*/ + // Calculate for the first output channel and initialize rowTotalMax + rowTotalMax = 0; + for (inputChIndex = 0; inputChIndex < pState->numInputCh; inputChIndex++) + rowTotalMax += pState->ptrCoeff[inputChIndex]; + + // Calculate all other channels + for (outputChIndex = 1; outputChIndex < pState->numOutputCh; outputChIndex++) + { + rowTotal[outputChIndex] = 0; + + for (inputChIndex = 0; inputChIndex < pState->numInputCh; inputChIndex++) + rowTotal[outputChIndex] += pState->ptrCoeff[pState->numInputCh * outputChIndex + inputChIndex]; + // Find maximum + if (rowTotalMax < rowTotal[outputChIndex]) + rowTotalMax = rowTotal[outputChIndex]; + } + + for (outputChIndex = 0; outputChIndex < pState->numOutputCh; outputChIndex++) + { + // Normalize the coefficients for each row + for (inputChIndex = 0; inputChIndex < pState->numInputCh; inputChIndex++) + { + // If coefficient is not zero or if there is only one non-zero coefficient in a row, normalize + coefficient = (int32)pState->ptrCoeff[pState->numInputCh * outputChIndex + inputChIndex]; + if (coefficient == rowTotalMax) + { + // Q14 format of unity + // temporary change to avoid signal saturation for a case, where none of the input channels actually + // contribute to output. (only for 8550, generic fix in next target) + if (0 == rowTotalMax) + { + pState->ptrCoeff[pState->numInputCh * outputChIndex + inputChIndex] = 16384 / pState->numInputCh; + } + else + { + pState->ptrCoeff[pState->numInputCh * outputChIndex + inputChIndex] = 16384; + } + } + else if ((coefficient != 0) && (rowTotalMax != 0)) + { + coefficient = s32_div_s32_s32_normalized(coefficient, rowTotalMax, &shift); + // Convert and store the matrix mixing coefficient in Q14 format. + pState->ptrCoeff[pState->numInputCh * outputChIndex + inputChIndex] = + s16_saturate_s32(s32_shl_s32_sat(coefficient, (shift - 1))); + } + } + } +} + +/* +@brief Calculate input/output channel BitMasks + +@param pState : [in] Pointer to the state structure +@param pInputBitMask : [out] input channel bit mask calculated +@param pOutputBitMask : [out] output channel bit mask calcualted +*/ +void CalculateInOutChBitMask(ChMixerStateStruct *pState, uint64 *pInputBitMaskArray, uint64 *pOutputBitMaskArray) +{ + uint32 chIndex; + ChMixerChType remapFromCh, remapToCh; + + for (uint32 cnt = 0; cnt < CH_MIXER_MAX_BITMASK_GROUPS; cnt++) + { + pInputBitMaskArray[cnt] = 0; + pOutputBitMaskArray[cnt] = 0; + } + + // Form bit mask of input channels. The bit mask is a 64-bit value with bits + // corresponding to the input channel types set. + for (chIndex = 0; chIndex < pState->numInputCh; chIndex++) + { + remapFromCh = pState->dynState.pInputChannels[chIndex]; + if (remapFromCh < CH_MIXER_BIT_MASK_SIZE) + { + pInputBitMaskArray[ChMixerBitMaskGroup_1to63] |= ((1ULL << remapFromCh)); + } + else if ((remapFromCh < 2 * CH_MIXER_BIT_MASK_SIZE)) + { + pInputBitMaskArray[ChMixerBitMaskGroup_64to127] |= ((1ULL << (remapFromCh & CH_MIXER_BIT_MASK_SIZE_MINUS_ONE))); + } + else if ((remapFromCh < 3 * CH_MIXER_BIT_MASK_SIZE)) + { + pInputBitMaskArray[ChMixerBitMaskGroup_128to191] |= ((1ULL << (remapFromCh & CH_MIXER_BIT_MASK_SIZE_MINUS_ONE))); + } + } + + // Form bit mask of output channels. The bit mask is a 64-bit value with bits + // corresponding to the output channel types set. + + for (chIndex = 0; chIndex < pState->numOutputCh; chIndex++) + { + remapToCh = pState->dynState.pOutputChannels[chIndex]; + if (remapToCh < CH_MIXER_BIT_MASK_SIZE) + { + pOutputBitMaskArray[ChMixerBitMaskGroup_1to63] |= ((1ULL << remapToCh)); + } + else if ((remapToCh < 2 * CH_MIXER_BIT_MASK_SIZE)) + { + pOutputBitMaskArray[ChMixerBitMaskGroup_64to127] |= ((1ULL << (remapToCh & CH_MIXER_BIT_MASK_SIZE_MINUS_ONE))); + } + else if ((remapToCh < 3 * CH_MIXER_BIT_MASK_SIZE)) + { + pOutputBitMaskArray[ChMixerBitMaskGroup_128to191] |= ((1ULL << (remapToCh & CH_MIXER_BIT_MASK_SIZE_MINUS_ONE))); + } + } +} + +/* +@brief Apply the paired remmaping rules defined + +@param pState : [in/out] Pointer to the state structure +*/ +void ChMixerApplyPairedRemapRules(ChMixerStateStruct *pState) +{ + uint32 ruleIndex, chIndex; + uint64 inputBitMask[CH_MIXER_MAX_BITMASK_GROUPS], remapFromChBitMask, remapToChBitMask; + uint64 outputBitMask[CH_MIXER_MAX_BITMASK_GROUPS]; + + CalculateInOutChBitMask(pState, &inputBitMask[0], &outputBitMask[0]); + + for (ruleIndex = 0; ruleIndex < NUM_PAIRED_REMAP_RULES; ruleIndex++) + { + // Form the bit mask of the 'Remap from' channels in the rule + remapFromChBitMask = ((uint64)1 << PairedRemapRulesList[ruleIndex].fromCh1) | + ((uint64)1 << PairedRemapRulesList[ruleIndex].fromCh2); + // Form the bit mask of the 'Remap to' channels in the rule + remapToChBitMask = + ((uint64)1 << PairedRemapRulesList[ruleIndex].toCh1) | ((uint64)1 << PairedRemapRulesList[ruleIndex].toCh2); + // Check if the rule is applicable to this input/output channel configuration. + if (((outputBitMask[ChMixerBitMaskGroup_1to63] & remapToChBitMask) == remapToChBitMask) && + ((inputBitMask[ChMixerBitMaskGroup_1to63] & remapFromChBitMask) == remapFromChBitMask) && + ((outputBitMask[ChMixerBitMaskGroup_1to63] & remapFromChBitMask) != remapFromChBitMask)) + { + // Remap the channel + for (chIndex = 0; chIndex < pState->numInputCh; chIndex++) + { + if (pState->dynState.pInputChannels[chIndex] == PairedRemapRulesList[ruleIndex].fromCh1) + { + pState->dynState.pInputChannels[chIndex] = PairedRemapRulesList[ruleIndex].toCh1; + continue; + } + if (pState->dynState.pInputChannels[chIndex] == PairedRemapRulesList[ruleIndex].fromCh2) + { + pState->dynState.pInputChannels[chIndex] = PairedRemapRulesList[ruleIndex].toCh2; + continue; + } + } + } + } +} +/* +@brief Apply the paired remmaping rules defined + +@param pState : [in/out] Pointer to the state structure +*/ +ChMixerResultType ChMixerApplySingleChRemapRules(ChMixerStateStruct *pState) +{ + int32 ruleIndex, remapToChIndex; + uint32 inputChIndex, outputChIndex; + ChMixerChType remapFromCh, remapToCh; + uint64 inputBitMask[CH_MIXER_MAX_BITMASK_GROUPS], outputBitMask[CH_MIXER_MAX_BITMASK_GROUPS]; + uint64 remapToChBitMask, remapfromChBitMask, is_input_present_in_output; + int32 index; + + CalculateInOutChBitMask(pState, &inputBitMask[0], &outputBitMask[0]); + // Initialize the mixer matrix coefficients. + for (inputChIndex = 0; inputChIndex < pState->numInputCh; inputChIndex++) + for (outputChIndex = 0; outputChIndex < pState->numOutputCh; outputChIndex++) + { + index = pState->numInputCh * outputChIndex + inputChIndex; + pState->ptrCoeff[index] = 0; + } + + // Warning if output has custom channel type and input doesn't have any custom channel types + for (outputChIndex = 0; outputChIndex < pState->numOutputCh; outputChIndex++) + { + remapToCh = pState->dynState.pOutputChannels[outputChIndex]; + bool_t custom_input_ch_type = FALSE; + + // output has custom channel type + if (remapToCh > CH_MIXER_PCM_CH_RSD) + { + for (inputChIndex = 0; inputChIndex < pState->numInputCh; inputChIndex++) + { + remapFromCh = pState->dynState.pInputChannels[inputChIndex]; + + if (remapFromCh > CH_MIXER_PCM_CH_RSD) + { + custom_input_ch_type = TRUE; + break; + } + } + + if (!custom_input_ch_type) + { + AR_MSG(DBG_HIGH_PRIO, + "CHMIXER Lib: Warning: No coefficient array set, hence setting chmixer coefficient" + "corresponding to custom output channel type %d to zero", + remapToCh); + } + } + } + + // For each input channel, identify the mapping that must be done. + for (inputChIndex = 0; inputChIndex < pState->numInputCh; inputChIndex++) + { + remapFromCh = pState->dynState.pInputChannels[inputChIndex]; + is_input_present_in_output = 0; + if (pState->dynState.pInputChannels[inputChIndex] < CH_MIXER_BIT_MASK_SIZE) + { + remapfromChBitMask = (1ULL << remapFromCh); + is_input_present_in_output = (remapfromChBitMask & outputBitMask[ChMixerBitMaskGroup_1to63]); + } + else if (pState->dynState.pInputChannels[inputChIndex] < 2 * CH_MIXER_BIT_MASK_SIZE) + { + remapfromChBitMask = (1ULL << (remapFromCh & CH_MIXER_BIT_MASK_SIZE_MINUS_ONE)); + is_input_present_in_output = ((remapfromChBitMask) & outputBitMask[ChMixerBitMaskGroup_64to127]); + } + else if (pState->dynState.pInputChannels[inputChIndex] < 3 * CH_MIXER_BIT_MASK_SIZE) + { + remapfromChBitMask = (1ULL << (remapFromCh & CH_MIXER_BIT_MASK_SIZE_MINUS_ONE)); + is_input_present_in_output = ((remapfromChBitMask)&outputBitMask[ChMixerBitMaskGroup_128to191]); + } + if (is_input_present_in_output) + { + // If input channel is present in output, just copy it over (i.e) set matrix coefficient to unity + for (outputChIndex = 0; outputChIndex < pState->numOutputCh; outputChIndex++) + { + remapToCh = pState->dynState.pOutputChannels[outputChIndex]; + if (remapFromCh == remapToCh) + { + // Identify the index of the channel in the output + pState->ptrCoeff[pState->numInputCh * outputChIndex + inputChIndex] = 16384; + break; + } + } + } + else + { + // If input is a custom channel type and output doesn't have the same channel type (checked above) + if (remapFromCh > CH_MIXER_PCM_CH_RSD) + { + bool_t output_has_custom_ch_type = FALSE; + + for (outputChIndex = 0; outputChIndex < pState->numOutputCh; outputChIndex++) + { + remapToCh = pState->dynState.pOutputChannels[outputChIndex]; + if (remapToCh > CH_MIXER_PCM_CH_RSD) + { + output_has_custom_ch_type = TRUE; + break; + } + } + + /* If there is even one custom channel type in output, + implies inp and out have custom channel types which dont match and we error out */ + if (output_has_custom_ch_type) + { + AR_MSG(DBG_ERROR_PRIO, + "CHMIXER Lib: Error: No coefficient array found for input to ouput custom " + "channel type mapping"); + return CH_MIXER_ENOT_SUPPORTED; + } + else + { + // Warning if input has custom ch type but output doesnt + AR_MSG(DBG_HIGH_PRIO, + "CHMIXER Lib: Warning: No coefficient array set, hence setting coefficient " + "corresponding to custom input channel type %d to zero", + remapFromCh); + } + } + + for (ruleIndex = 0; ruleIndex < SingleChRemapRulesList[remapFromCh].numRulesPerFromCh; ruleIndex++) + { + // Check if this rule for a particular "Remap From" channel is applicable. + // Find the "Remap To" channel bit mask of the rule and see if the bit mask exists + // in the outputBitMask + remapToChBitMask = 0; + for (remapToChIndex = 0; remapToChIndex < SingleChRemapRulesList[remapFromCh].map[ruleIndex].numToChPerRule; + remapToChIndex++) + { + remapToChBitMask |= + ((uint64)1 << SingleChRemapRulesList[remapFromCh].map[ruleIndex].outMap[remapToChIndex].remapToCh); + } + // If the "Remap To" channel bit mask is present in the output, this rule is applicable. + if ((outputBitMask[ChMixerBitMaskGroup_1to63] & remapToChBitMask) == remapToChBitMask) + { + // This rule is applicable. So copy the mixing parameters + for (remapToChIndex = 0; + remapToChIndex < SingleChRemapRulesList[remapFromCh].map[ruleIndex].numToChPerRule; + remapToChIndex++) + { + for (outputChIndex = 0; outputChIndex < pState->numOutputCh; outputChIndex++) + { + if (SingleChRemapRulesList[remapFromCh].map[ruleIndex].outMap[remapToChIndex].remapToCh == + pState->dynState.pOutputChannels[outputChIndex]) + { + // Identify the index of the channel in the output + pState->ptrCoeff[pState->numInputCh * outputChIndex + inputChIndex] = + SingleChRemapRulesList[remapFromCh].map[ruleIndex].outMap[remapToChIndex].mixingParam; + break; + } + } + } + // No more rules can be applied for this input channel. + break; + } + } + } + } + + return CH_MIXER_SUCCESS; +} + +/* +@brief Setup the Channel mixer matrix + +@param pState : [in] Pointer to the state structure +*/ + +ChMixerResultType ChMixerSetupCoefficients(ChMixerStateStruct *pState) +{ + uint32 outputChIndex, inputChIndex; + int16 *pMatrixCoeffL16Q14; + // int16 *pMatrixCoeffStartL16Q14; + int8 * inputStep; + uint64 inputBitMask[CH_MIXER_MAX_BITMASK_GROUPS]; + uint64 outputBitMask[CH_MIXER_MAX_BITMASK_GROUPS]; + pState->num_active_coeff = 0; + + // Recalculate the input and output Bit Mask + CalculateInOutChBitMask(pState, &inputBitMask[0], &outputBitMask[0]); + + if (!pState->isCustomCoeffValid) + { + pState->ptrCoeff = pState->dynState.pMixerMatrixL16Q14; + + // Normalize the matrix coefficients so that the sum on a row is 1 (16384 in Q14). This means the total + // contribution of all the input channels to an output channel is 1 (no gain). + ChMixerMatrixNormalize(pState); + } + else + { + pState->ptrCoeff = pState->ptrCustomCoeffset; + } + + if ((inputBitMask[ChMixerBitMaskGroup_1to63] != outputBitMask[ChMixerBitMaskGroup_1to63]) || + (inputBitMask[ChMixerBitMaskGroup_64to127] != outputBitMask[ChMixerBitMaskGroup_64to127]) || + (inputBitMask[ChMixerBitMaskGroup_128to191] != outputBitMask[ChMixerBitMaskGroup_128to191]) || + (pState->isCustomCoeffValid)) + { + pState->isTrivialCopy = 0; + // Some of the coefficients in a row of the mixer matrix might be zero. This + // means the corresponding input channels do not contribute to the output channels + // The following piece of code, identifies which input channels contribute to which + // output channels. + for (outputChIndex = 0; outputChIndex < pState->numOutputCh; outputChIndex++) + { + inputStep = &pState->dynState.pInputStepMatrix[outputChIndex * (pState->numInputCh + 1)]; + pMatrixCoeffL16Q14 = pState->ptrCoeff + pState->numInputCh * outputChIndex; + for (inputChIndex = 0; inputChIndex < pState->numInputCh; inputChIndex++) + { + if (pMatrixCoeffL16Q14[inputChIndex] != 0) + { + *inputStep++ = (int8)inputChIndex; + pState->num_active_coeff++; + } + } + // This is the end marker for input pointer advance. + *inputStep = (int8)pState->numInputCh; + } + } + else + { + pState->isTrivialCopy = 1; + // Output is a trivial remap of the input. Identify what the remapping + // is and store it in the first row of the pState->inputStep matrix. + inputStep = &pState->dynState.pInputStepMatrix[0]; + for (outputChIndex = 0; (outputChIndex < pState->numOutputCh) && (outputChIndex < CH_MIXER_MAX_NUM_CH); + outputChIndex++) + { + for (inputChIndex = 0; inputChIndex < pState->numInputCh; inputChIndex++) + { + if (pState->dynState.pOutputChannels[outputChIndex] == pState->dynState.pInputChannels[inputChIndex]) + { + *inputStep++ = inputChIndex; + pState->num_active_coeff++; + break; + } + } + } + } + return CH_MIXER_SUCCESS; +} + +/* +@brief Return the num coefficients in inputstep matrix for kpps calculations. +*/ +uint32 ChMixerGetNumActiveCoefficients(ChMixerStateStruct *pState) +{ + return pState->num_active_coeff; +} + +/* +@brief return the memory requirements of channel mixer library +*/ +uint32 ChMixerGetdynMemoryReq(uint32 numInputChannels, uint32 numOutputChannels) +{ + uint32 dynMemorySize = 0; + + // memory for inputChannels + dynMemorySize = CH_MIXER_ALIGN_8_BYTE(sizeof(ChMixerChType) * (numInputChannels)); + // memory for outputChannels + dynMemorySize += CH_MIXER_ALIGN_8_BYTE(sizeof(ChMixerChType) * (numOutputChannels)); + // memory for mixerMatrixL16Q14 + dynMemorySize += CH_MIXER_ALIGN_8_BYTE(sizeof(int16) * (numInputChannels * numOutputChannels)); + // memory for inputStepMatrix + dynMemorySize += CH_MIXER_ALIGN_8_BYTE(sizeof(int8) * (numOutputChannels * (numInputChannels + 1))); + + AR_MSG(DBG_HIGH_PRIO, "CHMIXER Lib: dynamic_mem size %d", dynMemorySize); + return dynMemorySize; +} + +/* +@brief Return the size of state structure required to operate one instance of +Channel mixer. + +@param pSize: [out] Pointer to memory location where size of structure must be +updated. +*/ +void ChMixerGetInstanceSize(uint32 *pSize, uint32 numInputChannels, uint32 numOutputChannels) +{ + *pSize = CH_MIXER_ALIGN_8_BYTE(sizeof(ChMixerStateStruct)); + *pSize += CH_MIXER_ALIGN_8_BYTE(ChMixerGetdynMemoryReq(numInputChannels, numOutputChannels)); + AR_MSG(DBG_HIGH_PRIO, "CHMIXER Lib: total_mem size %d", *pSize); +} + +/* +@brief initialize the dynamic state structure variables +*/ +ChMixerResultType ChMixerInitMemory(void *mem_ptr, int32 mem_size, uint32 numInputChannels, uint32 numOutputChannels) +{ + uint32 getLibSize = 0; + int8 * base_mem_ptr = NULL; + ChMixerGetInstanceSize(&getLibSize, numInputChannels, numOutputChannels); + if (getLibSize > mem_size) + { + return CH_MIXER_ENOT_SUPPORTED; + } + base_mem_ptr = (int8 *)mem_ptr; + + ChMixerStateStruct *pState = (ChMixerStateStruct *)base_mem_ptr; + + base_mem_ptr += CH_MIXER_ALIGN_8_BYTE(sizeof(ChMixerStateStruct)); + pState->dynState.pInputChannels = (ChMixerChType *)base_mem_ptr; + + // offset by memory for inputChannels + base_mem_ptr += CH_MIXER_ALIGN_8_BYTE(sizeof(ChMixerChType) * (numInputChannels)); + pState->dynState.pOutputChannels = (ChMixerChType *)base_mem_ptr; + + // offset by memory for outputChannels + base_mem_ptr += CH_MIXER_ALIGN_8_BYTE(sizeof(ChMixerChType) * (numOutputChannels)); + pState->dynState.pMixerMatrixL16Q14 = (int16 *)base_mem_ptr; + + // offset by memory for mixerMatrixL16Q14 + base_mem_ptr += CH_MIXER_ALIGN_8_BYTE(sizeof(int16) * (numInputChannels * numOutputChannels)); + pState->dynState.pInputStepMatrix = (int8 *)base_mem_ptr; + + return CH_MIXER_SUCCESS; +} + +/* +@brief Initialize Channel mixing algorithm +Based on the input and output channels, identify the input & output channel +configurations respectively + +@param pCMState : [out] Pointer to the state structure that must be initialized. +@param numInputChannels : [in] Number of input channels. +@param inputChannels : [in] Pointer to array that indicates what each input channel is +@param numOutputChannels : [in] Number of output channels. +@param outputChannels : [in] Pointer to array that indicates what each output channel is +@param dataBitWidth : [in] Bit-width of data + +Return values: A value indicate success or failure ( a different value for each + failure) +*/ + +ChMixerResultType ChMixerInitialize(void * pCMState, + uint32 mem_size, + uint32 numInputChannels, + ChMixerChType *inputChannels, + uint32 numOutputChannels, + ChMixerChType *outputChannels, + uint32 dataBitWidth) +{ + ChMixerStateStruct *pState = (ChMixerStateStruct *)pCMState; + + if (ChMixerInitMemory(pCMState, mem_size, numInputChannels, numOutputChannels)) + { + return CH_MIXER_EBAD_PARAM; + } + + // Store variables for future reference + copy_in_out_chan_params(pState, numInputChannels, inputChannels, numOutputChannels, outputChannels, dataBitWidth); + pState->isCustomCoeffValid = 0; + + pState->ptrCoeff = pState->dynState.pMixerMatrixL16Q14; + + // Apply paired input channel remapping rules first + // For e.g, Remap (LS,LB) pair channels in input to (LB,RLC) in output + ChMixerApplyPairedRemapRules(pState); + + // Apply channel remapping rules for single channel + ChMixerApplySingleChRemapRules(pState); + + ChMixerSetupCoefficients(pState); + + return CH_MIXER_SUCCESS; +} + +/* +@brief set param interface for Channel mixing algorithm +Based on the parameters set by client, update the channel mixer library accordingly. + +@param pCMState : [out] Pointer to the state structure that must be initialized. +@param numInputChannels : [in] Number of input channels. +@param inputChannels : [in] Pointer to array that indicates what each input channel is +@param numOutputChannels : [in] Number of output channels. +@param outputChannels : [in] Pointer to array that indicates what each output channel is +@param dataBitWidth : [in] Bit-width of data +@param pCustomCoeffSet : [in] Pointer to array of custom coefficients sent through set param + +Return values: A value indicate success or failure ( a different value for each + failure) +*/ +ChMixerResultType ChMixerSetParam(void * pCMState, + uint32 mem_size, + uint32 numInputChannels, + ChMixerChType *inputChannels, + uint32 numOutputChannels, + ChMixerChType *outputChannels, + uint32 dataBitWidth, + void * pCustomCoeffSet) +{ + + ChMixerResultType result = CH_MIXER_SUCCESS; + ChMixerStateStruct *pState = (ChMixerStateStruct *)pCMState; + + if (NULL != pCustomCoeffSet) + { + if (ChMixerInitMemory(pCMState, mem_size, numInputChannels, numOutputChannels)) + { + return CH_MIXER_EBAD_PARAM; + } + // Store variables for future reference + copy_in_out_chan_params(pState, numInputChannels, inputChannels, numOutputChannels, outputChannels, dataBitWidth); + pState->isCustomCoeffValid = 1; + pState->ptrCustomCoeffset = (int16 *)pCustomCoeffSet; + + result = ChMixerSetupCoefficients(pState); + } + else + { + result = ChMixerInitialize(pCMState, + mem_size, + numInputChannels, + inputChannels, + numOutputChannels, + outputChannels, + dataBitWidth); + } + return result; +} + +static void copy_in_out_chan_params(ChMixerStateStruct *pState, + uint32 numInputChannels, + ChMixerChType * inputChannels, + uint32 numOutputChannels, + ChMixerChType * outputChannels, + uint32 dataBitWidth) +{ + int32 chIndex; + pState->numInputCh = numInputChannels; + pState->numOutputCh = numOutputChannels; + pState->dataBitWidth = dataBitWidth; + pState->isTrivialCopy = 0; + + for (chIndex = 0; chIndex < numInputChannels; chIndex++) + { + pState->dynState.pInputChannels[chIndex] = inputChannels[chIndex]; + } + for (chIndex = 0; chIndex < numOutputChannels; chIndex++) + { + pState->dynState.pOutputChannels[chIndex] = outputChannels[chIndex]; + } +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ diff --git a/modules/processing/channel_mixer/lib/src/ChannelMixerLib_island.c b/modules/processing/channel_mixer/lib/src/ChannelMixerLib_island.c new file mode 100644 index 0000000..d4c837c --- /dev/null +++ b/modules/processing/channel_mixer/lib/src/ChannelMixerLib_island.c @@ -0,0 +1,189 @@ +/*============================================================================ + @file ChannelMixerLib.c + + Impelementation of the Channel Mixer algorithm. */ + +/*========================================================================= +Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. +SPDX-License-Identifier: BSD-3-Clause +========================================================================= */ + +/* +@brief Apply the Channel mixing algorithm + +@param pCMState : [in] Pointer to the state structure +@param input : [in] Multi channel input pointer +@param output : [out] Multi channel output pointer +@param numSamples : [in] Number of samples in the input +*/ + +#include "ChannelMixerLib.h" +#include "ChannelMixerRemapRules.h" +#include "audio_basic_op.h" + +void ChMixerTrivialCopy(ChMixerStateStruct *pState, void **output, void **input, uint32 numSamples); + +/* +@brief Copy the input to the output in the order specified in the Channel mixer + state (inputStepMatrix) + +@param pCMState : [in] Pointer to the state structure +@param input : [in] Multi channel input pointer +@param output : [out] Multi channel output pointer +@param numSamples : [in] Number of samples in the input +*/ +void ChMixerTrivialCopy(ChMixerStateStruct *pState, void **output, void **input, uint32 numSamples) +{ + uint32 sampleIndex, chIndex; + // Load the order in which the copy must be done + int8 *inputStep = pState->dynState.pInputStepMatrix; + + // In this function, number of output channels == number of input channels. + if (16 == pState->dataBitWidth) + { + int16 *input16[CH_MIXER_MAX_NUM_CH]; + int16 *output16[CH_MIXER_MAX_NUM_CH]; + for (chIndex = 0; (chIndex < pState->numOutputCh) && (chIndex < CH_MIXER_MAX_NUM_CH); chIndex++) + { + // Identify the input and output channel start addresses + output16[chIndex] = ((int16 **)output)[chIndex]; + input16[chIndex] = ((int16 **)input)[inputStep[chIndex]]; + for (sampleIndex = 0; sampleIndex < numSamples; sampleIndex++) + { + // Copy each channel + *output16[chIndex] = *input16[chIndex]; + output16[chIndex]++; + input16[chIndex]++; + } + } + } + else + { + int32 *input32[CH_MIXER_MAX_NUM_CH]; + int32 *output32[CH_MIXER_MAX_NUM_CH]; + for (chIndex = 0; (chIndex < pState->numOutputCh) && (chIndex < CH_MIXER_MAX_NUM_CH); chIndex++) + { + // Identify the input and output channel start addresses + output32[chIndex] = ((int32 **)output)[chIndex]; + input32[chIndex] = ((int32 **)input)[inputStep[chIndex]]; + for (sampleIndex = 0; sampleIndex < numSamples; sampleIndex++) + { + // Copy each channel + *output32[chIndex] = *input32[chIndex]; + output32[chIndex]++; + input32[chIndex]++; + } + } + } +} + + +/* +@brief Apply the Channel mixing algorithm + +@param pCMState : [in] Pointer to the state structure +@param input : [in] Multi channel input pointer +@param output : [out] Multi channel output pointer +@param numSamples : [in] Number of samples in the input +*/ +void ChMixerProcess(void *pCMState, void **output, void **input, uint32 numSamples) +{ + + uint32 sampleIndex; + uint32 outputChIndex; + uint32 inputChIndex; + int16 *pMatrixCoeffL16Q14; + // int16 *pMatrixCoeffStartL16Q14; + int8 * inputStep; + int32 tempL32, prodL32; + int64 tempL64, prodL64; + ChMixerStateStruct *pState = (ChMixerStateStruct *)pCMState; + + if (pState->isTrivialCopy) + { + ChMixerTrivialCopy(pCMState, output, input, numSamples); + return; + } + + if (16 == pState->dataBitWidth) + { + // Generate output one channel at a time. This helps in reducing cache thrashing + for (outputChIndex = 0; outputChIndex < pState->numOutputCh; outputChIndex++) + { + // Matrix coefficients of the row corresponding to the output channel + pMatrixCoeffL16Q14 = pState->ptrCoeff + pState->numInputCh * outputChIndex; + + for (sampleIndex = 0; sampleIndex < numSamples; sampleIndex++) + { + inputStep = &pState->dynState.pInputStepMatrix[outputChIndex * (pState->numInputCh + 1)]; + // output[output channel i][sample] = matrix[output channel i ][input channel j] * + // input[input channel j][sample] + // The above product is calculated for all input channels and the products are summed. + // The data ordering can be exploited to write memory access based on pointers so that + // the compiler can generate efficient code. For e.g + // (1) All the output samples for a channel are stored one after the other. Hence in the + // above equation, output[output channel i][sample] is just *output[channel]++ + // (2) Similarly for calculating the output samples for a channel, only the factors in + // a particular row in the matrix are required. Since C implements matrices row-wise, + // we can access these coefficients as matrixCoeff++ + // (3) Input samples of all the channels must be accessed even for calculating output samples + // for one channels. So we initialize a local array with starting addresses of all input + // channels and increment each one of them as required. + tempL32 = 0; + for (inputChIndex = *inputStep; inputChIndex < pState->numInputCh; inputChIndex = *(++inputStep)) + { + int16 *in_ch_data_ptr = (int16 *)input[inputChIndex]; + prodL32 = s32_mult_s16_s16(*(in_ch_data_ptr + sampleIndex), pMatrixCoeffL16Q14[inputChIndex]); + + // Maximum number of channels is 8, so this should avoid any overflow + // We right shift by 4 to provide enough guard bits. + prodL32 = s32_shr_s32(prodL32, 4); + tempL32 = s32_add_s32_s32(tempL32, prodL32); + } + // Do the remaining right-shift to get it back to same QFactor as input + int16 *out_ch_data_ptr = (int16 *)output[outputChIndex]; + *(out_ch_data_ptr + sampleIndex) = s16_saturate_s32(s32_shr_s32(tempL32, Q14_FACTOR - 4)); + } + } + } + else + { + // Generate output one channel at a time. This helps in reducing cache thrashing + for (outputChIndex = 0; outputChIndex < pState->numOutputCh; outputChIndex++) + { + // Calculate the starting address for the input channels + + // Matrix coefficients of the row corresponding to the output channel + pMatrixCoeffL16Q14 = pState->ptrCoeff + pState->numInputCh * outputChIndex; + + for (sampleIndex = 0; sampleIndex < numSamples; sampleIndex++) + { + inputStep = &pState->dynState.pInputStepMatrix[outputChIndex * (pState->numInputCh + 1)]; + // output[output channel i][sample] = matrix[output channel i ][input channel j] * + // input[input channel j][sample] + // The above product is calculated for all input channels and the products are summed. + // The data ordering can be exploited to write memory access based on pointers so that + // the compiler can generate efficient code. For e.g + // (1) All the output samples for a channel are stored one after the other. Hence in the + // above equation, output[output channel i][sample] is just *output[channel]++ + // (2) Similarly for calculating the output samples for a channel, only the factors in + // a particular row in the matrix are required. Since C implements matrices row-wise, + // we can access these coefficients as matrixCoeff++ + // (3) Input samples of all the channels must be accessed even for calculating output samples + // for one channels. So we initialize a local array with starting addresses of all input + // channels and increment each one of them as required. + tempL64 = 0; + for (inputChIndex = *inputStep; inputChIndex < pState->numInputCh; inputChIndex = *(++inputStep)) + { + int32 *in_ch_data_ptr = (int32 *)input[inputChIndex]; + prodL64 = s64_mult_s32_s16(*(in_ch_data_ptr + sampleIndex), pMatrixCoeffL16Q14[inputChIndex]); + // Maximum number of channels is 8, so there are enough bits to avoid overflow + tempL64 = s64_add_s64_s64(tempL64, prodL64); + } + // Do the remaining right-shift to get it back to same QFactor as input + int32 *out_ch_data_ptr = (int32 *)output[outputChIndex]; + *(out_ch_data_ptr + sampleIndex) = s32_saturate_s64(s64_shl_s64(tempL64, -Q14_FACTOR)); + } + } + } +} diff --git a/modules/processing/channel_mixer/lib/src/ChannelMixerRemapRules.c b/modules/processing/channel_mixer/lib/src/ChannelMixerRemapRules.c new file mode 100644 index 0000000..e02ad87 --- /dev/null +++ b/modules/processing/channel_mixer/lib/src/ChannelMixerRemapRules.c @@ -0,0 +1,214 @@ +/*============================================================================ + @file ChannelMixerRemapRules.c + + Rules that will be used for down-mixing/up-mixing */ + +/*========================================================================= +Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. +SPDX-License-Identifier: BSD-3-Clause +========================================================================= */ + +#include "ChannelMixerRemapRules.h" + +/* Paired re-mappings. + ------------------ +If the pair of channels (specified in the first two elements of a row) are +present in the input (and not present in the output), and if the pair of +channels (specified in the last two elements of a row in the array below) are +present in the output, then the corresponding remapping will be applied. +*/ +const PairedRemapRules PairedRemapRulesList[NUM_PAIRED_REMAP_RULES] = +{ + // Remap LS,LB channels in input to LB,RLC in output + {CH_MIXER_PCM_CH_LS, CH_MIXER_PCM_CH_LB, CH_MIXER_PCM_CH_LB, CH_MIXER_PCM_CH_RLC}, + // Remap RS,RB channels in input to RB,RRC in output + {CH_MIXER_PCM_CH_RS, CH_MIXER_PCM_CH_RB, CH_MIXER_PCM_CH_RB, CH_MIXER_PCM_CH_RRC}, +}; + +/* Setup array for remapping a single input channel. + ------------------------------------------------- +These rules have the following characteristics: +1. For every input channel, a rule is defined to remap a channel to one +channel or multiple channels. +2. There can be multiple such rules per input channel. Each of these rules are +arranged in the decreasing order (i.e) a rule can be applied only when any +rule above has not been satisfied. +3. If the input channel is present in the output as well, then none of these rules will +be applied. +*/ +// Remap list for Front left channel +const InfoPerRemapToCh L_C[1] = { { CH_MIXER_PCM_CH_C, 11588 } }; +const ChannelMap L_map[1] = {{1, L_C}}; + +// Remap list for Front Right channel +const InfoPerRemapToCh R_C[1] = { { CH_MIXER_PCM_CH_C, 11588 } }; +const ChannelMap R_map[1] = {{1, R_C}}; + +// Remap list for front Center channel +const InfoPerRemapToCh C_L_R[2] = { { CH_MIXER_PCM_CH_L, 11588 }, { CH_MIXER_PCM_CH_R, 11588 } }; +const ChannelMap C_map[1] = {{2, C_L_R}}; + +// Remap list for Left-surround channel +const InfoPerRemapToCh LS_LB[1] = { { CH_MIXER_PCM_CH_LB, 16384 } }; +const InfoPerRemapToCh LS_L[1] = { { CH_MIXER_PCM_CH_L, 11588 } }; +const InfoPerRemapToCh LS_C[1] = { { CH_MIXER_PCM_CH_C, 8192 } }; +const ChannelMap LS_map[3] ={ {1, LS_LB}, {1, LS_L}, {1, LS_C} }; + +// Remap list for Right-surround channel +const InfoPerRemapToCh RS_RB[1] = { { CH_MIXER_PCM_CH_RB, 16384 } }; +const InfoPerRemapToCh RS_R[1] = { { CH_MIXER_PCM_CH_R, 11588 } }; +const InfoPerRemapToCh RS_C[1] = { { CH_MIXER_PCM_CH_C, 8192 } }; +const ChannelMap RS_map[3] ={ {1, RS_RB}, {1, RS_R}, {1, RS_C} }; + +// Remap list for LFE-I +const InfoPerRemapToCh LFEI_L_R[2] = { {CH_MIXER_PCM_CH_L, 4096}, {CH_MIXER_PCM_CH_R, 4096} }; +const InfoPerRemapToCh LFEI_C[1] = { {CH_MIXER_PCM_CH_C, 4096} }; +const ChannelMap LFEI_map[2] = { {2, LFEI_L_R}, {1, LFEI_C} }; + +// Remap list for Left Back channel +const InfoPerRemapToCh LB_LS[1] = { { CH_MIXER_PCM_CH_LS, 16384 } }; +const InfoPerRemapToCh LB_L[1] = { {CH_MIXER_PCM_CH_L, 11588} }; +const InfoPerRemapToCh LB_C[1] = { {CH_MIXER_PCM_CH_C, 8192} }; +const ChannelMap LB_map[3] = {{1, LB_LS}, {1, LB_L}, {1, LB_C}, }; + +// Remap list for Right Back channel +const InfoPerRemapToCh RB_RS[1] = { { CH_MIXER_PCM_CH_RS, 16384 } }; +const InfoPerRemapToCh RB_R[1] = { {CH_MIXER_PCM_CH_R, 11588} }; +const InfoPerRemapToCh RB_C[1] = { {CH_MIXER_PCM_CH_C, 8192} }; +const ChannelMap RB_map[3] = { {1, RB_RS}, {1, RB_R}, {1, RB_C}, }; + +// Remap list for Center Surround +const InfoPerRemapToCh CS_LB_RB[2] = { {CH_MIXER_PCM_CH_LB, 11588}, {CH_MIXER_PCM_CH_RB, 11588} }; +const InfoPerRemapToCh CS_C[1] = { {CH_MIXER_PCM_CH_C, 8192} }; +const InfoPerRemapToCh CS_L_R[2] = { {CH_MIXER_PCM_CH_L, 8192}, {CH_MIXER_PCM_CH_R, 8192} }; +const ChannelMap CS_map[3] = { {2, CS_LB_RB}, {1, CS_C}, {2, CS_L_R} } ; + +// Remap list for Top Surround +const InfoPerRemapToCh TS_LB_RB[2] = { {CH_MIXER_PCM_CH_LB, 11588}, {CH_MIXER_PCM_CH_RB, 11588} }; +const InfoPerRemapToCh TS_C[1] = { {CH_MIXER_PCM_CH_C, 8192} }; +const InfoPerRemapToCh TS_L_R[2] = { {CH_MIXER_PCM_CH_L, 8192}, {CH_MIXER_PCM_CH_R, 8192} }; +const ChannelMap TS_map[3] = { {2, TS_LB_RB}, {1, TS_C}, {2, TS_L_R} } ; + +// Remap list for Top-Front-Center +const InfoPerRemapToCh TFC_C[1] = { {CH_MIXER_PCM_CH_C, 16384} }; +const InfoPerRemapToCh TFC_L_R[2] = { {CH_MIXER_PCM_CH_L, 11588}, {CH_MIXER_PCM_CH_R, 11588} }; +const ChannelMap TFC_map[2] = { {1, TFC_C}, {2, TFC_L_R} }; + +// Remap list for Front Left of Center +const InfoPerRemapToCh FLC_L_C[2] = { {CH_MIXER_PCM_CH_L, 8192}, {CH_MIXER_PCM_CH_C, 8192} }; +const InfoPerRemapToCh FLC_L[1] = { {CH_MIXER_PCM_CH_L, 16384} }; +const InfoPerRemapToCh FLC_C[1] = { {CH_MIXER_PCM_CH_C, 11588} }; +const ChannelMap FLC_map[3] = { {2, FLC_L_C}, {1, FLC_L}, {1, FLC_C} }; + +// Remap list for Front Right of Center +const InfoPerRemapToCh FRC_R_C[2] = { {CH_MIXER_PCM_CH_R, 8192}, {CH_MIXER_PCM_CH_C, 8192} }; +const InfoPerRemapToCh FRC_R[1] = { {CH_MIXER_PCM_CH_R, 16384} }; +const InfoPerRemapToCh FRC_C[1] = { {CH_MIXER_PCM_CH_C, 11588} }; +const ChannelMap FRC_map[3] = { {2, FRC_R_C}, {1, FRC_R}, {1, FRC_C} }; + +// Remap list for Rear Left of Center +const InfoPerRemapToCh RLC_LB[1] = { {CH_MIXER_PCM_CH_LB, 16384} }; +const InfoPerRemapToCh RLC_CS[1] = { {CH_MIXER_PCM_CH_CS, 11588} }; +const InfoPerRemapToCh RLC_L[1] = { {CH_MIXER_PCM_CH_L, 11588} }; +const InfoPerRemapToCh RLC_C[1] = { {CH_MIXER_PCM_CH_C, 8192} }; +const ChannelMap RLC_map[4] = { { 1, RLC_LB }, {1, RLC_CS}, {1, RLC_L}, {1, RLC_C} }; + +// Remap list for Rear Right of Center +const InfoPerRemapToCh RRC_RB[1] = { {CH_MIXER_PCM_CH_RB, 16384} }; +const InfoPerRemapToCh RRC_CS[1] = { {CH_MIXER_PCM_CH_CS, 11588} }; +const InfoPerRemapToCh RRC_R[1] = { {CH_MIXER_PCM_CH_R, 11588} }; +const InfoPerRemapToCh RRC_C[1] = { {CH_MIXER_PCM_CH_C, 8192} }; +const ChannelMap RRC_map[4] = { { 1, RRC_RB }, {1, RRC_CS}, {1, RRC_R}, {1, RRC_C} }; + +// Remap list for Top Center +const InfoPerRemapToCh TC_C_CS[2] = { {CH_MIXER_PCM_CH_C, 8192}, {CH_MIXER_PCM_CH_CS, 8192} }; +const InfoPerRemapToCh TC_C[1] = { {CH_MIXER_PCM_CH_C, 8192} }; +const InfoPerRemapToCh TC_L_R[2] = { {CH_MIXER_PCM_CH_L, 11588}, {CH_MIXER_PCM_CH_R, 11588} }; +const ChannelMap TC_map[3] = { {2, TC_C_CS}, {1, TC_C}, {2, TC_L_R} }; + +// Some Common Remappings +const InfoPerRemapToCh CommonC_C[1] = { {CH_MIXER_PCM_CH_C, 8192} }; +const InfoPerRemapToCh CommonC_L_R[2] = { {CH_MIXER_PCM_CH_L, 11588}, {CH_MIXER_PCM_CH_R, 11588} }; +const ChannelMap CommonC_map[2] = { {1, CommonC_C}, {2, CommonC_L_R} }; + +const InfoPerRemapToCh CommonL_L[1] = { {CH_MIXER_PCM_CH_L, 11588} }; +const InfoPerRemapToCh CommonL_C[1] = { {CH_MIXER_PCM_CH_C, 8192} }; +const ChannelMap CommonL_map[2] = { {1, CommonL_L}, {1, CommonL_C} }; + +const InfoPerRemapToCh CommonR_R[1] = { {CH_MIXER_PCM_CH_R, 11588} }; +const InfoPerRemapToCh CommonR_C[1] = { {CH_MIXER_PCM_CH_C, 8192} }; +const ChannelMap CommonR_map[2] = { {1, CommonR_R}, {1, CommonR_C} }; + +// Form the rules +const SingleChRemapRules SingleChRemapRulesList[CH_MIXER_PCM_CH_MAX_TYPE + 1] = +{ + {0, NULL }, + // Remap list for Front left channel + {1, L_map }, + // Remap list for Front Right channel + {1, R_map }, + // Remap list for front Center channel + {1, C_map }, + // Remap list for Left-surround channel + {3, LS_map }, + // Remap list for Right-surround channel + {3, RS_map }, + // Remap list for LFE-I + {2, LFEI_map }, + // Remap list for Center Surround + {3, CS_map }, + // Remap list of Left back/Rear Left + {3, LB_map }, + // Remap list for Right back/Rear Right channel + {3, RB_map }, + // Remap list for Top Surround + {3, TS_map }, + // Remap list for Top-Front-Center + {2, TFC_map }, + // Remap list for Mono Surround + {3, CS_map }, + // Remap list for Front Left of Center + {3, FLC_map }, + // Remap list for Front Right of Center + {3, FRC_map }, + // Remap list for Rear Left of Center + {4, RLC_map }, + // Remap list for Rear Right of Center + {4, RRC_map }, + // Remap list for LFE II + {2, CommonC_map }, + // Remap list for Side-Left + {2, CommonL_map }, + // Remap list for Side-Right + {2, CommonR_map }, + // Remap list for Top Front Left + {2, CommonL_map }, + // Remap list for Top Front Right + {2, CommonR_map }, + // Remap list for Top Center + {3, TC_map }, + // Remap list for Top Back Left + {2, CommonL_map }, + // Remap list for Top Back Right + {2, CommonR_map }, + // Remap list for Top Side Left + {2, CommonL_map }, + // Remap list for Top Side Right + {2, CommonR_map }, + // Remap list for Top Back Center + {2, CommonC_map }, + // Remap list for Bottom Front Center + {2, CommonC_map }, + // Remap list for Bottom Front Left + {2, CommonL_map }, + // Remap list for Bottom Front Right + {2, CommonR_map }, + // Remap list for Left - Wide + {2, CommonL_map }, + // Remap list for Right - Wide + {2, CommonR_map }, + // Remap list for Left Side Direct + {2, CommonL_map }, + // Remap list for Right Side Direct + {2, CommonR_map }, +}; diff --git a/modules/processing/channel_mixer/lib/src/ChannelMixerRemapRules.h b/modules/processing/channel_mixer/lib/src/ChannelMixerRemapRules.h new file mode 100644 index 0000000..0ca744f --- /dev/null +++ b/modules/processing/channel_mixer/lib/src/ChannelMixerRemapRules.h @@ -0,0 +1,65 @@ +#ifndef CHANNEL_MIXER_REMAP_RULES_H +#define CHANNEL_MIXER_REMAP_RULES_H + +/*============================================================================ + @file ChannelMixerRemapRules.h + + Channel Mixer Remapping Rules header */ + +/*========================================================================= +Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. +SPDX-License-Identifier: BSD-3-Clause +========================================================================= */ + +#include "AudioComdef.h" +#include "ar_msg.h" +#include "ChannelMixerLib.h" + +#define NUM_PAIRED_REMAP_RULES 2 +#define Q14_FACTOR 14 + +// Define Remapping rules for paired input channels (i.e) a particular pair of +// input channels must be remapped to a particular pair of channels. +typedef struct _PairedRemapRules +{ + // The pair of input channels + ChMixerChType fromCh1; + ChMixerChType fromCh2; + // The pair of output channels + ChMixerChType toCh1; + ChMixerChType toCh2; +} PairedRemapRules; + +extern const PairedRemapRules PairedRemapRulesList[]; + +// Define remapping rules for a single input channel. If the input channel is +// not present in the output, then one of the following rules (for that input +// channel) will be applied. The rules for that input channel are arranged in a +// decreasing order of priority. +typedef struct _InfoPerRemapToCh +{ + ChMixerChType remapToCh; + int16_t mixingParam; // in Q14 +} InfoPerRemapToCh; + +typedef struct _ChannelMap +{ + int32_t numToChPerRule; + const InfoPerRemapToCh *outMap; +} ChannelMap; + +typedef struct _SingleChRemapRules +{ + int32_t numRulesPerFromCh; + const ChannelMap *map; +} SingleChRemapRules; + +extern const SingleChRemapRules SingleChRemapRulesList[]; + +// The following steps must be taken to add a new rule for an input channel. Every rule can be divided into a set of +// "remap to" channels. +// 1. For each "remap to" channel define the *InfoPerRemapToCh* structure. +// 2. Use the *ChannelMap* structure to define the rule as a set of "remap to" channels. +// 3. Add the rule to the list of rules for the channel and update the *SingleChRemapRulesList* + +#endif // #ifndef CHANNEL_MIXER_REMAP_RULES_H diff --git a/modules/processing/channel_mixer/lib/stub/ChannelMixerLib.c b/modules/processing/channel_mixer/lib/stub/ChannelMixerLib.c new file mode 100644 index 0000000..905b93a --- /dev/null +++ b/modules/processing/channel_mixer/lib/stub/ChannelMixerLib.c @@ -0,0 +1,97 @@ +/*============================================================================ + @file ChannelMixerLib.c + + Implementation of the Channel Mixer algorithm. */ + +/*========================================================================= +Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. +SPDX-License-Identifier: BSD-3-Clause +========================================================================= */ + +#include "ChannelMixerLib.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/* +@brief Return the size of state structure required to operate one instance of +Channel mixer. + +@param pSize: [out] Pointer to memory location where size of structure must be +updated. +*/ +void ChMixerGetInstanceSize(uint32 *pSize) +{ + *pSize = 0; + return; +} + +/* +@brief Initialize Channel mixing algorithm +Based on the input and output channels, identify the input & output channel +configurations respectively + +@param pCMState : [out] Pointer to the state structure that must be initialized. +@param numInputChannels : [in] Number of input channels. +@param inputChannels : [in] Pointer to array that indicates what each input channel is +@param numOutputChannels : [in] Number of output channels. +@param outputChannels : [in] Pointer to array that indicates what each output channel is +@param dataBitWidth : [in] Bit-width of data + +Return values: A value indicate success or failure ( a different value for each + failure) +*/ + +ChMixerResultType ChMixerInitialize(void *pCMState, uint32 mem_size, uint32 numInputChannels, + ChMixerChType *inputChannels, uint32 numOutputChannels, + ChMixerChType *outputChannels, uint32 dataBitWidth) +{ + return CH_MIXER_ENOT_SUPPORTED; +} + + +/* +@brief set param interface for Channel mixing algorithm +Based on the parameters set by client, update the channel mixer library accordingly. + +@param pCMState : [out] Pointer to the state structure that must be initialized. +@param numInputChannels : [in] Number of input channels. +@param inputChannels : [in] Pointer to array that indicates what each input channel is +@param numOutputChannels : [in] Number of output channels. +@param outputChannels : [in] Pointer to array that indicates what each output channel is +@param dataBitWidth : [in] Bit-width of data +@param pCustomCoeffSet : [in] Pointer to array of custom coefficients sent through set param + +Return values: A value indicate success or failure ( a different value for each + failure) +*/ +ChMixerResultType ChMixerSetParam(void *pCMState, uint32 mem_size, uint32 numInputChannels, + ChMixerChType *inputChannels, uint32 numOutputChannels, + ChMixerChType *outputChannels, uint32 dataBitWidth, + void *pCustomCoeffSet) +{ + return CH_MIXER_ENOT_SUPPORTED; +} + + + +/* +@brief Apply the Channel mixing algorithm + +@param pCMState : [in] Pointer to the state structure +@param input : [in] Multi channel input pointer +@param output : [out] Multi channel output pointer +@param numSamples : [in] Number of samples in the input +*/ +void ChMixerProcess(void *pCMState, void **output, void **input, uint32 numSamples) +{ + return; +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + + diff --git a/modules/processing/filters/multi_stage_iir/api/api_msiir.h b/modules/processing/filters/multi_stage_iir/api/api_msiir.h new file mode 100644 index 0000000..5e94f3f --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/api/api_msiir.h @@ -0,0 +1,696 @@ +#ifndef API_MSIIR_H +#define API_MSIIR_H +/*============================================================================== + @file api_msiir.h + @brief This file contains API structures and param for the IIR Filter Module +==============================================================================*/ + +/*======================================================================= + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause +=========================================================================*/ + + /*------------------------------------------------------------------------ + * Include files + * -----------------------------------------------------------------------*/ +#include "module_cmn_api.h" + +/*============================================================================== + Constants +==============================================================================*/ +// stack size measured for 8 channel 5 stage +#define CAPI_MSIIR_STACK_SIZE 3072 + +#define CAPI_MSIIR_MAX_IN_PORTS 1 + +#define CAPI_MSIIR_MAX_OUT_PORTS 1 + +/** Maximum number of channels for the multichannel IIR tuning filter. */ +#ifdef PROD_SPECIFIC_MAX_CH +#define IIR_TUNING_FILTER_MAX_CHANNELS_V2 128 +#else +#define IIR_TUNING_FILTER_MAX_CHANNELS_V2 32 +#endif + +#define MODULE_ID_MSIIR 0x07001014 +/** + @h2xml_title1 {Multi-Stage IIR (MSIIR) Filter Module API} + @h2xml_title_agile_rev {Multi-Stage IIR (MSIIR) Filter Module API} + @h2xml_title_date {July 31, 2018} + */ + +/** @h2xmlm_module {"MODULE_ID_MSIIR", MODULE_ID_MSIIR} + @h2xmlm_displayName {"MS-IIR Filter"} + @h2xmlm_modSearchKeys {filter, Audio} + @h2xmlm_description {ID of the Multi-Channel Multi-Stage IIR Tuning Filter module.\n + + - This module supports the following parameter IDs\n + - #PARAM_ID_MSIIR_TUNING_FILTER_ENABLE\n + - #PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN\n + - #PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS\n + - #PARAM_ID_MODULE_ENABLE\n +* +* - Supported Input Media Format:\n +* - Data Format : FIXED_POINT\n +* - fmt_id : Don't care\n +* - Sample Rates : Any\n +* - Number of channels : 1 to 128 (for certain products this module supports only 32 channels)\n +* - Channel type : 1 to 128\n +* - Bits per sample : 16, 32\n +* - Q format : 15, 27\n +* - Interleaving : de-interleaved unpacked\n +* - Signed/unsigned : Signed } + + @h2xmlm_dataMaxInputPorts {CAPI_MSIIR_MAX_IN_PORTS} + @h2xmlm_dataInputPorts {IN=2} + @h2xmlm_dataMaxOutputPorts {CAPI_MSIIR_MAX_OUT_PORTS} + @h2xmlm_dataOutputPorts {OUT=1} + @h2xmlm_supportedContTypes {APM_CONTAINER_TYPE_SC, APM_CONTAINER_TYPE_GC} + @h2xmlm_isOffloadable {true} + @h2xmlm_stackSize {CAPI_MSIIR_STACK_SIZE} + @h2xmlm_ToolPolicy {Calibration} + + @{ <-- Start of the Module --> + */ + + +/* ID of the Multichannel IIR Tuning Filter Enable parameters used by + MODULE_ID_MSIIR. + */ +#define PARAM_ID_MSIIR_TUNING_FILTER_ENABLE 0x08001020 + +/* Structure for holding one channel type - IIR enable pair. */ + +/* + This structure immediately follows the param_id_msiir_enable_t + structure. + */ +/** @h2xmlp_subStruct */ +#include "spf_begin_pack.h" +#include "spf_begin_pragma.h" + +struct param_id_msiir_ch_enable_t +{ + uint32_t channel_mask_lsb; + /**< @h2xmle_description {Lower 32 bits of the channel mask. Each bit corresponds to channel map + from 1 (PCM_CHANNEL_L) to 31 (PCM_CHANNEL_LW). + Bit 0 is reserved and must be set to zero.\n + -#A set bit indicates that the filter corresponding to the set channel-maps are enabled. + Bit position of the channel-map is obtained by left shifting (1 (left shift) Channel_map) + } + @h2xmle_default {0xFFFFFFFE} */ + + + uint32_t channel_mask_msb; + /**< @h2xmle_description {Upper 32 bits of the channel mask. Each bit corresponds to channel map + from 32 (PCM_CHANNEL_RW) to 63 (PCM_CUSTOM_CHANNEL_MAP_16).\n + -#A set bit indicates that the filter corresponding to the set channel-maps are enabled. + Bit position of the channel-map is obtained by left shifting (1 (left shift) (Channel_map - 32)) + } + @h2xmle_default {0xFFFFFFFF} */ + + uint32_t enable_flag; + /**< @h2xmle_description {Specifies whether the above channels are enabled.} + @h2xmle_rangeList {"Disable"=0; + "Enable"=1} + @h2xmle_default {0} */ +} +#include "spf_end_pragma.h" +#include "spf_end_pack.h" +; + +typedef struct param_id_msiir_ch_enable_t param_id_msiir_ch_enable_t; + +/** @h2xmlp_parameter {"PARAM_ID_MSIIR_TUNING_FILTER_ENABLE", PARAM_ID_MSIIR_TUNING_FILTER_ENABLE} + @h2xmlp_description {ID of the Multichannel IIR Tuning Filter Enable parameters used by + MODULE_ID_MSIIR.\n Payload of the PARAM_ID_MSIIR_TUNING_FILTER_ENABLE + channel type/IIR enable pairs used by the Multiple Channel IIR Tuning + Filter module.\n + This structure immediately follows the param_id_msiir_enable_t + structure.\n} + @h2xmlp_toolPolicy {NO_SUPPORT} + @h2xmlx_expandStructs {false} + */ +#include "spf_begin_pack.h" +#include "spf_begin_pragma.h" +/* Payload of the PARAM_ID_MSIIR_TUNING_FILTER_ENABLE + parameters used by the Multiple Channel IIR Tuning Filter module. + */ +struct param_id_msiir_enable_t +{ + uint32_t num_config; + /**< @h2xmle_description {Number of configurations for which enable flags are provided.\n} + @h2xmle_range {1..63} + @h2xmle_default {1} */ + + param_id_msiir_ch_enable_t enable_flag_settings[0]; + /**< @h2xmle_description {multi_ch_iir_cfg.} + @h2xmle_variableArraySize {num_config} + @h2xmle_default {0} */ +} +#include "spf_end_pragma.h" +#include "spf_end_pack.h" +; + +/* Structure for the multichannel IIR enable command */ +typedef struct param_id_msiir_enable_t param_id_msiir_enable_t; + + +/* ID of the Multiple Channel IIR Tuning Filter Pregain parameters used by + */ +#define PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN 0x08001021 + +/* Structure for holding one channel type - IIR pregain pair. */ + +/** @h2xmlp_subStruct */ +#include "spf_begin_pack.h" +#include "spf_begin_pragma.h" + +struct param_id_msiir_ch_pregain_t +{ + uint32_t channel_mask_lsb; + /**< @h2xmle_description {Lower 32 bits of the channel mask. Each bit corresponds to channel map + from 1 (PCM_CHANNEL_L) to 31 (PCM_CHANNEL_LW). + Bit 0 is reserved and must be set to zero.\n + -#A set bit indicates that the pregain is set on the corresponding channel-maps. + Bit position of the channel-map is obtained by left shifting (1 (left shift) Channel_map) + } + @h2xmle_default {0xFFFFFFFE} */ + + + uint32_t channel_mask_msb; + /**< @h2xmle_description {Upper 32 bits of the channel mask. Each bit corresponds to channel map + from 32 (PCM_CHANNEL_RW) to 63 (PCM_CUSTOM_CHANNEL_MAP_16).\n + -#A set bit indicates that the pregain is set on the corresponding channel-maps. + Bit position of the channel-map is obtained by left shifting (1 (left shift) (Channel_map - 32)) + } + @h2xmle_default {0xFFFFFFFF} */ + + int32_t pregain; + /**< @h2xmle_description {Pregain of the above channels (in Q27 format).} + @h2xmle_default {0x08000000} + @h2xmle_dataFormat {Q27} */ + +} +#include "spf_end_pragma.h" +#include "spf_end_pack.h" + ; + +typedef struct param_id_msiir_ch_pregain_t param_id_msiir_ch_pregain_t; + +/** @h2xmlp_parameter {"PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN", PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN} + @h2xmlp_description {Payload of the PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN + channel type/IIR pregain pairs used by the Multiple Channel IIR Tuning + Filter module.\n + This structure immediately follows the param_id_msiir_pregain__cfg_t + structure.\n} + @h2xmlp_toolPolicy {NO_SUPPORT} + @h2xmlx_expandStructs {false} */ +#include "spf_begin_pack.h" +#include "spf_begin_pragma.h" + +/* Payload of the PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN + parameters used by the Multiple Channel IIR Tuning Filter module. + */ +struct param_id_msiir_pregain_t +{ + uint32_t num_config; + /**< @h2xmle_description {Number of pregain configurations provided} + @h2xmle_range {1..63} + @h2xmle_default {1} */ + + param_id_msiir_ch_pregain_t pre_gain_settings[0]; + /**< @h2xmle_description {pre_gain_settings.} + @h2xmle_variableArraySize {num_config} */ +} +#include "spf_end_pragma.h" +#include "spf_end_pack.h" +; + +/* Structure for the multichannel IIR pregain command */ +typedef struct param_id_msiir_pregain_t param_id_msiir_pregain_t; + + +#define PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS 0x08001022 + +/** @h2xmlp_subStruct */ + +#include "spf_begin_pack.h" +#include "spf_begin_pragma.h" +struct param_id_msiir_ch_filter_config_t +{ + uint32_t channel_mask_lsb; + /**< @h2xmle_description {Lower 32 bits of the channel mask. Each bit corresponds to channel map + from 1 (PCM_CHANNEL_L) to 31 (PCM_CHANNEL_LW). + Bit 0 is reserved and must be set to zero.\n + -#A set bit indicates that the filter corresponding to the set channel-maps are configured. + Bit position of the channel-map is obtained by left shifting (1 (left shift) Channel_map) + } + @h2xmle_default {0xFFFFFFFE} */ + + + uint32_t channel_mask_msb; + /**< @h2xmle_description {Upper 32 bits of the channel mask. Each bit corresponds to channel map + from 32 (PCM_CHANNEL_RW) to 63 (PCM_CUSTOM_CHANNEL_MAP_16).\n + -#A set bit indicates that the filter corresponding to the set channel-maps are configured. + Bit position of the channel-map is obtained by left shifting (1 (left shift) (Channel_map - 32)) + } + @h2xmle_default {0xFFFFFFFF} */ + + uint16_t reserved; + /**< @h2xmle_description {Clients must set this field to 0.} + @h2xmle_rangeList {"0"=0} + @h2xmle_readOnly {true} + */ + + uint16_t num_biquad_stages; + /**< @h2xmle_description {Number of biquad-IIR bands.} + @h2xmle_range {0..20} + @h2xmle_default {1} + */ + + int32_t filter_coeffs[0]; + /**< @h2xmle_description {filter_coeffs} + @h2xmlx_expandArray {true} + @h2xmle_dataFormat {Q30} + @h2xmle_defaultList {0x40000000, 0, 0, 0, 0} + @h2xmle_policy {advanced} + @h2xmle_variableArraySize { "5*num_biquad_stages" } */ + +#if defined(__H2XML__) || defined(DOXYGEN_ONLY) + int16_t num_shift_factor[0]; +/**< @h2xmle_description {num_shift_factor} + @h2xmlx_expandArray {true} + @h2xmle_default {0x02} +@h2xmle_policy {advanced} + @h2xmle_variableArraySize {"num_biquad_stages" } */ +#endif // __H2XML__ || DOXYGEN_ONLY +} +#include "spf_end_pragma.h" +#include "spf_end_pack.h" +; + +typedef struct param_id_msiir_ch_filter_config_t param_id_msiir_ch_filter_config_t; + +/** @h2xmlp_parameter {"PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS", PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS} + @h2xmlp_description {Payload of the #PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS + parameters used by the Multichannel IIR Tuning Filter module.\nThis structure is followed by the multichannel IIR + filter coefficients as follows\n + - Channel type/configuration pairs - See the Payload format table.\n + - Sequence of int32 filter_coeffs - Five coefficients for each band, each + in int32 format in the order of b0, b1, b2, a1, a2.\n + - Sequence of int16 num_shift_factor - One int16 per band. The numerator + shift factor is related to the Q factor of the filter coefficients b0, + b1, b2.\n\n + There must be one data sequence per channel.\n + If the number of bands is odd, there must be an extra 16-bit padding by + the end of the numerator shift factors. This extra 16-bit padding makes + the entire structure 32-bit aligned. The padding bits must be all zeros.\n} + @h2xmlp_toolPolicy {NO_SUPPORT} + @h2xmlx_expandStructs {false} + */ +#include "spf_begin_pack.h" +#include "spf_begin_pragma.h" + +/* Payload of the #PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS + parameters used by the Multichannel IIR Tuning Filter module. + */ +struct param_id_msiir_config_t +{ + uint32_t num_config; + /**< @h2xmle_description {Number of channels for which enable flags are provided.} + @h2xmle_range {1..63} + @h2xmle_default {1} */ + + param_id_msiir_ch_filter_config_t multi_ch_iir_cfg[0]; + /**< @h2xmle_description {multi_ch_iir_cfg.} + @h2xmle_variableArraySize {num_config} */ +} +#include "spf_end_pragma.h" +#include "spf_end_pack.h" +; + +/* Structure for the multichannel IIR config params */ +typedef struct param_id_msiir_config_t param_id_msiir_config_t; + +/* ID of the Multichannel IIR Tuning Filter Enable parameters used by + MODULE_ID_MSIIR. + */ +#define PARAM_ID_MSIIR_TUNING_FILTER_ENABLE_V2 0x8001A8A + +/* Structure for holding one channel type - IIR enable pair. */ + +/* + This structure immediately follows the param_id_msiir_enable_t + structure. + */ +/** @h2xmlp_subStruct */ +#include "spf_begin_pack.h" +#include "spf_begin_pragma.h" + +typedef struct param_id_msiir_ch_enable_v2_t +{ + uint32_t channel_type_group_mask; + /**< @h2xmle_description {Indicates the mask for channel_type_mask_list array. + Each bit in channel_type_group_mask corresponds to a channel group. + Read as + Bit 0 corresponds to channel group 1, which includes channel map for channels 1-31. + Bit 1 corresponds to channel group 2, which includes channel map for channels 32-63. + Bit 2 corresponds to channel group 3, which includes channel map for channels 64-95. + Bit 3 corresponds to channel group 4, which includes channel map for channels 96-127. + Bit 4 corresponds to channel group 5, which includes channel map for channels 128-159. + A set bit (1) in channel_type_group_mask indicates that the channels in that channel group are configured. + } + @h2xmle_range {0..31} + @h2xmle_default {0x00000003} */ + + + uint32_t channel_type_mask_list[0]; + /**< @h2xmle_description {An array used to configure the channels for different channel groups. The array size depends on the number of + bits set in channel_type_group_mask.\n + For group 1, each bit of channel_type_mask_list corresponds to channel map from 1 (PCM_CHANNEL_L) to 31 (PCM_CHANNEL_LW).\n + Bit 0 of group 1 channel_type_mask_list is reserved and must always be set to zero.\n + For any other group, each bit of channel_type_mask_list corresponds to channel map from [32(group_no -1) to 32(group_no)-1].\n + Bit position of the channel-map for channel_type_mask_list of defined group is obtained by left shifting (1 (left shift) Channel_map%32 + } + @h2xmle_variableArraySizeFunction {GET_SET_BITS_COUNT, channel_type_group_mask} + @h2xmle_copySrcList {channel_mask_lsb, channel_mask_msb} + @h2xmle_defaultList {0xfffffffe, 0xffffffff} */ + uint32_t enable_flag; + /**< @h2xmle_description {Specifies whether the above channels are enabled.} + @h2xmle_copySrc {enable_flag} + @h2xmle_rangeList {"Disable"=0; + "Enable"=1} + @h2xmle_default {0} */ +} param_id_msiir_ch_enable_v2_t +#include "spf_end_pragma.h" +#include "spf_end_pack.h" +; + +/** @h2xmlp_parameter {"PARAM_ID_MSIIR_TUNING_FILTER_ENABLE_V2", PARAM_ID_MSIIR_TUNING_FILTER_ENABLE_V2} + @h2xmlp_copySrc {0x08001020} + @h2xmlp_description {ID of the Multichannel IIR Tuning Filter Enable parameters used by + MODULE_ID_MSIIR.\n Payload of the PARAM_ID_MSIIR_TUNING_FILTER_ENABLE_V2 + channel type/IIR enable pairs used by the Multiple Channel IIR Tuning + Filter module.\n + This structure immediately follows the param_id_msiir_enable_v2_t + structure.\n} + @h2xmlp_toolPolicy {RTC, Calibration} + @h2xmlx_expandStructs {false} + */ +#include "spf_begin_pack.h" +#include "spf_begin_pragma.h" +/* Payload of the PARAM_ID_MSIIR_TUNING_FILTER_ENABLE_V2 + parameters used by the Multiple Channel IIR Tuning Filter module. + */ +struct param_id_msiir_enable_v2_t +{ + uint32_t num_config; + /**< @h2xmle_description {Number of configurations for which enable flags are provided.} + @h2xmle_copySrc {num_config} + @h2xmle_range {1..MODULE_CMN_MAX_CHANNEL} + @h2xmle_default {1} */ +#ifdef __H2XML__ + param_id_msiir_ch_enable_v2_t enable_flag_settings[0]; + /**< @h2xmle_description {multi_ch_iir_cfg.} + @h2xmle_variableArraySize {num_config} + @h2xmle_default {0} */ +#endif +} +#include "spf_end_pragma.h" +#include "spf_end_pack.h" +; + +/* Structure for the multichannel IIR enable command */ +typedef struct param_id_msiir_enable_v2_t param_id_msiir_enable_v2_t; + + +/* ID of the Multiple Channel IIR Tuning Filter Pregain parameters used by + */ +#define PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN_V2 0x8001A8B + +/* Structure for holding one channel type - IIR pregain pair. */ + +/** @h2xmlp_subStruct */ +#include "spf_begin_pack.h" +#include "spf_begin_pragma.h" + +typedef struct param_id_msiir_ch_pregain_v2_t +{ + uint32_t channel_type_group_mask; + /**< @h2xmle_description {Indicates the mask for channel_type_mask_list array. + Each bit in channel_type_group_mask corresponds to a channel group. + Read as + Bit 0 corresponds to channel group 1, which includes channel map for channels 1-31. + Bit 1 corresponds to channel group 2, which includes channel map for channels 32-63. + Bit 2 corresponds to channel group 3, which includes channel map for channels 64-95. + Bit 3 corresponds to channel group 4, which includes channel map for channels 96-127. + Bit 4 corresponds to channel group 5, which includes channel map for channels 128-159. + A set bit (1) in channel_type_group_mask indicates that the channels in that channel group are configured. + } + @h2xmle_range {0..31} + @h2xmle_default {0x00000003} */ + + + uint32_t channel_type_mask_list[0]; + /**< @h2xmle_description {An array used to configure the channels for different channel groups. The array size depends on the number of + bits set in channel_type_group_mask.\n + For group 1, each bit of channel_type_mask_list corresponds to channel map from 1 (PCM_CHANNEL_L) to 31 (PCM_CHANNEL_LW).\n + Bit 0 of group 1 channel_type_mask_list is reserved and must always be set to zero.\n + For any other group, each bit of channel_type_mask_list corresponds to channel map from [32(group_no -1) to 32(group_no)-1].\n + Bit position of the channel-map for channel_type_mask_list of defined group is obtained by left shifting (1 (left shift) Channel_map%32 + } + @h2xmle_variableArraySizeFunction {GET_SET_BITS_COUNT, channel_type_group_mask} + @h2xmle_copySrcList {channel_mask_lsb, channel_mask_msb} + @h2xmle_defaultList {0xfffffffe, 0xffffffff} */ + + int32_t pregain; + /**< @h2xmle_description {Pregain of the above channels (in Q27 format).} + @h2xmle_copySrc {pregain} + @h2xmle_default {0x08000000} + @h2xmle_dataFormat {Q27} */ + +} param_id_msiir_ch_pregain_v2_t +#include "spf_end_pragma.h" +#include "spf_end_pack.h" + ; + +/** @h2xmlp_parameter {"PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN_V2", PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN_V2} + @h2xmlp_copySrc {0x08001021} + @h2xmlp_description {Payload of the PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN_V2 + channel type/IIR pregain pairs used by the Multiple Channel IIR Tuning + Filter module.\n + This structure immediately follows the param_id_msiir_pregain_cfg_v2_t + structure.\n} + @h2xmlp_toolPolicy {RTC, Calibration} + @h2xmlx_expandStructs {false} */ +#include "spf_begin_pack.h" +#include "spf_begin_pragma.h" + +/* Payload of the PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN_V2 + parameters used by the Multiple Channel IIR Tuning Filter module. + */ +struct param_id_msiir_pregain_v2_t +{ + uint32_t num_config; + /**< @h2xmle_description {Number of pregain configurations provided} + @h2xmle_copySrc {num_config} + @h2xmle_range {1..MODULE_CMN_MAX_CHANNEL} + @h2xmle_default {1} */ +#ifdef __H2XML__ + param_id_msiir_ch_pregain_v2_t pre_gain_settings[0]; + /**< @h2xmle_description {pre_gain_settings.} + @h2xmle_variableArraySize {num_config} */ +#endif +} +#include "spf_end_pragma.h" +#include "spf_end_pack.h" +; +/* Structure for the multichannel IIR pregain command */ +typedef struct param_id_msiir_pregain_v2_t param_id_msiir_pregain_v2_t; + + +#define PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS_V2 0x8001A8C + +/** @h2xmlp_subStruct */ + +#include "spf_begin_pack.h" +#include "spf_begin_pragma.h" +typedef struct param_id_msiir_ch_filter_config_v2_t +{ + uint32_t channel_type_group_mask; + /**< @h2xmle_description {Indicates the mask for channel_type_mask_list array. + Each bit in channel_type_group_mask corresponds to a channel group. + Read as + Bit 0 corresponds to channel group 1, which includes channel map for channels 1-31. + Bit 1 corresponds to channel group 2, which includes channel map for channels 32-63. + Bit 2 corresponds to channel group 3, which includes channel map for channels 64-95. + Bit 3 corresponds to channel group 4, which includes channel map for channels 96-127. + Bit 4 corresponds to channel group 5, which includes channel map for channels 128-159. + A set bit (1) in channel_type_group_mask indicates that the channels in that channel group are configured. + } + @h2xmle_range {0..31} + @h2xmle_default {0x00000003} */ + + + uint32_t channel_type_mask_list[0]; + /**< @h2xmle_description {An array used to configure the channels for different channel groups. The array size depends on the number of + bits set in channel_type_group_mask.\n + For group 1, each bit of channel_type_mask_list corresponds to channel map from 1 (PCM_CHANNEL_L) to 31 (PCM_CHANNEL_LW).\n + Bit 0 of group 1 channel_type_mask_list is reserved and must always be set to zero.\n + For any other group, each bit of channel_type_mask_list corresponds to channel map from [32(group_no -1) to 32(group_no)-1].\n + Bit position of the channel-map for channel_type_mask_list of defined group is obtained by left shifting (1 (left shift) Channel_map%32 + } + @h2xmle_variableArraySizeFunction {GET_SET_BITS_COUNT, channel_type_group_mask} + @h2xmle_copySrcList {channel_mask_lsb, channel_mask_msb} + @h2xmle_defaultList {0xfffffffe, 0xffffffff} */ + + uint16_t reserved; + /**< @h2xmle_description {Clients must set this field to 0.} + @h2xmle_copySrc {reserved} + @h2xmle_rangeList {"0"=0} + @h2xmle_readOnly {true} + */ + + uint16_t num_biquad_stages; + /**< @h2xmle_description {Number of biquad-IIR bands.} + @h2xmle_copySrc {num_biquad_stages} + @h2xmle_range {0..20} + @h2xmle_default {1} + */ + + int32_t filter_coeffs[0]; + /**< @h2xmle_description {filter_coeffs} + @h2xmle_copySrc {filter_coeffs} + @h2xmlx_expandArray {true} + @h2xmle_dataFormat {Q30} + @h2xmle_defaultList {0x40000000, 0, 0, 0, 0} + @h2xmle_policy {advanced} + @h2xmle_variableArraySize { "5*num_biquad_stages" } */ + +#if defined(__H2XML__) || defined(DOXYGEN_ONLY) + int16_t num_shift_factor[0]; +/**< @h2xmle_description {num_shift_factor} + @h2xmle_copySrc {num_shift_factor} + @h2xmlx_expandArray {true} + @h2xmle_default {0x02} + @h2xmle_policy {advanced} + @h2xmle_variableArraySize {"num_biquad_stages" } */ +#endif // __H2XML__ || DOXYGEN_ONLY +} param_id_msiir_ch_filter_config_v2_t +#include "spf_end_pragma.h" +#include "spf_end_pack.h" +; + +/** @h2xmlp_parameter {"PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS_V2", PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS_V2} + @h2xmlp_copySrc {0x08001022} + @h2xmlp_description {Payload of the #PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS_V2 + parameters used by the Multichannel IIR Tuning Filter module.\nThis structure is followed by the multichannel IIR + filter coefficients as follows\n + - Channel type/configuration pairs - See the Payload format table.\n + - Sequence of int32 filter_coeffs - Five coefficients for each band, each + in int32 format in the order of b0, b1, b2, a1, a2.\n + - Sequence of int16 num_shift_factor - One int16 per band. The numerator + shift factor is related to the Q factor of the filter coefficients b0, + b1, b2.\n\n + There must be one data sequence per channel.\n + If the number of bands is odd, there must be an extra 16-bit padding by + the end of the numerator shift factors. This extra 16-bit padding makes + the entire structure 32-bit aligned. The padding bits must be all zeros.\n} + @h2xmlp_toolPolicy {RTC, Calibration} + @h2xmlx_expandStructs {false} + */ +#include "spf_begin_pack.h" +#include "spf_begin_pragma.h" + +/* Payload of the #PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS_V2 + parameters used by the Multichannel IIR Tuning Filter module. + */ +struct param_id_msiir_config_v2_t +{ + uint32_t num_config; + /**< @h2xmle_description {Number of channels for which enable flags are provided.} + @h2xmle_copySrc {num_config} + @h2xmle_range {1..MODULE_CMN_MAX_CHANNEL} + @h2xmle_default {1} */ +#ifdef __H2XML__ + param_id_msiir_ch_filter_config_v2_t multi_ch_iir_cfg[0]; + /**< @h2xmle_description {multi_ch_iir_cfg.} + @h2xmle_variableArraySize {num_config} */ +#endif +} +#include "spf_end_pragma.h" +#include "spf_end_pack.h" +; +/* Structure for the multichannel IIR config params */ +typedef struct param_id_msiir_config_v2_t param_id_msiir_config_v2_t; + + + +/** + @h2xml_Select {param_id_msiir_enable_t} + @h2xmlm_InsertParameter +*/ + +/** + @h2xml_Select {param_id_msiir_pregain_t} + @h2xmlm_InsertParameter + +*/ + +/** + @h2xml_Select {param_id_msiir_config_t} + @h2xmlm_InsertParameter +*/ + +/** + @h2xml_Select {param_id_msiir_ch_pregain_t} + @h2xmlm_InsertParameter +*/ + +/** + @h2xml_Select {param_id_msiir_ch_enable_t} + @h2xmlm_InsertParameter +*/ + +/** + @h2xml_Select {param_id_msiir_ch_filter_config_t} + @h2xmlm_InsertParameter +*/ + +/** + @h2xml_Select {param_id_module_enable_t} + @h2xmlm_InsertParameter +*/ +/** + @h2xml_Select {param_id_msiir_enable_v2_t} + @h2xmlm_InsertParameter +*/ + +/** + @h2xml_Select {param_id_msiir_pregain_v2_t} + @h2xmlm_InsertParameter + +*/ + +/** + @h2xml_Select {param_id_msiir_config_v2_t} + @h2xmlm_InsertParameter +*/ +/** + @h2xml_Select {param_id_msiir_ch_pregain_v2_t} + @h2xmlm_InsertParameter +*/ + +/** + @h2xml_Select {param_id_msiir_ch_enable_v2_t} + @h2xmlm_InsertParameter +*/ + +/** + @h2xml_Select {param_id_msiir_ch_filter_config_v2_t} + @h2xmlm_InsertParameter +*/ +/** @} <-- End of the Module -->*/ + +#endif // API_MSIIR_H diff --git a/modules/processing/filters/multi_stage_iir/build/CMakeLists.txt b/modules/processing/filters/multi_stage_iir/build/CMakeLists.txt new file mode 100644 index 0000000..d570678 --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/build/CMakeLists.txt @@ -0,0 +1,44 @@ +#[[ + @file CMakeLists.txt + + @brief + + @copyright + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause +]] +cmake_minimum_required(VERSION 3.10) + +set(msiir_sources + ${LIB_ROOT}/capi/src/capi_multistageiir.cpp + ${LIB_ROOT}/capi/src/capi_multistageiir_utils.cpp + ${LIB_ROOT}/capi/src/capi_multistageiir_utils_v2.cpp + ${LIB_ROOT}/lib/src/CMultiStageIIR.c + ${LIB_ROOT}/lib/src/msiir.c +) + +set(msiir_includes + ${LIB_ROOT}/api + ${LIB_ROOT}/capi/inc + ${LIB_ROOT}/capi/src + ${LIB_ROOT}/lib/inc + ${LIB_ROOT}/lib/src + ../../../../cmn/common/utils/inc/ + ../../../../../fwk/spf/interfaces/module/shared_lib_api/inc/generic +) + +spf_module_sources( + KCONFIG CONFIG_MSIIR + NAME msiir + MAJOR_VER 1 + MINOR_VER 0 + AMDB_ITYPE "capi" + AMDB_MTYPE "PP" + AMDB_MID "0x07001014" + AMDB_TAG "capi_multistageiir" + AMDB_MOD_NAME "MODULE_ID_MSIIR" + SRCS ${msiir_sources} + INCLUDES ${msiir_includes} + H2XML_HEADERS "${LIB_ROOT}/api/api_msiir.h" + CFLAGS "" +) diff --git a/modules/processing/filters/multi_stage_iir/capi/inc/capi_multistageiir.h b/modules/processing/filters/multi_stage_iir/capi/inc/capi_multistageiir.h new file mode 100644 index 0000000..49d790c --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/capi/inc/capi_multistageiir.h @@ -0,0 +1,64 @@ +/* ======================================================================== */ +/** +@file capi_multistageiir.h + + Header file to implement the CAPI Interface for Multi-Stage IIR filter + object (MSIIR). +*/ + +/* ========================================================================= + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + ========================================================================== */ + +/*------------------------------------------------------------------------ + * Include files + * -----------------------------------------------------------------------*/ +#ifndef __CAPI_MULTISTAGEIIR_H +#define __CAPI_MULTISTAGEIIR_H + +#include "capi.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * Get static properties of multistage IIR module such as + * memory, stack requirements etc. + * See Elite_CAPI.h for more details. + */ +capi_err_t capi_multistageiir_get_static_properties( + capi_proplist_t* init_set_properties, + capi_proplist_t* static_properties); + +/** + * Instantiates(and allocates) the module memory. + * See Elite_CAPI.h for more details. + */ +capi_err_t capi_multistageiir_init( + capi_t* _pif, + capi_proplist_t* init_set_properties); + +capi_err_t capi_multistageiir_left_init( + capi_t* _pif, + capi_proplist_t* init_set_properties); + +capi_err_t capi_multistageiir_right_init( + capi_t* _pif, + capi_proplist_t* init_set_properties); + +capi_err_t capi_multistageiir_left_get_static_properties( + capi_proplist_t* init_set_properties, + capi_proplist_t* static_properties); + +capi_err_t capi_multistageiir_right_get_static_properties( + capi_proplist_t* init_set_properties, + capi_proplist_t* static_properties); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif //__CAPI_MULTISTAGEIIR_H + diff --git a/modules/processing/filters/multi_stage_iir/capi/src/capi_multistageiir.cpp b/modules/processing/filters/multi_stage_iir/capi/src/capi_multistageiir.cpp new file mode 100644 index 0000000..1bdb4c6 --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/capi/src/capi_multistageiir.cpp @@ -0,0 +1,743 @@ +/* ======================================================================== */ +/* +@file capi_multistageiir.cpp + + Source file to implement the Audio Post Processor Interface for Multi-Stage + IIR filters +*/ + +/* ========================================================================= + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + ========================================================================= */ + +/*------------------------------------------------------------------------ + * Include files + * -----------------------------------------------------------------------*/ +#include "capi_multistageiir_utils.h" +/*------------------------------------------------------------------------ + * Static declarations + * -----------------------------------------------------------------------*/ +static capi_err_t capi_multistageiir_process(capi_t * _pif, + capi_stream_data_t *input[], + capi_stream_data_t *output[]); + +static capi_err_t capi_multistageiir_end(capi_t *_pif); + +static capi_err_t capi_multistageiir_set_param(capi_t * _pif, + uint32_t param_id, + const capi_port_info_t *port_info_ptr, + capi_buf_t * params_ptr); + +static capi_err_t capi_multistageiir_get_param(capi_t * _pif, + uint32_t param_id, + const capi_port_info_t *port_info_ptr, + capi_buf_t * params_ptr); + +static capi_err_t capi_multistageiir_set_properties(capi_t *_pif, capi_proplist_t *props_ptr); + +static capi_err_t capi_multistageiir_get_properties(capi_t *_pif, capi_proplist_t *props_ptr); + +static const capi_vtbl_t capi_msiir_vtbl = { capi_multistageiir_process, + capi_multistageiir_end, + capi_multistageiir_set_param, + capi_multistageiir_get_param, + capi_multistageiir_set_properties, + capi_multistageiir_get_properties }; + +/*------------------------------------------------------------------------ + Function name: capi_multistageiir_process + Processes an input buffer and generates an output buffer. + * -----------------------------------------------------------------------*/ +static capi_err_t capi_multistageiir_process(capi_t * _pif, + capi_stream_data_t *input[], + capi_stream_data_t *output[]) +{ + capi_err_t result = CAPI_EOK; + capi_multistageiir_t *me = (capi_multistageiir_t *)(_pif); + + POSAL_ASSERT(me); + POSAL_ASSERT(input[0]); + POSAL_ASSERT(output[0]); + POSAL_ASSERT(me->media_fmt[0].format.num_channels <= input[0]->bufs_num); + POSAL_ASSERT(me->media_fmt[0].format.num_channels <= output[0]->bufs_num); + + void *inp_ptr[IIR_TUNING_FILTER_MAX_CHANNELS_V2] = { NULL }; + void *out_ptr[IIR_TUNING_FILTER_MAX_CHANNELS_V2] = { NULL }; + + // calculate number of samples for first channel + uint32_t num_samples = 0; + uint32_t shift_factor = 1; + + if (16 == me->msiir_static_vars.data_width) + { + shift_factor = 1; // samples are 16 bit, so divide by 2 + } + else if (32 == me->msiir_static_vars.data_width) + { + shift_factor = 2; // samples are 32 bit, so divide by 4 + } + + num_samples = capi_msiir_s32_min_s32_s32(input[0]->buf_ptr[0].actual_data_len >> shift_factor, + output[0]->buf_ptr[0].max_data_len >> shift_factor); + + for (uint32_t ch = 0; ch < me->media_fmt[0].format.num_channels; ch++) + { + inp_ptr[ch] = (void *)(input[0]->buf_ptr[ch].data_ptr); + out_ptr[ch] = (void *)(output[0]->buf_ptr[ch].data_ptr); + } + if (me->start_cross_fade) + { + result = capi_msiir_start_cross_fade(me); + if (CAPI_EOK != result) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : cross fade set param failed"); + } + } + // the library will loop through all the channels, and if the IIR are not enabled, the + // library will copy_input_to_output + for (uint32_t ch = 0; ch < me->media_fmt[0].format.num_channels; ch++) + { + MSIIR_RESULT result_lib = MSIIR_SUCCESS; + CROSS_FADE_RESULT result_cross_fade_lib = CROSS_FADE_SUCCESS; + + if (me->enable_flag[ch]) + { + result_lib = msiir_process_v2(&(me->msiir_lib[ch]), out_ptr[ch], inp_ptr[ch], num_samples); + + if (MSIIR_SUCCESS != result_lib) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : library process failed %d", result_lib); + return CAPI_EFAILED; + } + + // if the new msiir filters exist, check for cross fading processing + if (NULL != me->msiir_new_lib[ch].mem_ptr) + { + uint32_t param_size = 0; + + result_cross_fade_lib = audio_cross_fade_get_param(&(me->cross_fade_lib[ch]), + CROSS_FADE_PARAM_MODE, + (int8 *)&(me->cross_fade_flag[ch]), + (uint32)sizeof(me->cross_fade_flag[ch]), + (uint32 *)¶m_size); + if ((CROSS_FADE_SUCCESS != result_cross_fade_lib) || (0 == param_size)) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : cross fade processing failed"); + return CAPI_EFAILED; + } + + if (1 == me->cross_fade_flag[ch]) + { + int8 *cross_fade_in_ptrs[2]; + + cross_fade_in_ptrs[0] = (int8 *)out_ptr[ch]; + cross_fade_in_ptrs[1] = (int8 *)inp_ptr[ch]; + + // do the new msiir processing in-place on the input buffers + result_lib = msiir_process_v2(&(me->msiir_new_lib[ch]), inp_ptr[ch], inp_ptr[ch], num_samples); + + if (MSIIR_SUCCESS != result_lib) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : library process failed %d", result_lib); + return CAPI_EFAILED; + } + + result_cross_fade_lib = audio_cross_fade_process(&(me->cross_fade_lib[ch]), + (int8 *)out_ptr[ch], + cross_fade_in_ptrs, + (uint32)num_samples); + if (CROSS_FADE_SUCCESS != result_cross_fade_lib) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : cross fade processing failed"); + return CAPI_EFAILED; + } + + // get the cross fade flag again to see if it is done or not + result_cross_fade_lib = audio_cross_fade_get_param(&(me->cross_fade_lib[ch]), + CROSS_FADE_PARAM_MODE, + (int8 *)&(me->cross_fade_flag[ch]), + (uint32)sizeof(me->cross_fade_flag[ch]), + (uint32 *)¶m_size); + if (CROSS_FADE_SUCCESS != result_cross_fade_lib) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : cross fade processing failed"); + return CAPI_EFAILED; + } + + if (0 == me->cross_fade_flag[ch]) + { + // cross fading is done, release the old msiir filters + posal_memory_free(me->msiir_lib[ch].mem_ptr); + + me->msiir_lib[ch].mem_ptr = me->msiir_new_lib[ch].mem_ptr; + me->msiir_new_lib[ch].mem_ptr = NULL; + } + + } // if (1==me->cross_fade_flag[ch]) + else + { + // should not reach here, just to be safe + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : cross fade already done but msiir filters are not released!"); + return CAPI_EFAILED; + } + + } // if (NULL != me->msiir_new_lib) + + } // if (me->enable_flag) + else + { + // copy through if the filter is disabled + // se actual data length in place of num samples + memscpy(out_ptr[ch], (num_samples << shift_factor), inp_ptr[ch], (num_samples << shift_factor)); + } + + output[0]->buf_ptr[ch].actual_data_len = (num_samples << shift_factor); + input[0]->buf_ptr[ch].actual_data_len = (num_samples << shift_factor); + } + me->is_first_frame = FALSE; + + output[0]->flags = input[0]->flags; + if (input[0]->flags.is_timestamp_valid) + { + output[0]->timestamp = input[0]->timestamp - me->delay; + } + + return CAPI_EOK; +} + +/*------------------------------------------------------------------------ + Function name: capi_multistageiir_end + Returns the module to the uninitialized state and frees any memory + that was allocated by it. + @param[in,out] _pif Pointer to the module object. + @return Indication of success or failure. + * ----------------------------------------------------------------------*/ +static capi_err_t capi_multistageiir_end(capi_t *_pif) +{ + if (NULL == _pif) + { + MSIIR_MSG(MIID_UNKNOWN, DBG_ERROR_PRIO, "CAPI MSIIR : End received bad pointer, 0x%p", _pif); + return CAPI_EBADPARAM; + } + + capi_multistageiir_t *me = (capi_multistageiir_t *)(_pif); + + for (uint32_t ch = 0; ch < IIR_TUNING_FILTER_MAX_CHANNELS_V2; ch++) + { + if (NULL != me->msiir_lib[ch].mem_ptr) + { + posal_memory_free(me->msiir_lib[ch].mem_ptr); + me->msiir_lib[ch].mem_ptr = NULL; + } + + // if cross fading is active, release the new filters + if (NULL != me->msiir_new_lib[ch].mem_ptr) + { + posal_memory_free(me->msiir_new_lib[ch].mem_ptr); + me->msiir_new_lib[ch].mem_ptr = NULL; + } + + me->cross_fade_lib[ch].cross_fade_lib_mem_ptr = NULL; + } + + if (NULL != me->per_chan_msiir_cfg_max) + { + posal_memory_free(me->per_chan_msiir_cfg_max); + me->per_chan_msiir_cfg_max = NULL; + } + + if (NULL != me->pregain_params.params_ptr.data_ptr) + { + posal_memory_free(me->pregain_params.params_ptr.data_ptr); + me->pregain_params.params_ptr.data_ptr = NULL; + } + + if (NULL != me->enable_params.params_ptr.data_ptr) + { + posal_memory_free(me->enable_params.params_ptr.data_ptr); + me->enable_params.params_ptr.data_ptr = NULL; + } + + if (NULL != me->config_params.params_ptr.data_ptr) + { + posal_memory_free(me->config_params.params_ptr.data_ptr); + me->config_params.params_ptr.data_ptr = NULL; + } + + me->vtbl = NULL; + me->msiir_static_vars.max_stages = 0; + me->per_chan_mem_req.mem_size = 0; + me->per_chan_cross_fade_mem_req.cross_fade_lib_mem_size = 0; + + MSIIR_MSG(me->miid, DBG_HIGH_PRIO, "CAPI MSIIR : End done"); + return CAPI_EOK; +} + +/*------------------------------------------------------------------------ + Function name: capi_multistageiir_set_param + Sets either a parameter value or a parameter structure containing multiple + parameters. In the event of a failure, the appropriate error code is + returned. + * -----------------------------------------------------------------------*/ +static capi_err_t capi_multistageiir_set_param(capi_t * _pif, + uint32_t param_id, + const capi_port_info_t *port_info_ptr, + capi_buf_t * params_ptr) +{ + if (NULL == _pif || NULL == params_ptr) + { + MSIIR_MSG(MIID_UNKNOWN, DBG_ERROR_PRIO, "CAPI MSIIR : Set param received bad pointer, 0x%p, 0x%p", _pif, params_ptr); + return CAPI_EBADPARAM; + } + + capi_err_t result = CAPI_EOK; + bool_t cache_param_pending = FALSE; + + capi_multistageiir_t *me = (capi_multistageiir_t *)(_pif); + + switch (param_id) + { + case PARAM_ID_MODULE_ENABLE: + case FWK_EXTN_PARAM_ID_CONTAINER_FRAME_DURATION: + break; + case PARAM_ID_MSIIR_TUNING_FILTER_ENABLE: + case PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN: + case PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS: + { + if((VERSION_V2 == me->cfg_version) || (TRUE == me->higher_channel_map_present)) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : SetParam 0x%lx failed as V2 config is already configured for the module. " + "Cannot set both V1 and V2 configs simultaneously for the module OR higher than 63 channel map present in IMF (0/1): %lu", + param_id, + me->higher_channel_map_present); + return CAPI_EBADPARAM; + } + break; + } + break; + case PARAM_ID_MSIIR_TUNING_FILTER_ENABLE_V2: + case PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN_V2: + case PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS_V2: + { + if(VERSION_V1 == me->cfg_version) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : SetParam 0x%lx failed as V1 config is already configured for the module. " + "Cannot set both V1 and V2 configs simultaneously for the module",param_id); + return CAPI_EBADPARAM; + } + break; + } + default: + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : SetParam, unsupported param ID 0x%x", (int)param_id); + return CAPI_EUNSUPPORTED; + break; + } + } + switch (param_id) + { + case PARAM_ID_MODULE_ENABLE: + { + uint16_t paramSize = (uint16_t)params_ptr->actual_data_len; + if (paramSize >= sizeof(param_id_module_enable_t)) + { + param_id_module_enable_t *enable_ptr = (param_id_module_enable_t *)(params_ptr->data_ptr); + me->enable = enable_ptr->enable; + MSIIR_MSG(me->miid, DBG_HIGH_PRIO, "CAPI MSIIR : Set enable: %lu", enable_ptr->enable); + if (me->media_fmt_received) + { + result = capi_msiir_raise_process_check_event(me); + } + } + else + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : Enable/Disable, Bad param size %hu", paramSize); + result = CAPI_ENEEDMORE; + } + break; + } + + case PARAM_ID_MSIIR_TUNING_FILTER_ENABLE: + { + cache_param_pending = TRUE; + if (me->media_fmt_received) + { + result = capi_msiir_set_enable_disable_per_channel(me, params_ptr); + if (CAPI_FAILED(result)) + { + cache_param_pending = FALSE; + } + } + break; + } + case PARAM_ID_MSIIR_TUNING_FILTER_ENABLE_V2: + { + cache_param_pending = TRUE; + if (me->media_fmt_received) + { + result = capi_msiir_set_enable_disable_per_channel_v2(me, params_ptr, param_id); + if (CAPI_FAILED(result)) + { + cache_param_pending = FALSE; + } + } + break; + } + case PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN: + { + cache_param_pending = TRUE; + if (me->media_fmt_received) + { + result = capi_msiir_set_pregain_per_channel(me, params_ptr); + if (CAPI_FAILED(result)) + { + cache_param_pending = FALSE; + } + } + break; + } + case PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN_V2: + { + cache_param_pending = TRUE; + if (me->media_fmt_received) + { + result = capi_msiir_set_pregain_per_channel_v2(me, params_ptr, param_id); + if (CAPI_FAILED(result)) + { + cache_param_pending = FALSE; + } + } + break; + } + + case PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS: + { + cache_param_pending = TRUE; + if (me->media_fmt_received) + { + result = capi_msiir_set_config_per_channel(me, params_ptr); + if (CAPI_FAILED(result)) + { + cache_param_pending = FALSE; + } + } + break; + } + case PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS_V2: + { + cache_param_pending = TRUE; + if (me->media_fmt_received) + { + result = capi_msiir_set_config_per_channel_v2(me, params_ptr, param_id); + if (CAPI_FAILED(result)) + { + cache_param_pending = FALSE; + } + } + break; + } + case FWK_EXTN_PARAM_ID_CONTAINER_FRAME_DURATION: + { + if (params_ptr->actual_data_len < sizeof(fwk_extn_param_id_container_frame_duration_t)) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Invalid payload size for param_id=0x%lx actual_data_len=%lu ", + param_id, + params_ptr->actual_data_len); + result |= CAPI_ENEEDMORE; + break; + } + + fwk_extn_param_id_container_frame_duration_t *cfg_ptr = + (fwk_extn_param_id_container_frame_duration_t *)params_ptr->data_ptr; + + if (0 == cfg_ptr->duration_us) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR :Frame duration sent as 0ms. Ignoring."); + return result; + } + + if (me->cntr_frame_size_us != cfg_ptr->duration_us) + { + me->cntr_frame_size_us = cfg_ptr->duration_us; + MSIIR_MSG(me->miid, DBG_HIGH_PRIO, "CAPI MSIIR : Container frame size duration = %lu is set.", me->cntr_frame_size_us); + if (CAPI_EOK == result) + { + result = capi_msiir_check_raise_kpps_event(me, capi_msiir_get_kpps(me)); + } + } + + return result; + } + + default: + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : SetParam, unsupported param ID 0x%x", (int)param_id); + result = CAPI_EUNSUPPORTED; + break; + } + } + + if ((CAPI_EOK == result) && (me->start_cross_fade)) + { + result = capi_msiir_start_cross_fade(me); + } + if (cache_param_pending) + { + if (CAPI_FAILED(capi_msiir_cache_params(me, params_ptr, param_id))) + { + MSIIR_MSG(me->miid, DBG_HIGH_PRIO, "CAPI MSIIR : SetParam, Failed to cache param ID 0x%x", (int)param_id); + } + } + if (CAPI_EOK == result) + { + result = capi_msiir_check_raise_kpps_event(me, capi_msiir_get_kpps(me)); + } + return result; +} + +/*------------------------------------------------------------------------ + Function name: capi_multistageiir_get_param + Gets either a parameter value or a parameter structure containing multiple + parameters. In the event of a failure, the appropriate error code is + returned. + * -----------------------------------------------------------------------*/ +static capi_err_t capi_multistageiir_get_param(capi_t * _pif, + uint32_t param_id, + const capi_port_info_t *port_info_ptr, + capi_buf_t * params_ptr) +{ + if (NULL == _pif || NULL == params_ptr) + { + MSIIR_MSG(MIID_UNKNOWN, DBG_ERROR_PRIO, "CAPI MSIIR : Get param received bad pointer, 0x%p, 0x%p", _pif, params_ptr); + return CAPI_EBADPARAM; + } + + capi_err_t result = CAPI_EOK; + capi_multistageiir_t *me = (capi_multistageiir_t *)(_pif); + + switch (param_id) + { + case PARAM_ID_MODULE_ENABLE: + break; + case PARAM_ID_MSIIR_TUNING_FILTER_ENABLE: + case PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN: + case PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS: + { + if((VERSION_V2 == me->cfg_version) || (TRUE == me->higher_channel_map_present)) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : GetParam 0x%x failed as V2 config is already configured for the module. " + "Cannot perform both V1 and V2 operations simultaneously for the module OR higher than 63 channel map present in IMF (0/1): %lu", + (int)param_id, + me->higher_channel_map_present); + return CAPI_EBADPARAM; + } + break; + } + break; + case PARAM_ID_MSIIR_TUNING_FILTER_ENABLE_V2: + case PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN_V2: + case PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS_V2: + { + if(VERSION_V1 == me->cfg_version) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : GetParam 0x%x failed as V1 config is already configured for the module. " + "Cannot perform both V1 and V2 operations simultaneously for the module",(int)param_id); + return CAPI_EBADPARAM; + } + break; + } + default: + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : GetParam, unsupported param ID 0x%x", (int)param_id); + return CAPI_EUNSUPPORTED; + break; + } + } + switch (param_id) + { + case PARAM_ID_MODULE_ENABLE: + { + const uint32_t payloadSize = (uint32_t)(params_ptr->max_data_len); + if (payloadSize >= sizeof(param_id_module_enable_t)) + { + param_id_module_enable_t *pMSiirEnable = (param_id_module_enable_t *)(params_ptr->data_ptr); + pMSiirEnable->enable = me->enable; + params_ptr->actual_data_len = (uint32_t)sizeof(param_id_module_enable_t); + } + else + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : Get Enable Param, Bad payload size %lu", payloadSize); + result = CAPI_ENEEDMORE; + } + break; + } + + case PARAM_ID_MSIIR_TUNING_FILTER_ENABLE: + { + result = capi_msiir_get_enable_disable_per_channel(me, params_ptr); + break; + } + + case PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN: + { + result = capi_msiir_get_pregain_per_channel(me, params_ptr); + break; + } + + case PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS: + { + result = capi_msiir_get_config_per_channel(me, params_ptr); + break; + } + + case PARAM_ID_MSIIR_TUNING_FILTER_ENABLE_V2: + { + result = capi_msiir_get_enable_disable_per_channel_v2(me, params_ptr); + break; + } + + case PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN_V2: + { + result = capi_msiir_get_pregain_per_channel_v2(me, params_ptr); + break; + } + + case PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS_V2: + { + result = capi_msiir_get_config_per_channel_v2(me, params_ptr); + break; + } + + default: + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : Get unsupported param ID 0x%x", (int)param_id); + result = CAPI_EBADPARAM; + break; + } + } + return result; +} + +/*------------------------------------------------------------------------- + Function name: capi_multistageiir_set_properties + Sets a list of property values. + @param[in,out] _pif Pointer to the module object. + @param[in] props_ptr Contains the property values to be set. + @return Indication of success or failure. + *-------------------------------------------------------------------------*/ +static capi_err_t capi_multistageiir_set_properties(capi_t *_pif, capi_proplist_t *props_ptr) +{ + if (NULL == _pif) + return CAPI_EBADPARAM; + + capi_multistageiir_t *me_ptr = (capi_multistageiir_t *)_pif; + + return capi_msiir_process_set_properties(me_ptr, props_ptr); +} + +/*-------------------------------------------------------------------------- + Function name: capi_multistageiir_get_properties + Gets a list of property values. + @param[in,out] _pif Pointer to the module object. + @param[out] props_ptr Contains the empty structures that must be + filled with the appropriate property values + based on the property ids provided. + @return Indication of success or failure. + *-------------------------------------------------------------------------*/ +static capi_err_t capi_multistageiir_get_properties(capi_t *_pif, capi_proplist_t *props_ptr) +{ + if (NULL == _pif) + return CAPI_EBADPARAM; + + capi_multistageiir_t *me_ptr = (capi_multistageiir_t *)_pif; + + return capi_msiir_process_get_properties(me_ptr, props_ptr); +} + +/*-------------------------------------------------------------------------- + Function name: capi_multistageiir_init + Instantiate MSIIR module + @param[in,out] _pif Pointer to the module object. + @param[in] init_set_properties Properties set by the service to + be used while init(). + @return Indication of success or failure. + *-------------------------------------------------------------------------*/ +capi_err_t capi_multistageiir_init(capi_t *_pif, capi_proplist_t *init_set_properties) +{ + capi_err_t result = CAPI_EOK; + + if (NULL == _pif) + return CAPI_EBADPARAM; + + int8_t * ptr = (int8_t *)_pif; + capi_multistageiir_t *me_ptr = (capi_multistageiir_t *)ptr; + + memset(me_ptr, 0, sizeof(capi_multistageiir_t)); + + capi_msiir_init_media_fmt(me_ptr); + + // setup the channel index arrays + // change the size to sizeof(channel_map_to_index) + memset(&(me_ptr->channel_map_to_index), -1, sizeof(me_ptr->channel_map_to_index)); + + me_ptr->msiir_static_vars.data_width = me_ptr->media_fmt[0].format.bits_per_sample; + me_ptr->msiir_static_vars.max_stages = MSIIR_MAX_STAGES; + + me_ptr->per_chan_cross_fade_mem_req.cross_fade_lib_mem_size = 0; + + me_ptr->is_first_frame = TRUE; + me_ptr->media_fmt_received = FALSE; + me_ptr->start_cross_fade = FALSE; + me_ptr->enable = TRUE; + + me_ptr->vtbl = &capi_msiir_vtbl; + me_ptr->kpps = 0; + me_ptr->delay = 0; + me_ptr->bw = 0x7FFFFFFF; + me_ptr->num_channels_allocated = 0; + me_ptr->cntr_frame_size_us = DEFAULT_FRAME_SIZE_IN_US; + + me_ptr->cfg_version = DEFAULT; + /* init_set_properties contains OUT_BITS_PER_SAMPLE, + * EVENT_CALLBACK_INFO and PORT_INFO */ + if (NULL != init_set_properties) + { + result = capi_msiir_process_set_properties(me_ptr, init_set_properties); + result ^= (result & CAPI_EUNSUPPORTED); + } + MSIIR_MSG(me_ptr->miid, DBG_HIGH_PRIO, "CAPI MSIIR : Init Success!"); + + return result; +} +/*-------------------------------------------------------------------------- + Function name: capi_multistageiir_get_static_properties + Queries the static properties of MSIIR + @param[in] init_set_properties The same properties that will be sent + in the call to the init() function. + @param[out] static_properties Pointer to the structure that the + module must fill with the appropriate + values based on the property id. + @return Indication of success or failure. + *-------------------------------------------------------------------------*/ +capi_err_t capi_multistageiir_get_static_properties(capi_proplist_t *init_set_properties, + capi_proplist_t *static_properties) +{ + capi_err_t result = CAPI_EFAILED; + + if (NULL != init_set_properties) + { + MSIIR_MSG(MIID_UNKNOWN, DBG_HIGH_PRIO, "CAPI MSIIR: get static prop ignoring init_set_properties!"); + } + + if (NULL != static_properties) + { + result = capi_msiir_process_get_properties((capi_multistageiir_t *)NULL, static_properties); + } + else + { + MSIIR_MSG(MIID_UNKNOWN, DBG_ERROR_PRIO, "CAPI MSIIR: get static prop, Bad ptrs: %p", static_properties); + } + return result; +} diff --git a/modules/processing/filters/multi_stage_iir/capi/src/capi_multistageiir_utils.cpp b/modules/processing/filters/multi_stage_iir/capi/src/capi_multistageiir_utils.cpp new file mode 100644 index 0000000..b255872 --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/capi/src/capi_multistageiir_utils.cpp @@ -0,0 +1,2078 @@ +/* ======================================================================== */ +/* +@file capi_multistageiir_utils.cpp + + Source file to implement the Audio Post Processor Interface for Multi-Stage + IIR filters +*/ + +/* ========================================================================= + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + ========================================================================= */ + +/*------------------------------------------------------------------------ + * Include files + * -----------------------------------------------------------------------*/ +#include "capi_multistageiir_utils.h" +#include "msiir_calibration_api.h" +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define MSIIR_MASK_WIDTH 32 + +/*------------------------------------------------------------------------ + * Function declarations + * -----------------------------------------------------------------------*/ +static capi_err_t capi_msiir_set_params_to_lib(capi_multistageiir_t *me); + +static capi_err_t capi_msiir_create_new_msiir_filters(capi_multistageiir_t *me, uint32_t instance_id, uint32_t ch); + +static inline uint32_t align_to_8_byte(const uint32_t num) +{ + return ((num + 7) & (0xFFFFFFF8)); +} + +int32_t capi_msiir_s32_min_s32_s32(int32_t x, int32_t y) +{ + return ((x < y) ? x : y); +} + +static bool_t capi_msiir_is_valid_channel_type(const uint8_t channel_type) +{ + if (channel_type < PCM_CHANNEL_L) + { + return FALSE; + } + if (channel_type > PCM_MAX_CHANNEL_MAP_V2) + { + return FALSE; + } + return TRUE; +} + +static capi_err_t capi_msiir_check_raise_bw_event(capi_multistageiir_t *me) +{ + const uint32_t bw = 0; + + if (NULL == me->cb_info.event_cb) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : Event callback is not set. Unable to raise events!"); + return CAPI_EUNSUPPORTED; + } + + // Raise event if bw is changed. + if (bw != me->bw) + { + capi_err_t capi_result = CAPI_EOK; + capi_event_bandwidth_t event; + capi_event_info_t event_info; + + event.code_bandwidth = 0; + event.data_bandwidth = bw; + + event_info.port_info.is_valid = FALSE; + event_info.payload.actual_data_len = sizeof(event); + event_info.payload.max_data_len = sizeof(event); + event_info.payload.data_ptr = reinterpret_cast(&event); + capi_result = me->cb_info.event_cb(me->cb_info.event_context, CAPI_EVENT_BANDWIDTH, &event_info); + + if (CAPI_FAILED(capi_result)) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR: Failed to send BW event."); + } + else + { + me->bw = bw; + } + } + return CAPI_EOK; +} + +uint32_t capi_msiir_get_kpps(capi_multistageiir_t *me) +{ + uint64_t kpps = 0; + uint64_t kpps_per_channel = 0; + uint64_t frame_size = 0; + uint32_t num_channels = me->media_fmt[0].format.num_channels; + uint32_t num_biquad_stages = 0; + uint32_t bits_per_sample = me->media_fmt[0].format.bits_per_sample; + uint32_t sampling_rate = me->media_fmt[0].format.sampling_rate; + if ((sampling_rate == 0) || (!me->media_fmt_received)) + { + return 0; + } + + frame_size = (((uint64_t)(me->cntr_frame_size_us) * sampling_rate) / 1000000); + if (0 == frame_size) + { + return 0; + } + + for (uint32_t ch = 0; ch < num_channels; ch++) + { + kpps_per_channel = 0; + // channel_type has values range from 1 to 16 + uint8_t channel_type = me->media_fmt[0].channel_type[ch]; + + // convert channel_type to channel index, channel index has range 0 ~ 7 + int32_t ch_idx = me->channel_map_to_index[channel_type]; + num_biquad_stages = me->per_chan_msiir_cfg_max[ch_idx].num_stages; + if (me->enable_flag[ch]) + { + if (bits_per_sample == 16) + { + kpps_per_channel = + (((NUM_PACKETS_PER_SAMPLE_16BIT * frame_size + NUM_PACKETS_PER_STAGE_WITH_ZERO_SAMPLE_16BIT) * + num_biquad_stages + + NUM_PACKETS_WITH_ZERO_STAGE_16BIT) * + sampling_rate) / + (frame_size * 1000); + } + if (bits_per_sample == 32) + { + kpps_per_channel = + (((NUM_PACKETS_PER_SAMPLE_32BIT * frame_size + NUM_PACKETS_PER_STAGE_WITH_ZERO_SAMPLE_32BIT) * + num_biquad_stages + + NUM_PACKETS_WITH_ZERO_STAGE_32BIT) * + sampling_rate) / + (frame_size * 1000); + } + } + kpps += kpps_per_channel; + } + MSIIR_MSG(me->miid, DBG_HIGH_PRIO, "CAPI MSIIR : Updated KPPS value is %lu", (uint32_t)kpps); + return (uint32_t)kpps; +} + +capi_err_t capi_msiir_check_raise_kpps_event(capi_multistageiir_t *me_ptr, uint32_t val) +{ + capi_err_t result = CAPI_EOK; + + if (me_ptr->kpps == val) + { + return result; + } + if (CAPI_EOK == (result = capi_cmn_update_kpps_event(&me_ptr->cb_info, val))) + { + me_ptr->kpps = val; + } + return result; +} + +static capi_err_t capi_msiir_check_raise_delay_event(capi_multistageiir_t *me_ptr, uint32_t delay) +{ + if (me_ptr->delay == delay) + { + return CAPI_EOK; + } + me_ptr->delay = delay; + + return capi_cmn_update_algo_delay_event(&me_ptr->cb_info, me_ptr->delay); +} + +capi_err_t capi_msiir_raise_process_check_event(capi_multistageiir_t *me_ptr) +{ + uint32_t process_check = 0; + + for (uint32_t ch = 0; ch < me_ptr->media_fmt[0].format.num_channels; ch++) + { + // if the current channel is enabled, set TRUE and break the loop + process_check |= me_ptr->enable_flag[ch]; + } + process_check &= (uint32_t)me_ptr->enable; // Over-riding with the global enable flag + + return capi_cmn_update_process_check_event(&me_ptr->cb_info, process_check); +} + +capi_err_t capi_msiir_update_delay_event(capi_multistageiir_t *me) +{ + uint32_t delay = 0; + MSIIR_RESULT result_lib = MSIIR_SUCCESS; + + // find the max number of stages from all channels + int32_t max_stages = 0; + capi_one_chan_msiir_config_max_t one_chan_msiir_cfg_max; + + for (uint32_t ch = 0; ch < me->media_fmt[0].format.num_channels; ch++) + { + msiir_lib_t *obj_ptr = NULL; + + if ((me->cross_fade_flag[ch] == 0) || (me->msiir_new_lib[ch].mem_ptr == NULL)) + { + obj_ptr = (msiir_lib_t *)(&me->msiir_lib[ch]); + } + else + { + obj_ptr = (msiir_lib_t *)(&me->msiir_new_lib[ch]); + } + + uint32_t actual_param_size = 0; + + result_lib = msiir_get_param(obj_ptr, + MSIIR_PARAM_CONFIG, + (void *)&one_chan_msiir_cfg_max, + sizeof(one_chan_msiir_cfg_max), + &actual_param_size); + + if (actual_param_size > sizeof(one_chan_msiir_cfg_max) || (MSIIR_SUCCESS != result_lib)) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : get delay error result %d, size %lu", result_lib, actual_param_size); + return CAPI_EFAILED; + } + if (me->enable_flag[ch]) + { + max_stages = + (max_stages > one_chan_msiir_cfg_max.num_stages) ? max_stages : (one_chan_msiir_cfg_max.num_stages); + } + } + // assume each IIR stage has MSIIR_FILTER_STATES(2) samples of delay + delay = MSIIR_FILTER_STATES * max_stages; + delay = (delay * 1000000) / (me->media_fmt[0].format.sampling_rate); + + return capi_msiir_check_raise_delay_event(me, delay); +} + +static bool_t capi_msiir_is_supported_media_type_v2(capi_multistageiir_t *me, const capi_media_fmt_v2_t *format_ptr) +{ + if ((format_ptr->format.bits_per_sample != 16) && (format_ptr->format.bits_per_sample != 32)) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Only 16/24 bit data supported. Received %lu.", + format_ptr->format.bits_per_sample); + return FALSE; + } + + if ((format_ptr->format.data_interleaving != CAPI_DEINTERLEAVED_UNPACKED) && (format_ptr->format.num_channels != 1)) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : Interleaved data not supported."); + return FALSE; + } + + if (!format_ptr->format.data_is_signed) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : Unsigned data not supported."); + return FALSE; + } + + if ((format_ptr->format.q_factor != PCM_Q_FACTOR_15) && (format_ptr->format.q_factor != PCM_Q_FACTOR_27)) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPIv2 MSIIR: Q factor %lu is not supported.", format_ptr->format.q_factor); + return FALSE; + } + + if ((format_ptr->format.num_channels == 0) || (format_ptr->format.num_channels > IIR_TUNING_FILTER_MAX_CHANNELS_V2)) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Init received with %lu channels. Currently only %d are supported.", + format_ptr->format.num_channels, + IIR_TUNING_FILTER_MAX_CHANNELS_V2); + return FALSE; + } + + for (uint32_t ch = 0; ch < format_ptr->format.num_channels; ch++) + { + if (!capi_msiir_is_valid_channel_type(format_ptr->channel_type[ch])) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : channel number %lu has unsupported type %hu", + ch, + format_ptr->channel_type[ch]); + return FALSE; + } + if(format_ptr->channel_type[ch] > PCM_MAX_CHANNEL_MAP) + { + //If a higher channel map in the media format occurs even once in the execution history, + //the flag will be set to true from that point onward. + me->higher_channel_map_present = TRUE; + } + } + + return TRUE; +} + +capi_err_t capi_msiir_init_media_fmt(capi_multistageiir_t *me) +{ + capi_media_fmt_v2_t *media_fmt_ptr = &(me->media_fmt[0]); + + media_fmt_ptr->header.format_header.data_format = CAPI_FIXED_POINT; + media_fmt_ptr->format.minor_version = CAPI_MEDIA_FORMAT_MINOR_VERSION; + media_fmt_ptr->format.bits_per_sample = 16; + media_fmt_ptr->format.bitstream_format = CAPI_DATA_FORMAT_INVALID_VAL; + media_fmt_ptr->format.data_interleaving = CAPI_DEINTERLEAVED_UNPACKED; + media_fmt_ptr->format.data_is_signed = 1; + media_fmt_ptr->format.num_channels = CAPI_DATA_FORMAT_INVALID_VAL; + media_fmt_ptr->format.q_factor = PCM_Q_FACTOR_15; + media_fmt_ptr->format.sampling_rate = CAPI_DATA_FORMAT_INVALID_VAL; + + memset(media_fmt_ptr->channel_type, (uint8_t)CAPI_DATA_FORMAT_INVALID_VAL, sizeof(media_fmt_ptr->channel_type)); + + return CAPI_EOK; +} + +static capi_err_t capi_msiir_get_init_memory_req(uint32_t *size_ptr, uint32_t miid) +{ + cross_fade_lib_mem_req_t one_chan_cross_fade_lib_mem_req; + cross_fade_static_t cross_fade_static_vars; + + if (NULL == size_ptr) + { + MSIIR_MSG(miid, DBG_ERROR_PRIO, "CAPI MSIIR : Received bad pointer, 0x%p", size_ptr); + return CAPI_EBADPARAM; + } + + uint32_t num_channels = IIR_TUNING_FILTER_MAX_CHANNELS_V2; + uint32_t total_mem_size = 0; + + // cross fade library size + cross_fade_static_vars.data_width = 2; // cross fade use 1(16bits) or 2(32bits) + cross_fade_static_vars.sample_rate = MSIIR_DEFAULT_SAMPLE_RATE; + + one_chan_cross_fade_lib_mem_req.cross_fade_lib_mem_size = 0; + one_chan_cross_fade_lib_mem_req.cross_fade_lib_stack_size = 0; + + if (CROSS_FADE_SUCCESS != audio_cross_fade_get_mem_req(&one_chan_cross_fade_lib_mem_req, &cross_fade_static_vars)) + { + MSIIR_MSG(miid, DBG_ERROR_PRIO, "CAPI MSIIR : Get Init memory required failed get cross fade size"); + return CAPI_EFAILED; + } + + /* memory is allocated for the capi structure of MSIIR and for the crossfade library. + Crossfade library would be initialized after the media format has been received. + */ + total_mem_size += num_channels * align_to_8_byte(one_chan_cross_fade_lib_mem_req.cross_fade_lib_mem_size); + total_mem_size += align_to_8_byte(sizeof(capi_multistageiir_t)); + + *size_ptr = total_mem_size; +#ifdef CAPI_MSIIR_DEBUG_MSG + MSIIR_MSG(miid, DBG_HIGH_PRIO, "CAPI MSIIR : Get Init memory required done, requires %lu bytes", *size_ptr); +#endif + return CAPI_EOK; +} + +static capi_err_t capi_msiir_init_capi(capi_multistageiir_t *me, bool_t media_format_version) +{ + if (NULL == me) + { + MSIIR_MSG(MIID_UNKNOWN, DBG_ERROR_PRIO, "CAPI MSIIR : Init received bad pointer, 0x%p", me); + return CAPI_EBADPARAM; + } + + MSIIR_RESULT result_lib = MSIIR_SUCCESS; + + CROSS_FADE_RESULT result_cross_fade_lib = CROSS_FADE_SUCCESS; + + bool_t any_crossfade_in_progress = FALSE; + + // calculate the mem size for MSIIR library and re-alloc if needed + msiir_mem_req_t msiir_mem_req_by_reinit; + + msiir_mem_req_by_reinit.mem_size = 0; + + uint32_t msiir_bits_per_sample_before = (uint32_t)(me->msiir_static_vars.data_width); + me->msiir_static_vars.data_width = (int32_t)me->media_fmt[0].format.bits_per_sample; + + result_lib = msiir_get_mem_req(&msiir_mem_req_by_reinit, &(me->msiir_static_vars)); + if (MSIIR_SUCCESS != result_lib) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : Init error %d", result_lib); + return CAPI_EFAILED; + } + + // Check if any crossfade is in progress. + for (uint32_t ch = 0; ch < me->num_channels_allocated; ch++) + { + if (NULL == me->cross_fade_lib[ch].cross_fade_lib_mem_ptr) + { + continue; + } + uint32_t cur_crossfade_mode = FALSE; + uint32_t param_size = 0; + + result_cross_fade_lib = audio_cross_fade_get_param(&(me->cross_fade_lib[ch]), + CROSS_FADE_PARAM_MODE, + (int8 *)&(cur_crossfade_mode), + (uint32)sizeof(cur_crossfade_mode), + (uint32 *)¶m_size); + + if ((CROSS_FADE_SUCCESS != result_cross_fade_lib)) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Failed to get cross fade param module for ch %ld, result 0x%lx", + ch, + result_cross_fade_lib); + return CAPI_EFAILED; + } + + if (cur_crossfade_mode) + { + any_crossfade_in_progress = TRUE; + + me->cross_fade_flag[ch] = 0; //reset the flag and set it to the cross fade lib + + result_cross_fade_lib = audio_cross_fade_set_param(&(me->cross_fade_lib[ch]), + CROSS_FADE_PARAM_MODE, + (int8 *)&(me->cross_fade_flag[ch]), + (uint32)sizeof(me->cross_fade_flag[ch])); + //loop through all the channels and reset the crossfade flag for all the crossfade enabled channels + } + } + + // if the previous malloc of MSIIR library size is smaller than the current requirement, re-alloc the MSIIR library + if ((me->per_chan_mem_req.mem_size != (align_to_8_byte(msiir_mem_req_by_reinit.mem_size))) || + (me->num_channels_allocated != me->media_fmt[0].format.num_channels) || any_crossfade_in_progress) + { + capi_err_t result_capi = CAPI_EOK; + + // clear all the previous MSIIR library instances + for (uint32_t ch = 0; ch < IIR_TUNING_FILTER_MAX_CHANNELS_V2; ch++) + { + if (NULL != me->msiir_lib[ch].mem_ptr) + { + posal_memory_free(me->msiir_lib[ch].mem_ptr); + me->msiir_lib[ch].mem_ptr = NULL; + } + + // if cross fading is active, release the new filters + if (NULL != me->msiir_new_lib[ch].mem_ptr) + { + posal_memory_free(me->msiir_new_lib[ch].mem_ptr); + me->msiir_new_lib[ch].mem_ptr = NULL; + } + + me->cross_fade_lib[ch].cross_fade_lib_mem_ptr = NULL; + } + // allocate MSIIR lib instances + for (uint32_t ch = 0; ch < me->media_fmt[0].format.num_channels; ch++) + { + result_capi = capi_msiir_create_new_msiir_filters(me, MSIIR_CURRENT_INSTANCE, ch); + if (CAPI_EOK != result_capi) + { + return CAPI_EFAILED; + } + } + me->num_channels_allocated = me->media_fmt[0].format.num_channels; + } + else if (msiir_bits_per_sample_before != me->media_fmt[0].format.bits_per_sample) + { + // although we have enough memory for msiir library, the msiir static vars have + // changed after reinit, so we still need to reinit the current msiir library + for (uint32_t ch = 0; ch < me->media_fmt[0].format.num_channels; ch++) + { + void *msiir_ptr = me->msiir_lib[ch].mem_ptr; + + result_lib = + msiir_init_mem(&(me->msiir_lib[ch]), &(me->msiir_static_vars), msiir_ptr, me->per_chan_mem_req.mem_size); + + if (MSIIR_SUCCESS != result_lib) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : ReInit failed initialize library"); + return CAPI_EFAILED; + } + } + } + + // reinit cross fade library with new static vars + me->cross_fade_static_vars.data_width = (me->media_fmt[0].format.bits_per_sample >> 4); + me->cross_fade_static_vars.sample_rate = me->media_fmt[0].format.sampling_rate; + + result_cross_fade_lib = + audio_cross_fade_get_mem_req(&(me->per_chan_cross_fade_mem_req), &(me->cross_fade_static_vars)); + if ((CROSS_FADE_SUCCESS != result_cross_fade_lib) || (0 == me->per_chan_cross_fade_mem_req.cross_fade_lib_mem_size)) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : Init failed at cross fade"); + return CAPI_EFAILED; + } + + int8_t *ptr = (int8_t *)me; + ptr += align_to_8_byte(sizeof(capi_multistageiir_t)); + + // allocate crossfade memory dynamically + for (uint32_t ch = 0; ch < me->media_fmt[0].format.num_channels; ch++) + { + result_cross_fade_lib = audio_cross_fade_init_memory(&(me->cross_fade_lib[ch]), + &(me->cross_fade_static_vars), + (int8 *)ptr, + me->per_chan_cross_fade_mem_req.cross_fade_lib_mem_size); + if (CROSS_FADE_SUCCESS != result_cross_fade_lib) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : Init failed init cross fade"); + return CAPI_EFAILED; + } + + ptr += align_to_8_byte(me->per_chan_cross_fade_mem_req.cross_fade_lib_mem_size); + } + me->per_chan_cross_fade_mem_req.cross_fade_lib_mem_size = + align_to_8_byte(me->per_chan_cross_fade_mem_req.cross_fade_lib_mem_size); + + // Calculate and raise all the events here such as kpps, output media format, delay and process check + capi_err_t capi_result = CAPI_EOK; + + capi_result = capi_msiir_check_raise_kpps_event(me, capi_msiir_get_kpps(me)); + + capi_result |= capi_msiir_check_raise_bw_event(me); + capi_result |= capi_msiir_update_delay_event(me); + + capi_result |= capi_msiir_raise_process_check_event(me); + + capi_result |= capi_cmn_output_media_fmt_event_v2(&me->cb_info, me->media_fmt, FALSE, 0); + + if (CAPI_FAILED(capi_result)) + { + for (uint32_t ch = 0; ch < IIR_TUNING_FILTER_MAX_CHANNELS_V2; ch++) + { + if (NULL != me->msiir_lib[ch].mem_ptr) + { + posal_memory_free(me->msiir_lib[ch].mem_ptr); + me->msiir_lib[ch].mem_ptr = NULL; + } + + // if cross fading is active, release the new filters + if (NULL != me->msiir_new_lib[ch].mem_ptr) + { + posal_memory_free(me->msiir_new_lib[ch].mem_ptr); + me->msiir_new_lib[ch].mem_ptr = NULL; + } + + me->cross_fade_lib[ch].cross_fade_lib_mem_ptr = NULL; + } + me->msiir_static_vars.max_stages = 0; + me->per_chan_mem_req.mem_size = 0; + me->per_chan_cross_fade_mem_req.cross_fade_lib_mem_size = 0; + return capi_result; + } +#ifdef CAPI_MSIIR_DEBUG_MSG + MSIIR_MSG(me->miid, DBG_HIGH_PRIO, + "CAPI MSIIR : Init done with outformat %lu,%lu,%lu,%lu,%lu", + me->media_fmt[0].format.num_channels, + me->media_fmt[0].format.bits_per_sample, + me->media_fmt[0].format.sampling_rate, + me->media_fmt[0].format.data_is_signed, + (uint32_t)me->media_fmt[0].format.data_interleaving); + MSIIR_MSG(me->miid, DBG_HIGH_PRIO, "CAPI MSIIR : init_capi end."); +#endif + return CAPI_EOK; +} + +capi_err_t capi_msiir_process_set_properties(capi_multistageiir_t *me, capi_proplist_t *props_ptr) +{ + capi_err_t result = CAPI_EOK; + capi_prop_t *prop_array = props_ptr->prop_ptr; + + if (me == NULL) + { + MSIIR_MSG(MIID_UNKNOWN, DBG_ERROR_PRIO, "Set common property received null ptr"); + return CAPI_EBADPARAM; + } + for (uint8_t i = 0; i < props_ptr->props_num; i++) + { + capi_buf_t *payload = &(prop_array[i].payload); + uint32_t miid = me ? me->miid : MIID_UNKNOWN; + switch (prop_array[i].id) + { + case CAPI_EVENT_CALLBACK_INFO: + { + if (payload->actual_data_len >= sizeof(capi_event_callback_info_t)) + { + capi_event_callback_info_t *data_ptr = (capi_event_callback_info_t *)payload->data_ptr; + if (NULL == data_ptr) + { + payload->actual_data_len = + 0; // according to CAPI V2, actual len should be num of bytes read(set)/written(get) + CAPI_SET_ERROR(result, CAPI_EBADPARAM); + continue; + } + me->cb_info = *data_ptr; + } + else + { + MSIIR_MSG(miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Set, Property id 0x%lx Bad param size %lu", + (uint32_t)prop_array[i].id, + payload->actual_data_len); + payload->actual_data_len = + 0; // according to CAPI V2, actual len should be num of bytes read(set)/written(get) + CAPI_SET_ERROR(result, CAPI_ENEEDMORE); + } + break; + } + case CAPI_PORT_NUM_INFO: + { + if (payload->actual_data_len >= sizeof(capi_port_num_info_t)) + { + capi_port_num_info_t *data_ptr = (capi_port_num_info_t *)payload->data_ptr; + + /* As number of ports supported is only 1, we check if the set property + * has been done with any other value + */ + if ((data_ptr->num_input_ports != CAPI_MSIIR_MAX_IN_PORTS) || + (data_ptr->num_output_ports != CAPI_MSIIR_MAX_OUT_PORTS)) + { + MSIIR_MSG(miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Set, Property id 0x%lx num of in and out ports cannot be other than 1", + (uint32_t)prop_array[i].id); + CAPI_SET_ERROR(result, CAPI_EBADPARAM); + } + } + else + { + MSIIR_MSG(miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Set, Property id 0x%lx Bad param size %lu", + (uint32_t)prop_array[i].id, + payload->actual_data_len); + payload->actual_data_len = + 0; // according to CAPI V2, actual len should be num of bytes read(set)/written(get) + CAPI_SET_ERROR(result, CAPI_ENEEDMORE); + } + break; + } + + case CAPI_HEAP_ID: + { + if (payload->actual_data_len >= sizeof(capi_heap_id_t)) + { + capi_heap_id_t *data_ptr = (capi_heap_id_t *)payload->data_ptr; + me->heap_id = data_ptr->heap_id; + } + else + { + MSIIR_MSG(miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Set, Property id 0x%lx Bad param size %lu", + (uint32_t)prop_array[i].id, + payload->actual_data_len); + payload->actual_data_len = + 0; // according to CAPI V2, actual len should be num of bytes read(set)/written(get) + CAPI_SET_ERROR(result, CAPI_ENEEDMORE); + } + break; + } + + case CAPI_ALGORITHMIC_RESET: + { + MSIIR_MSG(miid, DBG_HIGH_PRIO, "CAPI MSIIR : Algorithmic Reset"); + if (me->media_fmt_received) + { + for (uint32_t ch = 0; ch < me->media_fmt[0].format.num_channels; ch++) + { + msiir_set_param(&(me->msiir_lib[ch]), + MSIIR_PARAM_RESET, + NULL, + 0); // todo : Should we check for NULL ? + } + } + break; + } + + case CAPI_INPUT_MEDIA_FORMAT_V2: + { +#ifdef CAPI_MSIIR_DEBUG_MSG + MSIIR_MSG(miid, DBG_HIGH_PRIO, "CAPI MSIIR : Input Media Format Received."); +#endif + if (NULL == payload->data_ptr) + { + MSIIR_MSG(miid, DBG_ERROR_PRIO, + "CAPI FIR: Set property id 0x%lx, received null buffer.", + (uint32_t)prop_array[i].id); + CAPI_SET_ERROR(result, CAPI_EBADPARAM); + break; + } + uint32_t required_size = sizeof(capi_set_get_media_format_t) + sizeof(capi_standard_data_format_v2_t); + if (payload->actual_data_len >= required_size) + { + capi_media_fmt_v2_t *data_ptr = (capi_media_fmt_v2_t *)payload->data_ptr; + + if (data_ptr->format.minor_version >= CAPI_MEDIA_FORMAT_MINOR_VERSION) + { + if ((prop_array[i].port_info.is_valid && + (!prop_array[i].port_info.is_input_port || prop_array[i].port_info.port_index != 0)) || + (data_ptr->header.format_header.data_format != CAPI_FIXED_POINT)) + { + MSIIR_MSG(miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Set, failed to set Property id 0x%lx due to invalid/unexpected values", + (uint32_t)prop_array[i].id); + payload->actual_data_len = + 0; // according to CAPI V2, actual len should be num of bytes read(set)/written(get) + CAPI_SET_ERROR(result, CAPI_EBADPARAM); + continue; + } + + if (FALSE == capi_msiir_is_supported_media_type_v2(me, data_ptr)) + { + CAPI_SET_ERROR(result, CAPI_EUNSUPPORTED); + continue; + } + required_size += data_ptr->format.num_channels * sizeof(capi_channel_type_t); + if (payload->actual_data_len < required_size) + { + MSIIR_MSG(miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Set, Property id 0x%lx Bad param size %lu", + (uint32_t)prop_array[i].id, + payload->actual_data_len); + payload->actual_data_len = + 0; // according to CAPI V2, actual len should be num of bytes read(set)/written(get) + CAPI_SET_ERROR(result, CAPI_ENEEDMORE); + break; + } + if (me->media_fmt[0].format.num_channels != data_ptr->format.num_channels) + { + if (NULL != me->per_chan_msiir_cfg_max) + { + posal_memory_free(me->per_chan_msiir_cfg_max); + me->per_chan_msiir_cfg_max = NULL; + } + me->per_chan_msiir_cfg_max = (capi_one_chan_msiir_config_max_t *) + posal_memory_malloc(data_ptr->format.num_channels * sizeof(capi_one_chan_msiir_config_max_t), + (POSAL_HEAP_ID)me->heap_id); + if (NULL == me->per_chan_msiir_cfg_max) + { + MSIIR_MSG(miid, DBG_FATAL_PRIO, + "CAPI MSIIR : Out of Memory for per channel msiir config. mem_size requested : %lu", + data_ptr->format.num_channels * sizeof(capi_one_chan_msiir_config_max_t)); + return CAPI_ENOMEMORY; + } + } + me->media_fmt_received = FALSE; + me->media_fmt[0].header.format_header = data_ptr->header.format_header; + me->media_fmt[0].format = data_ptr->format; + me->media_fmt[0].format.minor_version = CAPI_MEDIA_FORMAT_MINOR_VERSION; + for (uint32_t i = 0; i < me->media_fmt[0].format.num_channels; i++) + { + me->media_fmt[0].channel_type[i] = data_ptr->channel_type[i]; + } + + CAPI_SET_ERROR(result, capi_msiir_init_capi(me, 1)); + + if (CAPI_SUCCEEDED(result)) + { + me->media_fmt_received = TRUE; + memset(&(me->channel_map_to_index), -1, sizeof(me->channel_map_to_index)); + for (uint32_t ch = 0; ch < me->media_fmt[0].format.num_channels; ch++) + { + me->channel_map_to_index[me->media_fmt[0].channel_type[ch]] = ch; + } + + if (CAPI_FAILED(capi_msiir_set_params_to_lib(me))) + { + MSIIR_MSG(miid, DBG_ERROR_PRIO, "CAPI MSIIR : Setting cached params to the library failed!!!"); + } + result = capi_msiir_check_raise_kpps_event(me, capi_msiir_get_kpps(me)); + result |= capi_msiir_check_raise_bw_event(me); + result |= capi_msiir_update_delay_event(me); + result |= capi_msiir_raise_process_check_event(me); + } + else + { + MSIIR_MSG(miid, DBG_ERROR_PRIO, "CAPI MSIIR : Failed to create MS-IIR library"); + } + } + else + { + MSIIR_MSG(miid, DBG_ERROR_PRIO, "CAPI MSIIR : Invalid Minor version %lu", data_ptr->format.minor_version); + CAPI_SET_ERROR(result, CAPI_EBADPARAM); + } + } + else + { + MSIIR_MSG(miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Set, Property id 0x%lx Bad param size %lu", + (uint32_t)prop_array[i].id, + payload->actual_data_len); + payload->actual_data_len = + 0; // according to CAPI V2, actual len should be num of bytes read(set)/written(get) + CAPI_SET_ERROR(result, CAPI_ENEEDMORE); + } + break; + } + + case CAPI_CUSTOM_INIT_DATA: + { + break; + } + case CAPI_MODULE_INSTANCE_ID: + { + if (payload->actual_data_len >= sizeof(capi_module_instance_id_t)) + { + capi_module_instance_id_t *data_ptr = (capi_module_instance_id_t *)payload->data_ptr; + me->miid = data_ptr->module_instance_id; + MSIIR_MSG(miid, + DBG_LOW_PRIO, + "This module-id 0x%08lX, instance-id 0x%08lX", + data_ptr->module_id, + me->miid); + } + else + { + MSIIR_MSG(miid, + DBG_ERROR_PRIO, + "Set property id 0x%lx, Bad param size %lu", + prop_array[i].id, + payload->max_data_len); + result |= CAPI_ENEEDMORE; + } + break; + } // CAPI_MODULE_INSTANCE_ID + default: + { + payload->actual_data_len = + 0; // according to CAPI V2, actual len should be num of bytes read(set)/written(get) + CAPI_SET_ERROR(result, CAPI_EUNSUPPORTED); + break; + } + } +#ifdef CAPI_MSIIR_DEBUG_MSG + if (CAPI_FAILED(result)) + { + MSIIR_MSG(miid, DBG_ERROR_PRIO, "CAPI MSIIR : Set property for 0x%x failed with opcode %lu", prop_array[i].id, result); + } +#endif + } + // create a temp result and later accumulate them in a main result + return result; +} + +capi_err_t capi_msiir_process_get_properties(capi_multistageiir_t *me, capi_proplist_t *props_ptr) +{ + capi_err_t result = CAPI_EOK; + + capi_prop_t *prop_ptr = props_ptr->prop_ptr; + uint32_t i = 0; + + uint32_t fwk_extn_ids_arr[] = { FWK_EXTN_CONTAINER_FRAME_DURATION }; + uint32_t miid = me ? me->miid : MIID_UNKNOWN; + + capi_basic_prop_t mod_prop; + capi_msiir_get_init_memory_req(&mod_prop.init_memory_req, miid); + mod_prop.stack_size = CAPI_MSIIR_STACK_SIZE; + mod_prop.num_fwk_extns = sizeof(fwk_extn_ids_arr) / sizeof(fwk_extn_ids_arr[0]); + mod_prop.fwk_extn_ids_arr = fwk_extn_ids_arr; + mod_prop.is_inplace = FALSE; // not capable of in-place processing of data + mod_prop.req_data_buffering = FALSE; + mod_prop.max_metadata_size = 0; // NA + result = capi_cmn_get_basic_properties(props_ptr, &mod_prop); + if (CAPI_EOK != result) + { + MSIIR_MSG(miid, DBG_ERROR_PRIO, "MSIIR: Get common basic properties failed with result %lu", result); + return result; + } + + for (i = 0; i < props_ptr->props_num; i++) + { + capi_buf_t *payload = &prop_ptr[i].payload; + // Ignore prop_ptr[i].port_info.is_valid + uint32_t miid = me ? me->miid : MIID_UNKNOWN; + + switch (prop_ptr[i].id) + { + case CAPI_INIT_MEMORY_REQUIREMENT: + case CAPI_IS_INPLACE: + case CAPI_REQUIRES_DATA_BUFFERING: + case CAPI_STACK_SIZE: + case CAPI_NUM_NEEDED_FRAMEWORK_EXTENSIONS: + case CAPI_INTERFACE_EXTENSIONS: + { + break; + } + case CAPI_OUTPUT_MEDIA_FORMAT_V2: + { + if (NULL == me || (prop_ptr[i].port_info.is_valid && + (prop_ptr[i].port_info.is_input_port || prop_ptr[i].port_info.port_index != 0))) + { + MSIIR_MSG(miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Get, failed to get Property id 0x%lx due to invalid/unexpected values", + (uint32_t)prop_ptr[i].id); + payload->actual_data_len = + 0; // according to CAPI V2, actual len should be num of bytes read(set)/written(get) + CAPI_SET_ERROR(result, CAPI_EFAILED); + continue; + } + uint32_t required_size = sizeof(me->media_fmt[0].header) + sizeof(me->media_fmt[0].format); + + if (payload->max_data_len >= required_size) + { + capi_media_fmt_v2_t *data_ptr = (capi_media_fmt_v2_t *)payload->data_ptr; + capi_channel_type_t *channel_type; + uint32_t channel_type_size = sizeof(capi_channel_type_t) * me->media_fmt[0].format.num_channels; + channel_type_size = CAPI_MSIIR_ALIGN_4_BYTE(channel_type_size); + required_size += channel_type_size; + + if (payload->max_data_len < required_size) + { + MSIIR_MSG(miid, DBG_ERROR_PRIO, + "CAPI MSIIR: Get property id 0x%lx, Bad param size %lu", + (uint32_t)prop_ptr[i].id, + payload->max_data_len); + CAPI_SET_ERROR(result, CAPI_ENEEDMORE); + break; + } + + data_ptr->header = me->media_fmt[0].header; + data_ptr->format = me->media_fmt[0].format; + channel_type = + (capi_channel_type_t *)(((int8_t *)data_ptr) + sizeof(data_ptr->header) + sizeof(data_ptr->format)); + memscpy(channel_type, + sizeof(capi_channel_type_t) * data_ptr->format.num_channels, + me->media_fmt[0].channel_type, + sizeof(me->media_fmt[0].channel_type)); + payload->actual_data_len = required_size; + } + else + { + MSIIR_MSG(miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Get, Property id 0x%lx Bad param size %lu", + (uint32_t)prop_ptr[i].id, + payload->max_data_len); + payload->actual_data_len = + 0; // according to CAPI V2, actual len should be num of bytes read(set)/written(get) + CAPI_SET_ERROR(result, CAPI_ENEEDMORE); + } + break; + } + break; + + default: + { + payload->actual_data_len = 0; + CAPI_SET_ERROR(result, CAPI_EUNSUPPORTED); + } + } + } + return result; +} + +capi_err_t capi_msiir_cache_params(capi_multistageiir_t *me, capi_buf_t *params_ptr, uint32_t param_id) +{ + capi_err_t result = CAPI_EOK; + uint32_t malloc_size = params_ptr->actual_data_len; + capi_cached_params_t *cache_param_data_ptr = NULL; + uint32_t max_param_size = 0; + + switch (param_id) + { + case PARAM_ID_MSIIR_TUNING_FILTER_ENABLE: + max_param_size = + sizeof(param_id_msiir_enable_t) + IIR_TUNING_FILTER_MAX_CHANNELS_V2 * sizeof(param_id_msiir_ch_enable_t); + cache_param_data_ptr = &me->enable_params; + me->enable_params.param_id_type = MULTISTAGE_IIR_MCHAN_PARAM; + me->cfg_version = VERSION_V1; // indicates V1 version is set now and can only allow V1 version + // configs for upcoming set params + break; + + case PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN: + max_param_size = + sizeof(param_id_msiir_pregain_t) + IIR_TUNING_FILTER_MAX_CHANNELS_V2 * sizeof(param_id_msiir_ch_pregain_t); + cache_param_data_ptr = &me->pregain_params; + me->pregain_params.param_id_type = MULTISTAGE_IIR_MCHAN_PARAM; + me->cfg_version = VERSION_V1; // indicates V1 version is set now and can only allow V1 version + // configs for upcoming set params + break; + + case PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS: + max_param_size = sizeof(param_id_msiir_config_t) + + sizeof(param_id_msiir_ch_filter_config_t) * IIR_TUNING_FILTER_MAX_CHANNELS_V2 + + sizeof(int32_t) * MSIIR_MAX_STAGES * MSIIR_COEFF_LENGTH * IIR_TUNING_FILTER_MAX_CHANNELS_V2 + + sizeof(int16_t) * MSIIR_MAX_STAGES * IIR_TUNING_FILTER_MAX_CHANNELS_V2; + cache_param_data_ptr = &me->config_params; + me->config_params.param_id_type = MULTISTAGE_IIR_MCHAN_PARAM; + me->cfg_version = VERSION_V1; // indicates V1 version is set now and can only allow V1 version + // configs for upcoming set params + break; + + case PARAM_ID_MSIIR_TUNING_FILTER_ENABLE_V2: + max_param_size = + sizeof(param_id_msiir_enable_v2_t) + IIR_TUNING_FILTER_MAX_CHANNELS_V2 * ( + sizeof(param_id_msiir_ch_enable_v2_t) + CAPI_CMN_MAX_CHANNEL_MAP_GROUPS * sizeof(uint32_t)); + cache_param_data_ptr = &me->enable_params; + me->enable_params.param_id_type = MULTISTAGE_IIR_MCHAN_PARAM; + me->cfg_version = VERSION_V2; // indicates V2 version is set now and can only allow V2 version + // configs for upcoming set params + break; + + case PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN_V2: + max_param_size = + sizeof(param_id_msiir_pregain_v2_t) + IIR_TUNING_FILTER_MAX_CHANNELS_V2 * ( + sizeof(param_id_msiir_ch_pregain_v2_t) + CAPI_CMN_MAX_CHANNEL_MAP_GROUPS * sizeof(uint32_t)); + cache_param_data_ptr = &me->pregain_params; + me->pregain_params.param_id_type = MULTISTAGE_IIR_MCHAN_PARAM; + me->cfg_version = VERSION_V2; // indicates V2 version is set now and can only allow V2 version + // configs for upcoming set params + break; + + case PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS_V2: + max_param_size = sizeof(param_id_msiir_config_v2_t) + + sizeof(param_id_msiir_ch_filter_config_v2_t) * IIR_TUNING_FILTER_MAX_CHANNELS_V2 * ( + sizeof(uint32_t) * CAPI_CMN_MAX_CHANNEL_MAP_GROUPS + + sizeof(int32_t) * MSIIR_MAX_STAGES * MSIIR_COEFF_LENGTH + + sizeof(int16_t) * MSIIR_MAX_STAGES); + + cache_param_data_ptr = &me->config_params; + me->config_params.param_id_type = MULTISTAGE_IIR_MCHAN_PARAM; + me->cfg_version = VERSION_V2; // indicates V2 version is set now and can only allow V2 version + // configs for upcoming set params + break; + default: + return CAPI_EUNSUPPORTED; + break; + } + + malloc_size = (max_param_size > params_ptr->actual_data_len) ? params_ptr->actual_data_len : max_param_size; + + if ((cache_param_data_ptr->params_ptr.max_data_len != malloc_size) || + (cache_param_data_ptr->params_ptr.data_ptr == NULL)) + { + if (cache_param_data_ptr->params_ptr.data_ptr != NULL) + { + posal_memory_free(cache_param_data_ptr->params_ptr.data_ptr); + cache_param_data_ptr->params_ptr.data_ptr = NULL; + cache_param_data_ptr->params_ptr.max_data_len = 0; + cache_param_data_ptr->params_ptr.actual_data_len = 0; + } + + cache_param_data_ptr->params_ptr.data_ptr = + (int8_t *)posal_memory_malloc(malloc_size, (POSAL_HEAP_ID)me->heap_id); + if (cache_param_data_ptr->params_ptr.data_ptr == NULL) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : " + "Memory allocation failed for size %lu", + malloc_size); + cache_param_data_ptr->params_ptr.max_data_len = 0; + cache_param_data_ptr->params_ptr.actual_data_len = 0; + return CAPI_ENOMEMORY; + } + + cache_param_data_ptr->params_ptr.max_data_len = malloc_size; + cache_param_data_ptr->params_ptr.actual_data_len = 0; + } + + cache_param_data_ptr->params_ptr.actual_data_len = memscpy(cache_param_data_ptr->params_ptr.data_ptr, + cache_param_data_ptr->params_ptr.max_data_len, + params_ptr->data_ptr, + params_ptr->actual_data_len); + + return result; +} + +capi_err_t capi_msiir_set_params_to_lib(capi_multistageiir_t *me) +{ + capi_err_t temp_result = CAPI_EOK, result = CAPI_EOK; +#ifdef CAPI_MSIIR_DEBUG_MSG + MSIIR_MSG(me->miid, DBG_HIGH_PRIO, "CAPI MSIIR : Setting the cached params if any"); +#endif + + switch (me->enable_params.param_id_type) + { + case MULTISTAGE_IIR_MCHAN_PARAM: + if (me->enable_params.params_ptr.data_ptr != NULL) + { + if ((VERSION_V1 == me->cfg_version) && (FALSE == me->higher_channel_map_present)) + { + temp_result = capi_msiir_set_enable_disable_per_channel(me, &me->enable_params.params_ptr); + } + else if (VERSION_V2 == me->cfg_version) + { + temp_result = capi_msiir_set_enable_disable_per_channel_v2(me, &me->enable_params.params_ptr, + PARAM_ID_MSIIR_TUNING_FILTER_ENABLE_V2); + } + else + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : Unsupported config version set"); + result = CAPI_EUNSUPPORTED; + } + } + break; + } + result |= temp_result; + + switch (me->pregain_params.param_id_type) + { + case MULTISTAGE_IIR_MCHAN_PARAM: + if (me->pregain_params.params_ptr.data_ptr != NULL) + { + if ((VERSION_V1 == me->cfg_version) && (FALSE == me->higher_channel_map_present)) + { + temp_result = capi_msiir_set_pregain_per_channel(me, &me->pregain_params.params_ptr); + } + else if (VERSION_V2 == me->cfg_version) + { + temp_result = capi_msiir_set_pregain_per_channel_v2(me, &me->pregain_params.params_ptr, + PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN_V2); + } + else + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : Unsupported config version set"); + result = CAPI_EUNSUPPORTED; + } + } + break; + } + result |= temp_result; + switch (me->config_params.param_id_type) + { + case MULTISTAGE_IIR_MCHAN_PARAM: + if (me->config_params.params_ptr.data_ptr != NULL) + { + if ((VERSION_V1 == me->cfg_version) && (FALSE == me->higher_channel_map_present)) + { + temp_result = capi_msiir_set_config_per_channel(me, &me->config_params.params_ptr); + } + else if (VERSION_V2 == me->cfg_version) + { + temp_result = capi_msiir_set_config_per_channel_v2(me, &me->config_params.params_ptr, + PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS_V2); + } + else + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : Unsupported config version set"); + result = CAPI_EUNSUPPORTED; + } + } + break; + } + result |= temp_result; + return result; +} + +// to check if the same channel is set at two different configs +bool_t check_channel_mask_msiir(uint8_t *iir_param_ptr, uint32_t param_id) +{ + uint32_t num_config = 0; + uint64_t check_channel_mask = 0x0; + uint64_t current_channel_mask = 0x0; + bool_t check = TRUE; + uint32_t offset = 0; + uint32_t buf_var = 0; + offset = memscpy(&num_config, sizeof(uint32_t), iir_param_ptr, sizeof(uint32_t)); + uint8_t *data_ptr = iir_param_ptr; + uint8_t *data_ptr_read = iir_param_ptr; + + for (uint32_t cnfg_cntr = 0; cnfg_cntr < num_config; cnfg_cntr++) + { + data_ptr += offset; + offset = 0; + data_ptr_read = data_ptr; + // present configuration + offset += memscpy(¤t_channel_mask, sizeof(uint64_t), data_ptr, sizeof(uint64_t)); + data_ptr_read += offset; + + switch (param_id) + { + + case PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS: + { + data_ptr_read += sizeof(uint16_t); // for reserved field + offset += sizeof(uint16_t); + offset += memscpy(&buf_var, + sizeof(uint16_t), + data_ptr_read, + sizeof(uint16_t)); // buf_var here is used to store number of biquad stages + offset += buf_var * (MSIIR_COEFF_LENGTH * sizeof(int32_t) + sizeof(int16_t)); + if (buf_var & 0x1) + { + data_ptr += sizeof(int16_t); // 16-bits padding + } + break; + } + default: + { + // default case is for both enable and pregain params + offset += sizeof(uint32_t); + } + } + + current_channel_mask = current_channel_mask >> 1; // ignoring the reserve bit + if (!(check_channel_mask & current_channel_mask)) + { + check_channel_mask |= current_channel_mask; + } + else + { + check = FALSE; + return check; + } + } + return check; +} + +capi_err_t capi_msiir_set_enable_disable_per_channel(capi_multistageiir_t *me, const capi_buf_t *params_ptr) +{ + + param_id_msiir_enable_t *iir_enable_ptr = (param_id_msiir_enable_t *)(params_ptr->data_ptr); + uint32_t num_configs = iir_enable_ptr->num_config; + uint32_t required_payload_size = + (uint32_t)(sizeof(iir_enable_ptr->num_config) + (num_configs * sizeof(param_id_msiir_ch_enable_t))); + + if (params_ptr->actual_data_len < required_payload_size) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Set Enable, Bad param size %lu, required %lu", + params_ptr->actual_data_len, + required_payload_size); + return AR_ENEEDMORE; + } + // validating the payload received + if (!check_channel_mask_msiir((uint8_t *)iir_enable_ptr, PARAM_ID_MSIIR_TUNING_FILTER_ENABLE)) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : Received bad enable param "); + return CAPI_EBADPARAM; + } + + uint64_t channel_mask = 0; + // loop through all the configs + for (uint32_t cnfg_cntr = 0; cnfg_cntr < num_configs; cnfg_cntr++) + { + channel_mask = (uint64_t)iir_enable_ptr->enable_flag_settings[cnfg_cntr].channel_mask_msb << 32 | + iir_enable_ptr->enable_flag_settings[cnfg_cntr].channel_mask_lsb; + if (channel_mask & 0x1) + { + MSIIR_MSG(me->miid, DBG_LOW_PRIO, "capi_msiir: Warning Set enable: reserve bit is set 1"); + } + // setting the mute data channel wise (channels can be in the range 1 ~ MSIIR_MAX_CHANNEL) + for (uint32_t j = 1; j < (MSIIR_MAX_CHANNEL + 1); j++) + { + channel_mask = channel_mask >> 1; + if (channel_mask & 0x1) + { + // convert channel_type to channel index, channel index has range 0 ~ 7 + int32_t ch_idx = me->channel_map_to_index[j]; + if ((ch_idx < 0) || (ch_idx >= (int32_t)me->media_fmt[0].format.num_channels)) + { + continue; + } + me->enable_flag[ch_idx] = iir_enable_ptr->enable_flag_settings[cnfg_cntr].enable_flag; + MSIIR_MSG(me->miid, DBG_HIGH_PRIO, "CAPI MSIIR : Set enable: channel: %hu enable: %lu", j, me->enable_flag[ch_idx]); + } + } + } + capi_msiir_raise_process_check_event(me); + capi_msiir_update_delay_event(me); + return CAPI_EOK; +} + +capi_err_t capi_msiir_set_pregain_per_channel(capi_multistageiir_t *me, const capi_buf_t *params_ptr) +{ + MSIIR_RESULT result_lib = MSIIR_SUCCESS; + + param_id_msiir_pregain_t *iir_pregain_ptr = (param_id_msiir_pregain_t *)(params_ptr->data_ptr); + + uint32_t num_configs = iir_pregain_ptr->num_config; + + uint32_t required_payload_size = + (uint32_t)(sizeof(iir_pregain_ptr->num_config) + (num_configs * sizeof(param_id_msiir_ch_pregain_t))); + + if (params_ptr->actual_data_len < required_payload_size) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Set pregain, Bad param size %lu, required %lu", + params_ptr->actual_data_len, + required_payload_size); + return CAPI_ENEEDMORE; + } + + // validating the payload received + if (!check_channel_mask_msiir((uint8_t *)iir_pregain_ptr, PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN)) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : Received bad pregain param "); + return CAPI_EBADPARAM; + } + + uint64_t channel_mask = 0; + // loop through all the configs + for (uint32_t cnfg_cntr = 0; cnfg_cntr < num_configs; cnfg_cntr++) + { + channel_mask = (uint64_t)iir_pregain_ptr->pre_gain_settings[cnfg_cntr].channel_mask_msb << 32 | + iir_pregain_ptr->pre_gain_settings[cnfg_cntr].channel_mask_lsb; + + if (channel_mask & 0x1) + { + MSIIR_MSG(me->miid, DBG_LOW_PRIO, "capi_msiir: Warning Set pregain: reserve bit is set 1"); + } + // setting the gain data channel wise + for (uint32_t j = 1; j < MSIIR_MAX_CHANNEL + 1; j++) + { + channel_mask = channel_mask >> 1; + if (channel_mask & 0x1) + { + // convert channel_type to channel index, channel index has range 0 ~ 7 + int32_t ch_idx = me->channel_map_to_index[j]; + if ((ch_idx < 0) || (ch_idx >= (int32_t)me->media_fmt[0].format.num_channels)) + { + continue; + } + msiir_pregain_t pregain = (msiir_pregain_t)(iir_pregain_ptr->pre_gain_settings[cnfg_cntr].pregain); + if ((me->is_first_frame) || (me->per_chan_msiir_pregain[ch_idx] == pregain)) + { + result_lib = + msiir_set_param(&(me->msiir_lib[ch_idx]), MSIIR_PARAM_PREGAIN, (void *)&pregain, sizeof(pregain)); + if (MSIIR_SUCCESS != result_lib) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : Set pregain error %d", result_lib); + return CAPI_EFAILED; + } + MSIIR_MSG(me->miid, DBG_HIGH_PRIO, "CAPI MSIIR : Set pregain: channel: %hu pregain: 0x%ld", j, pregain); + } + else + { + me->start_cross_fade = TRUE; + } + // save the new configuration + me->per_chan_msiir_pregain[ch_idx] = pregain; + } + } + } + return CAPI_EOK; +} + +capi_err_t capi_msiir_set_config_per_channel(capi_multistageiir_t *me, const capi_buf_t *params_ptr) +{ + param_id_msiir_config_t *iir_cfg_ptr = (param_id_msiir_config_t *)(params_ptr->data_ptr); + uint32_t num_configs = iir_cfg_ptr->num_config; + uint64_t channel_mask = 0; + // validating the payload received + if (!check_channel_mask_msiir((uint8_t *)iir_cfg_ptr, PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS)) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : Received bad config param "); + return CAPI_EBADPARAM; + } + uint8_t *data_ptr = (uint8_t *)(((uint8_t *)(params_ptr->data_ptr)) + sizeof(param_id_msiir_config_t)); + uint8_t *data_ptr_1 = (uint8_t *)(((uint8_t *)(params_ptr->data_ptr)) + sizeof(param_id_msiir_config_t)); + uint32_t offset = 0; + + for (uint32_t cnfg_cntr = 0; cnfg_cntr < num_configs; cnfg_cntr++) + { + data_ptr_1 += offset; + param_id_msiir_ch_filter_config_t *this_chan_cfg_inp_ptr = (param_id_msiir_ch_filter_config_t *)data_ptr_1; + channel_mask = (uint64_t)this_chan_cfg_inp_ptr->channel_mask_msb << 32 | this_chan_cfg_inp_ptr->channel_mask_lsb; + + if (channel_mask & 0x1) + { + MSIIR_MSG(me->miid, DBG_LOW_PRIO, "capi_msiir: Warning Set config: reserve bit is set 1"); + } + for (uint32_t j = 1; j < MSIIR_MAX_CHANNEL + 1; j++) + { + data_ptr = data_ptr_1; + channel_mask = channel_mask >> 1; + if (channel_mask & 0x1) + { + uint8_t channel_type = j; + int32_t ch_idx = me->channel_map_to_index[channel_type]; + int32_t num_biquad_stages = (int32_t)(this_chan_cfg_inp_ptr->num_biquad_stages); + + MSIIR_MSG(me->miid, DBG_HIGH_PRIO, + "CAPI MSIIR : Set config for channel_type %d, stages %lu ch_idx %lu", + channel_type, + num_biquad_stages, + ch_idx); + + if ((ch_idx < 0) || (ch_idx >= (int32_t)me->media_fmt[0].format.num_channels)) + { + offset = sizeof(param_id_msiir_ch_filter_config_t); + offset += num_biquad_stages * MSIIR_COEFF_LENGTH * sizeof(int32_t); + offset += num_biquad_stages * sizeof(int16_t); + offset += (num_biquad_stages & 0x1) ? sizeof(int16_t): 0; + continue; + } + offset = 0; + if ((!me->is_first_frame) && (num_biquad_stages != me->per_chan_msiir_cfg_max[ch_idx].num_stages)) + { + // do cross fading if num stages changed in the middle of data processing + me->start_cross_fade = TRUE; + } + + me->per_chan_msiir_cfg_max[ch_idx].num_stages = num_biquad_stages; + + // move pointer to filter coeffs of current channel + data_ptr += sizeof(param_id_msiir_ch_filter_config_t); + offset += sizeof(param_id_msiir_ch_filter_config_t); + + int32_t *coeff_ptr = (int32_t *)data_ptr; + + for (int32_t stage = 0; stage < num_biquad_stages; stage++) + { + for (int32_t idx = 0; idx < MSIIR_COEFF_LENGTH; idx++) + { + int32_t iir_coeff = *coeff_ptr++; + + if ((!me->is_first_frame) && + (iir_coeff != me->per_chan_msiir_cfg_max[ch_idx].coeffs_struct[stage].iir_coeffs[idx])) + { + me->start_cross_fade = TRUE; + } + + me->per_chan_msiir_cfg_max[ch_idx].coeffs_struct[stage].iir_coeffs[idx] = iir_coeff; + } + } + + size_t coeff_size = num_biquad_stages * MSIIR_COEFF_LENGTH * sizeof(int32_t); + + // move pointer to numerator shift factors of current channel + data_ptr += coeff_size; + offset += coeff_size; + + int16_t *shift_factor_ptr = (int16_t *)data_ptr; + + for (int32_t stage = 0; stage < num_biquad_stages; stage++) + { + int32_t shift_factor = (int32_t)(*shift_factor_ptr++); + + if ((!me->is_first_frame) && + (shift_factor != me->per_chan_msiir_cfg_max[ch_idx].coeffs_struct[stage].shift_factor)) + { + me->start_cross_fade = TRUE; + } + + me->per_chan_msiir_cfg_max[ch_idx].coeffs_struct[stage].shift_factor = shift_factor; + } + + size_t numerator_shift_fac_size = num_biquad_stages * sizeof(int16_t); + + data_ptr += numerator_shift_fac_size; + offset += numerator_shift_fac_size; + + // move pointer to skip the padding bit if necessary + bool_t is_odd_stages = (bool_t)(num_biquad_stages & 0x1); + if (is_odd_stages) + { + data_ptr += sizeof(int16_t); // 16-bits padding + offset += sizeof(int16_t); + } + + if (!me->start_cross_fade) + { + uint32_t param_size = sizeof(me->per_chan_msiir_cfg_max[ch_idx].num_stages) + + num_biquad_stages * MSIIR_COEFF_LENGTH * sizeof(int32_t) + + num_biquad_stages * sizeof(int32_t); + + msiir_set_param(&(me->msiir_lib[ch_idx]), + MSIIR_PARAM_CONFIG, + (void *)&(me->per_chan_msiir_cfg_max[ch_idx]), + param_size); + + // when we reach here, data_ptr should points to the start of next channel's + // config params (param_id_channel_type_msiir_config_pair_t followed by filter coeff and + // num_shift_factor + MSIIR_MSG(me->miid, DBG_HIGH_PRIO, + "CAPI MSIIR : Set config for channel_type %d, stages %lu", + channel_type, + num_biquad_stages); + capi_msiir_update_delay_event(me); + } + } + } + } + + return CAPI_EOK; +} + +capi_err_t capi_msiir_get_enable_disable_per_channel(capi_multistageiir_t *me, capi_buf_t *params_ptr) +{ + if (me->enable_params.params_ptr.data_ptr) + { + if (params_ptr->max_data_len < me->enable_params.params_ptr.actual_data_len) + { + params_ptr->actual_data_len = me->enable_params.params_ptr.actual_data_len; + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Get Enable/Disable, Bad payload size %lu: actual = %lu", + params_ptr->max_data_len, + me->enable_params.params_ptr.actual_data_len); + + return CAPI_ENEEDMORE; + } + params_ptr->actual_data_len = memscpy(params_ptr->data_ptr, + params_ptr->max_data_len, + me->enable_params.params_ptr.data_ptr, + me->enable_params.params_ptr.actual_data_len); + } + else if (me->media_fmt_received) + { + uint32_t num_channels = me->media_fmt[0].format.num_channels; + uint32_t num_config = 0; + uint32_t enable_channel_mask_lsb = 0; + uint32_t enable_channel_mask_msb = 0; + uint32_t disable_channel_mask_lsb = 0; + uint32_t disable_channel_mask_msb = 0; + + for (uint32_t ch = 0; ch < num_channels; ch++) + { + uint8_t ctype = me->media_fmt[0].channel_type[ch]; + if (me->enable_flag[ch] == 1) + { + if (ctype < MSIIR_MASK_WIDTH) // getting enable params for channels 1 ~ 31 (in channel_mask_lsb) + { + enable_channel_mask_lsb = enable_channel_mask_lsb | (1 << ctype); + } + else // getting enable params for 32 ~ 63 (in channel_mask_msb) + { + enable_channel_mask_msb = enable_channel_mask_msb | (1 << (ctype - 32)); + } + } + else + { + if (ctype < MSIIR_MASK_WIDTH) // getting enable params for channels 1 ~ 31 (in channel_mask_lsb) + { + disable_channel_mask_lsb = disable_channel_mask_lsb | (1 << ctype); + } + else // getting enable params for 32 ~ 63 (in channel_mask_msb) + { + disable_channel_mask_msb = disable_channel_mask_msb | (1 << (ctype - 32)); + } + } + } + + if (disable_channel_mask_lsb | disable_channel_mask_msb) + { + num_config++; + } + if (enable_channel_mask_lsb | enable_channel_mask_msb) + { + num_config++; + } + + param_id_msiir_enable_t *iir_enable_pkt_ptr = (param_id_msiir_enable_t *)(params_ptr->data_ptr); + uint32_t required_payload_size = (uint32_t)(sizeof(uint32_t) + num_config * sizeof(param_id_msiir_ch_enable_t)); + + if (params_ptr->max_data_len < required_payload_size) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : Get Enable/Disable, Bad payload size %lu", params_ptr->max_data_len); + params_ptr->actual_data_len = 0; + return CAPI_ENEEDMORE; + } + + iir_enable_pkt_ptr->num_config = 0; + iir_enable_pkt_ptr->enable_flag_settings[0].channel_mask_lsb = 0; + iir_enable_pkt_ptr->enable_flag_settings[0].channel_mask_msb = 0; + + if (disable_channel_mask_lsb | disable_channel_mask_msb) + { + iir_enable_pkt_ptr->enable_flag_settings[iir_enable_pkt_ptr->num_config].channel_mask_lsb = disable_channel_mask_lsb; + iir_enable_pkt_ptr->enable_flag_settings[iir_enable_pkt_ptr->num_config].channel_mask_msb = disable_channel_mask_msb; + iir_enable_pkt_ptr->enable_flag_settings[iir_enable_pkt_ptr->num_config].enable_flag = 0; + iir_enable_pkt_ptr->num_config ++; + + MSIIR_MSG(me->miid, DBG_HIGH_PRIO, "CAPI MSIIR : Get Enable/Disable, Disabled channel mask lsb: %lu, msb: %lu", + disable_channel_mask_lsb, + disable_channel_mask_msb); + } + if (enable_channel_mask_lsb | enable_channel_mask_msb) + { + iir_enable_pkt_ptr->enable_flag_settings[iir_enable_pkt_ptr->num_config].channel_mask_lsb = enable_channel_mask_lsb; + iir_enable_pkt_ptr->enable_flag_settings[iir_enable_pkt_ptr->num_config].channel_mask_msb = enable_channel_mask_msb; + iir_enable_pkt_ptr->enable_flag_settings[iir_enable_pkt_ptr->num_config].enable_flag = 1; + iir_enable_pkt_ptr->num_config ++; + + MSIIR_MSG(me->miid, DBG_HIGH_PRIO, "CAPI MSIIR : Get Enable/Disable, Enabled channel mask lsb: %lu, msb: %lu", + enable_channel_mask_lsb, + enable_channel_mask_msb); + } + params_ptr->actual_data_len = required_payload_size; + } + else + { + params_ptr->actual_data_len = 0; + return CAPI_EBADPARAM; + } + return CAPI_EOK; +} + +capi_err_t capi_msiir_get_pregain_per_channel(capi_multistageiir_t *me, capi_buf_t *params_ptr) +{ + if (me->pregain_params.params_ptr.data_ptr) + { + if (params_ptr->max_data_len < me->pregain_params.params_ptr.actual_data_len) + { + params_ptr->actual_data_len = me->pregain_params.params_ptr.actual_data_len; + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Get pregain, Bad payload size %lu: actual = %lu", + params_ptr->max_data_len, + me->pregain_params.params_ptr.actual_data_len); + + return CAPI_ENEEDMORE; + } + params_ptr->actual_data_len = memscpy(params_ptr->data_ptr, + params_ptr->max_data_len, + me->pregain_params.params_ptr.data_ptr, + me->pregain_params.params_ptr.actual_data_len); + } + else if (me->media_fmt_received) + { + param_id_msiir_pregain_t *iir_pregain_pkt_ptr = (param_id_msiir_pregain_t *)(params_ptr->data_ptr); + + uint32_t num_channels = me->media_fmt[0].format.num_channels; + uint32_t required_payload_size = + (uint32_t)(sizeof(iir_pregain_pkt_ptr->num_config) + (num_channels * sizeof(param_id_msiir_ch_pregain_t))); + if (params_ptr->max_data_len < required_payload_size) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : Get pregain, Bad payload size %lu", params_ptr->max_data_len); + return CAPI_ENEEDMORE; + } + + iir_pregain_pkt_ptr->num_config = num_channels; + // getting one channel params per config + + for (uint32_t ch = 0; ch < num_channels; ch++) + { + uint8_t channel_type_by_reinit = me->media_fmt[0].channel_type[ch]; + if (channel_type_by_reinit < MSIIR_MASK_WIDTH) // for checking channels from 1~31 in channel_mask_lsb + { + iir_pregain_pkt_ptr->pre_gain_settings[ch].channel_mask_lsb = + iir_pregain_pkt_ptr->pre_gain_settings[ch].channel_mask_lsb | (1 << channel_type_by_reinit); + } + else // for checking channels from 32~63 in channel_mask_msb + { + iir_pregain_pkt_ptr->pre_gain_settings[ch].channel_mask_msb = + iir_pregain_pkt_ptr->pre_gain_settings[ch].channel_mask_msb | (1 << (channel_type_by_reinit - 32)); + } + // unity gain in Q27 + msiir_pregain_t pregain_lib = (msiir_pregain_t)((int32_t)(1 << MSIIR_Q_PREGAIN)); + + uint32_t actual_param_size = 0; + MSIIR_RESULT result_lib = MSIIR_SUCCESS; + + if ((me->cross_fade_flag[ch] == 0) || (me->msiir_new_lib[ch].mem_ptr == NULL)) + { + result_lib = msiir_get_param(&(me->msiir_lib[ch]), + MSIIR_PARAM_PREGAIN, + (void *)&pregain_lib, + sizeof(pregain_lib), + &actual_param_size); + + if (actual_param_size > sizeof(pregain_lib) || (MSIIR_SUCCESS != result_lib)) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : get pregain error result %d, size %lu", + result_lib, + actual_param_size); + return CAPI_EFAILED; + } + } + else + { + result_lib = msiir_get_param(&(me->msiir_new_lib[ch]), + MSIIR_PARAM_PREGAIN, + (void *)&pregain_lib, + sizeof(pregain_lib), + &actual_param_size); + if (actual_param_size > sizeof(pregain_lib) || (MSIIR_SUCCESS != result_lib)) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : get pregain error result %d, size %lu", + result_lib, + actual_param_size); + return CAPI_EFAILED; + } + } + + int32_t pregain = (int32_t)pregain_lib; + + iir_pregain_pkt_ptr->pre_gain_settings[ch].pregain = pregain; + } + + params_ptr->actual_data_len = required_payload_size; + } + else + { + params_ptr->actual_data_len = 0; + return CAPI_EBADPARAM; + } + + return CAPI_EOK; +} + +capi_err_t capi_msiir_get_config_per_channel(capi_multistageiir_t *me, capi_buf_t *params_ptr) +{ + if (me->config_params.params_ptr.data_ptr) + { + if (params_ptr->max_data_len < me->config_params.params_ptr.actual_data_len) + { + params_ptr->actual_data_len = me->config_params.params_ptr.actual_data_len; + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Get IIR configuration, Bad payload size %lu, Actual = %lu", + params_ptr->max_data_len, + params_ptr->actual_data_len); + + return CAPI_ENEEDMORE; + } + + params_ptr->actual_data_len = memscpy(params_ptr->data_ptr, + params_ptr->max_data_len, + me->config_params.params_ptr.data_ptr, + me->config_params.params_ptr.actual_data_len); + } + else if (me->media_fmt_received) + { + + param_id_msiir_config_t *iir_config_pkt_ptr = (param_id_msiir_config_t *)(params_ptr->data_ptr); + uint8_t *data_ptr = (uint8_t *)(((uint8_t *)(params_ptr->data_ptr)) + sizeof(param_id_msiir_config_t)); + + uint32_t num_channels = me->media_fmt[0].format.num_channels; + iir_config_pkt_ptr->num_config = num_channels; + // getting one channel's tuning config params per each config + + uint32_t actual_size = sizeof(param_id_msiir_config_t); + + for (uint32_t ch = 0; ch < num_channels; ch++) + { + param_id_msiir_ch_filter_config_t *this_chan_cfg_ptr = (param_id_msiir_ch_filter_config_t *)data_ptr; + + uint8_t channel_type_by_reinit = me->media_fmt[0].channel_type[ch]; + this_chan_cfg_ptr->channel_mask_lsb = 0; + this_chan_cfg_ptr->channel_mask_msb = 0; + if (channel_type_by_reinit < 32) + { + this_chan_cfg_ptr->channel_mask_lsb = (1 << channel_type_by_reinit); + } + else if (channel_type_by_reinit < 64) + { + this_chan_cfg_ptr->channel_mask_msb = (1 << (channel_type_by_reinit - 32)); + } + + this_chan_cfg_ptr->reserved = 0; + + capi_one_chan_msiir_config_max_t *one_chan_msiir_config_max_ptr = &(me->default_per_chan_msiir_cfg_max); + // if the filter is disabled, the library uses default 0 stages for copy through + int32_t num_biquad_stages = 0; + + uint32_t actual_param_size = 0; + MSIIR_RESULT result_lib = MSIIR_SUCCESS; + + one_chan_msiir_config_max_ptr = &(me->per_chan_msiir_cfg_max[ch]); + if ((me->cross_fade_flag[ch] == 0) || (me->msiir_new_lib[ch].mem_ptr == NULL)) + { +#ifdef CAPI_MSIIR_DEBUG_MSG + MSIIR_MSG(me->miid, DBG_LOW_PRIO, "CAPI MSIIR : Config get param from CUR_INST "); +#endif + result_lib = msiir_get_param(&(me->msiir_lib[ch]), + MSIIR_PARAM_CONFIG, + (void *)one_chan_msiir_config_max_ptr, + sizeof(me->per_chan_msiir_cfg_max[ch]), + &actual_param_size); + if (actual_param_size > sizeof(me->per_chan_msiir_cfg_max[ch]) || (MSIIR_SUCCESS != result_lib)) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : get delay error result %d, size %lu", + result_lib, + actual_param_size); + return CAPI_EFAILED; + } + } + else + { +#ifdef CAPI_MSIIR_DEBUG_MSG + MSIIR_MSG(me->miid, DBG_LOW_PRIO, "CAPI MSIIR : Config get param from NEW_INST because crossfading "); +#endif + result_lib = msiir_get_param(&(me->msiir_new_lib[ch]), + MSIIR_PARAM_CONFIG, + (void *)one_chan_msiir_config_max_ptr, + sizeof(me->per_chan_msiir_cfg_max[ch]), + &actual_param_size); + if (actual_param_size > sizeof(me->per_chan_msiir_cfg_max[ch]) || (MSIIR_SUCCESS != result_lib)) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : get delay error result %d, size %lu", + result_lib, + actual_param_size); + return CAPI_EFAILED; + } + } + + num_biquad_stages = one_chan_msiir_config_max_ptr->num_stages; + + if (num_biquad_stages > (int32_t)MSIIR_MAX_STAGES) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Get config params, bad internal num_biquad_stages %d", + (int)num_biquad_stages); + return CAPI_EFAILED; + } + + this_chan_cfg_ptr->num_biquad_stages = num_biquad_stages; + + // move pointer to filter coeffs + data_ptr += sizeof(param_id_msiir_ch_filter_config_t); + actual_size += sizeof(param_id_msiir_ch_filter_config_t); + + int32_t *coeff_ptr = (int32_t *)data_ptr; + for (int32_t stage = 0; stage < num_biquad_stages; stage++) + { + for (int32_t idx = 0; idx < MSIIR_COEFF_LENGTH; idx++) + { + *coeff_ptr++ = one_chan_msiir_config_max_ptr->coeffs_struct[stage].iir_coeffs[idx]; + } + } + + size_t coeff_size = num_biquad_stages * MSIIR_COEFF_LENGTH * sizeof(int32_t); + + // move pointer to numerator shift factors + data_ptr += coeff_size; + // add size of filter coeffs + actual_size += coeff_size; + + int16_t *shift_factor_ptr = (int16_t *)data_ptr; + + for (int32_t stage = 0; stage < num_biquad_stages; stage++) + { + *shift_factor_ptr++ = (int16_t)(one_chan_msiir_config_max_ptr->coeffs_struct[stage].shift_factor); + } + + size_t num_shift_fac_size = num_biquad_stages * sizeof(int16_t); + + // move pointer to config params of next channel + data_ptr += num_shift_fac_size; + // add size of numerator shift factors + actual_size += num_shift_fac_size; + + // add padding if odd stages + bool_t is_odd_stages = (bool_t)(num_biquad_stages & 0x1); + if (is_odd_stages) + { + data_ptr += sizeof(int16_t); + actual_size += sizeof(int16_t); // 16-bits padding + } + + } // for (ch) + + params_ptr->actual_data_len = actual_size; + + if (params_ptr->max_data_len < actual_size) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Get config params, Bad payload size %d, required %d", + (int)params_ptr->max_data_len, + (int)actual_size); + return CAPI_ENEEDMORE; + } + } + else + { + params_ptr->actual_data_len = 0; + return CAPI_EBADPARAM; + } + return CAPI_EOK; +} + +// This function should be called after msiir_init_mem, so that the library's +// default configuration parameters are retrieved and saved by APPI +capi_err_t capi_msiir_get_default_pregain_config(capi_multistageiir_t *me, uint32_t ch) +{ + MSIIR_RESULT result_lib = MSIIR_SUCCESS; + + msiir_pregain_t pregain_lib = (msiir_pregain_t)((int32_t)(1 << MSIIR_Q_PREGAIN)); + + uint32_t actual_param_size = 0; + + // get the pregain + result_lib = msiir_get_param(&(me->msiir_lib[ch]), + MSIIR_PARAM_PREGAIN, + (void *)&pregain_lib, + sizeof(pregain_lib), + &actual_param_size); + if (actual_param_size > sizeof(pregain_lib) || (MSIIR_SUCCESS != result_lib)) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : get default pregain error result %d, size %lu", + result_lib, + actual_param_size); + return CAPI_EFAILED; + } + me->per_chan_msiir_pregain[ch] = pregain_lib; + + // get the config (num_stages, coeffs and shift factors) + capi_one_chan_msiir_config_max_t *one_chan_msiir_config_max_ptr = &(me->per_chan_msiir_cfg_max[ch]); + + result_lib = msiir_get_param(&(me->msiir_lib[ch]), + MSIIR_PARAM_CONFIG, + (void *)one_chan_msiir_config_max_ptr, + sizeof(me->per_chan_msiir_cfg_max[ch]), + &actual_param_size); + if (actual_param_size > sizeof(me->per_chan_msiir_cfg_max[ch]) || (MSIIR_SUCCESS != result_lib)) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : get default config error result %d, size %lu", + result_lib, + actual_param_size); + return CAPI_EFAILED; + } + + return CAPI_EOK; +} + +capi_err_t capi_msiir_start_cross_fade(capi_multistageiir_t *me) +{ + CROSS_FADE_RESULT result_cross_fade_lib = CROSS_FADE_SUCCESS; + MSIIR_RESULT result_msiir_lib = MSIIR_SUCCESS; + + for (uint32_t ch = 0; ch < me->media_fmt[0].format.num_channels; ch++) + { + uint32_t param_size = 0; + + result_cross_fade_lib = audio_cross_fade_get_param(&(me->cross_fade_lib[ch]), + CROSS_FADE_PARAM_MODE, + (int8 *)&(me->cross_fade_flag[ch]), + (uint32)sizeof(me->cross_fade_flag[ch]), + (uint32 *)¶m_size); + if ((CROSS_FADE_SUCCESS != result_cross_fade_lib) || (0 == param_size)) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : setup cross fade failed"); + return CAPI_EFAILED; + } + + if (1 == me->cross_fade_flag[ch]) + { +#ifdef CAPI_MSIIR_DEBUG_MSG + MSIIR_MSG(me->miid, DBG_HIGH_PRIO, "CAPI MSIIR : cross fade is active for current channel, new params are cached!"); +#endif + continue; + } + + // create new MSIIR library if it does not exist yet + if (NULL == me->msiir_new_lib[ch].mem_ptr) + { + capi_err_t capi_result = CAPI_EOK; + + capi_result = capi_msiir_create_new_msiir_filters(me, MSIIR_NEW_INSTANCE, ch); + if (CAPI_EOK != capi_result) + { + return capi_result; + } + } + + // set the new params to the new MSIIR filters + result_msiir_lib = msiir_set_param(&(me->msiir_new_lib[ch]), + MSIIR_PARAM_PREGAIN, + (void *)&(me->per_chan_msiir_pregain[ch]), + (uint32)sizeof(me->per_chan_msiir_pregain[ch])); + if (MSIIR_SUCCESS != result_msiir_lib) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : start cross fade failed setting pregain"); + return CAPI_EFAILED; + } + + int32_t num_stages = me->per_chan_msiir_cfg_max[ch].num_stages; + + param_size = sizeof(me->per_chan_msiir_cfg_max[0].num_stages); + param_size += num_stages * sizeof(me->per_chan_msiir_cfg_max[0].coeffs_struct[0]); + + result_msiir_lib = msiir_set_param(&(me->msiir_new_lib[ch]), + MSIIR_PARAM_CONFIG, + (void *)&(me->per_chan_msiir_cfg_max[ch]), + param_size); + if (MSIIR_SUCCESS != result_msiir_lib) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : start cross fade failed setting Config"); + return CAPI_EFAILED; + } + + // start/enable cross fade for current channel + me->cross_fade_flag[ch] = 1; + + result_cross_fade_lib = audio_cross_fade_set_param(&(me->cross_fade_lib[ch]), + CROSS_FADE_PARAM_MODE, + (int8 *)&(me->cross_fade_flag[ch]), + (uint32)sizeof(me->cross_fade_flag[ch])); + if (CROSS_FADE_SUCCESS != result_cross_fade_lib) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : start cross fade failed"); + return CAPI_EFAILED; + } + + capi_msiir_update_delay_event(me); + // resets start_cross_fade flag after set params completed + me->start_cross_fade = FALSE; + + } // for (ch) + + return CAPI_EOK; +} + +static capi_err_t capi_msiir_create_new_msiir_filters(capi_multistageiir_t *me, uint32_t instance_id, uint32_t ch) +{ + MSIIR_RESULT result_lib = MSIIR_SUCCESS; + + me->per_chan_mem_req.mem_size = 0; + + result_lib = msiir_get_mem_req(&(me->per_chan_mem_req), &(me->msiir_static_vars)); + if ((MSIIR_SUCCESS != result_lib) || (0 == me->per_chan_mem_req.mem_size)) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : Getsize error"); + return CAPI_EFAILED; + } + + uint32_t malloc_size = align_to_8_byte(me->per_chan_mem_req.mem_size); + + void *ptr = posal_memory_malloc(malloc_size, (POSAL_HEAP_ID)me->heap_id); + if (NULL == ptr) + { + MSIIR_MSG(me->miid, DBG_FATAL_PRIO, "CAPI MSIIR : creating new MSIIR: Out of Memory. mem_size requested : %lu", malloc_size); + return CAPI_ENOMEMORY; + } + + if (MSIIR_NEW_INSTANCE == instance_id) + { + if (NULL != me->msiir_new_lib[ch].mem_ptr) + { +#ifdef CAPI_MSIIR_DEBUG_MSG + MSIIR_MSG(me->miid, DBG_HIGH_PRIO, + "CAPI MSIIR : releasing existing library ptr before creating new 0x%p", + me->msiir_new_lib[ch].mem_ptr); +#endif + posal_memory_free(me->msiir_new_lib[ch].mem_ptr); + me->msiir_new_lib[ch].mem_ptr = NULL; + } + + result_lib = msiir_init_mem(&(me->msiir_new_lib[ch]), &(me->msiir_static_vars), ptr, malloc_size); + if (MSIIR_SUCCESS != result_lib) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : failed initialize new MSIIR library"); + posal_memory_free(ptr); + me->msiir_new_lib[ch].mem_ptr = NULL; + return CAPI_EFAILED; + } + } + else if (MSIIR_CURRENT_INSTANCE == instance_id) + { + if (NULL != me->msiir_lib[ch].mem_ptr) + { +#ifdef CAPI_MSIIR_DEBUG_MSG + MSIIR_MSG(me->miid, DBG_HIGH_PRIO, + "CAPI MSIIR : releasing existing library ptr before creating new 0x%p", + me->msiir_lib[ch].mem_ptr); +#endif + posal_memory_free(me->msiir_lib[ch].mem_ptr); + me->msiir_lib[ch].mem_ptr = NULL; + } + + result_lib = msiir_init_mem(&(me->msiir_lib[ch]), &(me->msiir_static_vars), ptr, malloc_size); + if (MSIIR_SUCCESS != result_lib) + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : failed initialize new MSIIR library"); + posal_memory_free(ptr); + me->msiir_lib[ch].mem_ptr = NULL; + return CAPI_EFAILED; + } + + // no cross fading if only current instances are created + me->msiir_new_lib[ch].mem_ptr = NULL; + + // read and save the library's default settings for pregain and config (coeffs and shift factors) + if (CAPI_EOK != capi_msiir_get_default_pregain_config(me, ch)) + { + return CAPI_EFAILED; + } + } + else + { + MSIIR_MSG(me->miid, DBG_ERROR_PRIO, "CAPI MSIIR : invalid instance id when creating MSIIR library"); + posal_memory_free(ptr); + return CAPI_EFAILED; + } + + me->per_chan_mem_req.mem_size = malloc_size; +#ifdef CAPI_MSIIR_DEBUG_MSG + MSIIR_MSG(me->miid, DBG_HIGH_PRIO, "CAPI MSIIR : created new MSIIR library of size %lu", malloc_size); +#endif + return CAPI_EOK; +} + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/modules/processing/filters/multi_stage_iir/capi/src/capi_multistageiir_utils.h b/modules/processing/filters/multi_stage_iir/capi/src/capi_multistageiir_utils.h new file mode 100644 index 0000000..4061408 --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/capi/src/capi_multistageiir_utils.h @@ -0,0 +1,285 @@ +#ifndef CAPI_MULTISTAGEIIR_UTILS_H +#define CAPI_MULTISTAGEIIR_UTILS_H +/* ======================================================================== */ +/** +@file capi_multistageiir_utils.h + + Header file to implement the CAPI Interface for Multi-Stage IIR filter + object (MSIIR). +*/ + +/* ========================================================================= + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + ========================================================================= */ + +/*------------------------------------------------------------------------ + * Include files + * -----------------------------------------------------------------------*/ + + +#ifndef CAPI_STANDALONE +#include "shared_lib_api.h" +#else +#include "module_cmn_api.h" +#endif + +/* Only internal dependencies should be present here, others should be covered in shared_lib_api.h */ +#include "capi_multistageiir.h" +#include "msiir_api.h" +#include "api_msiir.h" +#include "crossfade_api.h" + +#include "capi_cmn.h" +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifdef PROD_SPECIFIC_MAX_CH +#define MSIIR_MAX_CHANNEL PROD_SPECIFIC_MAX_CH +#else +#define MSIIR_MAX_CHANNEL 32 +#endif + +#define CAPI_MSIIR_DEBUG_MSG 0 +#define DEFAULT_FRAME_SIZE_IN_US 5000 + +#define NUM_PACKETS_PER_SAMPLE_16BIT 8 +#define NUM_PACKETS_PER_STAGE_WITH_ZERO_SAMPLE_16BIT 19 +#define NUM_PACKETS_WITH_ZERO_STAGE_16BIT 220 +#define NUM_PACKETS_PER_SAMPLE_32BIT 7 +#define NUM_PACKETS_PER_STAGE_WITH_ZERO_SAMPLE_32BIT 17 +#define NUM_PACKETS_WITH_ZERO_STAGE_32BIT 220 + +#define CAPI_MSIIR_ALIGN_4_BYTE(x) (((x) + 3) & (0xFFFFFFFC)) + +/*------------------------------------------------------------------------ + * Static constants + * -----------------------------------------------------------------------*/ +//#define CAPI_MSIIR_DEBUG_MSG +static const uint32_t MSIIR_MAX_STAGES = 20; +static const uint32_t MSIIR_FILTER_STATES = 2; + +static const uint32_t MSIIR_CURRENT_INSTANCE = 0; +static const uint32_t MSIIR_NEW_INSTANCE = 1; + +static const uint32_t MSIIR_Q_PREGAIN_LEGACY = 13; +static const uint32_t MSIIR_DEFAULT_SAMPLE_RATE = 48000; + +typedef enum +{ + MULTISTAGE_IIR_LEFT_PAN=0, + MULTISTAGE_IIR_CENTER_PAN, + MULTISTAGE_IIR_RIGHT_PAN, +} MultiStageIIRPan; + + +typedef enum +{ + MULTISTAGE_IIR_INVALID_PARAM=0, + MULTISTAGE_IIR_MCHAN_PARAM, +} MultiStageIIRParamType; +/*------------------------------------------------------------------------ + * Macros, Defines, Type declarations + * -----------------------------------------------------------------------*/ + +/* debug message */ +#define MIID_UNKNOWN 0 +#define MSIIR_MSG_PREFIX "CAPI MULTISTAGE IIR:[%lX] " +#define MSIIR_MSG(ID, xx_ss_mask, xx_fmt, ...)\ + AR_MSG(xx_ss_mask, MSIIR_MSG_PREFIX xx_fmt, ID, ##__VA_ARGS__) + +typedef struct capi_one_chan_msiir_config_max_t +{ + int32_t num_stages; + msiir_coeffs_t coeffs_struct[MSIIR_MAX_STAGES]; +} capi_one_chan_msiir_config_max_t; + +typedef struct capi_cached_params_t +{ + uint32_t param_id_type; + capi_buf_t params_ptr; +} capi_cached_params_t; + +typedef enum capi_msiir_config_version_t +{ + DEFAULT = 0, + VERSION_V1 = 1, + VERSION_V2 = 2 +} capi_msiir_config_version_t; + +typedef struct capi_one_chan_msiir_config_static_param_t +{ + uint16_t reserved; + uint16_t num_biquad_stages; +} capi_one_chan_msiir_config_static_param_t; + +typedef struct capi_multistageiir_t +{ + // capi + const capi_vtbl_t *vtbl; // capi function table pointer + capi_event_callback_info_t cb_info; + uint32_t heap_id; + uint32_t kpps; + uint32_t bw; + uint32_t delay; + //media format + capi_media_fmt_v2_t media_fmt[CAPI_MSIIR_MAX_OUT_PORTS]; + int32_t channel_map_to_index[PCM_MAX_CHANNEL_MAP_V2+1]; //converts channel_type to index, +1 to array size so that channel_type can be used directly to index into this array + // core library + msiir_lib_t msiir_lib[IIR_TUNING_FILTER_MAX_CHANNELS_V2]; + msiir_lib_t msiir_new_lib[IIR_TUNING_FILTER_MAX_CHANNELS_V2]; + msiir_static_vars_t msiir_static_vars; + msiir_mem_req_t per_chan_mem_req; + uint32_t enable_flag[IIR_TUNING_FILTER_MAX_CHANNELS_V2]; + uint32_t num_channels_allocated; + // cross fade + cross_fade_lib_t cross_fade_lib[IIR_TUNING_FILTER_MAX_CHANNELS_V2]; + cross_fade_static_t cross_fade_static_vars; + cross_fade_lib_mem_req_t per_chan_cross_fade_mem_req; + uint32_t cross_fade_flag[IIR_TUNING_FILTER_MAX_CHANNELS_V2]; + // config params + msiir_pregain_t per_chan_msiir_pregain[IIR_TUNING_FILTER_MAX_CHANNELS_V2]; + capi_one_chan_msiir_config_max_t *per_chan_msiir_cfg_max; + capi_one_chan_msiir_config_max_t default_per_chan_msiir_cfg_max; + // flags + bool_t is_first_frame; + bool_t media_fmt_received; + bool_t start_cross_fade; + bool_t enable; // specifies the state of the module + //This is used for storing Capi Module instance id + uint32_t miid; + //this flag is to detect if higher than 63 channel maps received in media format + bool_t higher_channel_map_present; + // cache params + capi_cached_params_t enable_params; + capi_cached_params_t pregain_params; + capi_cached_params_t config_params; + uint32_t cntr_frame_size_us; + capi_msiir_config_version_t cfg_version; +} capi_multistageiir_t; + +bool_t check_channel_mask_msiir(uint8_t *iir_param_ptr,uint32_t param_id) ; + +capi_err_t capi_msiir_update_delay_event(capi_multistageiir_t *me); + +capi_err_t capi_msiir_set_enable_disable_per_channel(capi_multistageiir_t *me, + const capi_buf_t * params_ptr); + +capi_err_t capi_msiir_set_pregain_per_channel(capi_multistageiir_t *me, const capi_buf_t *params_ptr); + +capi_err_t capi_msiir_set_config_per_channel( + capi_multistageiir_t *me, + const capi_buf_t *params_ptr); + +capi_err_t capi_msiir_get_enable_disable_per_channel( + capi_multistageiir_t *me, + capi_buf_t *params_ptr); + +capi_err_t capi_msiir_get_pregain_per_channel( + capi_multistageiir_t *me, + capi_buf_t *params_ptr); + +capi_err_t capi_msiir_get_config_per_channel( + capi_multistageiir_t *me, + capi_buf_t *params_ptr); + +capi_err_t capi_msiir_get_default_pregain_config( + capi_multistageiir_t *me, + uint32_t ch); + +capi_err_t capi_msiir_start_cross_fade( + capi_multistageiir_t *me); + +capi_err_t capi_msiir_process_set_properties( + capi_multistageiir_t *me, + capi_proplist_t *props_ptr); + +capi_err_t capi_msiir_process_get_properties( + capi_multistageiir_t *me, + capi_proplist_t *props_ptr); + +capi_err_t capi_msiir_init_media_fmt( + capi_multistageiir_t *me); + +capi_err_t capi_msiir_cache_params( + capi_multistageiir_t *me, + capi_buf_t *params_ptr, + uint32_t param_id); + +capi_err_t capi_msiir_check_raise_kpps_event( + capi_multistageiir_t *me, + uint32_t val); + +uint32_t capi_msiir_get_kpps( + capi_multistageiir_t *me); + +int32_t capi_msiir_s32_min_s32_s32( + int32_t x, + int32_t y); + +capi_err_t capi_msiir_raise_process_check_event(capi_multistageiir_t *me_ptr); + +capi_err_t capi_msiir_set_enable_disable_per_channel_v2(capi_multistageiir_t *me_ptr, + const capi_buf_t *params_ptr, + uint32_t param_id); + +capi_err_t capi_msiir_validate_multichannel_v2_payload(capi_multistageiir_t *me_ptr, + int8_t * data_ptr, + uint32_t param_id, + uint32_t param_size, + uint32_t * req_payload_size, + uint32_t base_payload_size, + uint32_t per_cfg_base_payload_size); + +capi_err_t capi_msiir_validate_per_channel_v2_payload(capi_multistageiir_t *me_ptr, + uint32_t num_cfg, + int8_t * payload_cfg_ptr, + uint32_t param_size, + uint32_t * required_size_ptr, + uint32_t param_id, + uint32_t base_payload_size, + uint32_t per_cfg_base_payload_size); + +capi_err_t capi_msiir_set_enable_disable_v2_payload(capi_multistageiir_t *me_ptr, + int8_t* data_ptr, + uint32_t per_cfg_base_payload_size); + +capi_err_t capi_msiir_set_pregain_per_channel_v2(capi_multistageiir_t *me_ptr, + const capi_buf_t *params_ptr, + uint32_t param_id);; + +capi_err_t capi_msiir_set_pregain_v2_payload(capi_multistageiir_t *me_ptr, + int8_t* data_ptr, + uint32_t per_cfg_base_payload_size); + +capi_err_t capi_msiir_set_config_per_channel_v2(capi_multistageiir_t *me_ptr, + const capi_buf_t *params_ptr, + uint32_t param_id); + +capi_err_t capi_msiir_set_config_v2_payload(capi_multistageiir_t *me_ptr, + int8_t* payload_ptr); + +capi_err_t capi_msiir_get_enable_disable_per_channel_v2(capi_multistageiir_t *me_ptr, + capi_buf_t *params_ptr); + +capi_err_t capi_msiir_get_pregain_per_channel_v2( + capi_multistageiir_t *me_ptr, + capi_buf_t *params_ptr); + +capi_err_t capi_msiir_get_config_per_channel_v2( + capi_multistageiir_t *me_ptr, + capi_buf_t *params_ptr); + +bool_t capi_msiir_check_multi_ch_channel_mask_v2_param(uint32_t miid, + uint32_t num_config, + uint32_t param_id, + int8_t * param_ptr, + uint32_t base_payload_size, + uint32_t per_cfg_base_payload_size); +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif //__CAPI_MULTISTAGEIIR_UTILS_H + diff --git a/modules/processing/filters/multi_stage_iir/capi/src/capi_multistageiir_utils_v2.cpp b/modules/processing/filters/multi_stage_iir/capi/src/capi_multistageiir_utils_v2.cpp new file mode 100644 index 0000000..ee60cdc --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/capi/src/capi_multistageiir_utils_v2.cpp @@ -0,0 +1,1093 @@ +/* ======================================================================== */ +/* +@file capi_multistageiir_utils_v2.cpp + + Source file to implement the Audio Post Processor Interface for Multi-Stage + IIR filters +*/ + +/* ========================================================================= + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + ========================================================================= */ + +/*------------------------------------------------------------------------ + * Include files + * -----------------------------------------------------------------------*/ +#include "capi_multistageiir_utils.h" +/*------------------------------------------------------------------------ + * Static declarations + * -----------------------------------------------------------------------*/ +/*------------------------------------------------------------------------ + * Function declarations + * -----------------------------------------------------------------------*/ +capi_err_t capi_msiir_validate_multichannel_v2_payload(capi_multistageiir_t *me_ptr, + int8_t * data_ptr, + uint32_t param_id, + uint32_t param_size, + uint32_t * req_payload_size, + uint32_t base_payload_size, + uint32_t per_cfg_base_payload_size) +{ + int8_t * temp_cfg_ptr = data_ptr; + uint32_t capi_result = CAPI_EOK; + uint32_t num_cfg = *((uint32_t *)data_ptr); + +#ifdef CAPI_MSIIR_DEBUG_MSG + MSIIR_MSG(me_ptr->miid, DBG_HIGH_PRIO, "Received num_config = %lu for PID = 0x%lx", num_cfg, param_id); +#endif + + if (num_cfg < 1 || num_cfg > PCM_MAX_CHANNEL_MAP_V2) + { + MSIIR_MSG(me_ptr->miid, DBG_ERROR_PRIO, "CAPI MSIIR : Received incorrect num_config parameter - %lu", num_cfg); + return CAPI_EBADPARAM; + } + + capi_result = capi_msiir_validate_per_channel_v2_payload(me_ptr, + num_cfg, + temp_cfg_ptr, + param_size, + req_payload_size, + param_id, + base_payload_size, + per_cfg_base_payload_size); + if (CAPI_FAILED(capi_result)) + { + MSIIR_MSG(me_ptr->miid, DBG_ERROR_PRIO, "CAPI MSIIR : Configuartion SetParam 0x%lx failed.", param_id); + return capi_result; + } + + return CAPI_EOK; +} + +capi_err_t capi_msiir_validate_per_channel_v2_payload(capi_multistageiir_t *me_ptr, + uint32_t num_cfg, + int8_t * payload_cfg_ptr, + uint32_t param_size, + uint32_t * required_size_ptr, + uint32_t param_id, + uint32_t base_payload_size, + uint32_t per_cfg_base_payload_size) +{ + capi_err_t capi_result = CAPI_EOK; + int8_t * base_payload_ptr = payload_cfg_ptr; // points to the start of the payload + uint32_t config_size = 0; + uint32_t per_cfg_payload_size = 0; + *required_size_ptr += base_payload_size; // 4B + base_payload_ptr += *required_size_ptr; // 4B + // configuration payload + // _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ + //| num_ | channel_type_group_mask | dynamic channel_type_mask_list[0] | payload1 | _ _ _ _ + //|config| _ _ _ _ _ _ _ _ _ _ _ _ |_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |_ _ _ _ _ |_ _ _ _ _ _ + // + + for (uint32_t count = 0; count < num_cfg; count++) + { + if ((*required_size_ptr + per_cfg_base_payload_size) > param_size) + { + MSIIR_MSG(me_ptr->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Failed while validating required size for config #%lu. Total config: %lu", + count, + num_cfg); + return CAPI_ENEEDMORE; + } + else + { + uint32_t ch_type_group_mask = 0; + base_payload_ptr += config_size; + ch_type_group_mask = *((uint32_t *)base_payload_ptr); + + if (PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS_V2 == param_id) + { + uint16_t num_biquad_stages = *((uint16_t*)(base_payload_ptr + + (CAPI_CMN_INT32_SIZE_IN_BYTES + (capi_cmn_count_set_bits(ch_type_group_mask) * CAPI_CMN_INT32_SIZE_IN_BYTES) + + sizeof(uint16_t)))); // accessing num_biquad_stages + + uint32_t incr_size = num_biquad_stages * MSIIR_COEFF_LENGTH * sizeof(int32_t); //filter_coeffs_payload_size + incr_size += num_biquad_stages * sizeof(int16_t); //num_shift_factor + incr_size += (num_biquad_stages & 0x1) ? sizeof(int16_t): 0; + + per_cfg_payload_size = per_cfg_base_payload_size + incr_size; // per cfg size excluding size for channel mask list +#ifdef CAPI_MSIIR_DEBUG_MSG + MSIIR_MSG(me_ptr->miid, DBG_HIGH_PRIO, "CAPI MSIIR : config: %lu, num_biquad_stages is %lu, incr_size: %lu, per_cfg_base_payload_size: %lu", + count, + num_biquad_stages, + incr_size, + per_cfg_base_payload_size); +#endif + } + else + { + per_cfg_payload_size = per_cfg_base_payload_size; // per cfg size excluding size for channel mask list + } +#ifdef CAPI_MSIIR_DEBUG_MSG + MSIIR_MSG(me_ptr->miid, DBG_MED_PRIO, + "CAPI MSIIR : Channel group mask %lu received for param 0x%#x. base_payload_ptr = %xlx", + ch_type_group_mask, + param_id, + base_payload_ptr); +#endif + // check for group mask payload if only desired bits are set or not + if (0 == (ch_type_group_mask >> CAPI_CMN_MAX_CHANNEL_MAP_GROUPS)) + { + capi_result = capi_cmn_check_payload_validation(me_ptr->miid, ch_type_group_mask, per_cfg_payload_size, + count, param_size, &config_size, required_size_ptr); + if(CAPI_FAILED(capi_result)) + { + return capi_result; + } + } + else + { + MSIIR_MSG(me_ptr->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Invalid configuration of channel group mask: %lu. More than maximum valid channel groups are " + "set, maximum valid channel groups: %lu.", + ch_type_group_mask, + CAPI_CMN_MAX_CHANNEL_MAP_GROUPS); + return CAPI_EBADPARAM; + } + } + } +#ifdef CAPI_MSIIR_DEBUG_MSG + MSIIR_MSG(me_ptr->miid, DBG_MED_PRIO, "CAPI MSIIR : calculated size for param is %lu", *required_size_ptr); +#endif + // validate the configs for duplication + if (!capi_msiir_check_multi_ch_channel_mask_v2_param(me_ptr->miid, + num_cfg, + param_id, + (int8_t *)payload_cfg_ptr, + base_payload_size, + per_cfg_base_payload_size)) + { + MSIIR_MSG(me_ptr->miid, DBG_ERROR_PRIO, "CAPI MSIIR : Trying to set different configuartion to same channel, returning"); + return CAPI_EBADPARAM; + } + return CAPI_EOK; +} + +/* Returns FALSE if same channel is set to multiple times in different configs */ +bool_t capi_msiir_check_multi_ch_channel_mask_v2_param(uint32_t miid, + uint32_t num_config, + uint32_t param_id, + int8_t * param_ptr, + uint32_t base_payload_size, + uint32_t per_cfg_base_payload_size) +{ + uint32_t *temp_mask_list_ptr = NULL; + int8_t * data_ptr = param_ptr; // points to the start of the payload + uint32_t check_channel_mask_arr[CAPI_CMN_MAX_CHANNEL_MAP_GROUPS] = { 0 }; + uint32_t current_channel_mask_arr[CAPI_CMN_MAX_CHANNEL_MAP_GROUPS] = { 0 }; + bool_t check = TRUE; + uint32_t offset = CAPI_CMN_INT32_SIZE_IN_BYTES; + uint32_t per_cfg_payload_size = 0; + + for (uint32_t cnfg_cntr = 0; cnfg_cntr < num_config; cnfg_cntr++) + { + uint32_t channel_group_mask = 0; + // Increment data ptr by calculated offset to point at next payload's group_mask + data_ptr += offset; + channel_group_mask = *((uint32_t *)data_ptr); + if (PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS_V2 == param_id) + { + uint16_t num_biquad_stages = *((uint16_t*)(data_ptr + + (CAPI_CMN_INT32_SIZE_IN_BYTES + (capi_cmn_count_set_bits(channel_group_mask) * CAPI_CMN_INT32_SIZE_IN_BYTES) + + sizeof(uint16_t)))); // accessing num_biquad_stages + + uint32_t incr_size = num_biquad_stages * MSIIR_COEFF_LENGTH * sizeof(int32_t); //filter_coeffs_payload_size + incr_size += num_biquad_stages * sizeof(int16_t); //num_shift_factor + incr_size += (num_biquad_stages & 0x1) ? sizeof(int16_t): 0; + + per_cfg_payload_size = per_cfg_base_payload_size + incr_size; // per cfg size excluding size for channel mask list +#ifdef CAPI_MSIIR_DEBUG_MSG + MSIIR_MSG(miid, DBG_HIGH_PRIO, "CAPI MSIIR : config: %lu, num_biquad_stages is %lu, incr_size: %lu, per_cfg_base_payload_size: %lu", + cnfg_cntr, + num_biquad_stages, + incr_size, + per_cfg_base_payload_size); +#endif + } + else + { + per_cfg_payload_size = per_cfg_base_payload_size; // per cfg size excluding size for channel mask list + } +#ifdef CAPI_MSIIR_DEBUG_MSG + MSIIR_MSG(miid, DBG_HIGH_PRIO, "CAPI MSIIR : channel_group_mask %lu, offset %lu", channel_group_mask, offset); +#endif + temp_mask_list_ptr = (uint32_t *)(data_ptr + CAPI_CMN_INT32_SIZE_IN_BYTES); + if(!capi_cmn_check_v2_channel_mask_duplication(miid, + cnfg_cntr, + channel_group_mask, + temp_mask_list_ptr, + current_channel_mask_arr, + check_channel_mask_arr, + &offset, + per_cfg_payload_size)) + { + return FALSE; + } + } + return check; +} + +capi_err_t capi_msiir_set_enable_disable_per_channel_v2(capi_multistageiir_t *me_ptr, + const capi_buf_t *params_ptr, + uint32_t param_id) +{ + int8_t *data_ptr = params_ptr->data_ptr; + capi_err_t capi_result = CAPI_EOK; + uint32_t param_size = params_ptr->actual_data_len; + if (param_size < sizeof(param_id_msiir_enable_v2_t)) + { + MSIIR_MSG(me_ptr->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Enable SetParam 0x%lx, invalid param size %lx ", + param_id, + param_size); + capi_result = CAPI_ENEEDMORE; + return capi_result; + } + uint32_t req_payload_size = 0; + uint32_t base_payload_size = sizeof(param_id_msiir_enable_v2_t); + uint32_t per_cfg_base_payload_size = sizeof(param_id_msiir_ch_enable_v2_t); + capi_result = capi_msiir_validate_multichannel_v2_payload(me_ptr, + data_ptr, + param_id, + param_size, + &req_payload_size, + base_payload_size, + per_cfg_base_payload_size); + if (CAPI_FAILED(capi_result)) + { + MSIIR_MSG(me_ptr->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Enable V2 SetParam 0x%lx, invalid param size %lu ,required_size %lu", + param_id, + param_size, + req_payload_size); + return capi_result; + } + if (param_size < req_payload_size) + { + MSIIR_MSG(me_ptr->miid, DBG_ERROR_PRIO, "CAPI MSIIR : Insufficient payload size %d. Req size %lu", param_size, req_payload_size); + return CAPI_ENEEDMORE; + } + else + { + // set the payload + capi_result = capi_msiir_set_enable_disable_v2_payload(me_ptr, data_ptr, per_cfg_base_payload_size); + if (CAPI_FAILED(capi_result)) + { + return capi_result; + } + MSIIR_MSG(me_ptr->miid, DBG_HIGH_PRIO, "CAPI MSIIR : Enable V2 Set Param set Successfully"); + } + capi_msiir_raise_process_check_event(me_ptr); + capi_msiir_update_delay_event(me_ptr); + return CAPI_EOK; +} + +capi_err_t capi_msiir_set_enable_disable_v2_payload(capi_multistageiir_t *me_ptr, + int8_t* data_ptr, + uint32_t per_cfg_base_payload_size) +{ + uint32_t * ch_mask_list_ptr = NULL; + uint32_t * enable_ref_ptr = NULL; + uint32_t num_cfg = *((uint32_t*)data_ptr); + uint32_t offset = sizeof(param_id_msiir_enable_v2_t); + + // loop through all the configs + for (uint32_t count = 0; count < num_cfg; count++) + { + uint32_t ch_mask_arr_index = 0; + uint32_t channel_group_mask = 0; + uint32_t ch_mask_list_size = 0; + + // Increment data ptr by calculated offset to point at next payload's group_mask + data_ptr += offset; + param_id_msiir_ch_enable_v2_t *temp_en_cfg_ptr = (param_id_msiir_ch_enable_v2_t *)data_ptr; + channel_group_mask = temp_en_cfg_ptr->channel_type_group_mask; + + ch_mask_list_ptr = (uint32_t *)(data_ptr + CAPI_CMN_INT32_SIZE_IN_BYTES); // points to the mask array + ch_mask_list_size = capi_cmn_count_set_bits(channel_group_mask); + enable_ref_ptr = ch_mask_list_ptr + ch_mask_list_size; // points to the enable payload + + MSIIR_MSG(me_ptr->miid, DBG_HIGH_PRIO, "CAPI MSIIR :Enable for config #%lu is %lu", count + 1, *enable_ref_ptr); + + for (uint32_t group_no = 0; group_no < CAPI_CMN_MAX_CHANNEL_MAP_GROUPS; group_no++) + { + // check if the group is configured. + if (CAPI_CMN_IS_BIT_SET_AT_POS_IN_32B_VAL(channel_group_mask, group_no)) + { + uint32_t start_ch_map = group_no * CAPI_CMN_CHANNELS_PER_MASK; + uint32_t end_ch_map = (group_no + 1) * CAPI_CMN_CHANNELS_PER_MASK; + uint32_t j = 0; + if(0 == group_no) + { + start_ch_map = 1; //ignoring reserved bit 0 + j = 1; + } + if(PCM_MAX_CHANNEL_MAP_V2 < end_ch_map) + { + end_ch_map = PCM_MAX_CHANNEL_MAP_V2 + 1; + } + // iterate over the set of channel maps for this group + for (uint32_t i = start_ch_map; i < end_ch_map; i++, j++) + { + uint32_t ch_mask = (1 << j); + if (ch_mask & ch_mask_list_ptr[ch_mask_arr_index]) // check if this channel is set anywhere in this group + { + // convert channel_type to channel index, channel index has range 0 ~ (PCM_MAX_CHANNEL_MAP_V2-1) + int32_t ch_idx = me_ptr->channel_map_to_index[i]; + if ((ch_idx < 0) || (ch_idx >= (int32_t)me_ptr->media_fmt[0].format.num_channels)) + { + continue; + } + me_ptr->enable_flag[ch_idx] = *enable_ref_ptr; + //AR_MSG(DBG_MED_PRIO, "CAPI MSIIR : Set enable: channel: %lu enable: %lu", i, me_ptr->enable_flag[ch_idx]); + } + } + //MOVE TO HERE //print the range as well +#ifdef CAPI_MSIIR_DEBUG_MSG + MSIIR_MSG(me_ptr->miid, DBG_HIGH_PRIO, + "CAPI MSIIR : config: #%lu, channel mask: %#lx, group no: %lu," + "includes channels from %lu to %lu. Enable: %#lx", + count, + ch_mask_list_ptr[ch_mask_arr_index], + group_no, + start_ch_map, + end_ch_map - 1, + *enable_ref_ptr); +#endif + ch_mask_arr_index++; + } + } + offset = per_cfg_base_payload_size + (ch_mask_arr_index << 2); //sizeof(uint32_t) + } + return CAPI_EOK; +} + +capi_err_t capi_msiir_set_pregain_per_channel_v2(capi_multistageiir_t *me_ptr, + const capi_buf_t *params_ptr, + uint32_t param_id) +{ + int8_t *data_ptr = params_ptr->data_ptr; + capi_err_t capi_result = CAPI_EOK; + int32_t param_size = params_ptr->actual_data_len; + + if (param_size < sizeof(param_id_msiir_pregain_v2_t)) + { + MSIIR_MSG(me_ptr->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Pregain SetParam 0x%lx, invalid param size %lx ", + param_id, + param_size); + capi_result = CAPI_ENEEDMORE; + return capi_result; + } + uint32_t req_payload_size = 0; + uint32_t base_payload_size = sizeof(param_id_msiir_pregain_v2_t); + uint32_t per_cfg_base_payload_size = sizeof(param_id_msiir_ch_pregain_v2_t); + capi_result = capi_msiir_validate_multichannel_v2_payload(me_ptr, + data_ptr, + param_id, + param_size, + &req_payload_size, + base_payload_size, + per_cfg_base_payload_size); + if (CAPI_FAILED(capi_result)) + { + MSIIR_MSG(me_ptr->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Pregain V2 SetParam 0x%lx, invalid param size %lu ,required_size %lu", + param_id, + param_size, + req_payload_size); + return capi_result; + } + if (param_size < req_payload_size) + { + MSIIR_MSG(me_ptr->miid, DBG_ERROR_PRIO, "CAPI MSIIR : Insufficient payload size %d. Req size %lu", param_size, req_payload_size); + return CAPI_ENEEDMORE; + } + else + { + // set the payload + capi_result = capi_msiir_set_pregain_v2_payload(me_ptr, data_ptr, per_cfg_base_payload_size); + if (CAPI_FAILED(capi_result)) + { + return capi_result; + } + MSIIR_MSG(me_ptr->miid, DBG_HIGH_PRIO, "CAPI MSIIR : Pregain V2 Set Param set Successfully"); + } + return CAPI_EOK; +} + +capi_err_t capi_msiir_set_pregain_v2_payload(capi_multistageiir_t *me_ptr, + int8_t* data_ptr, + uint32_t per_cfg_base_payload_size) +{ + MSIIR_RESULT result_lib = MSIIR_SUCCESS; + uint32_t num_cfg = *((uint32_t*)data_ptr); + uint32_t offset = sizeof(param_id_msiir_pregain_v2_t); + + // loop through all the configs + for (uint32_t count = 0; count < num_cfg; count++) + { + uint32_t *ch_mask_list_ptr = NULL; + uint32_t *pregain_ref_ptr = NULL; + uint32_t ch_mask_arr_index = 0; + uint32_t channel_group_mask = 0; + uint32_t ch_mask_list_size = 0; + + // Increment data ptr by calculated offset to point at next payload's group_mask + data_ptr += offset; //4B + param_id_msiir_ch_pregain_v2_t *temp_pg_cfg_ptr = (param_id_msiir_ch_pregain_v2_t *)data_ptr; + channel_group_mask = temp_pg_cfg_ptr->channel_type_group_mask; + + ch_mask_list_ptr = (uint32_t *)(data_ptr + CAPI_CMN_INT32_SIZE_IN_BYTES); // points to the mask array + ch_mask_list_size = capi_cmn_count_set_bits(channel_group_mask); + pregain_ref_ptr = ch_mask_list_ptr + ch_mask_list_size; // points to the en payload + + MSIIR_MSG(me_ptr->miid, DBG_HIGH_PRIO, "CAPI MSIIR :Pregain for config #%lu is %lu", count, *pregain_ref_ptr); + + for (uint32_t group_no = 0; group_no < CAPI_CMN_MAX_CHANNEL_MAP_GROUPS; group_no++) + { + // check if the group is configured. + if (CAPI_CMN_IS_BIT_SET_AT_POS_IN_32B_VAL(channel_group_mask, group_no)) + { + msiir_pregain_t pregain = 0; + uint32_t start_ch_map = group_no * CAPI_CMN_CHANNELS_PER_MASK; + uint32_t end_ch_map = (group_no + 1) * CAPI_CMN_CHANNELS_PER_MASK; + uint32_t j = 0; + if(0 == group_no) + { + start_ch_map = 1; //ignoring reserved bit 0 + j = 1; + } + if(PCM_MAX_CHANNEL_MAP_V2 < end_ch_map) + { + end_ch_map = PCM_MAX_CHANNEL_MAP_V2 + 1; + } + // iterate over the set of channel maps for this group + for (uint32_t i = start_ch_map; i < end_ch_map; i++, j++) + { + uint32_t ch_mask = (1 << j); + if (ch_mask & ch_mask_list_ptr[ch_mask_arr_index]) // check if this channel is set anywhere in this group + { + // convert channel_type to channel index, channel index has range 0 ~ (PCM_MAX_CHANNEL_MAP_V2-1) + int32_t ch_idx = me_ptr->channel_map_to_index[i]; + if ((ch_idx < 0) || (ch_idx >= (int32_t)me_ptr->media_fmt[0].format.num_channels)) + { + continue; + } + pregain = (msiir_pregain_t)(*pregain_ref_ptr); + if ((me_ptr->is_first_frame) || (me_ptr->per_chan_msiir_pregain[ch_idx] == pregain)) + { + result_lib = + msiir_set_param(&(me_ptr->msiir_lib[ch_idx]), MSIIR_PARAM_PREGAIN, (void *)&pregain, sizeof(pregain)); + if (MSIIR_SUCCESS != result_lib) + { + MSIIR_MSG(me_ptr->miid, DBG_ERROR_PRIO, "CAPI MSIIR : Set pregain error %d", result_lib); + return CAPI_EFAILED; + } + } + else + { + me_ptr->start_cross_fade = TRUE; + } + // save the new configuration + me_ptr->per_chan_msiir_pregain[ch_idx] = pregain; + } + } +#ifdef CAPI_MSIIR_DEBUG_MSG + MSIIR_MSG(me_ptr->miid, DBG_HIGH_PRIO, + "CAPI MSIIR : config: #%lu, channel mask: %#lx, group no: %lu," + "includes channels from %lu to %lu. pregain: %#lx", + count, + ch_mask_list_ptr[ch_mask_arr_index], + group_no, + start_ch_map, + end_ch_map - 1, + pregain); +#endif + ch_mask_arr_index++; + } + } + offset = per_cfg_base_payload_size + (ch_mask_arr_index << 2); //sizeof(uint32_t) + } + return CAPI_EOK; +} + +capi_err_t capi_msiir_set_config_per_channel_v2(capi_multistageiir_t *me_ptr, + const capi_buf_t *params_ptr, + uint32_t param_id) +{ + int8_t *data_ptr = params_ptr->data_ptr; + capi_err_t capi_result = CAPI_EOK; + uint32_t param_size = params_ptr->actual_data_len; + + if (param_size < sizeof(param_id_msiir_config_v2_t)) + { + MSIIR_MSG(me_ptr->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Filter config SetParam 0x%lx, invalid param size %lx ", + param_id, + param_size); + capi_result = CAPI_ENEEDMORE; + return capi_result; + } + uint32_t req_payload_size = 0; + uint32_t base_payload_size = sizeof(param_id_msiir_config_v2_t); + uint32_t per_cfg_base_payload_size = sizeof(param_id_msiir_ch_filter_config_v2_t); + capi_result = capi_msiir_validate_multichannel_v2_payload(me_ptr, + data_ptr, + param_id, + param_size, + &req_payload_size, + base_payload_size, + per_cfg_base_payload_size); + if (CAPI_FAILED(capi_result)) + { + MSIIR_MSG(me_ptr->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Filter config V2 SetParam 0x%lx, invalid param size %lu ,required_size %lu", + param_id, + param_size, + req_payload_size); + return capi_result; + } + if (param_size < req_payload_size) + { + MSIIR_MSG(me_ptr->miid, DBG_ERROR_PRIO, "CAPI MSIIR : Insufficient payload size %d. Req size %lu", param_size, req_payload_size); + return CAPI_ENEEDMORE; + } + else + { + // set the payload + capi_result = capi_msiir_set_config_v2_payload(me_ptr, data_ptr); + if (CAPI_FAILED(capi_result)) + { + MSIIR_MSG(me_ptr->miid, DBG_ERROR_PRIO, "CAPI MSIIR : SET V2 cfg payload failed"); + return capi_result; + } + MSIIR_MSG(me_ptr->miid, DBG_HIGH_PRIO, "CAPI MSIIR : Filter config V2 Set Param set Successfully"); + } + return CAPI_EOK; +} + +capi_err_t capi_msiir_set_config_v2_payload(capi_multistageiir_t *me_ptr, + int8_t* payload_ptr) +{ + uint32_t num_cfg = *((uint32_t*)payload_ptr); + + uint8_t *data_ptr = (uint8_t *)(((uint8_t *)(payload_ptr)) + sizeof(param_id_msiir_config_v2_t)); + uint8_t *data_ptr_1 = (uint8_t *)(((uint8_t *)(payload_ptr)) + sizeof(param_id_msiir_config_v2_t)); + uint32_t offset = 0; + /// + // loop through all the configs + for (uint32_t count = 0; count < num_cfg; count++) + { + uint32_t *ch_mask_list_ptr = NULL; + uint32_t ch_mask_arr_index = 0; + uint32_t channel_group_mask = 0; + uint32_t ch_mask_list_size = 0; + + // Increment data ptr by calculated offset to point at next payload's group_mask + data_ptr_1 += offset; + param_id_msiir_ch_filter_config_v2_t *this_chan_cfg_inp_ptr = (param_id_msiir_ch_filter_config_v2_t *)data_ptr_1; + channel_group_mask = this_chan_cfg_inp_ptr->channel_type_group_mask; + + data_ptr = data_ptr_1; + ch_mask_list_ptr = (uint32_t *)(data_ptr + CAPI_CMN_INT32_SIZE_IN_BYTES); // points to the mask array + ch_mask_list_size = capi_cmn_count_set_bits(channel_group_mask); + uint32_t ch_mask_list_size_in_bytes = (ch_mask_list_size * CAPI_CMN_INT32_SIZE_IN_BYTES); + int32_t num_biquad_stages = *((uint16_t*)(data_ptr + CAPI_CMN_INT32_SIZE_IN_BYTES + ch_mask_list_size_in_bytes + + sizeof(uint16_t))); // incrementing offset to point to the num_biquad_stages +#ifdef CAPI_MSIIR_DEBUG_MSG + MSIIR_MSG(me_ptr->miid, DBG_HIGH_PRIO, + "CAPI MSIIR : Set config for config %lu, channel group mask %lu, stages %lu", + count+1, + channel_group_mask, + num_biquad_stages); +#endif + data_ptr += (sizeof(param_id_msiir_ch_filter_config_v2_t) + ch_mask_list_size_in_bytes); + offset = (sizeof(param_id_msiir_ch_filter_config_v2_t) + ch_mask_list_size_in_bytes); + + // move pointer to filter coeffs of current config + int32_t *coeff_ptr = (int32_t *)data_ptr; + int32_t *coeff_ptr_1 = (int32_t *)data_ptr; + + size_t coeff_size = num_biquad_stages * MSIIR_COEFF_LENGTH * sizeof(int32_t); + + // move pointer to numerator shift factors of current config + data_ptr += coeff_size; + offset += coeff_size; + + int16_t *shift_factor_ptr = (int16_t *)data_ptr; + int16_t *shift_factor_ptr_1 = (int16_t *)data_ptr; + size_t numerator_shift_fac_size = num_biquad_stages * sizeof(int16_t); + + data_ptr += numerator_shift_fac_size; + offset += numerator_shift_fac_size; + + // move pointer to skip the padding bit if necessary + bool_t is_odd_stages = (bool_t)(num_biquad_stages & 0x1); + if (is_odd_stages) + { + data_ptr += sizeof(int16_t); // 16-bits padding + offset += sizeof(int16_t); + } +#ifdef CAPI_MSIIR_DEBUG_MSG + MSIIR_MSG(me_ptr->miid, DBG_HIGH_PRIO,"CAPI MSIIR : offset for config %lu is %lu.", + count + 1, offset); +#endif + for (uint32_t group_no = 0; group_no < CAPI_CMN_MAX_CHANNEL_MAP_GROUPS; group_no++) + { + // check if the group is configured. + if (CAPI_CMN_IS_BIT_SET_AT_POS_IN_32B_VAL(channel_group_mask, group_no)) + { + uint32_t start_ch_map = group_no * CAPI_CMN_CHANNELS_PER_MASK; + uint32_t end_ch_map = (group_no + 1) * CAPI_CMN_CHANNELS_PER_MASK; + uint32_t j = 0; + if(0 == group_no) + { + start_ch_map = 1; //ignoring reserved bit 0 + j = 1; + } + if(PCM_MAX_CHANNEL_MAP_V2 < end_ch_map) + { + end_ch_map = PCM_MAX_CHANNEL_MAP_V2 + 1; + } + // iterate over the set of channel maps for this group + for (uint32_t i = start_ch_map; i < end_ch_map; i++, j++) + { + uint32_t ch_mask = (1 << j); + + if (ch_mask & ch_mask_list_ptr[ch_mask_arr_index]) // check if this channel is set anywhere in this group + { + // convert channel_type to channel index, channel index has range 0 ~ (PCM_MAX_CHANNEL_MAP_V2-1) + int32_t ch_idx = me_ptr->channel_map_to_index[i]; +#ifdef CAPI_MSIIR_DEBUG_MSG + MSIIR_MSG(me_ptr->miid, DBG_HIGH_PRIO,"CAPI MSIIR : channel mask : %lu, ch_idx : %ld, ch_mask_list_ptr[%lu] : %#lx.", + ch_mask, + ch_idx, + ch_mask_arr_index, + ch_mask_list_ptr[ch_mask_arr_index]); +#endif + if ((ch_idx < 0) || (ch_idx >= (int32_t)me_ptr->media_fmt[0].format.num_channels)) + { + /*offset = sizeof(param_id_msiir_ch_filter_config_v2_t) + ch_mask_list_size_in_bytes; + offset += num_biquad_stages * MSIIR_COEFF_LENGTH * sizeof(int32_t); // filter_coeffs + offset += num_biquad_stages * sizeof(int16_t); // num_shift_factor + offset += (num_biquad_stages & 0x1) ? sizeof(int16_t): 0; // zero padding */ + continue; + } + //offset = 0; + if ((!me_ptr->is_first_frame) && (num_biquad_stages != me_ptr->per_chan_msiir_cfg_max[ch_idx].num_stages)) + { + // do cross fading if num stages changed in the middle of data processing + me_ptr->start_cross_fade = TRUE; + } + + me_ptr->per_chan_msiir_cfg_max[ch_idx].num_stages = num_biquad_stages; + + coeff_ptr = coeff_ptr_1; + shift_factor_ptr = shift_factor_ptr_1; + + for (int32_t stage = 0; stage < num_biquad_stages; stage++) + { + for (int32_t idx = 0; idx < MSIIR_COEFF_LENGTH; idx++) + { + int32_t iir_coeff = *coeff_ptr++; + + if ((!me_ptr->is_first_frame) && + (iir_coeff != me_ptr->per_chan_msiir_cfg_max[ch_idx].coeffs_struct[stage].iir_coeffs[idx])) + { + me_ptr->start_cross_fade = TRUE; + } + + me_ptr->per_chan_msiir_cfg_max[ch_idx].coeffs_struct[stage].iir_coeffs[idx] = iir_coeff; + } + } + + for (int32_t stage = 0; stage < num_biquad_stages; stage++) + { + int32_t shift_factor = (int32_t)(*shift_factor_ptr++); + + if ((!me_ptr->is_first_frame) && + (shift_factor != me_ptr->per_chan_msiir_cfg_max[ch_idx].coeffs_struct[stage].shift_factor)) + { + me_ptr->start_cross_fade = TRUE; + } + + me_ptr->per_chan_msiir_cfg_max[ch_idx].coeffs_struct[stage].shift_factor = shift_factor; + } + + if (!me_ptr->start_cross_fade) + { + uint32_t param_size = sizeof(me_ptr->per_chan_msiir_cfg_max[ch_idx].num_stages) + + num_biquad_stages * MSIIR_COEFF_LENGTH * sizeof(int32_t) + + num_biquad_stages * sizeof(int32_t); + + msiir_set_param(&(me_ptr->msiir_lib[ch_idx]), + MSIIR_PARAM_CONFIG, + (void *)&(me_ptr->per_chan_msiir_cfg_max[ch_idx]), + param_size); + + // when we reach here, data_ptr should points to the start of next channel's + // config params (param_id_channel_type_msiir_config_pair_t followed by filter coeff and + // num_shift_factor + capi_msiir_update_delay_event(me_ptr); + } + } + } + MSIIR_MSG(me_ptr->miid, DBG_HIGH_PRIO, + "CAPI MSIIR : Set config for group %d, includes channels from %lu to %lu, stages %lu.", + group_no, + start_ch_map, + end_ch_map, + num_biquad_stages); + ch_mask_arr_index++; + } + } + } + + return CAPI_EOK; +} + +capi_err_t capi_msiir_get_enable_disable_per_channel_v2(capi_multistageiir_t *me_ptr, + capi_buf_t *params_ptr) +{ + if (me_ptr->enable_params.params_ptr.data_ptr) + { + if (params_ptr->max_data_len < me_ptr->enable_params.params_ptr.actual_data_len) + { + params_ptr->actual_data_len = me_ptr->enable_params.params_ptr.actual_data_len; + MSIIR_MSG(me_ptr->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Get Enable/Disable, Bad payload size %lu: actual = %lu", + params_ptr->max_data_len, + me_ptr->enable_params.params_ptr.actual_data_len); + return CAPI_ENEEDMORE; + } + params_ptr->actual_data_len = memscpy(params_ptr->data_ptr, + params_ptr->max_data_len, + me_ptr->enable_params.params_ptr.data_ptr, + me_ptr->enable_params.params_ptr.actual_data_len); + } + else if (me_ptr->media_fmt_received) + { + param_id_msiir_config_v2_t *en_dis_cfg_ptr = (param_id_msiir_config_v2_t *)params_ptr->data_ptr; + uint32_t *payload_ptr = (uint32_t *)params_ptr->data_ptr; + uint32_t chan_mask_list[CAPI_CMN_MAX_CHANNEL_MAP_GROUPS] = { 0 }; + uint32_t channel_type_group_mask = 0; + uint32_t num_channels = me_ptr->media_fmt[0].format.num_channels; + en_dis_cfg_ptr->num_config = 1; //disable + + uint32_t required_payload_size = sizeof(param_id_msiir_config_v2_t) + + sizeof(param_id_msiir_ch_enable_v2_t) + + CAPI_CMN_GET_MAX_CHANNEL_GROUPS_NEEDED(num_channels) * CAPI_CMN_INT32_SIZE_IN_BYTES; + + if (params_ptr->max_data_len < required_payload_size) + { + MSIIR_MSG(me_ptr->miid, DBG_ERROR_PRIO, "CAPI MSIIR : Get Enable/Disable, Bad payload size %lu", params_ptr->max_data_len); + params_ptr->actual_data_len = 0; + return CAPI_ENEEDMORE; + } + + for(uint32_t ch = 0; ch < num_channels; ch ++) + { + uint32_t curr_channel_type = me_ptr->media_fmt[0].channel_type[ch]; + uint32_t curr_ch_mask = (1 << CAPI_CMN_MOD_WITH_32(curr_channel_type)); + uint32_t curr_ch_index_grp_no = CAPI_CMN_DIVIDE_WITH_32(curr_channel_type); + uint32_t curr_group_mask = (1 << CAPI_CMN_MOD_WITH_32(curr_ch_index_grp_no)); + + if(!CAPI_CMN_IS_BIT_SET_AT_POS_IN_32B_VAL(curr_group_mask, curr_ch_index_grp_no)) //if this group is not set, then only set + { + channel_type_group_mask |= (1 << CAPI_CMN_MOD_WITH_32(curr_ch_index_grp_no)); + } + chan_mask_list[curr_ch_index_grp_no] |= curr_ch_mask; + } + payload_ptr++; //ref to group_mask + + param_id_msiir_ch_enable_v2_t *multi_ch_iir_enable_ptr = (param_id_msiir_ch_enable_v2_t*)payload_ptr; + multi_ch_iir_enable_ptr->channel_type_group_mask = channel_type_group_mask; + + uint32_t *ch_mask_list_ptr = (payload_ptr++); + uint32_t ch_mask_array_idx = 0; + for(uint32_t group_no = 0; group_no < CAPI_CMN_MAX_CHANNEL_MAP_GROUPS; group_no ++) + { + if(chan_mask_list[group_no]) + { + ch_mask_list_ptr[ch_mask_array_idx] = chan_mask_list[group_no]; + ch_mask_array_idx++; + } + } + payload_ptr += ch_mask_array_idx; + *(payload_ptr) = 0; //disable value + + params_ptr->actual_data_len = required_payload_size; + } + else + { + params_ptr->actual_data_len = 0; + return CAPI_EBADPARAM; + } + return CAPI_EOK; +} + +capi_err_t capi_msiir_get_pregain_per_channel_v2( + capi_multistageiir_t *me_ptr, + capi_buf_t *params_ptr) +{ + if (me_ptr->pregain_params.params_ptr.data_ptr) + { + if (params_ptr->max_data_len < me_ptr->pregain_params.params_ptr.actual_data_len) + { + params_ptr->actual_data_len = me_ptr->pregain_params.params_ptr.actual_data_len; + MSIIR_MSG(me_ptr->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Get pregain, Bad payload size %lu: actual = %lu", + params_ptr->max_data_len, + me_ptr->pregain_params.params_ptr.actual_data_len); + + return CAPI_ENEEDMORE; + } + params_ptr->actual_data_len = memscpy(params_ptr->data_ptr, + params_ptr->max_data_len, + me_ptr->pregain_params.params_ptr.data_ptr, + me_ptr->pregain_params.params_ptr.actual_data_len); + } + else if (me_ptr->media_fmt_received) + { + param_id_msiir_pregain_v2_t *iir_pregain_pkt_ptr = (param_id_msiir_pregain_v2_t *)(params_ptr->data_ptr); + uint32_t *payload_ptr = (uint32_t *)params_ptr->data_ptr; + uint32_t chan_mask_list[CAPI_CMN_MAX_CHANNEL_MAP_GROUPS] = { 0 }; + uint32_t channel_type_group_mask = 0; + uint32_t num_channels = me_ptr->media_fmt[0].format.num_channels; + iir_pregain_pkt_ptr->num_config = 1; // would return the same default pregain value + // for all channels + + uint32_t required_payload_size = sizeof(param_id_msiir_pregain_v2_t) + + sizeof(param_id_msiir_ch_pregain_v2_t) + + CAPI_CMN_GET_MAX_CHANNEL_GROUPS_NEEDED(num_channels) * CAPI_CMN_INT32_SIZE_IN_BYTES; + + if (params_ptr->max_data_len < required_payload_size) + { + MSIIR_MSG(me_ptr->miid, DBG_ERROR_PRIO, "CAPI MSIIR : Get pregain, Bad payload size %lu", params_ptr->max_data_len); + return CAPI_ENEEDMORE; + } + + for(uint32_t ch = 0; ch < num_channels; ch ++) + { + uint32_t curr_channel_type = me_ptr->media_fmt[0].channel_type[ch]; + uint32_t curr_ch_mask = (1 << CAPI_CMN_MOD_WITH_32(curr_channel_type)); + uint32_t curr_ch_index_grp_no = CAPI_CMN_DIVIDE_WITH_32(curr_channel_type); + uint32_t curr_group_mask = (1 << CAPI_CMN_MOD_WITH_32(curr_ch_index_grp_no)); + + if(!CAPI_CMN_IS_BIT_SET_AT_POS_IN_32B_VAL(curr_group_mask, curr_ch_index_grp_no)) //if this group is not set, then only set + { + channel_type_group_mask |= (1 << CAPI_CMN_MOD_WITH_32(curr_ch_index_grp_no)); + } + chan_mask_list[curr_ch_index_grp_no] |= curr_ch_mask; + } + // unity gain in Q27 + msiir_pregain_t pregain_lib = (msiir_pregain_t)((int32_t)(1 << MSIIR_Q_PREGAIN)); + uint32_t actual_param_size = 0; + MSIIR_RESULT result_lib = MSIIR_SUCCESS; + result_lib = msiir_get_param(&(me_ptr->msiir_lib[0]), + MSIIR_PARAM_PREGAIN, + (void *)&pregain_lib, + sizeof(pregain_lib), + &actual_param_size); + + if (actual_param_size > sizeof(pregain_lib) || (MSIIR_SUCCESS != result_lib)) + { + MSIIR_MSG(me_ptr->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : get pregain error result %d, size %lu", + result_lib, + actual_param_size); + return CAPI_EFAILED; + } + int32_t pregain = (int32_t)pregain_lib; + + payload_ptr++; //ref to group_mask + + param_id_msiir_ch_pregain_v2_t *multi_ch_iir_pregain_ptr = (param_id_msiir_ch_pregain_v2_t*)payload_ptr; + multi_ch_iir_pregain_ptr->channel_type_group_mask = channel_type_group_mask; + + uint32_t *ch_mask_list_ptr = (payload_ptr++); + uint32_t ch_mask_array_idx = 0; + for(uint32_t group_no = 0; group_no < CAPI_CMN_MAX_CHANNEL_MAP_GROUPS; group_no ++) + { + if(chan_mask_list[group_no]) + { + ch_mask_list_ptr[ch_mask_array_idx] = chan_mask_list[group_no]; + ch_mask_array_idx++; + } + } + payload_ptr += ch_mask_array_idx; + *(payload_ptr) = pregain; //pregain value + + params_ptr->actual_data_len = required_payload_size; + } + else + { + params_ptr->actual_data_len = 0; + return CAPI_EBADPARAM; + } + return CAPI_EOK; +} + +capi_err_t capi_msiir_get_config_per_channel_v2( + capi_multistageiir_t *me_ptr, + capi_buf_t *params_ptr) +{ + if (me_ptr->config_params.params_ptr.data_ptr) + { + if (params_ptr->max_data_len < me_ptr->config_params.params_ptr.actual_data_len) + { + params_ptr->actual_data_len = me_ptr->config_params.params_ptr.actual_data_len; + MSIIR_MSG(me_ptr->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Get IIR configuration, Bad payload size %lu, Actual = %lu", + params_ptr->max_data_len, + params_ptr->actual_data_len); + return CAPI_ENEEDMORE; + } + params_ptr->actual_data_len = memscpy(params_ptr->data_ptr, + params_ptr->max_data_len, + me_ptr->config_params.params_ptr.data_ptr, + me_ptr->config_params.params_ptr.actual_data_len); + } + else if (me_ptr->media_fmt_received) + { + param_id_msiir_config_v2_t *iir_config_pkt_ptr = (param_id_msiir_config_v2_t *)(params_ptr->data_ptr); + uint32_t *payload_ptr = (uint32_t *)params_ptr->data_ptr; + uint32_t chan_mask_list[CAPI_CMN_MAX_CHANNEL_MAP_GROUPS] = { 0 }; + uint32_t channel_type_group_mask = 0; + uint32_t num_channels = me_ptr->media_fmt[0].format.num_channels; + uint32_t actual_size = sizeof(param_id_msiir_config_v2_t); + + iir_config_pkt_ptr->num_config = 1; // would return the same default pregain value + // for all channels + uint8_t *data_ptr = (uint8_t *)(((uint8_t *)(params_ptr->data_ptr)) + sizeof(param_id_msiir_config_v2_t)); + payload_ptr = (uint32_t *)data_ptr; + + for(uint32_t ch = 0; ch < num_channels; ch ++) + { + uint32_t curr_channel_type = me_ptr->media_fmt[0].channel_type[ch]; + uint32_t curr_ch_mask = (1 << CAPI_CMN_MOD_WITH_32(curr_channel_type)); + uint32_t curr_ch_index_grp_no = CAPI_CMN_DIVIDE_WITH_32(curr_channel_type); + uint32_t curr_group_mask = (1 << CAPI_CMN_MOD_WITH_32(curr_ch_index_grp_no)); + + if(!CAPI_CMN_IS_BIT_SET_AT_POS_IN_32B_VAL(curr_group_mask, curr_ch_index_grp_no)) //if this group is not set, then only set + { + channel_type_group_mask |= (1 << CAPI_CMN_MOD_WITH_32(curr_ch_index_grp_no)); + } + chan_mask_list[curr_ch_index_grp_no] |= curr_ch_mask; + } + *payload_ptr = channel_type_group_mask; + + uint32_t *ch_mask_list_ptr = (payload_ptr++); + uint32_t ch_mask_array_idx = 0; + for(uint32_t group_no = 0; group_no < CAPI_CMN_MAX_CHANNEL_MAP_GROUPS; group_no ++) + { + if(chan_mask_list[group_no]) + { + ch_mask_list_ptr[ch_mask_array_idx] = chan_mask_list[group_no]; + ch_mask_array_idx++; + } + } + payload_ptr += ch_mask_array_idx; + capi_one_chan_msiir_config_static_param_t *this_chan_cfg_ptr = (capi_one_chan_msiir_config_static_param_t*)payload_ptr; + this_chan_cfg_ptr->reserved = 0; + + capi_one_chan_msiir_config_max_t *one_chan_msiir_config_max_ptr = &(me_ptr->default_per_chan_msiir_cfg_max); + // if the filter is disabled, the library uses default 0 stages for copy through + uint32_t num_biquad_stages = 0; + uint32_t actual_param_size = 0; + MSIIR_RESULT result_lib = MSIIR_SUCCESS; + + one_chan_msiir_config_max_ptr = &(me_ptr->per_chan_msiir_cfg_max[0]); +#ifdef CAPI_MSIIR_DEBUG_MSG + MSIIR_MSG(me_ptr->miid, DBG_LOW_PRIO, "CAPI MSIIR : Config get param from CUR_INST "); +#endif + result_lib = msiir_get_param(&(me_ptr->msiir_lib[0]), + MSIIR_PARAM_CONFIG, + (void *)one_chan_msiir_config_max_ptr, + sizeof(me_ptr->per_chan_msiir_cfg_max[0]), + &actual_param_size); + if (actual_param_size > sizeof(me_ptr->per_chan_msiir_cfg_max[0]) || (MSIIR_SUCCESS != result_lib)) + { + MSIIR_MSG(me_ptr->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : get delay error result %d, size %lu", + result_lib, + actual_param_size); + return CAPI_EFAILED; + } + + num_biquad_stages = one_chan_msiir_config_max_ptr->num_stages; + + if (num_biquad_stages > (int32_t)MSIIR_MAX_STAGES) + { + MSIIR_MSG(me_ptr->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Get config params, bad internal num_biquad_stages %d", + (int)num_biquad_stages); + return CAPI_EFAILED; + } + + this_chan_cfg_ptr->num_biquad_stages = num_biquad_stages; + + // move pointer to filter coeffs + data_ptr += sizeof(param_id_msiir_ch_filter_config_v2_t) + + ch_mask_array_idx * CAPI_CMN_INT32_SIZE_IN_BYTES; + actual_size += sizeof(param_id_msiir_ch_filter_config_v2_t) + + ch_mask_array_idx * CAPI_CMN_INT32_SIZE_IN_BYTES; + + int32_t *coeff_ptr = (int32_t *)data_ptr; + for (int32_t stage = 0; stage < num_biquad_stages; stage++) + { + for (int32_t idx = 0; idx < MSIIR_COEFF_LENGTH; idx++) + { + *coeff_ptr++ = one_chan_msiir_config_max_ptr->coeffs_struct[stage].iir_coeffs[idx]; + } + } + + size_t coeff_size = num_biquad_stages * MSIIR_COEFF_LENGTH * sizeof(int32_t); + + // move pointer to numerator shift factors + data_ptr += coeff_size; + // add size of filter coeffs + actual_size += coeff_size; + + int16_t *shift_factor_ptr = (int16_t *)data_ptr; + + for (int32_t stage = 0; stage < num_biquad_stages; stage++) + { + *shift_factor_ptr++ = (int16_t)(one_chan_msiir_config_max_ptr->coeffs_struct[stage].shift_factor); + } + + size_t num_shift_fac_size = num_biquad_stages * sizeof(int16_t); + + // add size of numerator shift factors + actual_size += num_shift_fac_size; + + // add padding if odd stages + bool_t is_odd_stages = (bool_t)(num_biquad_stages & 0x1); + if (is_odd_stages) + { + data_ptr += num_shift_fac_size + sizeof(int16_t); + actual_size += sizeof(int16_t); // 16-bits padding + } + + params_ptr->actual_data_len = actual_size; + + if (params_ptr->max_data_len < actual_size) + { + MSIIR_MSG(me_ptr->miid, DBG_ERROR_PRIO, + "CAPI MSIIR : Get config params, Bad payload size %d, required %d", + (int)params_ptr->max_data_len, + (int)actual_size); + return CAPI_ENEEDMORE; + } + } + else + { + params_ptr->actual_data_len = 0; + return CAPI_EBADPARAM; + } + + return CAPI_EOK; +} diff --git a/modules/processing/filters/multi_stage_iir/capi/stub_src/capi_multistageiir_stub.cpp b/modules/processing/filters/multi_stage_iir/capi/stub_src/capi_multistageiir_stub.cpp new file mode 100644 index 0000000..1901f34 --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/capi/stub_src/capi_multistageiir_stub.cpp @@ -0,0 +1,84 @@ +/* ======================================================================== */ +/* +@file capi_multistageiir_stub.cpp.cpp + + Source file to implement the Audio Post Processor Interface for Multi-Stage + IIR filters +*/ + +/* ========================================================================= + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause + ========================================================================= */ + +/*------------------------------------------------------------------------ + * Include files + * -----------------------------------------------------------------------*/ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "capi.h" + +/*------------------------------------------------------------------------ + * Static declarations + * -----------------------------------------------------------------------*/ + +/** + * Get static properties of multistage IIR module such as + * memory, stack requirements etc. + * See Elite_CAPI.h for more details. + */ +capi_err_t capi_multistageiir_get_static_properties( capi_proplist_t* init_set_properties, + capi_proplist_t* static_properties) +{ + ; + return CAPI_EUNSUPPORTED; +} + +/** + * Instantiates(and allocates) the module memory. + * See Elite_CAPI.h for more details. + */ +capi_err_t capi_multistageiir_init( capi_t* _pif, + capi_proplist_t* init_set_properties) +{ + + return CAPI_EUNSUPPORTED; +} + + +capi_err_t capi_multistageiir_left_init( + capi_t* _pif, + capi_proplist_t* init_set_properties) +{ + return CAPI_EUNSUPPORTED; +} + +capi_err_t capi_multistageiir_right_init( + capi_t* _pif, + capi_proplist_t* init_set_properties) +{ + return CAPI_EUNSUPPORTED; +} + +capi_err_t capi_multistageiir_left_get_static_properties( + capi_proplist_t* init_set_properties, + capi_proplist_t* static_properties) +{ + return CAPI_EUNSUPPORTED; +} + +capi_err_t capi_multistageiir_right_get_static_properties( + capi_proplist_t* init_set_properties, + capi_proplist_t* static_properties) +{ + return CAPI_EUNSUPPORTED; +} + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + diff --git a/modules/processing/filters/multi_stage_iir/capi/tst/cfg/ffa_handset_nb.cfg b/modules/processing/filters/multi_stage_iir/capi/tst/cfg/ffa_handset_nb.cfg new file mode 100644 index 0000000..7709d5f --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/capi/tst/cfg/ffa_handset_nb.cfg @@ -0,0 +1,121 @@ +# Configuration file for Multiband IIR unit test +# in.raw generated from harvard.wav + +# Reset algorithm +SetParamInband + PayloadSizeInBytes 32 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 10 00 00 00 # Size of Payload + 41 0c 01 00 # ADM_MODULE_ID_TX_IIR_FILTER 0x00010C41 + 07 07 01 00 # APPI_PARAM_ID_ALGORITHMIC_RESET 0x00010707 + 04 00 00 00 # Param Size and Padding + 00 00 00 00 # Dummy payload + +# Set Media Format +# Only signed/interleaved input is accepted and +# signed/interleaved output will be generated +SetMediaFormat + SetNumInChannels 1 + SetNumOutChannels 1 + SetInSamplingRate 8000 + SetOutSamplingRate 8000 + SetBytesPerSample 2 + +# Set IIR Enable +SetParamInband + PayloadSizeInBytes 32 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 10 00 00 00 # Size of Payload + 41 0c 01 00 # ADM_MODULE_ID_TX_IIR_FILTER 0x00010C41 + 42 0c 01 00 # ASM_PARAM_ID_IIR_TUNING_FILTER_ENABLE_CONFIG 0x00010C42 + 04 00 00 00 # Param Size and Padding + 01 00 00 00 # enable and padding + +# Set IIR Pregain +SetParamInband + PayloadSizeInBytes 32 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 10 00 00 00 # Size of Payload + 41 0c 01 00 # ADM_MODULE_ID_TX_IIR_FILTER 0x00010C41 + 43 0c 01 00 # AASM_PARAM_ID_IIR_TUNING_FILTER_PRE_GAIN 0x00010C43 + 04 00 00 00 # Param Size and Padding + 00 20 00 00 # pregain and padding + +# Set IIR Coefficients +# These coefficients are derived from the following document in Agile +# MSM 8660 + QTR 8615 (Timpani), Audio System VI with Android OS +# Figure 3-1 of 80-VT567-26 Rev A +SetParamInband + PayloadSizeInBytes 152 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 88 00 00 00 # Size of Payload + 41 0c 01 00 # ADM_MODULE_ID_TX_IIR_FILTER 0x00010C41 + 44 0c 01 00 # ASM_PARAM_ID_IIR_TUNING_FILTER_CONFIG_PARAMS 0x00010C44 + 7c 00 00 00 # Param Size and Padding + 05 00 00 00 # Number of stages + 00 00 00 40 # Stage 1 (b0, b1, b2, a1, a2) + F9 D8 3A 09 + C1 F9 E1 F9 + 62 A4 A1 0C + 76 9C 20 FB + 4B F3 02 41 # Stage 2 (b0, b1, b2, a1, a2) + AF 99 BC FF + 0D EC 23 26 + F6 CD B4 08 + 16 77 5A 1C + 4B F3 02 41 # Stage 3 (b0, b1, b2, a1, a2) + 18 4B 13 D9 + F3 4B 6E 2C + F9 AC DB DB + 3A 49 94 28 + 4B F3 02 41 # Stage 4 (b0, b1, b2, a1, a2) + 23 1D 72 B7 + 07 0C 64 2C + 6C F9 3E B7 + 67 FB A3 2C + 4B F3 02 41 # Stage 5 (b0, b1, b2, a1, a2) + 9E 6D 26 26 + 65 59 CB 30 + 10 F0 E6 27 + 79 A1 2E 31 + 02 00 02 00 # Shift factors + 02 00 02 00 + 02 00 01 00 # Panning + 01 00 01 00 + 01 00 01 00 + +ProcessData + NumBuffers 1000 + +# Get IIR Enable +GetParamInband + PayloadSizeInBytes 24 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 41 0c 01 00 # ADM_MODULE_ID_TX_IIR_FILTER 0x00010C41 + 43 0c 01 00 # AASM_PARAM_ID_IIR_TUNING_FILTER_PRE_GAIN 0x00010C43 + 04 00 00 00 # Param Size and Padding + RefPayloadSizeInBytes 4 + 00 20 00 00 # enable + +# Get IIR algorithm Delay +GetParamInband + PayloadSizeInBytes 24 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 41 0c 01 00 # ADM_MODULE_ID_TX_IIR_FILTER 0x00010C41 + 08 07 01 00 # APPI_PARAM_ID_ALGORITHMIC_DELAY 0x00010708 + 04 00 00 00 # Param Size and Padding + RefPayloadSizeInBytes 4 + 0A 00 00 00 + diff --git a/modules/processing/filters/multi_stage_iir/capi/tst/cfg/ffa_handset_wb.cfg b/modules/processing/filters/multi_stage_iir/capi/tst/cfg/ffa_handset_wb.cfg new file mode 100644 index 0000000..2697b50 --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/capi/tst/cfg/ffa_handset_wb.cfg @@ -0,0 +1,121 @@ +# Configuration file for MultiStage IIR unit test +# in.raw generated from harvard_WB.wav + +# Reset algorithm +SetParamInband + PayloadSizeInBytes 32 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 10 00 00 00 # Size of Payload + 41 0c 01 00 # ADM_MODULE_ID_TX_IIR_FILTER 0x00010C41 + 07 07 01 00 # APPI_PARAM_ID_ALGORITHMIC_RESET 0x00010707 + 04 00 00 00 # Param Size and Padding + 00 00 00 00 # Dummy payload + +# Set Media Format +# Only signed/interleaved input is accepted and +# signed/interleaved output will be generated +SetMediaFormat + SetNumInChannels 1 + SetNumOutChannels 1 + SetInSamplingRate 16000 + SetOutSamplingRate 16000 + SetBytesPerSample 2 + +# Set IIR Enable +SetParamInband + PayloadSizeInBytes 32 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 10 00 00 00 # Size of Payload + 41 0c 01 00 # ADM_MODULE_ID_TX_IIR_FILTER 0x00010C41 + 42 0c 01 00 # ASM_PARAM_ID_IIR_TUNING_FILTER_ENABLE_CONFIG 0x00010C42 + 04 00 00 00 # Param Size and Padding + 01 00 00 00 # enable and padding + +# Set IIR Pregain +SetParamInband + PayloadSizeInBytes 32 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 10 00 00 00 # Size of Payload + 41 0c 01 00 # ADM_MODULE_ID_TX_IIR_FILTER 0x00010C41 + 43 0c 01 00 # AASM_PARAM_ID_IIR_TUNING_FILTER_PRE_GAIN 0x00010C43 + 04 00 00 00 # Param Size and Padding + 00 20 00 00 # pregain and padding + +# Set IIR Coefficients +# These coefficients are derived from the following document in Agile +# MSM 8660 + QTR 8615 (Timpani), Audio System VI with Android OS +# Figure 3-1 of 80-VT567-26 Rev A +SetParamInband + PayloadSizeInBytes 152 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 88 00 00 00 # Size of Payload + 41 0c 01 00 # ADM_MODULE_ID_TX_IIR_FILTER 0x00010C41 + 44 0c 01 00 # ASM_PARAM_ID_IIR_TUNING_FILTER_CONFIG_PARAMS 0x00010C44 + 7c 00 00 00 # Param Size and Padding + 05 00 00 00 # Number of stages + 00 00 00 40 # Stage 1 (b0, b1, b2, a1, a2) + 0A 54 80 A9 + AE E1 93 1B + DB 2C 78 E9 + C8 BE CE 16 + 4C 52 62 4A # Stage 2 (b0, b1, b2, a1, a2) + 8A 85 F2 AC + 48 6A D0 2A + 7F 93 86 A1 + 08 C0 55 24 + 4C 52 62 4A # Stage 3 (b0, b1, b2, a1, a2) + 56 82 8E 3E + 7A 0F 58 31 + EA 15 CE 32 + 7A B4 E6 2A + 4C 52 62 4A # Stage 4 (b0, b1, b2, a1, a2) + 16 F7 05 0F + AE C9 78 35 + 00 6C 12 09 + 6E CF B8 2E + 4C 52 62 4A # Stage 5 (b0, b1, b2, a1, a2) + 5F EF FF DB + DC 8A A8 3C + A9 1B 97 E7 + 88 D4 99 31 + 02 00 02 00 # Shift factors + 02 00 02 00 + 02 00 02 00 # Panning + 01 00 01 00 + 01 00 01 00 + +ProcessData + NumBuffers 1000 + +# Get IIR Enable +GetParamInband + PayloadSizeInBytes 24 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 41 0c 01 00 # ADM_MODULE_ID_TX_IIR_FILTER 0x00010C41 + 43 0c 01 00 # AASM_PARAM_ID_IIR_TUNING_FILTER_PRE_GAIN 0x00010C43 + 04 00 00 00 # Param Size and Padding + RefPayloadSizeInBytes 4 + 00 20 00 00 # enable + +# Get IIR algorithm Delay +GetParamInband + PayloadSizeInBytes 24 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 41 0c 01 00 # ADM_MODULE_ID_TX_IIR_FILTER 0x00010C41 + 08 07 01 00 # APPI_PARAM_ID_ALGORITHMIC_DELAY 0x00010708 + 04 00 00 00 # Param Size and Padding + RefPayloadSizeInBytes 4 + 0A 00 00 00 + diff --git a/modules/processing/filters/multi_stage_iir/capi/tst/cfg/left_zero_right_hpf.cfg b/modules/processing/filters/multi_stage_iir/capi/tst/cfg/left_zero_right_hpf.cfg new file mode 100644 index 0000000..c22535a --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/capi/tst/cfg/left_zero_right_hpf.cfg @@ -0,0 +1,146 @@ +# Configuration file for Multistage IIR unit test + +# Reset algorithm +SetParamInband + PayloadSizeInBytes 32 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 10 00 00 00 # Size of Payload + 1f 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F this field is ignored in UT + 07 07 01 00 # APPI_PARAM_ID_ALGORITHMIC_RESET 0x00010707 + 04 00 00 00 # Param Size and Padding + 00 00 00 00 # Dummy payload + +# Set Media Format +# Only signed/interleaved input is accepted and +# signed/interleaved output will be generated +SetMediaFormat + SetNumInChannels 2 + SetNumOutChannels 2 + SetInSamplingRate 48000 + SetOutSamplingRate 48000 + SetBytesPerSample 2 + +# Set MSIIR Enable +SetParamInband + PayloadSizeInBytes 48 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 10 00 00 00 # Size of Payload + 1f 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1c 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_ENABLE 0x0001031C + 14 00 00 00 # Param Size and Padding + 02 00 00 00 # num_channels + 01 # channel_type = PCM_CHANNEL_L + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 01 00 00 00 # enable_flag + 02 # channel_type = PCM_CHANNEL_R + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 01 00 00 00 # enable_flag + + +# Set MSIIR Pregain +SetParamInband + PayloadSizeInBytes 40 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 18 00 00 00 # Size of Payload + 1f 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1d 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN 0x0001031D + 0c 00 00 00 # Param Size and Padding + 01 00 00 00 # num_channels + 02 # channel_type = PCM_CHANNEL_R + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 00 00 00 08 # pregain, Q27 + +# These coefficients are for HPF, given by Tuning folks (Khalid Siddiqui) +SetParamInband + PayloadSizeInBytes 132 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 74 00 00 00 # Size of Payload + 1f 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1e 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS 0x0001031E + 68 00 00 00 # Param Size and Padding + 02 00 00 00 # num_channels + 01 # channel_type = PCM_CHANNEL_L + 00 # reserved + 01 00 # num_biquad_stages + 00 00 00 00 # Stage 1 (b0, b1, b2, a1, a2)=0 left chan is zeroed + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 00 00 00 00 + 02 00 # Stage 1 numerator shift factor + 00 00 # padding + 02 # channel_type = PCM_CHANNEL_R + 00 # reserved + 03 00 # num_biquad_stages + 00 00 00 40 # Stage 1 (b0, b1, b2, a1, a2) + 00 00 00 80 + 00 00 00 40 + 54 2B EF 82 + 49 D5 73 3D + E2 A6 52 3B # Stage 2 (b0, b1, b2, a1, a2) + 3B B2 5A 89 + E2 A6 52 3B + 00 15 1A 87 + 0B 9F 45 39 + E2 A6 52 3B # Stage 3 (b0, b1, b2, a1, a2) + 3B B2 5A 89 + E2 A6 52 3B + 3D 4C 62 89 + 57 99 FB 36 + 02 00 # numerator shift factors + 02 00 + 02 00 + 00 00 # padding + +ProcessData + NumBuffers 200 + +# Get MSIIR Enable +GetParamInband + PayloadSizeInBytes 24 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 1f 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1c 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_ENABLE 0x0001031C + 44 00 00 00 # Param Size and Padding + RefPayloadSizeInBytes 20 + 02 00 00 00 # num_channels + 01 # channel_type = PCM_CHANNEL_L + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 01 00 00 00 # enable_flag + 02 # channel_type = PCM_CHANNEL_R + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 01 00 00 00 # enable_flag + + +# Get MSIIR algorithm Delay +GetParamInband + PayloadSizeInBytes 24 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 1f 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 08 07 01 00 # APPI_PARAM_ID_ALGORITHMIC_DELAY 0x00010708 + 04 00 00 00 # Param Size and Padding + RefPayloadSizeInBytes 4 + 06 00 00 00 + diff --git a/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_3channel_32bit.cfg b/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_3channel_32bit.cfg new file mode 100644 index 0000000..074b46b --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_3channel_32bit.cfg @@ -0,0 +1,194 @@ +# Configuration file for MultiStage IIR unit test +# input generated from Chirp.raw and Square.raw + +# Reset algorithm +SetParamInband + PayloadSizeInBytes 32 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 10 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 07 07 01 00 # APPI_PARAM_ID_ALGORITHMIC_RESET 0x00010707 + 04 00 00 00 # Param Size and Padding + 00 00 00 00 # Dummy payload + +# Set Media Format +# Only signed/interleaved input is accepted and +# signed/interleaved output will be generated +SetMediaFormat + SetNumInChannels 3 + SetNumOutChannels 3 + SetInSamplingRate 48000 + SetOutSamplingRate 48000 + SetBytesPerSample 4 + +# Set IIR Enable +SetParamInband + PayloadSizeInBytes 56 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 28 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1C 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_ENABLE 0x0001031C + 1C 00 00 00 # Param Size and Padding + 03 00 00 00 # num_channels + 01 # channel_type = PCM_CHANNEL_L + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 00 00 00 00 # enable_flag + 02 # channel_type = PCM_CHANNEL_R + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 01 00 00 00 # enable_flag + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 01 00 00 00 # enable_flag + + +# Set IIR Pregain +SetParamInband + PayloadSizeInBytes 40 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 18 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1D 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN 0x0001031D + 0C 00 00 00 # Param Size and Padding + 01 00 00 00 # num_channels + 02 # channel_type = PCM_CHANNEL_R + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 00 00 00 08 # pregain, Q27 unity gain + +# Set IIR Coefficients +# Filter coefficients take from System reference test config MSIIR_Unlinked.cfg +SetParamInband + PayloadSizeInBytes 264 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + F8 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1E 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS 0x0001031E + EC 00 00 00 # Param Size and Padding + 02 00 00 00 # num_channels + 02 # channel_type = PCM_CHANNEL_R + 00 # reserved + 05 00 # num_biquad_stages + 98 8B 3A 3F # Stage 1 (b0, b1, b2, a1, a2) + 04 47 A4 0B + F8 81 95 32 + 6D A5 9E 0B + 28 AF D5 31 + DB CC 2B 3E # Stage 2 (b0, b1, b2, a1, a2) + B9 0C F2 B7 + FE 58 80 30 + 6C 83 BF B8 + 5C 7A 65 2F + C7 16 D6 38 # Stage 3 (b0, b1, b2, a1, a2) + DF 70 E9 CC + BA C6 25 1F + 55 4D CD CB + AF 17 2F 24 + 60 6B 68 37 # Stage 4 (b0, b1, b2, a1, a2) + 0B 1B CD 02 + 52 E0 5E 24 + EA 67 E2 FC + 35 F0 88 2A + A7 BD 60 65 # Stage 5 (b0, b1, b2, a1, a2) + E4 1F 22 D9 + 1C DD 61 31 + 1A 0D A0 DF + C1 7C 0F 2C + 02 00 # Stage 1 numerator shift factor + 02 00 # Stage 2 numerator shift factor + 02 00 # Stage 3 numerator shift factor + 02 00 # Stage 4 numerator shift factor + 02 00 # Stage 5 numerator shift factor + 00 00 # padding + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved + 05 00 # num_biquad_stages + 3D F2 C4 3E # Stage 1 (b0, b1, b2, a1, a2) + 6E 1F 75 04 + 12 BA 6D 31 + D9 E6 6C 04 + B8 24 A5 32 + BE 7C CB 3D # Stage 2 (b0, b1, b2, a1, a2) + E8 61 94 B4 + 48 7D 92 2C + 38 35 6F B1 + 2F B2 18 2F + 05 80 EC 3C # Stage 3 (b0, b1, b2, a1, a2) + 1C 6E CA F3 + EC 69 E2 29 + A4 6B 2A F6 + CD 18 7E 2C + B9 B1 F2 3F # Stage 4 (b0, b1, b2, a1, a2) + BC EF AA C3 + 86 C4 51 26 + 12 8D E0 BF + 11 0F B4 26 + CE 86 10 40 # Stage 4 (b0, b1, b2, a1, a2) + 54 F8 67 D4 + 43 A2 98 32 + 0E F3 D1 DA + 04 31 4B 27 + 02 00 # numerator shift factors + 02 00 + 02 00 + 02 00 + 02 00 + 00 00 # padding + +ProcessData + NumBuffers 1000 + +# Get IIR Enable +GetParamInband + PayloadSizeInBytes 24 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1C 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_ENABLE 0x0001031C + 44 00 00 00 # Param Size and Padding + RefPayloadSizeInBytes 28 + 03 00 00 00 # num_channels + 01 # channel_type = PCM_CHANNEL_L + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 00 00 00 00 # enable_flag + 02 # channel_type = PCM_CHANNEL_R + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 01 00 00 00 # enable_flag + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 01 00 00 00 # enable_flag + + +# Get IIR algorithm Delay +#GetParamInband +# PayloadSizeInBytes 24 +# 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 +# 41 0c 01 00 # ADM_MODULE_ID_TX_IIR_FILTER 0x00010C41 +# 08 07 01 00 # APPI_PARAM_ID_ALGORITHMIC_DELAY 0x00010708 +# 04 00 00 00 # Param Size and Padding +# RefPayloadSizeInBytes 4 +# 0A 00 00 00 + diff --git a/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_cross_fade_stereo_16bit.cfg b/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_cross_fade_stereo_16bit.cfg new file mode 100644 index 0000000..0bb5593 --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_cross_fade_stereo_16bit.cfg @@ -0,0 +1,126 @@ +# Configuration file for MultiStage IIR unit test +# in.raw generated from harvard_WB.wav + +# Reset algorithm +SetParamInband + PayloadSizeInBytes 32 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 10 00 00 00 # Size of Payload + 41 0c 01 00 # ADM_MODULE_ID_TX_IIR_FILTER 0x00010C41 + 07 07 01 00 # APPI_PARAM_ID_ALGORITHMIC_RESET 0x00010707 + 04 00 00 00 # Param Size and Padding + 00 00 00 00 # Dummy payload + +# Set Media Format +# Only signed/interleaved input is accepted and +# signed/interleaved output will be generated +SetMediaFormat + SetNumInChannels 2 + SetNumOutChannels 2 + SetInSamplingRate 48000 + SetOutSamplingRate 48000 + SetBytesPerSample 2 + +# Set IIR Enable +SetParamInband + PayloadSizeInBytes 32 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 10 00 00 00 # Size of Payload + 02 0C 01 00 # ASM_MODULE_ID_IIR_TUNING_FILTER 0x00010C02 + 03 0C 01 00 # ASM_PARAM_ID_IIR_TUNING_FILTER_ENABLE_CONFIG 0x00010C03 + 04 00 00 00 # Param Size and Padding + 01 00 00 00 # enable + +# Set IIR Pregain +SetParamInband + PayloadSizeInBytes 32 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 10 00 00 00 # Size of Payload + 02 0C 01 00 # ASM_MODULE_ID_IIR_TUNING_FILTER 0x00010C02 + 04 0C 01 00 # ASM_PARAM_ID_IIR_TUNING_FILTER_PRE_GAIN 0x00010C04 + 04 00 00 00 # Param Size and Padding + 00 20 # pregain, unity gain in Q13 + 00 00 # padding + +# Set IIR Coefficients +# These coefficients are derived from the following document in Agile +# MSM 8660 + QTR 8615 (Timpani), Audio System VI with Android OS +# Figure 3-1 of 80-VT567-26 Rev A +SetParamInband + PayloadSizeInBytes 152 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 88 00 00 00 # Size of Payload + 02 0C 01 00 # ASM_MODULE_ID_IIR_TUNING_FILTER 0x00010C02 + 05 0C 01 00 # ASM_PARAM_ID_IIR_TUNING_FILTER_CONFIG_PARAMS 0x00010C05 + 7C 00 00 00 # Param Size and Padding + 05 00 00 00 # Number of stages + 00 00 00 40 # Stage 1 (b0, b1, b2, a1, a2) + 0A 54 80 A9 + AE E1 93 1B + DB 2C 78 E9 + C8 BE CE 16 + 4C 52 62 4A # Stage 2 (b0, b1, b2, a1, a2) + 8A 85 F2 AC + 48 6A D0 2A + 7F 93 86 A1 + 08 C0 55 24 + 4C 52 62 4A # Stage 3 (b0, b1, b2, a1, a2) + 56 82 8E 3E + 7A 0F 58 31 + EA 15 CE 32 + 7A B4 E6 2A + 4C 52 62 4A # Stage 4 (b0, b1, b2, a1, a2) + 16 F7 05 0F + AE C9 78 35 + 00 6C 12 09 + 6E CF B8 2E + 4C 52 62 4A # Stage 5 (b0, b1, b2, a1, a2) + 5F EF FF DB + DC 8A A8 3C + A9 1B 97 E7 + 88 D4 99 31 + 02 00 02 00 # Shift factors + 02 00 02 00 + 02 00 02 00 # Panning + 01 00 01 00 + 01 00 01 00 + +ProcessData + NumBuffers 500 + +# Set IIR Pregain +SetParamInband + PayloadSizeInBytes 32 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 10 00 00 00 # Size of Payload + 02 0C 01 00 # ASM_MODULE_ID_IIR_TUNING_FILTER 0x00010C02 + 04 0C 01 00 # ASM_PARAM_ID_IIR_TUNING_FILTER_PRE_GAIN 0x00010C04 + 04 00 00 00 # Param Size and Padding + 00 04 # pregain changed, cross fading should happen + 00 00 # padding + +ProcessData + NumBuffers 500 + +# Get IIR Enable +GetParamInband + PayloadSizeInBytes 24 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 02 0C 01 00 # ASM_MODULE_ID_IIR_TUNING_FILTER 0x00010C02 + 03 0C 01 00 # AASM_PARAM_ID_IIR_TUNING_FILTER_PRE_GAIN 0x00010C03 + 04 00 00 00 # Param Size and Padding + RefPayloadSizeInBytes 4 + 01 00 00 00 # enable + diff --git a/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_HPF_32bit.cfg b/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_HPF_32bit.cfg new file mode 100644 index 0000000..3218ca7 --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_HPF_32bit.cfg @@ -0,0 +1,128 @@ +# Configuration file for MultiStage IIR unit test +# input generated from Chirp.raw and Square.raw + +# Reset algorithm +SetParamInband + PayloadSizeInBytes 32 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 10 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 07 07 01 00 # APPI_PARAM_ID_ALGORITHMIC_RESET 0x00010707 + 04 00 00 00 # Param Size and Padding + 00 00 00 00 # Dummy payload + +# Set Media Format +# Only signed/interleaved input is accepted and +# signed/interleaved output will be generated +SetMediaFormat + SetNumInChannels 1 + SetNumOutChannels 1 + SetInSamplingRate 48000 + SetOutSamplingRate 48000 + SetBytesPerSample 4 + +# Set IIR Enable +SetParamInband + PayloadSizeInBytes 40 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 18 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1C 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_ENABLE 0x0001031C + 0C 00 00 00 # Param Size and Padding + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 01 00 00 00 # enable_flag + + +# Set IIR Pregain +SetParamInband + PayloadSizeInBytes 40 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 18 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1D 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN 0x0001031D + 0C 00 00 00 # Param Size and Padding + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 00 00 00 08 # pregain, Q27 Unity + +# Set IIR Coefficients +# Filter coefficients take from System reference test MSIIR_HPF.cfg +SetParamInband + PayloadSizeInBytes 104 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 58 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1E 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS 0x0001031E + 4C 00 00 00 # Param Size and Padding + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved + 03 00 # num_biquad_stages + B8 DB B9 2F # Stage 1 (b0, b1, b2, a1, a2) + D8 6F A2 A0 + B8 DB B9 2F + 90 09 C6 A4 + 28 51 97 23 + 51 39 CE 3E # Stage 2 (b0, b1, b2, a1, a2) + 5A CB 90 83 + 51 39 CE 3E + 04 79 AD 83 + 85 5F B6 3E + 8A 2F 00 39 # Stage 3 (b0, b1, b2, a1, a2) + 8D AD A8 8E + 8A 2F 00 39 + FB E1 BF 89 + 74 30 9D 39 + 02 00 # Stage 1 numerator shift factor + 02 00 # Stage 2 numerator shift factor + 02 00 # Stage 3 numerator shift factor + 00 00 # padding + +ProcessData + NumBuffers 1000 + +# Get IIR Enable +GetParamInband + PayloadSizeInBytes 24 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1C 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_ENABLE 0x0001031C + 44 00 00 00 # Param Size and Padding + RefPayloadSizeInBytes 12 + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 01 00 00 00 # enable_flag + + +# Get IIR algorithm Delay +#GetParamInband +# PayloadSizeInBytes 24 +# 00 00 00 00 # Data Payload address +# 00 00 00 00 +# 00 00 00 00 +# 41 0c 01 00 # ADM_MODULE_ID_TX_IIR_FILTER 0x00010C41 +# 08 07 01 00 # APPI_PARAM_ID_ALGORITHMIC_DELAY 0x00010708 +# 04 00 00 00 # Param Size and Padding +# RefPayloadSizeInBytes 4 +# 0A 00 00 00 + diff --git a/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_LGE_HPF_16bit.cfg b/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_LGE_HPF_16bit.cfg new file mode 100644 index 0000000..4b7930b --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_LGE_HPF_16bit.cfg @@ -0,0 +1,129 @@ +# Configuration file for MultiStage IIR unit test +# input generated from Chirp.raw and Square.raw + +# Reset algorithm +SetParamInband + PayloadSizeInBytes 32 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 10 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 07 07 01 00 # APPI_PARAM_ID_ALGORITHMIC_RESET 0x00010707 + 04 00 00 00 # Param Size and Padding + 00 00 00 00 # Dummy payload + +# Set Media Format +# Only signed/interleaved input is accepted and +# signed/interleaved output will be generated +SetMediaFormat + SetNumInChannels 1 + SetNumOutChannels 1 + SetInSamplingRate 48000 + SetOutSamplingRate 48000 + SetBytesPerSample 2 + +# Set IIR Enable +SetParamInband + PayloadSizeInBytes 40 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 18 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1C 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_ENABLE 0x0001031C + 0C 00 00 00 # Param Size and Padding + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 01 00 00 00 # enable_flag + + +# Set IIR Pregain +SetParamInband + PayloadSizeInBytes 40 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 18 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1D 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN 0x0001031D + 0C 00 00 00 # Param Size and Padding + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 00 00 00 08 # pregain, Q27 Unity + +# Set IIR Coefficients +# Filter coefficients take from System reference test LGE_HPF_16bit.cfg +# the same set of coeffs are used to init default HPF in audproc service +SetParamInband + PayloadSizeInBytes 104 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 58 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1E 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS 0x0001031E + 4C 00 00 00 # Param Size and Padding + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved + 03 00 # num_biquad_stages + F8 73 DF 3F # Stage 1 (b0, b1, b2, a1, a2) + 03 32 41 80 + F8 73 DF 3F + F5 87 41 80 + E2 3D BF 3F + 48 55 AB 3F # Stage 2 (b0, b1, b2, a1, a2) + 52 5F A9 80 + 48 55 AB 3F + FE B4 A9 80 + 3B 00 57 3F + 3A AC CB 3F # Stage 3 (b0, b1, b2, a1, a2) + C6 53 34 C0 + 00 00 00 00 + 8C A7 68 C0 + 00 00 00 00 + 02 00 # Stage 1 numerator shift factor + 02 00 # Stage 2 numerator shift factor + 02 00 # Stage 3 numerator shift factor + 00 00 # padding + +ProcessData + NumBuffers 1000 + +# Get IIR Enable +GetParamInband + PayloadSizeInBytes 24 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1C 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_ENABLE 0x0001031C + 44 00 00 00 # Param Size and Padding + RefPayloadSizeInBytes 12 + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 01 00 00 00 # enable_flag + + +# Get IIR algorithm Delay +#GetParamInband +# PayloadSizeInBytes 24 +# 00 00 00 00 # Data Payload address +# 00 00 00 00 +# 00 00 00 00 +# 41 0c 01 00 # ADM_MODULE_ID_TX_IIR_FILTER 0x00010C41 +# 08 07 01 00 # APPI_PARAM_ID_ALGORITHMIC_DELAY 0x00010708 +# 04 00 00 00 # Param Size and Padding +# RefPayloadSizeInBytes 4 +# 0A 00 00 00 + diff --git a/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_LPF_32bit.cfg b/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_LPF_32bit.cfg new file mode 100644 index 0000000..200640b --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_LPF_32bit.cfg @@ -0,0 +1,128 @@ +# Configuration file for MultiStage IIR unit test +# input generated from Chirp.raw and Square.raw + +# Reset algorithm +SetParamInband + PayloadSizeInBytes 32 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 10 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 07 07 01 00 # APPI_PARAM_ID_ALGORITHMIC_RESET 0x00010707 + 04 00 00 00 # Param Size and Padding + 00 00 00 00 # Dummy payload + +# Set Media Format +# Only signed/interleaved input is accepted and +# signed/interleaved output will be generated +SetMediaFormat + SetNumInChannels 1 + SetNumOutChannels 1 + SetInSamplingRate 48000 + SetOutSamplingRate 48000 + SetBytesPerSample 4 + +# Set IIR Enable +SetParamInband + PayloadSizeInBytes 40 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 18 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1C 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_ENABLE 0x0001031C + 0C 00 00 00 # Param Size and Padding + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 01 00 00 00 # enable_flag + + +# Set IIR Pregain +SetParamInband + PayloadSizeInBytes 40 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 18 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1D 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN 0x0001031D + 0C 00 00 00 # Param Size and Padding + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 00 00 00 08 # pregain, Q27 Unity + +# Set IIR Coefficients +# Filter coefficients take from System reference test MSIIR_LPF.cfg +SetParamInband + PayloadSizeInBytes 104 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 58 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1E 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS 0x0001031E + 4C 00 00 00 # Param Size and Padding + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved + 03 00 # num_biquad_stages + 80 2E 0A 1D # Stage 1 (b0, b1, b2, a1, a2) + FF 31 A6 39 + 80 2E 0A 1D + D2 D9 10 25 + 2D B5 A9 0E + 67 CF C8 3B # Stage 2 (b0, b1, b2, a1, a2) + 8B 15 AF 6E + 67 CF C8 3B + F0 12 8F 6A + D2 D4 9A 3C + 9B B2 FF 30 # Stage 3 (b0, b1, b2, a1, a2) + 88 30 70 5D + 9B B2 FF 30 + 6B CD 60 59 + 2D 19 9A 30 + 02 00 # Stage 1 numerator shift factor + 02 00 # Stage 2 numerator shift factor + 02 00 # Stage 3 numerator shift factor + 00 00 # padding + +ProcessData + NumBuffers 1000 + +# Get IIR Enable +GetParamInband + PayloadSizeInBytes 24 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1C 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_ENABLE 0x0001031C + 44 00 00 00 # Param Size and Padding + RefPayloadSizeInBytes 12 + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 01 00 00 00 # enable_flag + + +# Get IIR algorithm Delay +#GetParamInband +# PayloadSizeInBytes 24 +# 00 00 00 00 # Data Payload address +# 00 00 00 00 +# 00 00 00 00 +# 41 0c 01 00 # ADM_MODULE_ID_TX_IIR_FILTER 0x00010C41 +# 08 07 01 00 # APPI_PARAM_ID_ALGORITHMIC_DELAY 0x00010708 +# 04 00 00 00 # Param Size and Padding +# RefPayloadSizeInBytes 4 +# 0A 00 00 00 + diff --git a/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_Stop_32bit.cfg b/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_Stop_32bit.cfg new file mode 100644 index 0000000..cca56c4 --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_Stop_32bit.cfg @@ -0,0 +1,141 @@ +# Configuration file for MultiStage IIR unit test +# input generated from Chirp.raw and Square.raw + +# Reset algorithm +SetParamInband + PayloadSizeInBytes 32 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 10 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 07 07 01 00 # APPI_PARAM_ID_ALGORITHMIC_RESET 0x00010707 + 04 00 00 00 # Param Size and Padding + 00 00 00 00 # Dummy payload + +# Set Media Format +# Only signed/interleaved input is accepted and +# signed/interleaved output will be generated +SetMediaFormat + SetNumInChannels 1 + SetNumOutChannels 1 + SetInSamplingRate 48000 + SetOutSamplingRate 48000 + SetBytesPerSample 4 + +# Set IIR Enable +SetParamInband + PayloadSizeInBytes 40 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 18 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1C 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_ENABLE 0x0001031C + 0C 00 00 00 # Param Size and Padding + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 01 00 00 00 # enable_flag + + +# Set IIR Pregain +SetParamInband + PayloadSizeInBytes 40 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 18 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1D 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN 0x0001031D + 0C 00 00 00 # Param Size and Padding + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 00 00 00 08 # pregain, Q27 unity gain + +# Set IIR Coefficients +# Filter coefficients take from System reference test MSIIR_Stop.cfg +SetParamInband + PayloadSizeInBytes 148 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 84 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1E 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS 0x0001031E + 78 00 00 00 # Param Size and Padding + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved + 05 00 # num_biquad_stages + 3D F2 C4 3E # Stage 1 (b0, b1, b2, a1, a2) + 6E 1F 75 04 + 12 BA 6D 31 + D9 E6 6C 04 + B8 24 A5 32 + BE 7C CB 3D # Stage 2 (b0, b1, b2, a1, a2) + E8 61 94 B4 + 48 7D 92 2C + 38 35 6F B1 + 2F B2 18 2F + 05 80 EC 3C # Stage 3 (b0, b1, b2, a1, a2) + 1C 6E CA F3 + EC 69 E2 29 + A4 6B 2A F6 + CD 18 7E 2C + B9 B1 F2 3F # Stage 4 (b0, b1, b2, a1, a2) + BC EF AA C3 + 86 C4 51 26 + 12 8D E0 BF + 11 0F B4 26 + CE 86 10 40 # Stage 5 (b0, b1, b2, a1, a2) + 54 F8 67 D4 + 43 A2 98 32 + 0E F3 D1 DA + 04 31 4B 27 + 02 00 # Stage 1 numerator shift factor + 02 00 # Stage 2 numerator shift factor + 02 00 # Stage 3 numerator shift factor + 02 00 # Stage 4 numerator shift factor + 02 00 # Stage 5 numerator shift factor + 00 00 # padding + + +ProcessData + NumBuffers 1000 + +# Get IIR Enable +GetParamInband + PayloadSizeInBytes 24 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1C 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_ENABLE 0x0001031C + 44 00 00 00 # Param Size and Padding + RefPayloadSizeInBytes 12 + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 01 00 00 00 # enable_flag + + +# Get IIR algorithm Delay +#GetParamInband +# PayloadSizeInBytes 24 +# 00 00 00 00 # Data Payload address +# 00 00 00 00 +# 00 00 00 00 +# 41 0c 01 00 # ADM_MODULE_ID_TX_IIR_FILTER 0x00010C41 +# 08 07 01 00 # APPI_PARAM_ID_ALGORITHMIC_DELAY 0x00010708 +# 04 00 00 00 # Param Size and Padding +# RefPayloadSizeInBytes 4 +# 0A 00 00 00 + diff --git a/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_numShiftFactor_stage1_32bit.cfg b/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_numShiftFactor_stage1_32bit.cfg new file mode 100644 index 0000000..8ad3f8b --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_numShiftFactor_stage1_32bit.cfg @@ -0,0 +1,141 @@ +# Configuration file for MultiStage IIR unit test +# input generated from Chirp.raw and Square.raw + +# Reset algorithm +SetParamInband + PayloadSizeInBytes 32 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 10 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 07 07 01 00 # APPI_PARAM_ID_ALGORITHMIC_RESET 0x00010707 + 04 00 00 00 # Param Size and Padding + 00 00 00 00 # Dummy payload + +# Set Media Format +# Only signed/interleaved input is accepted and +# signed/interleaved output will be generated +SetMediaFormat + SetNumInChannels 1 + SetNumOutChannels 1 + SetInSamplingRate 48000 + SetOutSamplingRate 48000 + SetBytesPerSample 4 + +# Set IIR Enable +SetParamInband + PayloadSizeInBytes 40 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 18 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1C 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_ENABLE 0x0001031C + 0C 00 00 00 # Param Size and Padding + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 01 00 00 00 # enable_flag + + +# Set IIR Pregain +SetParamInband + PayloadSizeInBytes 40 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 18 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1D 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN 0x0001031D + 0C 00 00 00 # Param Size and Padding + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 00 00 00 08 # pregain, Q27 unity gain + +# Set IIR Coefficients +# Filter coefficients take from System reference test MSIIR_Boost_numShiftFactor_stage1.cfg +SetParamInband + PayloadSizeInBytes 148 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 84 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1E 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS 0x0001031E + 78 00 00 00 # Param Size and Padding + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved + 05 00 # num_biquad_stages + 98 8B 3A 3F # Stage 1 (b0, b1, b2, a1, a2) + 04 47 A4 0B + F8 81 95 32 + 6D A5 9E 0B + 28 AF D5 31 + DB CC 2B 3E # Stage 2 (b0, b1, b2, a1, a2) + B9 0C F2 B7 + FE 58 80 30 + 6C 83 BF B8 + 5C 7A 65 2F + C7 16 D6 38 # Stage 3 (b0, b1, b2, a1, a2) + DF 70 E9 CC + BA C6 25 1F + 55 4D CD CB + AF 17 2F 24 + 60 6B 68 37 # Stage 4 (b0, b1, b2, a1, a2) + 0B 1B CD 02 + 52 E0 5E 24 + EA 67 E2 FC + 35 F0 88 2A + A7 BD 60 65 # Stage 5 (b0, b1, b2, a1, a2) + E4 1F 22 D9 + 1C DD 61 31 + 1A 0D A0 DF + C1 7C 0F 2C + 03 00 # Stage 1 numerator shift factor + 02 00 # Stage 2 numerator shift factor + 02 00 # Stage 3 numerator shift factor + 02 00 # Stage 4 numerator shift factor + 02 00 # Stage 5 numerator shift factor + 00 00 # padding + + +ProcessData + NumBuffers 1000 + +# Get IIR Enable +GetParamInband + PayloadSizeInBytes 24 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1C 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_ENABLE 0x0001031C + 44 00 00 00 # Param Size and Padding + RefPayloadSizeInBytes 12 + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 01 00 00 00 # enable_flag + + +# Get IIR algorithm Delay +#GetParamInband +# PayloadSizeInBytes 24 +# 00 00 00 00 # Data Payload address +# 00 00 00 00 +# 00 00 00 00 +# 41 0c 01 00 # ADM_MODULE_ID_TX_IIR_FILTER 0x00010C41 +# 08 07 01 00 # APPI_PARAM_ID_ALGORITHMIC_DELAY 0x00010708 +# 04 00 00 00 # Param Size and Padding +# RefPayloadSizeInBytes 4 +# 0A 00 00 00 + diff --git a/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_numShiftFactor_stage1low_32bit.cfg b/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_numShiftFactor_stage1low_32bit.cfg new file mode 100644 index 0000000..c7955ae --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_numShiftFactor_stage1low_32bit.cfg @@ -0,0 +1,141 @@ +# Configuration file for MultiStage IIR unit test +# input generated from Chirp.raw and Square.raw + +# Reset algorithm +SetParamInband + PayloadSizeInBytes 32 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 10 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 07 07 01 00 # APPI_PARAM_ID_ALGORITHMIC_RESET 0x00010707 + 04 00 00 00 # Param Size and Padding + 00 00 00 00 # Dummy payload + +# Set Media Format +# Only signed/interleaved input is accepted and +# signed/interleaved output will be generated +SetMediaFormat + SetNumInChannels 1 + SetNumOutChannels 1 + SetInSamplingRate 48000 + SetOutSamplingRate 48000 + SetBytesPerSample 4 + +# Set IIR Enable +SetParamInband + PayloadSizeInBytes 40 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 18 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1C 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_ENABLE 0x0001031C + 0C 00 00 00 # Param Size and Padding + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 01 00 00 00 # enable_flag + + +# Set IIR Pregain +SetParamInband + PayloadSizeInBytes 40 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 18 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1D 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN 0x0001031D + 0C 00 00 00 # Param Size and Padding + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 00 00 00 08 # pregain, Q27 unity gain + +# Set IIR Coefficients +# Filter coefficients take from System reference test MSIIR_Boost_numShiftFactor_stage1low.cfg +SetParamInband + PayloadSizeInBytes 148 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 84 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1E 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS 0x0001031E + 78 00 00 00 # Param Size and Padding + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved + 05 00 # num_biquad_stages + 98 8B 3A 3F # Stage 1 (b0, b1, b2, a1, a2) + 04 47 A4 0B + F8 81 95 32 + 6D A5 9E 0B + 28 AF D5 31 + DB CC 2B 3E # Stage 2 (b0, b1, b2, a1, a2) + B9 0C F2 B7 + FE 58 80 30 + 6C 83 BF B8 + 5C 7A 65 2F + C7 16 D6 38 # Stage 3 (b0, b1, b2, a1, a2) + DF 70 E9 CC + BA C6 25 1F + 55 4D CD CB + AF 17 2F 24 + 60 6B 68 37 # Stage 4 (b0, b1, b2, a1, a2) + 0B 1B CD 02 + 52 E0 5E 24 + EA 67 E2 FC + 35 F0 88 2A + A7 BD 60 65 # Stage 5 (b0, b1, b2, a1, a2) + E4 1F 22 D9 + 1C DD 61 31 + 1A 0D A0 DF + C1 7C 0F 2C + 01 00 # Stage 1 numerator shift factor + 02 00 # Stage 2 numerator shift factor + 02 00 # Stage 3 numerator shift factor + 02 00 # Stage 4 numerator shift factor + 02 00 # Stage 5 numerator shift factor + 00 00 # padding + + +ProcessData + NumBuffers 1000 + +# Get IIR Enable +GetParamInband + PayloadSizeInBytes 24 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1C 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_ENABLE 0x0001031C + 44 00 00 00 # Param Size and Padding + RefPayloadSizeInBytes 12 + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 01 00 00 00 # enable_flag + + +# Get IIR algorithm Delay +#GetParamInband +# PayloadSizeInBytes 24 +# 00 00 00 00 # Data Payload address +# 00 00 00 00 +# 00 00 00 00 +# 41 0c 01 00 # ADM_MODULE_ID_TX_IIR_FILTER 0x00010C41 +# 08 07 01 00 # APPI_PARAM_ID_ALGORITHMIC_DELAY 0x00010708 +# 04 00 00 00 # Param Size and Padding +# RefPayloadSizeInBytes 4 +# 0A 00 00 00 + diff --git a/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_preGain_max_32bit.cfg b/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_preGain_max_32bit.cfg new file mode 100644 index 0000000..ae43bf9 --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_preGain_max_32bit.cfg @@ -0,0 +1,94 @@ +# Configuration file for MultiStage IIR unit test +# input generated from Chirp.raw and Square.raw + +# Reset algorithm +SetParamInband + PayloadSizeInBytes 32 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 10 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 07 07 01 00 # APPI_PARAM_ID_ALGORITHMIC_RESET 0x00010707 + 04 00 00 00 # Param Size and Padding + 00 00 00 00 # Dummy payload + +# Set Media Format +# Only signed/interleaved input is accepted and +# signed/interleaved output will be generated +SetMediaFormat + SetNumInChannels 1 + SetNumOutChannels 1 + SetInSamplingRate 48000 + SetOutSamplingRate 48000 + SetBytesPerSample 4 + +# Set IIR Enable +SetParamInband + PayloadSizeInBytes 40 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 18 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1C 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_ENABLE 0x0001031C + 0C 00 00 00 # Param Size and Padding + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 01 00 00 00 # enable_flag + + +# Set IIR Pregain +SetParamInband + PayloadSizeInBytes 40 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 18 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1D 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN 0x0001031D + 0C 00 00 00 # Param Size and Padding + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + FF FF FF 7F # pregain, Q27 + + +ProcessData + NumBuffers 1000 + +# Get IIR Enable +GetParamInband + PayloadSizeInBytes 24 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1C 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_ENABLE 0x0001031C + 44 00 00 00 # Param Size and Padding + RefPayloadSizeInBytes 12 + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 01 00 00 00 # enable_flag + + +# Get IIR algorithm Delay +#GetParamInband +# PayloadSizeInBytes 24 +# 00 00 00 00 # Data Payload address +# 00 00 00 00 +# 00 00 00 00 +# 41 0c 01 00 # ADM_MODULE_ID_TX_IIR_FILTER 0x00010C41 +# 08 07 01 00 # APPI_PARAM_ID_ALGORITHMIC_DELAY 0x00010708 +# 04 00 00 00 # Param Size and Padding +# RefPayloadSizeInBytes 4 +# 0A 00 00 00 + diff --git a/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_preGain_minus5_32bit.cfg b/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_preGain_minus5_32bit.cfg new file mode 100644 index 0000000..cf440cd --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/capi/tst/cfg/msiir_mono_preGain_minus5_32bit.cfg @@ -0,0 +1,128 @@ +# Configuration file for MultiStage IIR unit test +# input generated from Chirp.raw and Square.raw + +# Reset algorithm +SetParamInband + PayloadSizeInBytes 32 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 10 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 07 07 01 00 # APPI_PARAM_ID_ALGORITHMIC_RESET 0x00010707 + 04 00 00 00 # Param Size and Padding + 00 00 00 00 # Dummy payload + +# Set Media Format +# Only signed/interleaved input is accepted and +# signed/interleaved output will be generated +SetMediaFormat + SetNumInChannels 1 + SetNumOutChannels 1 + SetInSamplingRate 48000 + SetOutSamplingRate 48000 + SetBytesPerSample 4 + +# Set IIR Enable +SetParamInband + PayloadSizeInBytes 40 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 18 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1C 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_ENABLE 0x0001031C + 0C 00 00 00 # Param Size and Padding + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 01 00 00 00 # enable_flag + + +# Set IIR Pregain +SetParamInband + PayloadSizeInBytes 40 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 18 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1D 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_PREGAIN 0x0001031D + 0C 00 00 00 # Param Size and Padding + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + CF AC 7F 04 # pregain, Q27 -5dB + +# Set IIR Coefficients +# Filter coefficients take from System reference test MSIIR_pregain_-5.cfg +SetParamInband + PayloadSizeInBytes 104 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 58 00 00 00 # Size of Payload + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1E 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_CONFIG_PARAMS 0x0001031E + 4C 00 00 00 # Param Size and Padding + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved + 03 00 # num_biquad_stages + 80 2E 0A 1D # Stage 1 (b0, b1, b2, a1, a2) + FF 31 A6 39 + 80 2E 0A 1D + D2 D9 10 25 + 2D B5 A9 0E + 67 CF C8 3B # Stage 2 (b0, b1, b2, a1, a2) + 8B 15 AF 6E + 67 CF C8 3B + F0 12 8F 6A + D2 D4 9A 3C + 9B B2 FF 30 # Stage 3 (b0, b1, b2, a1, a2) + 88 30 70 5D + 9B B2 FF 30 + 6B CD 60 59 + 2D 19 9A 30 + 02 00 # Stage 1 numerator shift factor + 02 00 # Stage 2 numerator shift factor + 02 00 # Stage 3 numerator shift factor + 00 00 # padding + +ProcessData + NumBuffers 1000 + +# Get IIR Enable +GetParamInband + PayloadSizeInBytes 24 + 00 00 00 00 # Data Payload address + 00 00 00 00 + 00 00 00 00 + 1F 03 01 00 # AUDPROC_MODULE_ID_MCHAN_IIR_TUNING_FILTER 0x0001031F + 1C 03 01 00 # PARAM_ID_MSIIR_TUNING_FILTER_ENABLE 0x0001031C + 44 00 00 00 # Param Size and Padding + RefPayloadSizeInBytes 12 + 01 00 00 00 # num_channels + 03 # channel_type = PCM_CHANNEL_C + 00 # reserved1 + 00 # reserved2 + 00 # reserved3 + 01 00 00 00 # enable_flag + + +# Get IIR algorithm Delay +#GetParamInband +# PayloadSizeInBytes 24 +# 00 00 00 00 # Data Payload address +# 00 00 00 00 +# 00 00 00 00 +# 41 0c 01 00 # ADM_MODULE_ID_TX_IIR_FILTER 0x00010C41 +# 08 07 01 00 # APPI_PARAM_ID_ALGORITHMIC_DELAY 0x00010708 +# 04 00 00 00 # Param Size and Padding +# RefPayloadSizeInBytes 4 +# 0A 00 00 00 + diff --git a/modules/processing/filters/multi_stage_iir/capi/tst/main.cpp b/modules/processing/filters/multi_stage_iir/capi/tst/main.cpp new file mode 100644 index 0000000..47579ee --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/capi/tst/main.cpp @@ -0,0 +1,171 @@ +/*========================================================================== + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + *==========================================================================*/ + +/** + * @file main.cpp + * + * Standalone Setup for MSIIR Modules module + */ + +#ifndef CAPI_MODULE_HEADER +#error "Please specify the include header for module" +#endif + +#ifndef CAPI_STATIC_PROP_FUNCTION +#error "Please specify the capi static property function in makefile" +#endif + +#ifndef CAPI_INIT_FUNCTION +#error "Please specify the capi init function in makefile" +#endif + + +#include "capi_test.h" +#include "media_fmt_api.h" +#include CAPI_MODULE_HEADER + + +int main(int argc, char* argv[]) +{ + capi_err_t result = CAPI_EOK; + + module_info_t module; + memset(&module, 0, sizeof(module_info_t)); + + /* Read input arguments */ + args_t input_args; + get_eargs(argc, argv, &input_args); + + if ((module.finp = fopen(input_args.input_filename, "rb")) == NULL) + { + fprintf(stderr, "%s: ERROR CODE 2- Cannot open input file '%s'.\n", + argv[0], + input_args.input_filename); + exit(-1); + } + + if ((module.fout = fopen(input_args.output_filename, "wb")) == NULL) + { + fprintf(stderr, "%s: ERROR CODE 3 - Cannot open output file '%s'.\n", + argv[0], + input_args.output_filename); + fclose(module.finp); + exit(-1); + } + if ((module.fCfg = fopen(input_args.config_filename, "rb")) == NULL) + { + fprintf(stderr, "%s: ERROR CODE 3 - Cannot open config file '%s'.\n", + argv[0], + input_args.config_filename); + fclose(module.finp); + fclose(module.fout); + exit(-1); + } + + /* STEP 1: Get size requirements of CAPI */ + AR_MSG(DBG_HIGH_PRIO,"MAIN: -----------------"); + AR_MSG(DBG_HIGH_PRIO,"MAIN: Initialize module"); + AR_MSG(DBG_HIGH_PRIO,"MAIN: -----------------"); + + /* Query for CAPI size */ + capi_proplist_t static_properties; + static_properties.props_num = 1; + capi_prop_t prop_ptr[static_properties.props_num]; + static_properties.prop_ptr = prop_ptr; + + /* Populate INIT_MEMORY_REQUIREMENT query */ + capi_init_memory_requirement_t mem_req; + prop_ptr[0].id = CAPI_INIT_MEMORY_REQUIREMENT; + prop_ptr[0].payload.data_ptr = (int8_t *)&mem_req; + prop_ptr[0].port_info.is_valid = FALSE; + + result = CAPI_STATIC_PROP_FUNCTION( + NULL, &static_properties); + if (CAPI_EOK != result) + { + AR_MSG(DBG_HIGH_PRIO, + "Failed to query for module size"); + fclose(module.finp); + fclose(module.fout); + fclose(module.fCfg); + return CAPI_EFAILED; + } + + AR_MSG(DBG_HIGH_PRIO, + "%lu bytes required for module.", + mem_req.size_in_bytes); + + /* STEP 2: Allocate memory */ + uint8_t *ptr = (uint8_t*)posal_memory_malloc( + mem_req.size_in_bytes, POSAL_HEAP_DEFAULT); + if (NULL == ptr) + { + AR_MSG(DBG_ERROR_PRIO, + "MAIN: Memory allocation error"); + fclose(module.finp); + fclose(module.fout); + fclose(module.fCfg); + return CAPI_ENOMEMORY; + } + module.module_ptr = (capi_t *)ptr; + AR_MSG(DBG_HIGH_PRIO, + "Module allocated for %lu bytes of memory at location 0x%p.", + mem_req.size_in_bytes, + ptr); + + /* STEP 3: Initialize module */ + capi_event_callback_info_t cb_info = capi_tst_get_cb_info(&module); + capi_prop_t cb_prop; + cb_prop.id = CAPI_EVENT_CALLBACK_INFO; + cb_prop.payload.max_data_len = sizeof(cb_info); + cb_prop.payload.actual_data_len = sizeof(cb_info); + cb_prop.payload.data_ptr = reinterpret_cast(&cb_info); + + capi_proplist_t init_proplist; + init_proplist.props_num = 1; + init_proplist.prop_ptr = &cb_prop; + + AR_MSG(DBG_HIGH_PRIO, "capiv2 init"); + result = CAPI_INIT_FUNCTION((capi_t *)ptr, &init_proplist); + + if (CAPI_EOK != result) + { + AR_MSG(DBG_ERROR_PRIO, "MAIN: Initialization error"); + posal_memory_free(ptr); + fclose(module.finp); + fclose(module.fout); + fclose(module.fCfg); + return result; + } + + module.out_buffer_len = module.in_buffer_len; + module.requires_data_buffering = FALSE; + module.is_enabled = TRUE; + module.alg_delay = 0; + + /* Run config file */ + AR_MSG(DBG_HIGH_PRIO,"MAIN: ----------------"); + AR_MSG(DBG_HIGH_PRIO,"MAIN: Run config file "); + AR_MSG(DBG_HIGH_PRIO,"MAIN: ----------------"); + + result = RunTest(&module); + if(CAPI_EOK != result) + { + AR_MSG(DBG_ERROR_PRIO, "MAIN: Error in RunTest"); + } + + /* Destroy CAPI V2 and free memory */ + module.module_ptr->vtbl_ptr->end(module.module_ptr); + + posal_memory_free(ptr); + + fclose (module.finp); + fclose (module.fout); + fclose (module.fCfg); + + AR_MSG(DBG_HIGH_PRIO,"MAIN: Done"); + return result; +} + diff --git a/modules/processing/filters/multi_stage_iir/capi/tst/test_cases.json b/modules/processing/filters/multi_stage_iir/capi/tst/test_cases.json new file mode 100644 index 0000000..adf75a9 --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/capi/tst/test_cases.json @@ -0,0 +1,41 @@ +{ + "Executable": "appi_msiir", + "TestCases": + [ + { + "Name":"left_zero_right_hpf", + "ConfigFile":"./cfg/left_zero_right_hpf.cfg", + "InputFile":"../../../../../../../aDSPTest/UnitTestRef/vectors/mozart_48k_stereo.raw", + "OutputFile":"./vectors/mozart_48k_stereo_lzero_rhpf_test.raw", + "Reference":"../../../../../../../aDSPTest/UnitTestRef/filter/msiir/mozart_48k_stereo_lzero_rhpf.raw" + }, + { + "Name":"ffa_handset_nb", + "ConfigFile":"./cfg/ffa_handset_nb.cfg", + "InputFile":"../../../../../../../aDSPTest/UnitTestRef/vectors/harvard_nb.raw", + "OutputFile":"./vectors/harvard_ffa_handset_nb_test.raw", + "Reference":"../../../../../../../aDSPTest/UnitTestRef/filter/msiir/harvard_ffa_handset_nb.raw" + }, + { + "Name":"ffa_handset_wb", + "ConfigFile":"./cfg/ffa_handset_wb.cfg", + "InputFile":"../../../../../../../aDSPTest/UnitTestRef/vectors/harvard_wb.raw", + "OutputFile":"./vectors/harvard_ffa_handset_wb_test.raw", + "Reference":"../../../../../../../aDSPTest/UnitTestRef/filter/msiir/harvard_ffa_handset_wb.raw" + }, + { + "Name":"LGE_HPF_Left_16bit", + "ConfigFile":"./cfg/msiir_mono_LGE_HPF_16bit.cfg", + "InputFile":"../../../../../../../aDSPTest/UnitTestRef/vectors/logged_audio_48kHz_L.raw", + "OutputFile":"./vectors/logged_audio_48kHz_L_LGE_HPF_test.raw", + "Reference":"../../../../../../../aDSPTest/UnitTestRef/filter/msiir/logged_audio_48kHz_L_LGE_HPF_ref.raw" + }, + { + "Name":"LGE_HPF_Right_16bit", + "ConfigFile":"./cfg/msiir_mono_LGE_HPF_16bit.cfg", + "InputFile":"../../../../../../../aDSPTest/UnitTestRef/vectors/logged_audio_48kHz_R.raw", + "OutputFile":"./vectors/logged_audio_48kHz_R_LGE_HPF_test.raw", + "Reference":"../../../../../../../aDSPTest/UnitTestRef/filter/msiir/logged_audio_48kHz_R_LGE_HPF_ref.raw" + } + ] +} diff --git a/modules/processing/filters/multi_stage_iir/capi/tst/test_cases_32bit.json b/modules/processing/filters/multi_stage_iir/capi/tst/test_cases_32bit.json new file mode 100644 index 0000000..31acbe5 --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/capi/tst/test_cases_32bit.json @@ -0,0 +1,62 @@ +{ + "Executable": "appi_msiir", + "TestCases": + [ + { + "Name":"msiir_mono_pregain_minus5_32bit", + "ConfigFile":"./cfg/msiir_mono_pregain_minus5_32bit.cfg", + "InputFile":"../../../../../../../aDSPTest/UnitTestRef/vectors/Chirp.raw", + "OutputFile":"./vectors/Chirp_pregain_minus5_test.raw", + "Reference":"../../../../../../../aDSPTest/UnitTestRef/filter/msiir/Chirp_pregain_minus5_ref.raw" + }, + { + "Name":"msiir_mono_pregain_max_32bit", + "ConfigFile":"./cfg/msiir_mono_pregain_max_32bit.cfg", + "InputFile":"../../../../../../../aDSPTest/UnitTestRef/vectors/Chirp.raw", + "OutputFile":"./vectors/Chirp_pregain_max_test.raw", + "Reference":"../../../../../../../aDSPTest/UnitTestRef/filter/msiir/Chirp_pregain_max_ref.raw" + }, + { + "Name":"msiir_mono_LPF_32bit", + "ConfigFile":"./cfg/msiir_mono_LPF_32bit.cfg", + "InputFile":"../../../../../../../aDSPTest/UnitTestRef/vectors/Chirp.raw", + "OutputFile":"./vectors/Chirp_LPF_test.raw", + "Reference":"../../../../../../../aDSPTest/UnitTestRef/filter/msiir/Chirp_LPF_ref.raw" + }, + { + "Name":"msiir_mono_HPF_32bit", + "ConfigFile":"./cfg/msiir_mono_HPF_32bit.cfg", + "InputFile":"../../../../../../../aDSPTest/UnitTestRef/vectors/Chirp.raw", + "OutputFile":"./vectors/Chirp_HPF_test.raw", + "Reference":"../../../../../../../aDSPTest/UnitTestRef/filter/msiir/Chirp_HPF_ref.raw" + }, + { + "Name":"msiir_mono_numShiftFactor_stage1_32bit", + "ConfigFile":"./cfg/msiir_mono_numShiftFactor_stage1_32bit.cfg", + "InputFile":"../../../../../../../aDSPTest/UnitTestRef/vectors/Chirp.raw", + "OutputFile":"./vectors/Chirp_numShiftFactor_stage1_test.raw", + "Reference":"../../../../../../../aDSPTest/UnitTestRef/filter/msiir/Chirp_numShiftFactor_stage1_ref.raw" + }, + { + "Name":"msiir_mono_numShiftFactor_stage1low_32bit", + "ConfigFile":"./cfg/msiir_mono_numShiftFactor_stage1low_32bit.cfg", + "InputFile":"../../../../../../../aDSPTest/UnitTestRef/vectors/Chirp.raw", + "OutputFile":"./vectors/Chirp_numShiftFactor_stage1low_test.raw", + "Reference":"../../../../../../../aDSPTest/UnitTestRef/filter/msiir/Chirp_numShiftFactor_stage1low_ref.raw" + }, + { + "Name":"msiir_mono_Stop_32bit", + "ConfigFile":"./cfg/msiir_mono_Stop_32bit.cfg", + "InputFile":"../../../../../../../aDSPTest/UnitTestRef/vectors/Chirp.raw", + "OutputFile":"./vectors/Chirp_Stop_test.raw", + "Reference":"../../../../../../../aDSPTest/UnitTestRef/filter/msiir/Chirp_Stop_ref.raw" + }, + { + "Name":"msiir_3ch_32bit", + "ConfigFile":"./cfg/msiir_3channel_32bit.cfg", + "InputFile":"../../../../../../../aDSPTest/UnitTestRef/vectors/Chirp_3ch_32bit.raw", + "OutputFile":"./vectors/Chirp_3ch_32bit_test.raw", + "Reference":"../../../../../../../aDSPTest/UnitTestRef/filter/msiir/Chirp_3ch_32bit_ref.raw" + } + ] +} diff --git a/modules/processing/filters/multi_stage_iir/capi/tst/test_cases_cross_fade.json b/modules/processing/filters/multi_stage_iir/capi/tst/test_cases_cross_fade.json new file mode 100644 index 0000000..1d9575e --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/capi/tst/test_cases_cross_fade.json @@ -0,0 +1,13 @@ +{ + "Executable": "appi_msiir", + "TestCases": + [ + { + "Name":"msiir_cross_fade_1", + "ConfigFile":"./cfg/msiir_cross_fade_stereo_16bit.cfg", + "InputFile":"../../../../../../../aDSPTest/UnitTestRef/vectors/48k_stereo_sine.raw", + "OutputFile":"./vectors/48k_stereo_sine_test_1.raw", + "Reference":"../../../../../../../aDSPTest/UnitTestRef/filter/msiir/48k_stereo_sine_1_ref.raw" + } + ] +} diff --git a/modules/processing/filters/multi_stage_iir/lib/inc/CMultiStageIIR.h b/modules/processing/filters/multi_stage_iir/lib/inc/CMultiStageIIR.h new file mode 100644 index 0000000..1535e6f --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/lib/inc/CMultiStageIIR.h @@ -0,0 +1,150 @@ +/*======================================================================== + * Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause + *======================================================================== + */ + +#ifndef _CMULTISTAGEIIR_H_ +#define _CMULTISTAGEIIR_H_ + +#include "posal_types.h" +#include "audpp_common.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/*============================================================================= +Constants +=============================================================================*/ +#define MULTISTAGE_IIR_NUM_COEFFS 3 +#define MULTISTAGE_IIR_DEN_COEFFS 2 +#define MULTISTAGE_IIR_FILTER_STATES 2 /* each biquad stage has a memory of length 2 */ + +#define MSIIR_Q_PREGAIN 27 +static const int32 MSIIR_UNITY_PREGAIN = (1 << MSIIR_Q_PREGAIN); + +/*============================================================================= +Enumerators +=============================================================================*/ +typedef enum +{ + MSIIR_LINKED, // same gain and filtering applied to all channels + MSIIR_UNLINKED // different specifications per channel +} MultiChannelMode; + +/*============================================================================= +Typedefs +=============================================================================*/ +typedef struct _MultiStageIIRCfgStruct +{ + boolean* pbIIRFilterEnable; /* Enable flag for IIR filters for all channels */ + + uint16 uiNumChannels; + + uint16* puiNumStages; /* no. of biquad stages */ + + int32* piPreGain; /*/< PreGain to be applied to input data */ + + int32** ppiFilterCoeff; /*/< Biquad filter coefficients */ + // *(ppiFilterCoeff + k) is the address to filter coefficients for kth channel: + // | first biquad stage | second biquad stage | third ... + // | b0 | b1 | b2 | a1 | a2 | b0 | b1 | b2 | a1 | a2 | b0 | ... + + MultiChannelMode mMultiChannelMode; /*/< MSIIR_LINKED or MSIIR_UNLINKED */ + + int16** ppiNumShiftFactor; /*/< Numerator shift factors*/ + // *(ppiNumShiftFactor + k) is the address to the numerator shift factors (one per biquad stage) for the kth channel + + uint16 uiBitsPerSample; /* 16-bit or 32-bit */ +} MultiStageIIRCfgStruct; + + +/* One MSIIRDataStruct per biquad stage, contains + filter coefficients, numerator shift factor and memory for states w1 & w2 */ +typedef struct _MSIIRDataStruct +{ + int64 iFiltMemory[MULTISTAGE_IIR_FILTER_STATES]; + + int32 nIIRFilterCoeffs[MULTISTAGE_IIR_NUM_COEFFS+MULTISTAGE_IIR_DEN_COEFFS]; + + int16 IIRFilterNumShiftFactor; /*/< Numerator shift factor */ +} MSIIRDataStruct; + + +typedef struct { + MSIIRDataStruct** ppMultiStageIIRStruct; + // *(*(ppMultiStageIIRStruct + k) + j) is the j-th stage MSIIRDataStruct for the k-th channel + + boolean* pbMultiStageIIREnable; + // *(pMultiStageIIREnable + k) is the enable flag for the k-th channel + + uint16 uiNumChannels; + MultiChannelMode mMultiChannelMode; /*/< MSIIR_LINKED or MSIIR_UNLINKED */ + + uint16* puiNumIIRStages; /* number of biquad stages for all channels*/ + + int32* piSTFPreGain; /* preGain for all channels */ + + uint16 uiBitsPerSample; /* Size of input and output data : 16 or 32 */ +} CMultiStageIIRLib; + + +/* + msiir_reset() clears the states (w1 & w2) for all biquad stages in the IIR filter +*/ +void msiir_reset(CMultiStageIIRLib *CMultiStageIIR_obj); + + +/** + @brief Use information in the configuration structure and provided memory to initialize the + fields of the CMultiStageIIR object. + + @param[in] CMultiStageIIR_obj – CMultiStageIIR object; the memory for internal pointer fields needs to be + passed in, see arguments 3-6 + @param[in] pIIRInpCfg - IIR Filter config Structure + @param[in] pIIRFilterStruct – properly calloc’ed + MSIIRDataStruct**; this memory will store the field + ppMultiStageIIRStruct + @param[in] pFilterEnable - properly calloc’ed boolean*; this memory will store the field pMultiStageIIREnable + @param[in] pPreGain - properly calloc’ed int32*; this memory will store the field piSTFPreGain + @param[in] pNumStages - properly calloc’ed uint16*; this memory will store the field puiNumIIRStages + + @return + * - PPSUCCESS - The initialization was successful. + * - error code - There was an error which needs to propagate. + */ +PPStatus msiir_initialize( CMultiStageIIRLib* CMultiStageIIR_obj, + MultiStageIIRCfgStruct* pIIRInpCfg , + MSIIRDataStruct** pIIRFilterStruct, + boolean* pFilterEnable, + int32* pPreGain, + uint16* pNumStages ); + + +/** + @brief This function processes data in the input buffer and writes to the output buffer; + calls Process_one_channel for every channel + + @param[in] CMultiStageIIR_obj – CmultiStageIIR object containing filter details + @param[in] pOut – array of pointers, each pointing to output data for a channel + @param[in] pInp – array of pointers, each pointing to input data for a channel + @param[in] iNrOfSamples – number of samples to be processed + + @return + * - PPSUCCESS - The initialization was successful. + * - error code - There was an error which needs to propagate. + */ +PPStatus msiir_process( CMultiStageIIRLib* CMultiStageIIR_obj, + void** pOut, + void** pInp, + int32 iNrOfSamples); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _CMULTISTAGEIIR_H_ */ + + diff --git a/modules/processing/filters/multi_stage_iir/lib/inc/msiir_api.h b/modules/processing/filters/multi_stage_iir/lib/inc/msiir_api.h new file mode 100644 index 0000000..a154620 --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/lib/inc/msiir_api.h @@ -0,0 +1,94 @@ +#ifndef MSIIR_API_H +#define MSIIR_API_H +/*============================================================================ + @file msiir_api.h + + Public API for Multi Stage IIR Filter (single channel). + + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause + +============================================================================*/ +#include "msiir_calibration_api.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/*---------------------------------------------------------------------------- + Constants +----------------------------------------------------------------------------*/ +#define MSIIR_LIB_VER (0x01000201) // lib version : 1.2.1 + // (major.minor.bug) (8.16.8 bits) + +#define MSIIR_Q_PREGAIN (27) // pregain Q factor (27) +/*---------------------------------------------------------------------------- + Error code +----------------------------------------------------------------------------*/ +typedef enum MSIIR_RESULT { + MSIIR_SUCCESS = 0, + MSIIR_FAILURE, + MSIIR_MEMERROR +} MSIIR_RESULT; + +/*---------------------------------------------------------------------------- + Type Declarations +----------------------------------------------------------------------------*/ +typedef struct msiir_static_vars_t { // ** static params + int32 data_width; // 16 (Q15 PCM) or 32 (Q27 PCM) + int32 max_stages; // max num of stages at setup time +} msiir_static_vars_t; + +typedef struct msiir_mem_req_t { // ** memory requirements + uint32 mem_size; // lib mem size (lib_mem_ptr ->) + uint32 stack_size; // max stack size +} msiir_mem_req_t; + +typedef struct msiir_lib_t { // ** library struct + void* mem_ptr; // mem pointer +} msiir_lib_t; + +/*---------------------------------------------------------------------------- + Function Declarations +----------------------------------------------------------------------------*/ + +// ** Process one block of samples (one channel) +// lib_ptr: [in] pointer to library structure +// out_ptr: [out] pointer to output sample block +// in_ptr: [in] pointer to input sample block +// samples: [in] number of samples to be processed per channel +MSIIR_RESULT msiir_process_v2(msiir_lib_t *lib_ptr, void *out_ptr, void *in_ptr, uint32 samples); + +// ** Get library memory requirements +// mem_req_ptr: [out] pointer to mem requirements structure +// msiir_static_vars_t: [in] pointer to static variable structure +MSIIR_RESULT msiir_get_mem_req(msiir_mem_req_t *mem_req_ptr, msiir_static_vars_t* static_vars_ptr); + +// ** Partition, initialize memory, and set default values +// lib_ptr: [in, out] pointer to library structure +// static_vars_ptr: [in] pointer to static variable structure +// mem_ptr: [in] pointer to allocated memory +// mem_size: [in] size of memory allocated +MSIIR_RESULT msiir_init_mem(msiir_lib_t *lib_ptr, msiir_static_vars_t* static_vars_ptr, void* mem_ptr, uint32 mem_size); + +// ** Set parameters to library +// lib_ptr: [in, out] pointer to lib structure +// param_id: [in] parameter id +// param_ptr: [in] pointer to the memory where the new values are stored +// param_size:[in] size of the memory pointed by param_ptr +MSIIR_RESULT msiir_set_param(msiir_lib_t* lib_ptr, uint32 param_id, void* param_ptr, uint32 param_size); + +// ** Get parameters from library +// lib_ptr: [in] pointer to library structure +// param_id: [in] parameter id +// param_ptr: [out] pointer to the memory where the retrieved value is going to be stored +// param_size:[in] size of the memory pointed by param_ptr +// param_actual_size_ptr: [out] pointer to memory that will hold the actual size of the parameter +MSIIR_RESULT msiir_get_param(msiir_lib_t* lib_ptr, uint32 param_id, void* param_ptr, uint32 param_size, uint32 *param_actual_size_ptr); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MSIIR_API_H */ + diff --git a/modules/processing/filters/multi_stage_iir/lib/inc/msiir_calibration_api.h b/modules/processing/filters/multi_stage_iir/lib/inc/msiir_calibration_api.h new file mode 100644 index 0000000..eb70a9a --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/lib/inc/msiir_calibration_api.h @@ -0,0 +1,54 @@ +#ifndef MSIIR_CALIBRATION_API_H +#define MSIIR_CALIBRATION_API_H +/*============================================================================ + @file msiir_calibration_api.h + + Calibration (Public) API for Multi Stage IIR Filter (single channel). + + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause +============================================================================*/ +#include "AudioComdef.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/*---------------------------------------------------------------------------- + Constants +----------------------------------------------------------------------------*/ +#define MSIIR_NUM_COEFFS (3) // numerator count +#define MSIIR_DEN_COEFFS (2) // denominator count +#define MSIIR_COEFF_LENGTH (MSIIR_NUM_COEFFS+MSIIR_DEN_COEFFS) // 5 + +/*---------------------------------------------------------------------------- + Parameters with IDs +----------------------------------------------------------------------------*/ +#define MSIIR_PARAM_LIB_VER (0) // ** param: library version +typedef int32 msiir_version_t; // access: get only + +#define MSIIR_PARAM_PREGAIN (1) // ** param: filter pregain +typedef int32 msiir_pregain_t; // range: [0, 0x7fffffff] (Q27) + // access: get & set + +#define MSIIR_PARAM_CONFIG (2) // ** param: stages, coeffs & shift + // access: get & set +typedef struct msiir_coeffs_t { + int32 iir_coeffs[MSIIR_COEFF_LENGTH]; // [b0, b1, b2, a1, a2] + int32 shift_factor; // shift factor for this stage +} msiir_coeffs_t; // + // +typedef struct msiir_config_t { // + int32 num_stages; // stage range: [0, max_stages] + // msiir_coeffs_t coeffs_struct[num_stages] +} msiir_config_t; + // coeffs struct will follow in memory immediately after this config struct + +#define MSIIR_PARAM_RESET (3) // ** param: reset filter memory + // access: set only +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MSIIR_CALIBRATION_API_H */ + diff --git a/modules/processing/filters/multi_stage_iir/lib/src/CMultiStageIIR.c b/modules/processing/filters/multi_stage_iir/lib/src/CMultiStageIIR.c new file mode 100644 index 0000000..7f19075 --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/lib/src/CMultiStageIIR.c @@ -0,0 +1,435 @@ +/*======================= COPYRIGHT NOTICE ==================================*] +[* Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + * SPDX-License-Identifier: BSD-3-Clause *] +[*===========================================================================*] + +/*======================================================================= + INCLUDE FILES FOR MODULE +========================================================================== */ +#include "CMultiStageIIR.h" +#include "audio_iir_tdf2.h" +#include +#include "audio_basic_op.h" +#include "ar_defs.h" +#ifdef AVS_BUILD_SOS +#include "capi_cmn.h" +#endif +/*----------------------------------------------------------------------- +** Internal function declarations +**-----------------------------------------------------------------------*/ +PPStatus msiir_process_one_channel( CMultiStageIIRLib *CMultiStageIIR_obj, + void* pOut, + void* pInp, + int32 iNrOfSamples, + uint16 k); + +PPStatus apply_multistage_iir( + MSIIRDataStruct* pMultiStageIIRStruct, + int16 iNumIIRStages, + void* pOut, + void* pInp, + int32 iNrOfSamples, + int16 uiBitsPerSample); + +PPStatus msiir_apply_stf_pregain( + int32 iSTFPreGain, + int16 QFactor, + void* pOut, + void* pInp, + int32 iNrOfSamples, + int16 uiBitsPerSample); + + +/*----------------------------------------------------------------------- +** Member Function definitions +**-----------------------------------------------------------------------*/ + +/* + Reset() clears the arrays in memory that store the states (w1,w2) of the + system for all IIR stages for all channels +*/ +void msiir_reset(CMultiStageIIRLib *CMultiStageIIR_obj) +{ + MSIIRDataStruct* pIIR; + int k; + + for (k = 0; k < CMultiStageIIR_obj->uiNumChannels; ++k) + { + //if (*(CMultiStageIIR_obj->ppMultiStageIIRStruct + k)) + //{ + int16 i; + int16 j; + + pIIR = *(CMultiStageIIR_obj->ppMultiStageIIRStruct + k); + for( i = 0; i < *(CMultiStageIIR_obj->puiNumIIRStages + k) ; i++) + { + for (j = 0 ; j < MULTISTAGE_IIR_FILTER_STATES ; j++) + { + pIIR->iFiltMemory[j] = 0; + } + pIIR++; + } + //} + } +} + +/* + Initialize CMultiStageIIR object using the configuration struct + The passed-in memory locations are used to store the fields of the CMultiStageIIR object +*/ +PPStatus msiir_initialize(CMultiStageIIRLib *CMultiStageIIR_obj, + MultiStageIIRCfgStruct* pIIRInpCfg , + MSIIRDataStruct** pIIRFilterStruct, + boolean * pFilterEnable, + int32 * pPreGain, + uint16 * pNumStages ) +{ + int16 i; + uint16 k; + MSIIRDataStruct* pIIR; + int32* pFiltCoeff; + int16* piNumShift; + + /* check pointers */ + if ((NULL == CMultiStageIIR_obj) || + (NULL == pIIRFilterStruct) || + (NULL == pIIRInpCfg->pbIIRFilterEnable) || + (NULL == pIIRInpCfg->puiNumStages) || + (NULL == pIIRInpCfg->piPreGain)) + { + return PPFAILURE; + } + + /* assign the non-pointer fields */ + CMultiStageIIR_obj->mMultiChannelMode = pIIRInpCfg->mMultiChannelMode; + CMultiStageIIR_obj->uiNumChannels = pIIRInpCfg->uiNumChannels; + CMultiStageIIR_obj->uiBitsPerSample = pIIRInpCfg->uiBitsPerSample; + + /* use the memory that was passed in for other fields */ + CMultiStageIIR_obj->pbMultiStageIIREnable = pFilterEnable; + CMultiStageIIR_obj->piSTFPreGain = pPreGain; + CMultiStageIIR_obj->puiNumIIRStages = pNumStages; + CMultiStageIIR_obj->ppMultiStageIIRStruct = pIIRFilterStruct; + + /* The following two lines are only to take care of level-4 warning (C4701). + We check that pIIRInpCfg->uiNumChannels is valid while calling Initialize, hence + it is guaranteed that we enter the next for loop, which will necessarily initialize + pFiltCoeff and piNumShift */ + pFiltCoeff = *(pIIRInpCfg->ppiFilterCoeff); + piNumShift = *(pIIRInpCfg->ppiNumShiftFactor); + + for (k = 0; k < CMultiStageIIR_obj->uiNumChannels; ++k) + { + /* pIIR: pointer to MSIIRDataStructs for current channel */ + pIIR = *(pIIRFilterStruct + k); + + /* + If mode is MSIIR_UNLINKED, pIIRInpCfg contains different specifications per channel + If mode is MSIIR_LINKED, use the specifications for the first channel (channel 0) + in pIIRInpCfg. + */ + if (MSIIR_UNLINKED == CMultiStageIIR_obj->mMultiChannelMode) + { + // if bad pointers in pIIRInpCfg, disable channel + if ((NULL == *(pIIRInpCfg->ppiFilterCoeff + k)) || + (NULL == *(pIIRInpCfg->ppiNumShiftFactor + k))) + { + *(CMultiStageIIR_obj->pbMultiStageIIREnable + k) = FALSE; + } + else + { + // channel-k specifications + pFiltCoeff = *(pIIRInpCfg->ppiFilterCoeff + k); + piNumShift = *(pIIRInpCfg->ppiNumShiftFactor + k); + *(pFilterEnable + k) = *(pIIRInpCfg->pbIIRFilterEnable + k); + *(pPreGain + k) = *(pIIRInpCfg->piPreGain + k); + *(pNumStages + k) = *(pIIRInpCfg->puiNumStages + k); + } + } + else /* LINKED */ + { + // channel-0 specifications + pFiltCoeff = *(pIIRInpCfg->ppiFilterCoeff); + piNumShift = *(pIIRInpCfg->ppiNumShiftFactor); + *(pFilterEnable + k) = *(pIIRInpCfg->pbIIRFilterEnable); + *(pPreGain + k) = *(pIIRInpCfg->piPreGain); + *(pNumStages + k) = *(pIIRInpCfg->puiNumStages); + } + + /* + assign filter coefficients and numerator shift factors + for all biquad stages in the current channel + */ + for (i = 0; i < *(CMultiStageIIR_obj->puiNumIIRStages + k); ++i) + { + int16 j; + for (j = 0; j < MULTISTAGE_IIR_NUM_COEFFS; ++j) + { + pIIR->nIIRFilterCoeffs[j] = *pFiltCoeff++; + } + for (j = 0; j < MULTISTAGE_IIR_DEN_COEFFS; ++j) + { + pIIR->nIIRFilterCoeffs[MULTISTAGE_IIR_NUM_COEFFS + j] = *pFiltCoeff++; + } + + pIIR->IIRFilterNumShiftFactor = *piNumShift++; + pIIR++; // move to the next biquad stage + } + } + + msiir_reset(CMultiStageIIR_obj); + return PPSUCCESS; +} + +/* + Calls Process_one_channel for every channel +*/ +PPStatus msiir_process(CMultiStageIIRLib *CMultiStageIIR_obj, void** pOut, void** pInp, int32 iNrOfSamples) +{ + PPStatus result; + uint16 k; + + if ((NULL == pOut) || (NULL == pInp) || (NULL == CMultiStageIIR_obj)) { + return PPFAILURE; + } + + if (CMultiStageIIR_obj->uiBitsPerSample == 16) + { + int16** pInp_cast; + int16** pOut_cast; + pInp_cast = (int16**)pInp; + pOut_cast = (int16**)pOut; + for (k = 0; k < CMultiStageIIR_obj->uiNumChannels; ++k) + { + result = msiir_process_one_channel( + CMultiStageIIR_obj, + *(pOut_cast + k), + *(pInp_cast + k), + iNrOfSamples, + k); + if (PPSUCCESS != result) { + return result; + } + } + } + else + { + int32** pInp_cast; + int32** pOut_cast; + pInp_cast = (int32**)pInp; + pOut_cast = (int32**)pOut; + for (k = 0; k < CMultiStageIIR_obj->uiNumChannels; ++k) + { + result = msiir_process_one_channel( + CMultiStageIIR_obj, + *(pOut_cast + k), + *(pInp_cast + k), + iNrOfSamples, + k); + if (PPSUCCESS != result) { + return result; + } + } + } + return PPSUCCESS; +} + +PPStatus msiir_process_one_channel(CMultiStageIIRLib *CMultiStageIIR_obj, void* pOut, void* pInp, int32 iNrOfSamples, uint16 k) +{ + // k = channel number + int32 STFPreGain_k; + MSIIRDataStruct* pIIR_k; + int16 numStages_k; + + numStages_k = *(CMultiStageIIR_obj->puiNumIIRStages + k); + pIIR_k = *(CMultiStageIIR_obj->ppMultiStageIIRStruct + k); + + if ( (FALSE == *(CMultiStageIIR_obj->pbMultiStageIIREnable + k)) + || (0==numStages_k) ) + { + if (pOut==pInp) + { + return PPSUCCESS; //inplace, do not copy input to output + } + /* filter not enabled, copy_input_to_output (bypass), input and output memory does not overlap */ + int32 bytes_per_sample = CMultiStageIIR_obj->uiBitsPerSample >> 3; + memscpy(pOut, bytes_per_sample*iNrOfSamples, pInp, bytes_per_sample*iNrOfSamples); + return PPSUCCESS; + } + + /* process data */ + /* If gain == 0, assign_zero_to_output + If gain == 1, apply_multiband_iir + Else apply_stf_pregain and apply_multiband_iir + */ + STFPreGain_k = *(CMultiStageIIR_obj->piSTFPreGain + k); + if (0 == STFPreGain_k) + { + int32 bytes_per_sample = CMultiStageIIR_obj->uiBitsPerSample >> 3; + memset(pOut, 0, bytes_per_sample*iNrOfSamples); + return PPSUCCESS; + } + + PPStatus result = PPSUCCESS; + if (MSIIR_UNITY_PREGAIN == STFPreGain_k) + { + result = apply_multistage_iir( + pIIR_k, + numStages_k, + pOut, + pInp, + iNrOfSamples, + CMultiStageIIR_obj->uiBitsPerSample); + } + else + { + result = msiir_apply_stf_pregain( + STFPreGain_k, + MSIIR_Q_PREGAIN, + pOut, + pInp, + iNrOfSamples, + CMultiStageIIR_obj->uiBitsPerSample); + if (PPSUCCESS != result) + { + return result; + } + result = apply_multistage_iir( + pIIR_k, + numStages_k, + pOut, + pOut, + iNrOfSamples, + CMultiStageIIR_obj->uiBitsPerSample); + } + + return result; +} + + +/* + Call iirTDF2 for all the stages + If-Else block for data = 16-bit OR 32-bit +*/ +PPStatus apply_multistage_iir(MSIIRDataStruct* pMultiStageIIRStruct, + int16 iNumIIRStages, + void *pOut, + void *pInp, + int32 iNrOfSamples, + int16 uiBitsPerSample) +{ + int32 i; + const int16 iDenShiftFac = 2; + MSIIRDataStruct* pIIR; + + pIIR = pMultiStageIIRStruct; + + if (uiBitsPerSample == 16) + { + int16 * pInp_new; + int16 * pOut_new; + pInp_new = (int16*)pInp; + pOut_new = (int16*)pOut; + + for (i = 0; i < iNumIIRStages; i++) + { + /* Filtering subroutine */ + iirTDF2_16( + pInp_new, + pOut_new, + iNrOfSamples, + &pIIR->nIIRFilterCoeffs[0], + &pIIR->nIIRFilterCoeffs[MULTISTAGE_IIR_NUM_COEFFS], + &pIIR->iFiltMemory[0], + pIIR->IIRFilterNumShiftFactor, + iDenShiftFac + ); + + /* For in-place computation, use output buffer (pointer) to feed into input buffer for next iteration */ + pInp_new = pOut_new; + + /* Increment IIR Filter structure pointer for the next biquad stage */ + pIIR++; + } + } else { + int32 * pInp_new; + int32 * pOut_new; + pInp_new = (int32*)pInp; + pOut_new = (int32*)pOut; + + for (i = 0; i < iNumIIRStages; i++) + { + /* Filtering subroutine */ + iirTDF2_32( + pInp_new, + pOut_new, + iNrOfSamples, + &pIIR->nIIRFilterCoeffs[0], + &pIIR->nIIRFilterCoeffs[MULTISTAGE_IIR_NUM_COEFFS], + &pIIR->iFiltMemory[0], + pIIR->IIRFilterNumShiftFactor, + iDenShiftFac + ); + + /* For in-place computation, use output buffer (pointer) to feed into input buffer for next iteration */ + pInp_new = pOut_new; + + /* Increment IIR Filter structure pointer for the next stage */ + pIIR++; + } + } + return PPSUCCESS; +} + + +/* + Multiply input signal by preGain value + If-Else block for data = 16-bit OR 32-bit +*/ +PPStatus msiir_apply_stf_pregain( + int32 iSTFPreGain, + int16 QFactor, + void *pOut, + void *pInp, + int32 iNrOfSamples, + int16 uiBitsPerSample) +{ + int32 i; + int16 right_shift; + + if ( (!pInp) || (!pOut) ) + { + return PPFAILURE; + } + + right_shift = 32 - QFactor; + + if (uiBitsPerSample == 16) + { + int16 * pInp_new; + int16 * pOut_new; + pInp_new = (int16*)pInp; + pOut_new = (int16*)pOut; + for (i = 0; i < iNrOfSamples ; i++) + { + *pOut_new = s16_saturate_s32(s32_saturate_s64(s64_mult_s32_s32_shift((int32)(*pInp_new),iSTFPreGain,right_shift))); + pInp_new++; + pOut_new++; + } + } else { + int32 * pInp_new; + int32 * pOut_new; + pInp_new = (int32*)pInp; + pOut_new = (int32*)pOut; + for (i = 0; i < iNrOfSamples ; i++) + { + *pOut_new = s32_saturate_s64(s64_mult_s32_s32_shift(*pInp_new,iSTFPreGain,right_shift)); + pInp_new++; + pOut_new++; + } + } + + return PPSUCCESS; +} + + diff --git a/modules/processing/filters/multi_stage_iir/lib/src/msiir.c b/modules/processing/filters/multi_stage_iir/lib/src/msiir.c new file mode 100644 index 0000000..bf2603c --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/lib/src/msiir.c @@ -0,0 +1,319 @@ +/*============================================================================ + @file msiir.c + + Multi Stage IIR Filter implementation. + + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause +============================================================================*/ + +/*---------------------------------------------------------------------------- + Includes +----------------------------------------------------------------------------*/ +#include "msiir.h" +#include "simple_mm.h" +#include "audio_dsp.h" +#include "audio_dsp32.h" +#include "audio_iir_tdf2.h" +#include "stringl.h" +#ifdef AVS_BUILD_SOS +#include "capi_cmn.h" +#endif +/*---------------------------------------------------------------------------- + Local Functions +----------------------------------------------------------------------------*/ +/* clear memorys for state w1 and w2 for all stages */ +static void reset(mult_stage_iir_t *obj_ptr) +{ + int32 i, j; + for (i = 0; i < obj_ptr->num_stages; ++i) { + for (j = 0; j < MSIIR_FILTER_STATES; ++j) { + obj_ptr->sos[i].states[j] = 0; + } + } +} + +/* set default values for the first run */ +static void set_default(mult_stage_iir_t *obj_ptr) +{ + obj_ptr->pre_gain = c_unity_pregain; + obj_ptr->num_stages = 0; + // by default there is no coeffs, and filter is a passthru + // when setting stages, coeffs must follow accordingly + // by using parameter id MSIIR_PARAM_CONFIG +} + +/* process for 16 bit data */ +static MSIIR_RESULT process_16(mult_stage_iir_t *obj_ptr, int16 *out_ptr, int16 *in_ptr, int32 samples) +{ + iir_data_t* iir_ptr = obj_ptr->sos; + int16 *sos_in_ptr = in_ptr; + int32 i; + + // 1. if zero gain, directly output zero + if (0 == obj_ptr->pre_gain) { + buffer_empty(out_ptr, samples); + return MSIIR_SUCCESS; + } + + // 2. if not unity pregain, apply it and store to output + if (c_unity_pregain != obj_ptr->pre_gain) { + buffer16_fill32(out_ptr, in_ptr, obj_ptr->pre_gain, MSIIR_Q_PREGAIN, samples); + sos_in_ptr = out_ptr; // later, process from the scratch mem (output) + } + + // 3. process sos sections + if (obj_ptr->num_stages <= 0) { + // bypass for invalid stage count + buffer_copy(out_ptr, sos_in_ptr, samples); + return MSIIR_SUCCESS; + } + + for (i = 0; i < obj_ptr->num_stages; i++) { + // filtering subroutine + iirTDF2_16(sos_in_ptr, out_ptr, samples, + &iir_ptr->coeffs[0], + &iir_ptr->coeffs[MSIIR_NUM_COEFFS], + &iir_ptr->states[0], + (int16)iir_ptr->shift_factor, + MSIIR_DEN_SHIFT); + + // feed correct ptrs for the next iteration + sos_in_ptr = out_ptr; + + // point to the next biquad + iir_ptr++; + } + return MSIIR_SUCCESS; +} + +/* process for 32 bit data */ +static MSIIR_RESULT process_32(mult_stage_iir_t *obj_ptr, int32 *out_ptr, int32 *in_ptr, int32 samples) +{ + iir_data_t* iir_ptr = obj_ptr->sos; + int32 *sos_in_ptr = in_ptr; + int32 i; + + // 1. if zero gain, output zero + if (0 == obj_ptr->pre_gain) { + memset(out_ptr, 0, samples*sizeof(*out_ptr)); + return MSIIR_SUCCESS; + } + + // 2. if not unity pregain apply it and store to output mem + if (c_unity_pregain != obj_ptr->pre_gain) { + buffer32_fill32(out_ptr, in_ptr, obj_ptr->pre_gain, MSIIR_Q_PREGAIN, samples); + sos_in_ptr = out_ptr; // later, process from the scratch mem (output) + } + + // 3. process sos sections + if (obj_ptr->num_stages <= 0) { + // bypass for invalid stage count + memscpy(out_ptr,samples*sizeof(*out_ptr), sos_in_ptr, samples*sizeof(*out_ptr)); + return MSIIR_SUCCESS; + } + + for (i = 0; i < obj_ptr->num_stages; i++) { + // filtering subroutine + iirTDF2_32(sos_in_ptr, out_ptr, samples, + &iir_ptr->coeffs[0], + &iir_ptr->coeffs[MSIIR_NUM_COEFFS], + &iir_ptr->states[0], + (int16)iir_ptr->shift_factor, + MSIIR_DEN_SHIFT); + + // feed correct pointer for the next iteration + sos_in_ptr = out_ptr; + + // point to the next biquad + iir_ptr++; + } + return MSIIR_SUCCESS; +} + + +/*----------------------------------------------------------------------------- + API Functions +-----------------------------------------------------------------------------*/ + +// ** Processing one block of samples with IIRTDF2 implementation +MSIIR_RESULT msiir_process_v2(msiir_lib_t *lib_ptr, void *out_ptr, void *in_ptr, uint32 samples) +{ + mult_stage_iir_t *obj_ptr = (mult_stage_iir_t *)lib_ptr->mem_ptr; + + if (16 == obj_ptr->static_vars.data_width) { + return process_16(obj_ptr, (int16 *)out_ptr, (int16 *)in_ptr, samples); + + } else if (32 == obj_ptr->static_vars.data_width) { + return process_32(obj_ptr, (int32 *)out_ptr, (int32 *)in_ptr, samples); + + } else { + return MSIIR_FAILURE; // invalid data width + } +} + +// ** Get memory requirements +MSIIR_RESULT msiir_get_mem_req(msiir_mem_req_t *mem_req_ptr, msiir_static_vars_t* static_vars_ptr) +{ + uint32 reqsize; + + /* main lib struct */ + reqsize = smm_malloc_size(sizeof(mult_stage_iir_t)); + + /* iir data struct for each stage */ + reqsize += smm_calloc_size(static_vars_ptr->max_stages, sizeof(iir_data_t)); + + /* store results */ + mem_req_ptr->mem_size = reqsize; + mem_req_ptr->stack_size = msiir_max_stack_size; + return MSIIR_SUCCESS; +} + +// ** Partition, initialize memory, and set default values +MSIIR_RESULT msiir_init_mem(msiir_lib_t *lib_ptr, msiir_static_vars_t* static_vars_ptr, void* mem_ptr, uint32 mem_size) +{ + SimpleMemMgr MemMgr; + SimpleMemMgr *smm = &MemMgr; + msiir_mem_req_t mem_req; + mult_stage_iir_t *obj_ptr; + + /* double check if allocated mem is enough */ + msiir_get_mem_req(&mem_req, static_vars_ptr); + if (mem_req.mem_size > mem_size) { + return MSIIR_MEMERROR; + } + + /* assign initial mem pointers to lib instance */ + lib_ptr->mem_ptr = mem_ptr; + smm_init(smm, lib_ptr->mem_ptr); + obj_ptr = (mult_stage_iir_t *)smm_malloc(smm, sizeof(mult_stage_iir_t)); + + /* copy static vars */ + obj_ptr->static_vars = *static_vars_ptr; + + /* allocate for sos sections */ + obj_ptr->sos = (iir_data_t *)smm_calloc( + smm, obj_ptr->static_vars.max_stages, sizeof(iir_data_t)); + + /* set default values */ + set_default(obj_ptr); + + return MSIIR_SUCCESS; +} + +// ** Set parameters to library +MSIIR_RESULT msiir_set_param(msiir_lib_t* lib_ptr, uint32 param_id, void* param_ptr, uint32 param_size) +{ + mult_stage_iir_t *obj_ptr = (mult_stage_iir_t *)lib_ptr->mem_ptr; + msiir_coeffs_t *coeffs_ptr; + msiir_config_t *cfg_ptr; + int32 i, j, reset_flag; + + switch (param_id) { + case MSIIR_PARAM_PREGAIN: + if (param_size == sizeof(msiir_pregain_t)) { + obj_ptr->pre_gain = *((msiir_pregain_t *)param_ptr); + } else { + return MSIIR_MEMERROR; + } + break; + + case MSIIR_PARAM_CONFIG: + cfg_ptr = (msiir_config_t *)param_ptr; + if (cfg_ptr->num_stages >= 0 && cfg_ptr->num_stages <= obj_ptr->static_vars.max_stages) { + obj_ptr->num_stages = cfg_ptr->num_stages; + } else { + return MSIIR_FAILURE; // invalid num stages must be within [1, max] + } + if (param_size == sizeof(msiir_config_t) + obj_ptr->num_stages * sizeof(msiir_coeffs_t)) { + + coeffs_ptr = (msiir_coeffs_t *)((char*)cfg_ptr + sizeof(msiir_config_t)); + + reset_flag = 0; + for (i = 0; i < obj_ptr->num_stages; ++i) { + for (j = 0; j < MSIIR_COEFF_LENGTH; ++j) { + // check if denominator has changes + if (j >= 3 && obj_ptr->sos[i].coeffs[j] != (coeffs_ptr+i)->iir_coeffs[j]) { + reset_flag = 1; + } + // copy coeffs + obj_ptr->sos[i].coeffs[j] = (coeffs_ptr+i)->iir_coeffs[j]; + } + obj_ptr->sos[i].shift_factor = (coeffs_ptr+i)->shift_factor; + } + // reset lib after config change + if (1 == reset_flag) { + reset(obj_ptr); + } + } else { + return MSIIR_MEMERROR; + } + break; + + case MSIIR_PARAM_RESET: + reset(obj_ptr); + break; + + default: + return MSIIR_FAILURE; // invalid id + } + + return MSIIR_SUCCESS; +} + +// ** Get parameters from library +MSIIR_RESULT msiir_get_param(msiir_lib_t* lib_ptr, uint32 param_id, void* param_ptr, uint32 param_size, uint32 *param_actual_size_ptr) +{ + mult_stage_iir_t *obj_ptr = (mult_stage_iir_t *)lib_ptr->mem_ptr; + msiir_coeffs_t *coeffs_ptr; + uint32 actual_size; + int32 i, j; + + switch (param_id) { + + case MSIIR_PARAM_LIB_VER: + if (param_size >= sizeof(msiir_version_t)) { + *((msiir_version_t *)param_ptr) = MSIIR_LIB_VER; + *param_actual_size_ptr = sizeof(msiir_version_t); + } else { + return MSIIR_MEMERROR; + } + break; + + case MSIIR_PARAM_PREGAIN: + if (param_size >= sizeof(msiir_pregain_t)) { + *((msiir_pregain_t *)param_ptr) = obj_ptr->pre_gain; + *param_actual_size_ptr = sizeof(msiir_pregain_t); + } else { + return MSIIR_MEMERROR; + } + break; + + case MSIIR_PARAM_CONFIG: + actual_size = sizeof(msiir_config_t) + obj_ptr->num_stages * sizeof(msiir_coeffs_t); + if (param_size >= actual_size) { + // copy num_stages + ((msiir_config_t *)param_ptr)->num_stages = obj_ptr->num_stages; + + coeffs_ptr = (msiir_coeffs_t *)((char *)param_ptr + sizeof(msiir_config_t)); + + for (i = 0; i < obj_ptr->num_stages; ++i) { + for (j = 0; j < MSIIR_COEFF_LENGTH; ++j) { + (coeffs_ptr+i)->iir_coeffs[j] = obj_ptr->sos[i].coeffs[j]; + } + (coeffs_ptr+i)->shift_factor = obj_ptr->sos[i].shift_factor; + } + // save size + *param_actual_size_ptr = actual_size; + + } else { + return MSIIR_MEMERROR; + } + break; + + default: + return MSIIR_FAILURE; // invalid id + } + + return MSIIR_SUCCESS; +} diff --git a/modules/processing/filters/multi_stage_iir/lib/src/msiir.h b/modules/processing/filters/multi_stage_iir/lib/src/msiir.h new file mode 100644 index 0000000..c6db71d --- /dev/null +++ b/modules/processing/filters/multi_stage_iir/lib/src/msiir.h @@ -0,0 +1,47 @@ +#ifndef MSIIR_H +#define MSIIR_H +/*============================================================================ + @file msiir.h + + Private Header for Multi Stage IIR Filter. + + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause +============================================================================*/ +#include "msiir_api.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/*---------------------------------------------------------------------------- + Constants +----------------------------------------------------------------------------*/ +#define MSIIR_FILTER_STATES (2) // mem length per biquad +#define MSIIR_DEN_SHIFT (2) // fixed denominator shift factor + +static const int32 msiir_max_stack_size = 2000; // worst case stack mem +static const int32 c_unity_pregain = 134217728; // unity gain (Q27) + +/*---------------------------------------------------------------------------- + Private Types +----------------------------------------------------------------------------*/ +typedef struct iir_data_t { // ** second order section type + int64 states[MSIIR_FILTER_STATES]; + int32 coeffs[MSIIR_COEFF_LENGTH]; + int32 shift_factor; +} iir_data_t; + +typedef struct mult_stage_iir_t{ // ** multi stage IIR + msiir_static_vars_t static_vars; // copy of static vars + msiir_pregain_t pre_gain; // filter pre gain + int32 num_stages; // num of stages in use + iir_data_t* sos; // second order sections +} mult_stage_iir_t; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* MSIIR_H */ + diff --git a/modules/processing/resamplers/dynamic_resampler/api/dynamic_resampler_api.h b/modules/processing/resamplers/dynamic_resampler/api/dynamic_resampler_api.h new file mode 100644 index 0000000..d8d3fc0 --- /dev/null +++ b/modules/processing/resamplers/dynamic_resampler/api/dynamic_resampler_api.h @@ -0,0 +1,162 @@ +#ifndef DYNAMIC_RESAMPLER_API_H +#define DYNAMIC_RESAMPLER_API_H + +/*============================================================================== + @file dynamic_resampler_api.h + @brief This file contains RESAMPLER API +==============================================================================*/ + +/* ======================================================================== + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause +===========================================================================*/ + +/*------------------------------------------------------------------------ + * Include files + * -----------------------------------------------------------------------*/ +#include "module_cmn_api.h" + +/** @h2xml_title1 {Dynamic Resampler Module API} + @h2xml_title_agile_rev {Dynamic Resampler Module API} + @h2xml_title_date {September, 2018} */ + +/*------------------------------------------------------------------------------ + Defines +------------------------------------------------------------------------------*/ +/* Input port ID of Dynamic Resampler module */ +#define DYN_RS_DATA_INPUT_PORT 0x2 + +/* Output port ID of Dynamic Resampler module */ +#define DYN_RS_DATA_OUTPUT_PORT 0x1 + +/*------------------------------------------------------------------------------ + Module +------------------------------------------------------------------------------*/ +#define MODULE_ID_DYNAMIC_RESAMPLER 0x07001016 +/** + @h2xmlm_module {"MODULE_ID_DYNAMIC_RESAMPLER", + MODULE_ID_DYNAMIC_RESAMPLER} + @h2xmlm_displayName {"Dynamic Resampler"} + @h2xmlm_modSearchKeys{resampler, Audio} + @h2xmlm_description {- Dynamic Resampler module.\n + - This module converts audio sampling rate and uses FIR filters to do it. \n + - Therefore the SNR is higher than IIR filters but - at the cost of higher Delay.\n + -\n + - Supports following params: + - PARAM_ID_DYNAMIC_RESAMPLER_CONFIG + - PARAM_ID_DYNAMIC_RESAMPLER_OUT_CFG + - \n + - Supported Input Media Format: \n + - Data Format : FIXED_POINT \n + - fmt_id : Don't care \n + - Sample Rates : >0 to 384 kHz \n + - Number of channels : 1 to 128 (for certain products this module supports only 32 channels) \n + - Channel type : Don't care \n + - Bits per sample : 16, 32 \n + - Q format : Don't care \n + - Interleaving : de-interleaved unpacked \n + - Signed/unsigned : Don't care } + + @h2xmlm_dataMaxInputPorts { 1 } + @h2xmlm_dataInputPorts { IN = DYN_RS_DATA_INPUT_PORT} + @h2xmlm_dataOutputPorts { OUT = DYN_RS_DATA_OUTPUT_PORT} + @h2xmlm_dataMaxOutputPorts { 1 } + @h2xmlm_supportedContTypes {APM_CONTAINER_TYPE_SC} + @h2xmlm_isOffloadable {true} + @h2xmlm_stackSize { 4096 } + @{ <-- Start of the Module --> +*/ + +/* ID of the Dynamic Resampler configuration parameter used by MODULE_ID_DYNAMIC_RESAMPLER. */ +#define PARAM_ID_DYNAMIC_RESAMPLER_CONFIG 0x08001025 + +/* Structure payload for: PARAM_ID_DYNAMIC_RESAMPLER_CONFIG*/ +/** @h2xmlp_parameter {"PARAM_ID_DYNAMIC_RESAMPLER_CONFIG", PARAM_ID_DYNAMIC_RESAMPLER_CONFIG} + @h2xmlp_description { Structure for setting the configuration for the Dynamic Resampler } + @h2xmlp_toolPolicy {Calibration; RTC} */ + +#include "spf_begin_pack.h" +struct param_id_dynamic_resampler_config_t +{ + uint32_t use_hw_rs; + /**< @h2xmle_description {Specifies whether to use the hardware or software resampler for DYNAMIC RESAMPLER \n + -> If the client wants to use hardware resampler, use_hw_rs should + be set to 1 and dynamic_mode and delay_type values will be saved but ignored. \n + -> Hardware resampler can be created only if the chip supports it \n + -> If hardware resampler creation fails for any reason, sw resampler will + be created with saved dynamic mode and delay type set by client \n + -\n + -> If use_hw_rs flag is set to 0, software resampler is to be used, + and dynamic_mode and delay_type params come into play \n + -\n + -> Get param query on this api will return the actual resampler being used. + It is not simply what the client set} + + @h2xmle_rangeList {"Use sw resampler" = 0, + "Use hw resampler" = 1} + @h2xmle_default {0} */ + + uint16_t dynamic_mode; + /**< @h2xmle_description {Specifies the mode of operation for the DYNAMIC RESAMPLER\n + -> This dynamic_mode value has significance only if sw resampler is used.} + @h2xmle_rangeList {"Generic resampling"= 0; + "Dynamic resampling"=1} + @h2xmle_default {0} */ + + + uint16_t delay_type; + /**< @h2xmle_description {Specifies the delay type for the DYNAMIC RESAMPLER mode\n + -> This delay_type value has significance only if sw resampler is used + AND the dynamic_mode value is set to 1. } + @h2xmle_rangeList {"High delay with smooth transition"= 0; + "Low delay with visible transitional phase distortion"=1} + @h2xmle_default {0} */ +} +#include "spf_end_pack.h" +; +/* Structure type def for above payload. */ +typedef struct param_id_dynamic_resampler_config_t param_id_dynamic_resampler_config_t; + + + +/* Set param for sending output sample rate */ +#define PARAM_ID_DYNAMIC_RESAMPLER_OUT_CFG 0x08001034 + +/** @h2xmlp_parameter {"PARAM_ID_DYNAMIC_RESAMPLER_OUT_CFG", PARAM_ID_DYNAMIC_RESAMPLER_OUT_CFG} + @h2xmlp_description {Specifies the output sample rate to be set.} + @h2xmlp_toolPolicy {Calibration; RTC} */ + +#include "spf_begin_pack.h" +struct param_id_dynamic_resampler_out_cfg_t +{ + int32_t sampling_rate; + /**< @h2xmle_description {Specifies the output sample rate\n + ->Ranges from -2 to 384KHz where + -2 is PARAM_VAL_UNSET and -1 is PARAM_VAL_NATIVE} + @h2xmle_rangelist {"PARAM_VAL_UNSET" = -2; + "PARAM_VAL_NATIVE" =-1; + "8 kHz"=8000; + "11.025 kHz"=11025; + "12 kHz"=12000; + "16 kHz"=16000; + "22.05 kHz"=22050; + "24 kHz"=24000; + "32 kHz"=32000; + "44.1 kHz"=44100; + "48 kHz"=48000; + "88.2 kHz"=88200; + "96 kHz"=96000; + "176.4 kHz"=176400; + "192 kHz"=192000; + "352.8 kHz"=352800; + "384 kHz"=384000} + @h2xmle_default {-1} */ +} +#include "spf_end_pack.h" +; +/* Structure type def for above payload. */ +typedef struct param_id_dynamic_resampler_out_cfg_t param_id_dynamic_resampler_out_cfg_t; + + +/** @} <-- End of the Module -->*/ +#endif //DYNAMIC_RESAMPLER_API_H diff --git a/modules/processing/resamplers/dynamic_resampler/bin/arm/libdynamic_resampler.a b/modules/processing/resamplers/dynamic_resampler/bin/arm/libdynamic_resampler.a new file mode 100644 index 0000000000000000000000000000000000000000..c2d0ef1bb2625fd23f605c7a69520961eb19a8a7 GIT binary patch literal 673450 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%0A-H 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

sCv52L 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 literal 0 HcmV?d00001 diff --git a/modules/processing/resamplers/dynamic_resampler/build/CMakeLists.txt b/modules/processing/resamplers/dynamic_resampler/build/CMakeLists.txt new file mode 100644 index 0000000..55e9fa4 --- /dev/null +++ b/modules/processing/resamplers/dynamic_resampler/build/CMakeLists.txt @@ -0,0 +1,33 @@ +#[[ + @file CMakeLists.txt + + @brief + + @copyright + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause +]] +cmake_minimum_required(VERSION 3.10) + +#Include directories +set(dynamic_resampler_includes + ${LIB_ROOT}/inc +) + +spf_module_sources( + KCONFIG CONFIG_DYNAMIC_RESAMPLER + NAME dynamic_resampler + MAJOR_VER 1 + MINOR_VER 0 + AMDB_ITYPE "capi" + AMDB_MTYPE "pp" + AMDB_MID "0x07001016" + AMDB_TAG "capi_dynamic_resampler" + AMDB_MOD_NAME "MODULE_ID_DYNAMIC_RESAMPLER" + INCLUDES ${dynamic_resampler_includes} + H2XML_HEADERS "${LIB_ROOT}/api/dynamic_resampler_api.h" + CFLAGS "" + STATIC_LIB_PATH "${LIB_ROOT}/bin/arm/libdynamic_resampler.a" +) + + diff --git a/modules/processing/resamplers/dynamic_resampler/inc/hw_rs_lib.h b/modules/processing/resamplers/dynamic_resampler/inc/hw_rs_lib.h new file mode 100644 index 0000000..8c2fe12 --- /dev/null +++ b/modules/processing/resamplers/dynamic_resampler/inc/hw_rs_lib.h @@ -0,0 +1,40 @@ +/**======================================================================== + @file hw_rs_lib.h + + @brief This file contains API's for allocating static variables needed for hw resampler + +/*========================================================================= +Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. +SPDX-License-Identifier: BSD-3-Clause +========================================================================= */ + +#ifndef _HW_RS_LIB_H_ +#define _HW_RS_LIB_H_ + +#include "ar_error_codes.h" +#include "posal.h" + + +#ifdef __cplusplus +extern "C" { +#endif //__cplusplus + +// Mutex Info Structure +typedef struct hw_resampler_mutex_t +{ + posal_mutex_t mutex_lock; // Mutex lock for Hw Resampler api call. +} hw_resampler_mutex_t; + +ar_result_t hw_rs_lib_static_init(); + +void hw_rs_lib_init(posal_atomic_word_t **phwrs_resource_state, + posal_atomic_word_t **estimated_time_of_all_suspended_session, + hw_resampler_mutex_t **mutex_info); + +ar_result_t hw_rs_lib_static_deinit(); + +#ifdef __cplusplus +} +#endif //__cplusplus + +#endif /* _HW_RS_LIB_H_ */ diff --git a/modules/processing/resamplers/dynamic_resampler/inc/hwsw_rs_lib.h b/modules/processing/resamplers/dynamic_resampler/inc/hwsw_rs_lib.h new file mode 100644 index 0000000..9d989fd --- /dev/null +++ b/modules/processing/resamplers/dynamic_resampler/inc/hwsw_rs_lib.h @@ -0,0 +1,263 @@ +/* ======================================================================== */ +/** + @file hwsw_rs_lib.h + Header file for combo hw- sw resampler lib +*/ + +/*========================================================================= +Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. +SPDX-License-Identifier: BSD-3-Clause +========================================================================= */ + +#ifndef HWSW_RS_LIB_H +#define HWSW_RS_LIB_H + +/*------------------------------------------------------------------------ + * Include files + * -----------------------------------------------------------------------*/ + +#ifndef CAPI_UNIT_TEST +#include "shared_lib_api.h" +#else +#include "Elite_intf_extns_change_media_fmt.h" +#include "capi.h" +#endif + +#include "audpp_util.h" +#include "audio_dsp32.h" +#include "resampler_32b_ext.h" +#include "hwsw_rs_lib_hw.h" +#include "hw_rs_lib.h" + +#ifdef __cplusplus +extern "C" { +#endif //__cplusplu +/*------------------------------------------------------------------------ + * Macros, Defines, Type declarations + * -----------------------------------------------------------------------*/ +#define INTM_GUARD_BITS 32 +// Enum to list the number of stages supported +typedef enum { + STAGE_ZERO, + STAGE_ONE, + MAX_MULTI_STAGES_SUPPORTED +}stage_t; + +//Enum: column indices for sampling rates in table +typedef enum{ + MS_IP_SR = 1, + MS_INT_SR = 2, + MS_OP_SR = 3, + MS_UNIT_IP_SMPLS = 4, // unit frame size input sample index + MS_UNIT_INTM_SMPLS = 5, + MS_UNIT_OP_SMPLS = 6 +}tbl_fs_idx; + +typedef enum{ + FIXED_IN = 0, + FIXED_OUT +}process_mode; + +#define TEN_MS 10 // duration in ms + +// Enum : Supported multi-stage list +typedef enum{ + DYRS_384_44_1, + MAX_MULTI_STAGE_SUPPORTED_LIST +}ms_lst_fs_t; + +const uint32_t multi_stage_sr_info[][7] = { + {DYRS_384_44_1, 384000, 192000, 44100, 384, 192, 44 }, +}; + +// To print all lib status messages +//#define HWSW_RESAMPLER_DEBUG_MSG 1 + +// To print each frame status +//#define HWSW_RESAMPLER_PRINT_FRAME_STATS 1 +/*------------------------------------------------------------------------ + * Structure definitions + * -----------------------------------------------------------------------*/ +typedef struct hwsw_rs_sw_mode_config +{ + uint16_t dynamic_mode; // 0: non-dynamic mode, 1: dynamic mode + uint16_t delay_type; // 0: High delay, 1: Low delay (relevant if dynamic mode is set) +} hwsw_rs_sw_mode_config_t; + +typedef struct hwsw_rs_sw_lib_config +{ + uint32_t first_frame; + int32_t config_params[20]; +} hwsw_rs_sw_lib_config_t; + +typedef struct hwsw_rs_sw_lib_mem_req +{ + uint32_t drsStructSize; + uint32_t drsGenCoeffSize; + uint32_t drsPerChannelDataMemSize; + uint32_t drsMemSize; + uint32_t drsFiltCoeffMxSize; +} hwsw_rs_sw_lib_mem_req_t; + +typedef struct hwsw_rs_sw_lib_mem_ptr +{ + // Library State structure memory + void *pStructMem; + + // Generated polyphase filter coefficients + void *pGenCoeff; + + // Per channel filter memory + void *pChannelData[CAPI_MAX_CHANNELS_V2]; + + // Coefficient matrix + void *pCoefMatrix; + + // Filter selected based on inp/out freq + void *pResampCoef; +} hwsw_rs_sw_lib_mem_ptr_t; + +typedef struct hwsw_rs_sw_memory +{ + hwsw_rs_sw_lib_mem_req_t drs_mem_req; + hwsw_rs_sw_lib_mem_req_t prev_drs_mem_req; + hwsw_rs_sw_lib_mem_ptr_t drs_mem_ptr; +} hwsw_rs_sw_memory_t; + +typedef struct hwsw_rs_media_fmt_t +{ + uint32_t inp_sample_rate; + uint16_t bits_per_sample; + uint16_t num_channels; + uint32_t output_sample_rate; + uint32_t q_factor; +} hwsw_rs_media_fmt_t; + +typedef struct hwsw_rs_events_config +{ + uint32_t KPPS; + uint32_t delay_in_us; + uint32_t bandwidth; +} hwsw_rs_events_config_t; + +typedef struct hwsw_rs_resampler_config +{ + uint32_t use_hw_rs; + uint16_t dynamic_mode; + uint16_t delay_type; +} hwsw_rs_resampler_config_t; + +typedef enum hwsw_rs_using_resampler_t { SW_RESAMPLER = 0, HW_RESAMPLER, NO_RESAMPLER } hwsw_rs_using_resampler_t; + +// Information required for multi-stage +typedef struct ms_info_t{ + uint32_t intm_max_smpls_ms; + uint32_t num_stages; + int32_t tbl_idx; // index in the Multi stage sampling rate conversion table + capi_stream_data_t intm_buf_ptr; // intermediate stage stream buffer +}ms_info_t; + +typedef struct hwsw_resampler_lib +{ + // Flag which tells us which resampler is being used + hwsw_rs_using_resampler_t this_resampler_instance_using; + + // Copy of Input Media format in lib + hwsw_rs_media_fmt_t media_fmt; + + /* sw related structs */ + // Resampling mode (dynamic or non-dynamic) (High Delay or Low Delay) + hwsw_rs_sw_mode_config_t sw_rs_config_param; + + // Sw resampler Library config structure. + hwsw_rs_sw_lib_config_t* sw_lib_config_ptr[MAX_MULTI_STAGES_SUPPORTED]; + + // info about allocated memory for sw resampler. + hwsw_rs_sw_memory_t* sw_rs_mem_ptr[MAX_MULTI_STAGES_SUPPORTED]; + + /* hw related structs */ + // hw resampler structure + hwsw_rs_hw_resampler_t hw_resampler; + + /* dm related params set to the lib through set param handling when receiving dm mode and num samples + * dm_mode decides if lib should operate in fixed out or fall back to default operation*/ + uint32_t dm_mode; + /* output num of samples are provided to lib to be used when dm mode is set to fixed out mode + * Lib by default only has info of output buffers max data length + * For lib default behavior this variable wont be used */ + uint32_t output_fixed_samples; + + //flag to check if it is a multi_stage_process + bool_t is_multi_stage_process; + + //multi stage information structure + ms_info_t ms_info; +#ifdef HWSW_RESAMPLER_DEBUG_MSG + FILE *fp_intm; + FILE *fp_out; +#endif +} hwsw_resampler_lib_t; + +/*------------------------------------------------------------------------ + * Function definitions + * -----------------------------------------------------------------------*/ +void hwsw_rs_lib_init(hwsw_resampler_lib_t *hwsw_rs_lib); + +ar_result_t hwsw_rs_lib_deinit(hwsw_resampler_lib_t *hwsw_rs_ptr); + +void hwsw_rs_hw_resampler_get_process_info(hwsw_rs_hw_resampler_t *hwrs_ptr, + uint32_t input_actual_samples, + uint32_t output_max_samples, + uint32_t * input_samples_to_consume_ptr, + uint32_t * output_samples_to_generate_ptr); + +void hwsw_rs_lib_process_get_hw_process_info(hwsw_resampler_lib_t *hwsw_rs_ptr, + uint32_t input_actual_samples, + uint32_t output_max_samples, + uint32_t * input_samples_to_consume_ptr, + uint32_t * output_samples_to_generate_ptr); + +ar_result_t hwsw_rs_lib_process(hwsw_resampler_lib_t *hwsw_rs_ptr, + capi_buf_t * input_buf_ptr, + capi_buf_t * output_buf_ptr, + uint32_t input_num_bufs, + uint32_t output_num_bufs, + uint32_t heap_id); + +ar_result_t hwsw_rs_lib_check_create_resampler_instance(hwsw_resampler_lib_t *hwsw_rs_ptr, uint32_t heap_id); + +void hwsw_rs_set_lib_mf(hwsw_resampler_lib_t *hwsw_rs_ptr, hwsw_rs_media_fmt_t *inp_mf); + +void hwsw_rs_lib_set_dm_config(hwsw_resampler_lib_t *hwsw_rs_ptr, uint32_t dm_mode, uint32_t fixed_out_samples); + +void hwsw_rs_lib_set_config(uint32_t use_hwrs, + uint16_t dyn_mode, + uint16_t delay, + hwsw_resampler_lib_t *hwsw_rs_ptr, + bool_t * update_rs); + +ar_result_t hwsw_rs_lib_set_hwrs_suspend_resume(bool_t is_suspend, hwsw_resampler_lib_t *hwsw_rs_ptr, uint32_t *create_rs); + +uint32_t hwsw_rs_lib_get_kpps(hwsw_resampler_lib_t *hwsw_rs_ptr); + +uint32_t hwsw_rs_lib_get_bw(hwsw_resampler_lib_t *hwsw_rs_ptr); + +uint32_t hwsw_rs_lib_get_alg_delay(hwsw_resampler_lib_t *hwsw_rs_ptr); + +void hwsw_rs_lib_get_process_check(hwsw_resampler_lib_t *hwsw_rs_ptr, uint32_t *process_check); + +ar_result_t hwsw_rs_lib_algo_reset(hwsw_resampler_lib_t *hwsw_rs_ptr); + +bool_t hwsw_rs_lib_is_multi_stage_supported (hwsw_resampler_lib_t *hwsw_rs_ptr); + +void hwsw_rs_lib_init_sw_rs_mem_inst(hwsw_resampler_lib_t *hwsw_rs_ptr, uint32_t stage_idx); + +ar_result_t hwsw_rs_lib_create_lib_inst(hwsw_resampler_lib_t *hwsw_rs_ptr, uint32_t stage_idx, uint32_t req_size_per_instance, + uint32_t heap_id); + + +#ifdef __cplusplus +} +#endif //__cplusplus + +#endif // HWSW_RS_LIB_H diff --git a/modules/processing/resamplers/dynamic_resampler/inc/hwsw_rs_lib_hw.h b/modules/processing/resamplers/dynamic_resampler/inc/hwsw_rs_lib_hw.h new file mode 100644 index 0000000..af0bf98 --- /dev/null +++ b/modules/processing/resamplers/dynamic_resampler/inc/hwsw_rs_lib_hw.h @@ -0,0 +1,173 @@ +/* ======================================================================== */ +/** + @file hwsw_rs_lib_hw.h + Header file for combo hw-sw resampler lib with hw defines +*/ + +/*========================================================================= +Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. +SPDX-License-Identifier: BSD-3-Clause +========================================================================= */ + +#ifndef HWSW_RS_LIB_HW_H +#define HWSW_RS_LIB_HW_H + +/*------------------------------------------------------------------------ + * Include files + * -----------------------------------------------------------------------*/ + +#include "posal.h" +#if ((defined __hexagon__) || (defined __qdsp6__)) +#include "hwd_devcfg.h" +#endif +#include "hw_rs_lib.h" + + +/*------------------------------------------------------------------------ + * Macros, Defines, Type declarations + * -----------------------------------------------------------------------*/ +static const uint32_t HW_RS_MAX_FREQ = 384000; +static const uint32_t HW_RS_MAX_CHANNELS = 32; + +// OSR for Down-sampling is 24. +static const uint32_t HW_RS_MAX_DOWNSAMPLE_RATIO = 24; + +// Highest OSR for Up-sampling is 160. +static const uint32_t HW_RS_MAX_UPSAMPLE_RATIO = 160; + +static const uint32_t HW_RS_BUFFER_DELAY_US = 10000; // 10 MS +static const uint32_t HW_RS_BUFFER_DELAY_MS = HW_RS_BUFFER_DELAY_US / 1000; // 10 MS +static const int32_t HW_RS_FRAME_SIZE_US = HW_RS_BUFFER_DELAY_US; // 10 MS +static const uint32_t HW_RS_PROCESS_TIME_THRESHOLD = 7000; + +/*------------------------------------------------------------------------ + * Structure definitions + * -----------------------------------------------------------------------*/ + +// Hw Resampler Input-Output Media info Structure. +typedef struct hwsw_rs_hw_resampler_media_info +{ + int32 in_sampling_rate; + int32 out_sampling_rate; + uint16 bits_per_sample; + uint16 num_channels; +} hwsw_rs_hw_resampler_media_info_t; + +// Hw Resampler job signal Structure +typedef struct hwsw_rs_hw_resampler_job_signal +{ + posal_signal_t sig_job_ptr; // signal for the job submitted + posal_channel_t channel_job_ptr; // channel used to embed the signal + uint32 sig_mask; +} hwsw_rs_hw_resampler_job_signal_t; + +// Hw Resampler Internal Buffer Structure +typedef struct hwsw_rs_hw_resampler_internal_buf +{ + capi_buf_t buf[HW_RS_MAX_CHANNELS]; + uint32_t start_byte_index; // read byte index in each buffer + uint32_t valid_bytes; // valid bytes in each buffer + uint32_t frame_size_in_bytes; // bytes allocated per buffer +} hwsw_rs_hw_resampler_internal_buf_t; + +// Hw Resampler Buffer Manager. +typedef struct hwsw_rs_hw_resampler_buffer_mgr +{ + // Buffer where data is copied from ext input buffer + hwsw_rs_hw_resampler_internal_buf_t *data_input_buffer_ptr; + + // Input Buffer which is used to call the Hw resampler process + hwsw_rs_hw_resampler_internal_buf_t *process_input_buffer_ptr; + + // Buffer from data is copied to ext output buffer + hwsw_rs_hw_resampler_internal_buf_t *data_output_buffer_ptr; + + // Output Buffer which is used to call the Hw resampler process + hwsw_rs_hw_resampler_internal_buf_t *process_output_buffer_ptr; + + // Pointer to the memory allocated for buffers. + int8_t *mem_ptr; + + // size of allocated memory. + uint32_t mem_size; + + // Total normalized input samples consumed by the module + uint32_t input_samples_consumed; + + // Total normalized output samples produced by the module + uint32_t output_samples_produced; + + // number of samples to consume precalculated before process + uint32_t input_samples_to_consume; + + // number of samples to produce precalculated before process + uint32_t output_samples_to_generate; +} hwsw_rs_hw_resampler_buffer_mgr_t; + +// Hw Resampler Structure +typedef struct hwsw_rs_hw_resampler +{ + /* + * This flag is used to identify if Hw resampler is supported by Chip. + * It can also be false if client want to force-disable the Hw resampler. + */ + bool_t hw_resampler_supported; + + /* + * This is in an internal flag which is set only when both hw resampler can be used + * AND client has asked to use it through the set param api + */ + bool_t use_hw_rs; + + /* + * This flag is used to identify if a job is completed or not. + * If it is true then it doesn't always mean that job is pending at Hw resampler. + * It is possible that Hw resampler has completed the job and raised the signal through + * callback function. But caller hasnt cleared that signal yet. + */ + bool_t is_job_pending; + + // Flag to identify if session is suspended or not. + bool_t is_session_suspended; + + /* + * It accumulates the minimum estimated time(at max clock) of all those jobs + * which are closed because of the session suspend. + * We intend to reopen these jobs once session is resumed. + * This variable is used to correct the Time threshold. + */ + posal_atomic_word_t *estimated_time_of_all_suspended_session; + + // Minimum estimated time(at max clock) to complete the current job only + uint32_t minimum_estimated_time_of_this_session; + + // Signal, used to identify if a job is completed by Hw resampler. + hwsw_rs_hw_resampler_job_signal_t job_signal; + + // Media info for which Hw resampler job is need to be opened. + hwsw_rs_hw_resampler_media_info_t media_info; + + // Buffer manager which keeps the pointer to all the process and data buffers. + hwsw_rs_hw_resampler_buffer_mgr_t buf_mgr; + + // Mutex, to avoid simultaneous access of Hw resampler api. + hw_resampler_mutex_t *mutex_info; + + // Hw resampler handle. + //rs_drv_handle_t rs_handle; + + // Hw resampler job config structure. + //rs_drv_job_cfg_t rs_job_cfg; + + // heap id, to allocate the memory for buffers. + uint32_t heap_id; + + // keeps record of number of Hw jobs currently opened. + posal_atomic_word_t *phwrs_resource_state; + +#if HW_RS_JOB_COMPLETION_TIME + uint64_t job_complete_time; +#endif +} hwsw_rs_hw_resampler_t; + +#endif // HWSW_RS_LIB_HW_H diff --git a/modules/processing/resamplers/dynamic_resampler/inc/multi_def_resampler.h b/modules/processing/resamplers/dynamic_resampler/inc/multi_def_resampler.h new file mode 100644 index 0000000..c74c3fe --- /dev/null +++ b/modules/processing/resamplers/dynamic_resampler/inc/multi_def_resampler.h @@ -0,0 +1,31 @@ +/*========================================================================= +Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. +SPDX-License-Identifier: BSD-3-Clause +========================================================================= */ + +#ifndef MULTI_DEF_RESAMPLER_H +#define MULTI_DEF_RESAMPLER_H + +#define resamp_output_latency resamp_output_latency_dynamic_resampler +#define resamp_input_latency resamp_input_latency_dynamic_resampler +#define resamp_size resamp_size_dynamic_resampler +#define select_filter select_filter_dynamic_resampler +#define init_upsamp init_upsamp_dynamic_resampler +#define straight_copy straight_copy_dynamic_resampler +#define gen_rs_filt_coefs_lin32 gen_rs_filt_coefs_lin32_dynamic_resampler +#define rsphase_update_frac rsphase_update_frac_dynamic_resampler +#define conv_32 conv_32_dynamic_resampler +#define init_dnsamp init_dnsamp_dynamic_resampler +#define conv_16X32 conv_16X32_dynamic_resampler +#define int_div_16 int_div_16_dynamic_resampler +#define int_div_32 int_div_32_dynamic_resampler +#define int_div_64 int_div_64_dynamic_resampler +#define frac_div_32 frac_div_32_dynamic_resampler +#define halfQ15 halfQ15_dynamic_resampler +#define oneQ15 oneQ15_dynamic_resampler +#define resampFiltQFac resampFiltQFac_dynamic_resampler +#define resampTotalPhGrp resampTotalPhGrp_dynamic_resampler +#define resampFiltLenGrp resampFiltLenGrp_dynamic_resampler + + +#endif /* MULTI_DEF_RESAMPLER_H */ diff --git a/modules/processing/resamplers/dynamic_resampler/inc/resampler_32b_ext.h b/modules/processing/resamplers/dynamic_resampler/inc/resampler_32b_ext.h new file mode 100644 index 0000000..299d12f --- /dev/null +++ b/modules/processing/resamplers/dynamic_resampler/inc/resampler_32b_ext.h @@ -0,0 +1,143 @@ +#include "multi_def_resampler.h" + +/*========================================================================= +Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. +SPDX-License-Identifier: BSD-3-Clause +========================================================================= */ + +#ifndef _RESAMPLER_32B_EXT_H_ +#define _RESAMPLER_32B_EXT_H_ + +#ifdef __cplusplus +extern "C" { +#endif +/*===========================================================================* + * INCLUDE HEADER FILES * + *===========================================================================*/ +#include "audio_basic_op.h" + +/*===========================================================================* + * Constants. * + *===========================================================================*/ + +/* For windows SPF builds we use the original resampler and + * not the qdsp or xtensa optimised versions */ +#if !((defined __hexagon__) || (defined __qdsp6__)) && !defined(__XTENSA__) +#define RESAMPLER_ORIGINAL +#endif + +#if defined(__XTENSA__) +#define RESAMPLER_C_OPT +#endif + +#ifndef RESAMPLER_ORIGINAL + #ifndef RESAMPLER_C_OPT + #define RESAMPLER_ASM_OPT + #endif +#endif + +#ifdef RESAMPLER_ORIGINAL + #undef RESAMPLER_C_OPT +#endif + + +/* Comprehensive analysis report covers the conversion cases from 8 to 384 kHz. + * It is safe to claim that the maximum supported frequency and down-conversion ratio + * are 384 kHz and 48, respectively. To claim higher frequency or conversion ratio, + * the comprehensive analysis must be conducted before claiming + */ +#define MAX_FREQ 1536000 +#define MAX_DOWN_RATIO 192 +#define ASYNC_RANGE 20 +#define MASK_QUALITY 0xF +#define MASK_INTERP 0x1 +#define MASK_ASYNC 0x1 +#define MASK_CHANNEL 0xF +#define MASK_INPUT 0x1 +#define MASK_BITS 0x1 +#define MASK_COEF 0x2 +#define MASK_DYNAMIC 0x1 +#define MASK_DELAY 0x1 +#define MASK_FILT_SEL 0x1 +#define MASK_DATA_QFACT 0x1 +#define SHIFT_QUALITY 6 +#define SHIFT_INTERP 10 +#define SHIFT_ASYNC 11 +#define SHIFT_CHANNEL 2 +#define SHIFT_INPUT 1 +#define SHIFT_BITS 0 +#define SHIFT_DYNAMIC 12 +#define SHIFT_DELAY 13 +#define SHIFT_FILTER_SELECT 14 +#define SHIFT_DATA_Q_FACTOR 15 + + +typedef enum +{ + MODE, /* SRC mode word */ + IN_FREQ, /* input frequency */ + OUT_FREQ, /* output frequency */ + RS_OSR, /* up-sampler OSR */ + RS_FLEN, /* up-sampler filter length */ + RS_MEM_LEN, /* up-sampler memory length */ + RS_QFAC, /* up-sampler filter coefficient q-factor */ + RS_SYMMETRIC, /* up-sampler filter symmetry indicator */ + MODE_COEF_BIT, /* coefficient bit width indicator, 16 bit input */ + /* 0 -- 16 bit filter coefficients */ + /* 1 -- 32 bit filter coefficients */ + MODE_INTERP, /* interpolation mode indicator */ + /* 0 -- linear interpolation */ + /* 1 -- quadratic interpolation */ + MODE_ASYNC, /* asynchronous indicator */ + NUM_CHAN, /* number of channels */ + MODE_DYNAMIC, /* dynamic resampler indicator */ + MODE_DYNAMIC_DELAY, /* delay mode for dyanmic resampler */ + /* 0 -- low delay with visible transitional phase distortion */ + /* 1 -- high delay with smooth transition */ + MODE_FILT_SEL_FLAG, + /* 0 -- disable new filter selection */ + /* 1 -- enable new filter selection */ + DATA_Q27_FLAG, + /* 0 -- not Q27 for 32bit data */ + /* 1 -- Q27 only when data width is 32 bits */ + + +} PARAM; + +/*===========================================================================* + * FUNCTION DECLARATIONS FOR BOTH UP-SAMPLER AND DOWN-SAMPLER * + *===========================================================================*/ +void resamp_size(int32 *par, int16 *pRsSize, int16 *pRsMemSize); +void* select_filter(int32 minFreq, int32 maxFreq, int32 async, int32* param); +int16 dynamic_resamp_output_latency(void *pRS); +int16 dynamic_resamp_input_latency(void *pRS); +int32 resamp_fixedin(void*, void**, void**, int32); +int32 resamp_fixedout(void*, void**, void**, int32); +int32 resamp_calc_fixedin(void*, int32); +int32 resamp_calc_fixedout(void*, int32); +void resamp_overshoot_protection_q27(void**, int32, int32); + +uint32 resamp_memreq_opt_coef_table(void*); +int32 resamp_gen_opt_coef_table(void*,void*,uint32); + +/*===========================================================================* + * FUNCTION DECLARATIONS FOR UP-SAMPLER * + *===========================================================================*/ +int32 init_upsamp(void*, int32*, void*, void*, void**); + +/*===========================================================================* + * FUNCTION DECLARATIONS FOR DOWN_SAMPLER * + *===========================================================================*/ +int32 init_dnsamp(void*, int32*, void*, void*, void**); + +/*===========================================================================* + * FUNCTION DECLARATIONS FOR DYNAMIC_RESAMPLER * + *===========================================================================*/ +int32 init_dynamic_resamp(void*, int32*, void*, void*, void**); +int32 reinit_dynamic_resamp(void* pStruct, int32 inFreq); + +#ifdef __cplusplus +} +#endif + +#endif /* _RESAMPLER_32B_EXT_H_ */ diff --git a/modules/processing/resamplers/dynamic_resampler/inc/rs_common.h b/modules/processing/resamplers/dynamic_resampler/inc/rs_common.h new file mode 100644 index 0000000..8726853 --- /dev/null +++ b/modules/processing/resamplers/dynamic_resampler/inc/rs_common.h @@ -0,0 +1,96 @@ +#include "multi_def_resampler.h" + +/*========================================================================= +Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. +SPDX-License-Identifier: BSD-3-Clause +========================================================================= */ + +#ifndef _RS_COMMON_H_ +#define _RS_COMMON_H_ + +#include "audio_basic_op.h" + +/*===========================================================================* + * TYPE DEFINITIONS * + *===========================================================================*/ + + +/*===========================================================================* + * constants * + *===========================================================================*/ +#define Q15_SHIFT 15 +#define Q20_SHIFT 20 +#define Q23_SHIFT 23 +#define MAX_Q15 32767 +#define MIN_Q15 -32768 + + +/*===========================================================================* + * EXTERNAL VARIABLES * + *===========================================================================*/ +extern const int16 oneQ15, halfQ15; +extern const int32 oneQ23, halfQ23; + +/*===========================================================================* + * STRUCTURES * + *===========================================================================*/ +typedef struct +{ + int64 totalPhStepCoef;/* Phase update step size for filter coefficient*/ + int32 fracPhMask; + int32 fracPhStepCoef; /* Phase update step size for filter coefficient*/ + int32 inFreq; /* input sampling rate */ + int32 outFreq; /* output sampling rate */ + int32 maxFreq; + int32 totalPhCap; /* cap of total phase */ + int32 totalPhStep; /* phase update step size for starting point */ + int32 curTotalPh; /* total phase for starting point */ + int32 fracPhStep; + int32 curFracPh; /* fractional phase for starting point, no init */ + int32 invMaxFreq; /* inverse of input frequency */ + int16 intPhStepCoef; /* Phase update step size for filter coefficient*/ + int16 sampStep; /* phase update step size for starting point */ + int16 intPhStep; + int16 curIntPh; /* integer phase for starting point, no init */ + int16 invMaxFreqShift;/* shifting factor of invInFreq */ + int16 totalIntPh; /* filter over sampling ratio */ + int16 convIndex; /* filter index for convolution */ + int16 loadIndex; /* filter index for loading filter memory */ + int16 async; /* Asynchronous mode indicator */ + int16 intRatio; /* Integer resampling ratio indicator */ +} RSPhase; + +typedef struct +{ + void *pMem; /* point to filter memory */ + void *pCoef; /* point to filter coefficient */ + void *pCoef2; + void *pGenCoef; /* pointer to generated filter coefficients */ + int32 halfQfac; /* one half for a given Q factor */ + int32 coefLen; /* length of filter coefficient */ + int32 coefLen2; /* half length of filter coefficient if filter */ + /* coefficients is symmetric. */ + int32 coefLen3; /* length of filter coefficient */ + int32 coefLen4; /* half length of filter coefficient if filter */ + /* coefficients is symmetric. */ + int16 coefStep; /* convolution step size of filter coefficient */ + int16 coefStep2; + int16 coefStart; + int16 qFac; /* Qfactor of the filter coefficients */ + int16 memLen; /* length of filter memory */ + int16 memStep; /* convolution step size of filter memmory */ +} Filters; + +/*===========================================================================* + * FUNCTION DECLARATIONS * + *===========================================================================*/ +int16 int_div_16(int16 num16, int16 den16, int16* rem16); +int32 int_div_32(int32 num32, int32 den32, int32* rem32); +int64 int_div_64(int64 num64, int64 den64, int64* rem64); +int32 frac_div_32(int32 num, int32 den, int16 Qfac); +int32 fxp_inv_32(int32 num, int16 qFac, int16 *shift); + +#endif /* _RS_COMMON_H_ */ +/*===========================================================================* + * End of rs_common.h * + *===========================================================================*/ diff --git a/modules/processing/resamplers/dynamic_resampler/inc/rs_driver.h b/modules/processing/resamplers/dynamic_resampler/inc/rs_driver.h new file mode 100644 index 0000000..297fa42 --- /dev/null +++ b/modules/processing/resamplers/dynamic_resampler/inc/rs_driver.h @@ -0,0 +1,460 @@ +/* +============================================================================ + +FILE: rs_driver.h + +DESCRIPTION: Resampler HW driver header file. + +/*========================================================================= +Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. +SPDX-License-Identifier: BSD-3-Clause +========================================================================= */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif //__cplusplus + +#ifndef RS_DRIVER_H +#define RS_DRIVER_H + +#include "typedef_drivers.h" +#include "posal.h" +#include "ar_defs.h" +#include "qurt.h" +#include "hwd_devcfg.h" + +//#define DRIVER_DEBUG 1 + +#define MAX_MULTI_RESAMP_CHANNELS 32 + +/** + * The following define is for HW Interrupt triger event type + */ + +typedef enum _rs_job_event_type +{ + RS_JOB_EVEN_INVALID = -1, + RS_JOB_EVEN_JOB_DONE = 1, + RS_JOB_EVEN_JOB_ERROR = 2, +} rs_job_event_type; + +/** +* The following define is for HW resampler job status +*/ +typedef enum _rs_job_status_type +{ + RS_JOB_STATUS_INVALID = -1, + RS_JOB_STATUS_ONGOING = 0, //job not finish yet + RS_JOB_STATUS_DONE, //job done + RS_JOB_STATUS_AHB_ERROR, //job error + RS_JOB_STATUS_SAMPLE_COUNT_ERROR, //job error +} rs_job_status_type; + +/** +* The following define is for HW resampler operation mode. +*/ +typedef enum _rs_operation_type +{ + RS_OPERATION_INVALID = -1, + RS_OPERATION_UPSAMPLING = 1, //up sampling only + RS_OPERATION_DNSAMPLING, //down sampling only + RS_OPERATION_RESAMPLING, //up sampling first then down sampling = Both, + //for example of 48 KHz to 44.1 KHz: upsampling 48 K to 88.2 K first then divide it by 2 +} rs_operation_type; + +/** +* The following define is for HW resampler operation priority. +*/ +typedef enum _rs_priority_type +{ + RS_PRIORITY_INVALID = -1, + RS_PRIORITY_LOW = 0, + RS_PRIORITY_HIGH, +} rs_priority_type; + +/** +* The following define is for HW resampler job submit type. +*/ +typedef enum _rs_job_submit_type +{ + RS_JOB_INVALID = -1, + RS_JOB_CANCEL = 0, + RS_JOB_SUBMIT, +} rs_job_submit_type; + +/** +* The following define is for HW resampler coeffcient source type. +*/ +typedef enum _rs_coeff_src_type +{ + RS_COEFF_SRC_INVALID = -1, + RS_COEFF_SRC_ROM = 0, + RS_COEFF_SRC_CUSTOMER, //coeff array is put in RAM +} rs_coeff_src_type; + +/** +* The following define is for HW resampler coeffcient type. It is valid only when coeff source is ROM. +*/ +typedef enum _rs_coeff_quality_type +{ + RS_COEFF_QUALITY_INVALID = -1, + RS_COEFF_QUALITY_HQ = 0, //coeff bit width = 16 + RS_COEFF_QUALITY_SHQ, //coeff bit width = 20 +} rs_coeff_quality_type; + +/** +* The following define is for HW resampler coeffcient type. It is valid only when coeff source is ROM. +*/ +typedef enum _rs_sample_width_type +{ + RS_SAMPLE_WIDTH_INVALID = -1, + RS_SAMPLE_WIDTH_16 = 0, + RS_SAMPLE_WIDTH_32, +} rs_sample_width_type; + +/** +* The following define is for HW upsampler filter type. +*/ +typedef enum _rs_upsamp_filter_type +{ + RS_UPSAMPLER_FILTER_ID_INVALID = -1, + RS_UPSAMPLER_FILTER_ID_1344 = 0, + RS_UPSAMPLER_FILTER_ID_8960, + RS_UPSAMPLER_FILTER_ID_MAX, +} rs_upsamp_filter_type; + +/** +* The following define is for HW downsampler filter type. +*/ +typedef enum _rs_dnsamp_filter_type +{ + RS_DNSAMPLER_FILTER_ID_INVALID = -1, + RS_DNSAMPLER_FILTER_ID_1344 = 0, + RS_DNSAMPLER_FILTER_ID_8960, //not supported + RS_DNSAMPLER_FILTER_ID_MAX, +} rs_dnsamp_filter_type; + +/** +* The following define is for HW resampler interpolation type. +*/ +typedef enum _rs_interp_type +{ + RS_INTERP_MODE_INVALID = -1, + RS_INTERP_MODE_LINEAR = 0, + RS_INTERP_MODE_QUAD, + RS_INTERP_MODE_MAX, +} rs_interp_type; + +/** +* The following define is for HW resampler force interpolation type. +*/ +typedef enum _rs_force_interpol_type +{ + RS_FORCE_INTERPOL_INVALID = -1, + RS_FORCE_INTERPOL_OFF = 0, //normal operation + RS_FORCE_INTERPOL_ON, //force fractional operation, used in streaming case +} rs_force_interpol_type; + + +/** +* The following define is for HW resampler dynamic resampler mode. +*/ +typedef enum _rs_dynamic_resampler_type +{ + RS_DYNAMIC_RESAMPLER_INVALID = -1, + RS_DYNAMIC_RESAMPLER_OFF = 0, //normal operation + RS_DYNAMIC_RESAMPLER_ON, //dynamic resampler mode +} rs_dynamic_resampler_type; + + +/** \brief resampler driver upsampler parameter struct */ +typedef struct _struct_rs_upsampler +{ + /** integer phase step size */ + uint16_t int_phase_step_size; // = samp_inc; + /** current integer phase */ + uint16_t cur_int_phase; // = samp; + /** fraction phase step size */ + uint32_t frac_phase_step_size; // = sub_samp_inc; + /** current fraction phase */ + uint32_t cur_frac_phase; // = sub_samp; + /** upsampler out freq */ + uint32_t out_freq; + /** fraction const */ + uint32_t frac_const; + /** shift const */ + uint16_t shift_const; + /** up sampler filter memory size */ + int16_t up_filt_mem_size; +} struct_rs_upsampler; + +/** \brief resampler driver downsampler parameter struct */ +typedef struct _struct_rs_dnsampler +{ + /** upsampler structure */ + struct_rs_upsampler up_samp_struct; + /** down sampler filter memory size */ + int16_t dn_filt_mem_size; + /** down sampler ratio. input stream is divided by this factor */ + int16_t ds_factor; + /** down sampler step size - number of coefficeints that needs to be skipped*/ + int16_t ds_step; + /** location of the end of the UpSampler filter = ptrDnSampStruct->filtMemOffset */ + int16_t up_sampler_flt_fptr; //UPSAMPLER ONLY up_sampler_flt_fptr = upsamp.filtMemOffset //DOWN SAMPLER ONLY up_sampler_flt_fptr = 0 //RESAMPLER up_sampler_flt_fptr = upsamp.filtMemOffset + /** location of next sample generated by UPsampler */ + int16_t up_sampler_flt_optr; //UPSAMPLER ONLY up_sampler_flt_optr = upsamp.outputOffset //DOWN SAMPLER ONLY up_sampler_flt_optr = 0 //RESAMPLER up_sampler_flt_optr = dnsamp.inputOffset + /** location of next input sample read from memory = ptrDnSampStruct->inputOffset */ + int16_t resampler_flt_iptr; //UPSAMPLER ONLY resampler_flt_iptr = upsamp.inputOffset //DOWN SAMPLER ONLY resampler_flt_iptr = dnsamp.inputOffset //RESAMPLER resampler_flt_iptr = upsamp.inputOffset + /** location of the end of the DnSampler Filter */ + int16_t dn_sampler_flt_fptr; //UPSAMPLER ONLY dn_sampler_flt_fptr = 0 //DOWN SAMPLER ONLY dn_sampler_flt_fptr = dnsamp.filtMemOffset //RESAMPLER dn_sampler_flt_fptr = dnsamp.filtMemOffset +}struct_rs_dnsampler; + +/** \brief resampler driver customer coeff parameter struct */ +typedef struct _struct_rs_cust_coeff +{ + /** Starting address of custom coefficients */ + uint32_t cust_coeff_base_addr; + /** customer coeff length */ + uint32_t cust_coeff_length; + /** up sampler customer coeff offset */ + uint16_t us_cust_coeff_cfg_offset; //in 16 bit word unit + /** up sampler customer coeff filter taps */ + uint16_t us_cust_coeff_cfg_filter_taps; + /** up sampler customer coeff polyphase */ + uint16_t us_cust_coeff_cfg_plyphase; + /** down sampler customer coeff offset */ + uint16_t ds_cust_coeff_cfg_offset; //in 16 bit word unit + /** down sampler customer coeff filter taps */ + uint16_t ds_cust_coeff_cfg_filter_taps; + /** need driver to allocate internal buffer for customer coeffcients. 1 - need 0 - not needed */ + uint16_t internal_coeff_buf_needed; +}struct_rs_cust_coeff; + + +/** \brief resampler driver hw resampler job parameter struct */ +typedef struct _struct_rs_job_config +{ + /** pointer to down sampler structure */ + struct_rs_dnsampler *ptr_dn_samp_struct; + /** pointer to customer coeff structure */ + struct_rs_cust_coeff *ptr_cust_coeff; + /** operation type: Up/Down/Both */ + rs_operation_type operation; + /** priority: High/Low */ + rs_priority_type prio; + /** number of channels, 1-8 */ + uint16_t num_channels; + /** sampler bit width: 16bit/24bit */ + rs_sample_width_type data_width; + /** coeff quality: 16bit/17bit/18bit */ + rs_coeff_quality_type coeff_width; + /** submit this job or not: 1:submit 0:cancel */ + rs_job_submit_type submit_job; + /** upsampler filter ID */ + rs_upsamp_filter_type upsamp_filt_id; + /** dnsampler filter ID */ + rs_dnsamp_filter_type dnsamp_filt_id; + /** hw resampler interpolation mode*/ + rs_interp_type interp_mode; + /** hw resampler sync mode*/ + rs_force_interpol_type force_interpol; + /** hw resampler dynamic resampler mode*/ + rs_dynamic_resampler_type dynamic_rs; + /** coeff source: ROM and RAM */ + rs_coeff_src_type coeff_src; + /** for internal input/output buffer 0: don't need, 1: need */ + uint16_t internal_buf_needed; + /** the max num of input samples per channel, valid if internal_buf_needed = 1, 4096 is maxium */ + /** if this field is set as zero, driver will set it as 4096 */ + uint16_t input_num_sample_max; +} struct_rs_job_config; + +/** \brief resampler driver buffer parameter struct */ +typedef struct _struct_rs_buf +{ + /** buffer pointer. all samples in filt memory are uint32_t format */ + uint32_t buf_ptr; + /** buffer length in bytes */ + uint16_t buf_length; +} struct_rs_buf; + +/** \brief HW Resampler driver for stream structure data updating */ +typedef struct _struct_rs_stream_data +{ + /** integer phase step size */ + uint16_t int_phase_step_size; // = samp_inc; + /** fraction phase step size */ + uint32_t frac_phase_step_size; // = sub_samp_inc; + /** current fraction phase */ + uint32_t cur_frac_phase; // = sub_samp; + /** fraction const */ + uint32_t frac_const; + /** shift const */ + uint16_t shift_const; + /** upsampler out freq */ + uint32_t med_freq; +} struct_rs_stream_data; + +/** \brief HW Resampler driver callback data type */ +typedef struct _struct_rs_cb_data +{ + /** Resampler driver return result */ + /** AR_EOK SUCCESS others FAILURE */ + ar_result_t result; + /** job ID */ + uint16_t job_id; + /** Error type */ + rs_job_status_type hw_error_type; +} struct_rs_cb_data; + +/** \brief HW Resampler driver callback function type */ +typedef void (*rs_job_event_callback_func) +( + /** A void pointer to user defined context */ + void *user_ctx_ptr, + /** Callback event type */ + rs_job_event_type event, + /** Callback data type */ + void *cb_data_ptr +); + + +/** \brief resampler driver hw resampler job timing info struct */ +typedef struct _struct_rs_job_timing_info +{ + int32_t num_input_sample_per_frame; + int32_t num_output_sample_per_frame; + /** frame size in microsecond unit. the frame szie should be matched with the num input sample sent via rs_send_job() */ + /** for example, for 48000 Hz to 44100 Hz resampling case, if frame_size = 10000 ns, then the num_input_sample for every frame is 48. */ + int32_t frame_size; + /** the time in microsecond unit client requires hw resampler done the job for this frame */ + int32_t budget_time_to_done; + /** the time in microsecond unit that hw resampler can finish the job. it is returned back from hw rs driver*/ + int32_t estimated_time_to_done; +} struct_rs_job_timing_info; + +/** +* This resampler driver init function and it is only called once. +* +* @return Success/failure of resampler init. +*/ +ar_result_t rs_init(void); + +/** +* This resampler driver deinit function and it is only called once. +* +* @return Success/failure of resampler deinit. +*/ +ar_result_t rs_deinit(void); + +/** +* This function is for resampler job init. +* +* @param[in] px - processor ID: 0 QDSP 1 APPLICATION PROCESSOR. +* @param[in] job_struct - point to job configure struct. +* @param[in] cb_func - callback function when job is done +* @param[in] user_ctx - user defined ctx used in callback function +* @return job ID, valid job ID is 0 to 15. +*/ +int16_t rs_init_job(uint16_t px, struct_rs_job_config* job_struct, rs_job_event_callback_func cb_func, void* user_ctx); + +/** +* This function is for sending the input samplers to driver +* +* @param[in] px - processor ID: 0 QDSP 1 APPLICATION PROCESSOR. +* @param[in] job_id - job ID. +* @param[in] in_samples - the num of input samples +* @param[in] out_samples - the expected num of output samples. +* @param[in] in_buf_ptr - pointer to an array which specific the input buffer pointer for every channel. +* @param[in] out_buf_ptr - pointer to an array which specific the output buffer pointer for every channel. +* @return Success/failure of sending this frame job. +*/ +ar_result_t rs_send_job(uint16_t px, int16_t job_id, uint16_t in_samples, uint16_t out_samples, int32_t** in_buf_ptr, int32_t** out_buf_ptr); + +/** +* This function is for reset hw resampler +* It is used in fast farward/rewind cases +* The frame sent via rs_send_job() is treated as the first frame after rs_reset_job() called +* +* @param[in] px - processor ID: 0 QDSP 1 APPLICATION PROCESSOR. +* @param[in] job_id - job ID. +* @param[in] job_struct - point to job configure struct. Some parameters are updated +* @return Success/failure of sending this frame job. +*/ +ar_result_t rs_reset_job(uint16_t px, int16_t job_id, struct_rs_job_config* job_struct); + +/** +* This function is for confirming the job. It is called after client getting the callback function. +* The output samples will be in output buffer after this function call +* +* @param[in] px - processor ID: 0 QDSP 1 APPLICATION PROCESSOR. +* @param[in] job_id - job ID. +* @param[in/out] samples_processed - num of samples has been processed +* @return job status. +*/ +rs_job_status_type rs_confirm_job(uint16_t px, int16_t job_id, uint32_t *samples_processed); + +/** +* This function is for uninit this job +* +* @param[in] px - processor ID: 0 QDSP 1 APPLICATION PROCESSOR. +* @param[in] job_id - job ID. +* @return void +*/ +void rs_uninit_job(uint16_t px, int16_t job_id); + +/** +* This function is for querying the stream configuration +* +* @param[in] job_id - job ID. +* @param[in] ptr_rs_dnsamp - point to struct_rs_dnsampler struct. +* @return Success/failure of query. +*/ +ar_result_t rs_get_stream_config(int16_t job_id, struct_rs_dnsampler *ptr_rs_dnsamp); + +/** +* This function is for setting the stream configuration +* +* @param[in] job_id - job ID. +* @param[in] ptr_rs_dnsamp - point to struct_rs_dnsampler struct. +* @return Success/failure of query. +*/ +ar_result_t rs_set_stream_config(int16_t job_id, struct_rs_dnsampler *ptr_rs_dnsamp); + +/** +* This function is for updaing the stream configuration +* +* @param[in] job_id - job ID. +* @param[in] ptr_rs_str_data - point to struct_rs_stream_data struct. +* @param[in] flag - for internal use only, 6 - update all parameters in struct_rs_stream_data; others: only int_phase_step_size and frac_phase_step_size +* @return Success/failure of query. +*/ +ar_result_t rs_set_stream_data(int16_t job_id, struct_rs_stream_data *ptr_rs_str_data, uint16_t flag); + +/** + * This function is for estimating the core clk + * + * @param[in] job_id the job_id of the stream + * @param[in] ptr_job_timing_info pointer to struct struct_rs_job_timing_info + * @return AR_EOK if succesful, else failure + */ +ar_result_t rs_estimate_core_clks(int16_t job_id, struct_rs_job_timing_info* ptr_job_timing_info); + + +/** + * This function is for querying hw resampler version + * + * @return hw resampler version + */ +uint32_t rs_query_hw_version(void); + +bool_t HwdDevCfg_HWSupport(HwdModuleType moduleType); +#endif + +#ifdef __cplusplus +} +#endif //__cplusplus + + diff --git a/modules/processing/resamplers/dynamic_resampler/inc/rs_driver_ext.h b/modules/processing/resamplers/dynamic_resampler/inc/rs_driver_ext.h new file mode 100644 index 0000000..22b4f8b --- /dev/null +++ b/modules/processing/resamplers/dynamic_resampler/inc/rs_driver_ext.h @@ -0,0 +1,410 @@ +/* +============================================================================ + +FILE: rs_driver_ext.h + +DESCRIPTION: Resampler HW driver extension header file. + +/*========================================================================= +Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. +SPDX-License-Identifier: BSD-3-Clause +========================================================================= */ + +#ifdef __cplusplus +extern "C" { +#endif //__cplusplus + +#ifndef RS_DRIVER_EXT_H +#define RS_DRIVER_EXT_H + +#include "posal.h" + +//The max number of jobs in queue is 8. +//The max number of jobs that resampler can process is 8. +//The number of IDs that can be used is 16 IDs. (valid ID is 0 to 15) + +#define MAX_NUM_OF_HW_RESAMPLER_JOB 8 +#define MAX_HW_RS_FREQ_SUPPORTED 192000 + +typedef void* rs_drv_handle_t; + +/** + * HW Resampler driver extension IOCtrl call property + */ +typedef enum _rs_drv_property_type +{ + RS_DRV_PROPERTY_UNDEFINED, + RS_DRV_PROPERTY_FORCE_INTERPOL_CONFIG = 0x1, + RS_DRV_PROPERTY_REGISTER_JOB_EVENT_CALLBACK = 0x2, //this is used only for async call + RS_DRV_PROPERTY_GET_REQUIRED_NUM_SAMPLES = 0x3, + RS_DRV_PROPERTY_ESTIMATE_CORE_CLK = 0x4, + RS_DRV_PROPERTY_DYNAMIC_RESAMPLER_CONFIG = 0x5, + + RS_DRV_PROPERTY_GLOBAL = 0x20000000, + RS_DRV_PROPERTY_HW_VERSION_QUERY, + RS_DRV_PROPERTY_MAX +}rs_drv_property_type; + +/** +* HW resampler driver extension upsampler filter type define. +*/ +typedef enum _rs_drv_upsamp_filter_type +{ + RS_DRV_UPSAMPLER_FILTER_ID_INVALID = -1, + RS_DRV_UPSAMPLER_FILTER_ID_1344 = 0, + RS_DRV_UPSAMPLER_FILTER_ID_8960, + RS_DRV_UPSAMPLER_FILTER_ID_MAX, +} rs_drv_upsamp_filter_type; + +/** +* HW resampler driver extension interpolation type define. +*/ +typedef enum _rs_drv_interp_type +{ + RS_DRV_INTERP_MODE_INVALID = -1, + RS_DRV_INTERP_MODE_LINEAR = 0, + RS_DRV_INTERP_MODE_QUAD, + RS_DRV_INTERP_MODE_MAX, +} rs_drv_interp_type; + +/** +* HW resampler driver extension force interpolation type.define +*/ +typedef enum _rs_drv_force_interpol_type +{ + RS_DRV_FORCE_INTERPOL_INVALID = -1, + RS_DRV_FORCE_INTERPOL_OFF = 0, //normal operation + RS_DRV_FORCE_INTERPOL_ON, //force fractional operation, used in streaming case +} rs_drv_force_interpol_type; + + +/** +* HW resampler driver extension dynamic resampler enable type.define +*/ +typedef enum _rs_drv_dynamic_resampler_type +{ + RS_DRV_DYNAMIC_RESAMPLER_INVALID = -1, + RS_DRV_DYNAMIC_RESAMPLER_OFF = 0, //normal operation + RS_DRV_DYNAMIC_RESAMPLER_ON, //dynamic resampler is enabled. used for fast/slow playabck +} rs_drv_dynamic_resampler_type; + +/** +* The following define is to inform HW resampler driver extension which clock mismatch detect mode used by client. +* This is used only in force fraction operation mode +* +* HW Resampler is running on local system clcok. The input stream maybe from network which is running in remote clock domain +* The output stream maybe driven from audio render device which maybe in another clock domain +* For example, for no drift case, the input freq is 48000 Hz and output freq is 48000 Hz. +* If there is the drift between remote clock domain and local system clock, when we use the local system clock to count the incoming +* audio samples, we can get for example 48005 smaple in one sec +* If there is the drift between device render clock domain and local system clock, when we use the local system clock to count the output +* audio samples, we can get for example 47995 smaple in one sec +* So we have the below user cases: +* 1) The drift is happened in INPUT path only --> RS_DRV_SAMPLE_RATE_DRIFT_MODE_INPUT +* 2) The drift is happened in OUTPUT path only --> RS_DRV_SAMPLE_RATE_DRIFT_MODE_OUTPUT +* 3) The drift is happened in both --> RS_DRV_SAMPLE_RATE_DRIFT_MODE_BOTH +* +* Call sequence for FORCE_FRACTION_OPERATION mode +* 1. Configure job as FORCE_FRACTION_OPERATION mode and start job as normal +* 2. Client detect the input or/and output sample rate period +* 3. If drift is detected, +* 3-1 Use RS_DRV_PROPERTY_FORCE_INTERPOL_CONFIG to configure FORCE_FRACTION_OPERATION mode +* 3-2 Calculate the num of input/output smaples +* 3-3 Send the job +* +* for case like 8 Khz -> 6.1 KHz, the resampler should be set as +* 8 -> 24.4 -> 6.1 +* 8 -> 24.4 -> force fraction mode +* 24.4 -> 6.1 -> normal down sampleing +*/ +typedef enum _rs_drv_sample_rate_drift_mode +{ + RS_DRV_SAMPLE_RATE_DRIFT_MODE_INVALID = -1, + RS_DRV_SAMPLE_RATE_DRIFT_MODE_INPUT = 0, + RS_DRV_SAMPLE_RATE_DRIFT_MODE_OUTPUT, + RS_DRV_SAMPLE_RATE_DRIFT_MODE_BOTH, +} rs_drv_sample_rate_drift_mode; + +/** +* HW resampler driver extension prediction mode define for calculate num of input/output samples +*/ +typedef enum _rs_drv_num_sample_predict_mode +{ + RS_DRV_NUM_SAMPLE_PREDICT_MODE_INVALID = -1, + RS_DRV_NUM_SAMPLE_PREDICT_MODE_FIXED_INPUT = 0, //Baseing on num of input samples to calculate the num of output samples + RS_DRV_NUM_SAMPLE_PREDICT_MODE_FIXED_OUTPUT, //Baseing on num of output samples to calculate the needed num of input samples +} rs_drv_num_sample_predict_mode; + +/** +* HW Resampler driver extension IOCtl call property info struct.define +*/ +typedef struct +{ + /** Property call type*/ + rs_drv_property_type prop; + /** Data size of property call*/ + int32 data_size; + /** Data pointer of property call*/ + void *data_ptr; + /** Reserved*/ + int32 flags; +} rs_drv_ioctl_prop_info_t; + + +/** +* The structure define for a buffer that is passed into resampler driver extension functions +*/ +typedef struct +{ + int8* data_ptr; /**< Data pointer to the raw data. */ + + uint32 actual_data_len; /**< Length of the valid data (in bytes). This + field has multiple uses, depending on the + context:\n + - When this buffer is an input to a function, the caller specifies + the number of valid bytes using this field. The callee overwrites this + field with the number of bytes that were consumed. + + - When this buffer is the output of a function, the caller does not + use this field. The callee fills this field with the number of generated + valid bytes in the output buffer. + */ + uint32 max_data_len; /**< Total allocated size of the buffer (in + bytes). When a pointer of type rs_drv_buf_t + is passed as an input argument of a + function, this parameter specifies the + allocated size of the buffer and is + untouched by the callee. */ +} rs_drv_buf_t; + +/** +* The structure is for a list of rs_drv_buf buffers. It can be used for functions +* that require multiple input or output buffers. +* +* This structure consists of a pointer to an array of type rs_drv_buf. For +* multichannel de-interleaved data, each buffer contains data from all +* the channels in sequence. +*/ +typedef struct +{ + rs_drv_buf_t* buf_ptr; /**< Array of rs_drv_buf buffer elements. */ + uint32 bufs_num; /**< Number of elements in the array. */ +} rs_drv_buflist_t; + + +/** +* HW Resampler driver extension job open parameter struct.define +*/ +typedef struct _rs_drv_job_cfg +{ + /** input sample rate */ + int32 in_freq; + /** output sample rate */ + int32 out_freq; + /** number of channels, 1-8 */ + uint16 num_channels; + /** sampler bit width: 16bit/32bit */ + uint16 samp_bit_width; + /** hw resampler upsampler filter type*/ + rs_drv_upsamp_filter_type upsamp_filt_id; + /** hw resampler interpolation mode*/ + rs_drv_interp_type interp_mode; + /** hw resampler force interpol mode*/ + rs_drv_force_interpol_type force_interpol; + /** hw resampler dynamic resampler mode*/ + rs_drv_dynamic_resampler_type dynamic_resampler; + /** for internal input/output buffer 0: don't need, 1: need */ + /** if this is set as 0, client should guarantee that both buffer pointer and buffer max length for each channel */ + /** of input/output are 32 byte aligned. The memory for each channel is physical continus */ + /** if force_interpol is set as ON, set internal_buf_needed to 0 to avoid any internal buffer overwriten as the internal */ + /** input/output buffer sizes are calculated basing on the in_freq and out_freq which are not drifted */ + uint16 internal_buf_needed; + /** the max num of input samples per channel, valid if internal_buf_needed = 1, 4096 is maxium */ + /** if this field is set as zero, driver will set it as 4096 */ + uint16 input_num_sample_max; +} rs_drv_job_cfg_t; + +/** +* HW Resampler driver extension IOCtl property call - async mode configure struct.define +*/ +typedef struct +{ + rs_drv_sample_rate_drift_mode mode; + /** input sample rate */ + int32 in_freq; + /** output sample rate */ + int32 out_freq; +} rs_drv_ioctl_prop_force_interpol_cfg_t; + + +/** +* HW Resampler driver extension IOCtl property call - get the num of required samples +* For "Fixed Input Mode" -- mode = RS_DRV_NUM_SAMPLE_PREDICT_MODE_FIXED_INPUT +* num_sample_in : specific the num of input sample by client +* num_sample_out: the num of output sample returned by resampler driver +* For "Fixed Output Mode" -- mode = RS_DRV_NUM_SAMPLE_PREDICT_MODE_FIXED_OUTPUT +* num_sample_in : the num of input sample returned by resampler driver +* num_sample_out: specific the num of output sample by client +*/ +typedef struct +{ + rs_drv_num_sample_predict_mode mode; + /** num of input samples */ + uint32 num_sample_in; + /** num of output samples */ + uint32 num_sample_out; +} rs_drv_ioctl_prop_get_num_sample_t; + +/** +* HW Resampler driver extension IOCtl property call - estimate hw resampler core clk +*/ +typedef struct +{ + /** frame size in microsecond unit. the frame szie should be matched with the num input sample sent via rs_drv_job_process() */ + /** for example, for 48000 Hz to 44100 Hz resampling case, if frame_size = 10000 us, then the num_input_sample for every frame is 480. */ + int32 frame_size; + /** the time in microsecond unit client requires hw resampler done the job for this frame */ + int32 budget_time_to_done; + /** the time in microsecond unit that hw resampler can finish the job. it is returned back from hw rs driver*/ + int32 estimated_time_to_done; +} rs_drv_ioctl_prop_estimate_core_clk_t; + + +/** +* HW Resampler driver extension IOCtl property call - dynamic resampler mode configure struct.define +*/ +typedef struct +{ + /** input sample rate */ + int32 in_freq; + /** output sample rate */ + int32 out_freq; +} rs_drv_ioctl_prop_dynamic_resampler_cfg_t; + +/** +* This resampler driver driver extension init function and it is only called once. +* +* @return Success/failure of resampler init. +*/ +ar_result_t rs_drv_init(void); + +/** +* This resampler driver driver extension deinit function and it is only called once. +* +* @return Success/failure of resampler deinit. +*/ +ar_result_t rs_drv_deinit(void); + +/** +* This function is for resampler driver extension job init. +* @param[in/out] rs_drv_h - this will be the service entry handle returned to the caller. +* @param[in] job_cfg_ptr - point to job configure struct. +* @return Success/failure of sending this frame job. +*/ +ar_result_t rs_drv_job_open(rs_drv_handle_t *rs_drv_h, rs_drv_job_cfg_t* job_cfg_ptr); + +/** +* This function is for processing the input/output data +* +* @param[in] rs_drv_h - input handle. +* @param[in] in_buflist_ptr - pointer to rs_drv_buflist. +* @param[in] out_buflist_ptr - pointer to rs_drv_buflist. +* @return Success/failure of sending this frame job. +* Note: +* 1. Client should use RS_DRV_PROPERTY_GET_REQUIRED_NUM_SAMPLES property call to get the num of input samples (for fixed output mode) +* OR num of output samples (for fixed input mode) before calling this function +* 2. Supposing: +* the num of input samples = num_input_samples +* the num of output samples = num_output_samples +* 3. Then set the input and output buffer as below: +* in_buflist_ptr->buf_ptr->actual_data_len = num_input_samples * sample_size_in_bytes +* out_buflist_ptr->buf_ptr->actual_data_len = num_output_samples * sample_size_in_bytes +* +*/ +ar_result_t rs_drv_job_process(rs_drv_handle_t rs_drv_h, rs_drv_buflist_t* in_buflist_ptr, rs_drv_buflist_t* out_buflist_ptr); + +/** +* This function is for processing the input/output data +* +* @param[in] rs_drv_h - input handle. +* @return Success/failure of sending this frame job. +* Note: +* This function is used for fast farward/rewind cases +* The frame sent via rs_drv_job_process() is treated as the first frame after rs_drv_job_reset() called. +*/ +ar_result_t rs_drv_job_reset(rs_drv_handle_t rs_drv_h); + +/** +* This function is for uninit this job +* +* @param[in] rs_drv_h - input handle +* @return void +*/ +ar_result_t rs_drv_job_close(rs_drv_handle_t rs_drv_h); + +/** +* HW Resampler driver extension IOCtl function +* +* @param[in] rs_drv_h - input handle +* @param[in] prop_info_ptr point to property info structure +* @return Success/failure IOCtl call. +*/ +ar_result_t rs_drv_ioctl(rs_drv_handle_t rs_drv_h, rs_drv_ioctl_prop_info_t *prop_info_ptr); + +// +//async call APIs +// +/** + * The following define is for HW Interrupt triger event type + */ + +typedef enum _rs_drv_job_event_type +{ + RS_DRV_JOB_EVEN_INVALID = -1, + RS_DRV_JOB_EVEN_JOB_DONE = 1, + RS_DRV_JOB_EVEN_JOB_ERROR = 2, +} rs_drv_job_event_type; + +/** \brief HW Resampler driver callback function type */ +typedef void (*rs_drv_job_callback_func) +( + rs_drv_handle_t rs_drv_h, + /** Callback event type */ + rs_drv_job_event_type event, + /** Callback data type */ + void *cb_data_ptr +); + + +/** +* HW Resampler driver extension IOCtl property call - job event notification struct.define +*/ +typedef struct +{ + /** Event type, not used now*/ + rs_drv_job_event_type event; + /** Callback function */ + rs_drv_job_callback_func job_event_callback_func; + /** User defined context */ + void *user_ctx_ptr; +} rs_drv_ioctl_prop_job_event_notification_t; + + +/** +* This function is for getting the output data and how many input data consumed +* This API is just used for async call case +* +* @param[in] rs_drv_h - input handle. +* @param[in] in_buflist_ptr - pointer to rs_drv_buflist. +* @param[in] out_buflist_ptr - pointer to rs_drv_buflist. +* @return Success/failure of sending this frame job. +*/ +ar_result_t rs_drv_job_process_done(rs_drv_handle_t rs_drv_h, rs_drv_buflist_t* in_buflist_ptr, rs_drv_buflist_t* out_buflist_ptr); + +#endif + +#ifdef __cplusplus +} +#endif //__cplusplus + + diff --git a/modules/processing/resamplers/iir_resampler/api/iir_resampler_api.h b/modules/processing/resamplers/iir_resampler/api/iir_resampler_api.h new file mode 100644 index 0000000..c706de5 --- /dev/null +++ b/modules/processing/resamplers/iir_resampler/api/iir_resampler_api.h @@ -0,0 +1,176 @@ +/*========================================================================*/ +/** +@file iir_resampler_api.h + +@brief iir_resampler_api.h: This file contains the Module Id, Param IDs and configuration + structures exposed by the IIR Resampler Module. +*/ +/*======================================================================= + Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. + SPDX-License-Identifier: BSD-3-Clause +=========================================================================*/ + +#ifndef IIR_RESAMPLER_API_H +#define IIR_RESAMPLER_API_H + +/*------------------------------------------------------------------------ + * Include files + * -----------------------------------------------------------------------*/ +#include "module_cmn_api.h" + + /** + @h2xml_title1 {IIR RESAMPLER API} + @h2xml_title_agile_rev {IIR RESAMPLER API} + @h2xml_title_date {July 31, 2018} + */ + +/*============================================================================== + Constants +==============================================================================*/ +/** @ingroup ar_spf_mod_iir_resam_macros + Size of the stack for the IIR Resampler module. */ +#define IIR_RS_STACK_SIZE 1024 + +/** @ingroup ar_spf_mod_iir_resam_macros + Maximum ports for the IIR Resampler module. */ +#define IIR_RS_MAX_PORTS 1 + +/** @ingroup ar_spf_mod_iir_resam_macros + ID of the IIR RESAMPLER module. + + This module converts audio sampling rate. Uses IIR filters to do it. + Therefore it renders output with a lower Delay compared to FIR. + + @subhead4{Supported input sample rates} + - 8000 Hz + - 16000 Hz + - 24000 Hz + - 32000 Hz + - 48000 Hz + + @subhead4{Supported parameter IDs} + - #PARAM_ID_IIR_RESAMPLER_OUT_CFG @lstsp1 + - #PARAM_ID_IIR_RESAMPELR_VERSION + + @subhead4{Supported input media format ID} + - Data Format : FIXED_POINT @lstsp1 + - fmt_id : Don't care @lstsp1 + - Sample Rates : 8000, 16000, 24000, 32000, 48000 (Hz) @lstsp1 + - Number of channels : 1 to 128 (for certain products this module supports only 32 channels) @lstsp1 + - Channel type : 0 to 128 @lstsp1 + - Bits per sample : 16 @lstsp1 + - Q format : 15, 27, 31 @lstsp1 + - Interleaving : de-interleaved unpacked @lstsp1 + - Signed/unsigned : Don't care + */ +#define MODULE_ID_IIR_RESAMPLER 0x07001018 +/** + @h2xmlm_module {"MODULE_ID_IIR_RESAMPLER", + MODULE_ID_IIR_RESAMPLER} + @h2xmlm_displayName {"IIR Resampler"} + @h2xmlm_modSearchKeys{resampler, Audio, Voice} + @h2xmlm_description {ID of the IIR RESAMPLER module.\n + + -This module converts audio sampling rate. Uses IIR filters to do it. + Therefore it renders output with a lower Delay compared to FIR.\n + - Input Sample Rates Supported: 8000, 16000, 24000, 32000, 48000 (Hz) \n + - This module supports the following parameter IDs\n + - #PARAM_ID_IIR_RESAMPLER_OUT_CFG\n + - #PARAM_ID_IIR_RESAMPELR_VERSION\n + + * - Supported Input Media Format: \n + * - Data Format : FIXED_POINT \n + * - fmt_id : Don't care\n + * - Sample Rates : 8000, 16000, 24000, 32000, 48000 (Hz)\n + * - Number of channels : 1 to 128 (for certain products this module supports only 32 channels)\n + * - Channel type : 0 to 128\n + * - Bits per sample : 16\n + * - Q format : 15, 27, 31\n + * - Interleaving : de-interleaved unpacked\n + * - Signed/unsigned : Don't care} + + @h2xmlm_dataMaxInputPorts {IIR_RS_MAX_PORTS} + @h2xmlm_dataInputPorts {IN=2} + @h2xmlm_dataMaxOutputPorts {IIR_RS_MAX_PORTS} + @h2xmlm_dataOutputPorts {OUT=1} + @h2xmlm_supportedContTypes {APM_CONTAINER_TYPE_SC, APM_CONTAINER_TYPE_GC} + @h2xmlm_isOffloadable {true} + @h2xmlm_stackSize {IIR_RS_STACK_SIZE} + @h2xmlm_ToolPolicy {Calibration} + + @{ <-- Start of the Module --> +*/ +/*------------------------------------------------------------------------ + * Macros, Defines, Type declarations + * -----------------------------------------------------------------------*/ + +/** @ingroup ar_spf_mod_iir_resam_macros + Set param for sending output sample rate. + IIR Resampler will allocate channel memory and configure the algorithm + with this.*/ +#define PARAM_ID_IIR_RESAMPLER_OUT_CFG 0x08001033 +typedef struct param_id_iir_resampler_out_cfg_t param_id_iir_resampler_out_cfg_t; + +/** @h2xmlp_parameter {"PARAM_ID_IIR_RESAMPLER_OUT_CFG", PARAM_ID_IIR_RESAMPLER_OUT_CFG} + @h2xmlp_description {Specifies the output sample rate.IIR Resampler will allocate channel memory and + configure the algorithm with this.} + @h2xmlp_toolPolicy {Calibration; RTC } */ + +/** @ingroup ar_spf_mod_iir_resam_macros + Specifies the output sample rate.IIR Resampler will allocate channel memory and + configure the algorithm with this. */ + +#include "spf_begin_pack.h" +struct param_id_iir_resampler_out_cfg_t +{ + int32_t sampling_rate; + /**< Specifies the output sample rate; 0 is invalid. */ + + /**< @h2xmle_description {Specifies the output sample rate; 0 is invalid} + @h2xmle_rangeList {"PARAM_VAL_NATIVE"=-1; + "PARAM_VAL_UNSET"=-2; + "8 kHz"= 8000; + "16 kHz"=16000; + "24 kHz"=24000; + "32 kHz"=32000; + "48 kHz"=48000; + "96 kHz"=96000; + "192 kHz"=192000; + "384 kHz"=384000} + @h2xmle_default {-1} */ +} +#include "spf_end_pack.h" +; + + +/** @ingroup ar_spf_mod_iir_resam_macros + IIR Resampler module version. */ +#define PARAM_ID_IIR_RESAMPELR_VERSION 0x08001319 +typedef struct iir_resampler_lib_version_param_t iir_resampler_lib_version_param_t; + +/** @h2xmlp_parameter {"PARAM_ID_IIR_RESAMPELR_VERSION", PARAM_ID_IIR_RESAMPELR_VERSION} + @h2xmlp_description {The current version of the library is returned} + @h2xmlp_toolPolicy {RTC_READONLY} */ + +/** @ingroup ar_spf_mod_iir_resam_macros + The current version of the library is returned. */ + +#include "spf_begin_pack.h" +struct iir_resampler_lib_version_param_t +{ + uint32_t lib_version_lsb; + /**< Lower 32 bits of the 64-bit library version number. */ + + /**< @h2xmle_description {Lower 32 bits of the 64-bit library version number} */ + + uint32_t lib_version_msb; + /**< Higher 32 bits of the 64-bit library version number. */ + + /**< @h2xmle_description {Higher 32 bits of the 64-bit library version number} */ +} + +#include "spf_end_pack.h" +; + +/** @} <-- End of the Module -->*/ +#endif /* IIR_RESAMPLER_API_H */ diff --git a/modules/processing/resamplers/iir_resampler/bin/arm/libiir_resampler.a b/modules/processing/resamplers/iir_resampler/bin/arm/libiir_resampler.a new file mode 100644 index 0000000000000000000000000000000000000000..84c93cddffae3cbeb659912b9a275e4f88d21f90 GIT binary patch literal 248210 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

QXC#K+RSHW7LTp_*KXMR3GiR@X_As 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 Cq10{w literal 0 HcmV?d00001 diff --git a/modules/processing/resamplers/iir_resampler/build/CMakeLists.txt b/modules/processing/resamplers/iir_resampler/build/CMakeLists.txt new file mode 100644 index 0000000..a3f381f --- /dev/null +++ b/modules/processing/resamplers/iir_resampler/build/CMakeLists.txt @@ -0,0 +1,30 @@ +#[[ + @file CMakeLists.txt + + @brief + + @copyright + Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved. + SPDX-License-Identifier: BSD-3-Clause +]] +cmake_minimum_required(VERSION 3.10) + +set(iir_resampler_includes + ${LIB_ROOT}/inc +) + +spf_module_sources( + KCONFIG CONFIG_IIR_RESAMPLER + NAME iir_resampler + MAJOR_VER 1 + MINOR_VER 0 + AMDB_ITYPE "capi" + AMDB_MTYPE "pp" + AMDB_MID "0x07001018" + AMDB_TAG "capi_iir_resampler" + AMDB_MOD_NAME "MODULE_ID_IIR_RESAMPLER" + INCLUDES ${iir_resampler_includes} + H2XML_HEADERS "${LIB_ROOT}/api/iir_resampler_api.h" + CFLAGS "" + STATIC_LIB_PATH "${LIB_ROOT}/bin/arm/libiir_resampler.a" +) \ No newline at end of file diff --git a/modules/processing/resamplers/iir_resampler/inc/iir_resampler.h b/modules/processing/resamplers/iir_resampler/inc/iir_resampler.h new file mode 100644 index 0000000..afa60dd --- /dev/null +++ b/modules/processing/resamplers/iir_resampler/inc/iir_resampler.h @@ -0,0 +1,429 @@ +/*========================================================================= +Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. +SPDX-License-Identifier: BSD-3-Clause +========================================================================= */ +#ifndef __IIR_RESAMPER_H__ +#define __IIR_RESAMPER_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "AudioComdef.h" + +#if defined(__XTENSA__) +#include "NatureDSP_types_hifi5.h" +#include "NatureDSP_Signal_hifi5.h" +#endif + +/* KPPS numbers for various modes. These are required for evaluating the offsets in various modules */ +//DC: TBD KPI numbers to be added +#define IIR_RESAMPER_KPPS_8K_TO_16K (329 ) +#define IIR_RESAMPER_KPPS_8K_TO_24K (329 ) +#define IIR_RESAMPER_KPPS_8K_TO_32K (748 ) +#define IIR_RESAMPER_KPPS_8K_TO_48K (945 ) +#define IIR_RESAMPER_KPPS_8K_TO_96K (945 ) +#define IIR_RESAMPER_KPPS_8K_TO_192K (945 ) +#define IIR_RESAMPER_KPPS_8K_TO_384K (945 ) +#define IIR_RESAMPER_KPPS_8K_MAX (945 ) + +#define IIR_RESAMPER_KPPS_16K_TO_8K (325 ) +#define IIR_RESAMPER_KPPS_16K_TO_24K (325 ) +#define IIR_RESAMPER_KPPS_16K_TO_32K (756 ) +#define IIR_RESAMPER_KPPS_16K_TO_48K (1074) +#define IIR_RESAMPER_KPPS_16K_TO_96K (1074) +#define IIR_RESAMPER_KPPS_16K_TO_192K (1074) +#define IIR_RESAMPER_KPPS_16K_TO_384K (1074) +#define IIR_RESAMPER_KPPS_16K_MAX (1074) + +#define IIR_RESAMPER_KPPS_24K_TO_8K (325 ) +#define IIR_RESAMPER_KPPS_24K_TO_16K (325 ) +#define IIR_RESAMPER_KPPS_24K_TO_32K (756 ) +#define IIR_RESAMPER_KPPS_24K_TO_48K (1074) +#define IIR_RESAMPER_KPPS_24K_TO_96K (1074) +#define IIR_RESAMPER_KPPS_24K_TO_192K (1074) +#define IIR_RESAMPER_KPPS_24K_TO_384K (1074) +#define IIR_RESAMPER_KPPS_24K_MAX (1074) + + +#define IIR_RESAMPER_KPPS_32K_TO_8K (736 ) +#define IIR_RESAMPER_KPPS_32K_TO_16K (748 ) +#define IIR_RESAMPER_KPPS_32K_TO_24K (748 ) +#define IIR_RESAMPER_KPPS_32K_TO_48K (2445) +#define IIR_RESAMPER_KPPS_32K_TO_96K (2445) +#define IIR_RESAMPER_KPPS_32K_TO_192K (2445) +#define IIR_RESAMPER_KPPS_32K_TO_384K (2445) +#define IIR_RESAMPER_KPPS_32K_MAX (2445) + +#define IIR_RESAMPER_KPPS_48K_TO_8K (927 ) +#define IIR_RESAMPER_KPPS_48K_TO_16K (1058) +#define IIR_RESAMPER_KPPS_48K_TO_24K (1058) +#define IIR_RESAMPER_KPPS_48K_TO_32K (2437) +#define IIR_RESAMPER_KPPS_48K_TO_96K (2437) +#define IIR_RESAMPER_KPPS_48K_TO_192K (2437) +#define IIR_RESAMPER_KPPS_48K_TO_384K (2437) +#define IIR_RESAMPER_KPPS_48K_MAX (2437) + +#define IIR_RESAMPER_KPPS_96K_TO_8K (927 ) +#define IIR_RESAMPER_KPPS_96K_TO_16K (1058) +#define IIR_RESAMPER_KPPS_96K_TO_24K (1058) +#define IIR_RESAMPER_KPPS_96K_TO_32K (2437) +#define IIR_RESAMPER_KPPS_96K_TO_48K (2437) +#define IIR_RESAMPER_KPPS_96K_TO_192K (2437) +#define IIR_RESAMPER_KPPS_96K_TO_384K (2437) +#define IIR_RESAMPER_KPPS_96K_MAX (2437) + +#define IIR_RESAMPER_KPPS_192K_TO_8K (927 ) +#define IIR_RESAMPER_KPPS_192K_TO_16K (1058) +#define IIR_RESAMPER_KPPS_192K_TO_24K (1058) +#define IIR_RESAMPER_KPPS_192K_TO_32K (2437) +#define IIR_RESAMPER_KPPS_192K_TO_48K (2437) +#define IIR_RESAMPER_KPPS_192K_TO_96K (2437) +#define IIR_RESAMPER_KPPS_192K_TO_384K (2437) +#define IIR_RESAMPER_KPPS_192K_MAX (2437) + +#define IIR_RESAMPER_KPPS_384K_TO_8K (927 ) +#define IIR_RESAMPER_KPPS_384K_TO_16K (1058) +#define IIR_RESAMPER_KPPS_384K_TO_24K (1058) +#define IIR_RESAMPER_KPPS_384K_TO_32K (2437) +#define IIR_RESAMPER_KPPS_384K_TO_48K (2437) +#define IIR_RESAMPER_KPPS_384K_TO_96K (2437) +#define IIR_RESAMPER_KPPS_384K_TO_192K (2437) +#define IIR_RESAMPER_KPPS_384K_MAX (2437) + +#define RS_MAX_STAGES (10) + +#ifdef PROD_SPECIFIC_MAX_CH +#define MAX_CHANNELS PROD_SPECIFIC_MAX_CH +#else +#define MAX_CHANNELS 32 +#endif + +#define RS_PRECISION_COEFF 16 +#define IIR_RESAMPLER_MAX_STACK_SIZE 8192 + +#define IIR_RESAMPLER_RELEASE_VERSION_MSB (0x02000101) +#define IIR_RESAMPLER_RELEASE_VERSION_LSB (0x00000000) + +#define IIR_RESAMPLER_IO_CONFIG (1) // refers to iir_resampler_io_config_t in iir_resampler_api.h +#define IIR_RESAMPLER_MEM_CONFIG (2) // refers to iir_resampler_memory_config_t in iir_resampler_api.h +#define IIR_RESAMPLER_BUF_PTR_ARRAY_PTR (3) // refers to pointer to input/output data buffer pointer array +#define PARAM_ID_IIR_RESAMPLER_DELAY (4) // refers to iir_resampler_delay_config_t + + +#define IIR_RESAMPLER_MAX_INPUT_CHANNELS (1) // maximum input TX channels +#define IIR_RESAMPLER_MAX_OUTPUT_CHANNELS (1) // maximum output channels +#define IIR_RESAMPLER_MIN_INPUT_CHANNELS (1) // minimum input TX channels +#define IIR_RESAMPLER_MIN_OUTPUT_CHANNELS (1) // minimum output channels + +#ifdef PROD_SPECIFIC_MAX_CH +#define IIR_RESAMPLER_MAX_NUM_CHAN PROD_SPECIFIC_MAX_CH +#else +#define IIR_RESAMPLER_MAX_NUM_CHAN 32 +#endif + +#define IIR_RESAMPLER_SUCCESS (0) // successful result +#define IIR_RESAMPLER_FAIL (1) // failed result + +typedef uint32_t IIR_RESAMPLER_STRUCT_IDS; +typedef uint32_t IIR_RESAMPLER_RESULT; + + +typedef struct iir_resampler_config_struct_t { + uint32 input_sampling_rate; + uint32 output_sampling_rate; + uint32 input_dynamic_range; + uint32 input_frame_samples; + uint32 output_frame_samples; + uint32 filter_sampling_rate; + uint32 filter_cut_off_freq; + int32 up_sample_flag; + int32 dn_sample_flag; + uint32 up_sample_factor; + uint32 dn_sample_factor; + uint32 stages; +#if (RS_PRECISION_COEFF==32) + const int32 *f_x[RS_MAX_STAGES]; + const int32 *f_y[RS_MAX_STAGES]; +#else + const int16 *f_x[RS_MAX_STAGES]; + const int16 *f_y[RS_MAX_STAGES]; +#endif + int32 gain; + int32 q_in; + int32 q_out; + int32 q_coeff; + int32 q_g; + int32 q_x; + int32 q_y; + int32 q_accu; + uint32 single_channel_mem_size; //Memoryfor single channel + uint32 num_channels; + uint32 filter_scratch_size; // used for determining the scratch buffer size + uint32 filter_frame_samples; // number of filtering samples per frame + uint32 group_delay_samples_x1000; + int32 reserved; // used for alignment +} iir_resampler_config_struct_t; + +typedef struct iir_resampler_channel_memory_struct_t { + int32 *x_delay_ptr[RS_MAX_STAGES]; + int32 *y_delay_ptr[RS_MAX_STAGES]; + int32* filter_scratch_buf_ptr; // used for intermediate filtering stage data + int32 reserved; // used for alignment + /* Memory for the above pointers*/ +} iir_resampler_channel_memory_struct_t; + +#if defined(__XTENSA__) +typedef struct biquad_t{ + bqriir32x16_df1_handle_t bq_handle; + void *scratchmem; + int32 *y_32_out; + void *objmem; +}biquad_t; + +typedef struct iir_resampler_lib_t { + iir_resampler_config_struct_t iir_resampler_config_structure; + iir_resampler_channel_memory_struct_t *iir_resampler_channel_memory_ptr[MAX_CHANNELS]; + biquad_t biquad_instance; +}iir_resampler_lib_t; +#else +typedef struct iir_resampler_lib_t { + iir_resampler_config_struct_t iir_resampler_config_structure; + iir_resampler_channel_memory_struct_t *iir_resampler_channel_memory_ptr[MAX_CHANNELS]; +}iir_resampler_lib_t; +#endif + +/* Supported sample rates +*/ +typedef enum IIR_RESAMPLER_SAMPLE_RATE +{ + EIGHT_K = 8000, // sample rate: 8k NB + SIXTEEN_K = 16000, // sample rate: 16k WB + TWENTY_FOUR_K = 24000, // sample rate: 24k + THIRTY_TWO_K = 32000, // sample rate: 32k SWB + FOURTY_EIGHT_K = 48000, // sample rate: 48k FB + NINETY_SIX_K = 96000, // sample rate: 96k + ONE_NINETY_TWO_K = 192000, // sample rate: 192k + THREE_EIGHTY_FOUR_K = 384000 // sample rate: 384k +}IIR_RESAMPLER_SAMPLE_RATE; +//Make sure that variables are of size greater than or equal to uint_32 + + +/************************************ Structures ****************************/ +/* +iir_resampler_memory_config_t + +iir_resampler_memory_config_t is associated with IIR_RESAMPLER_MEM_CONFIG id. +This is the library structure encapsulating memory details of the library +required for creating the instance. Caller allocates this structure for +callee to fill. + +Supported for iir_resampler_get_req(...) function + +lib_instance_mem_size: [OUT]: callee fills size of library instance memory required in bytes +lib_stack_mem_size: [OUT]: callee fills size of the stack memory required by library +*/ +typedef struct iir_resampler_memory_config_t { + uint32_t lib_instance_mem_size; + uint32_t lib_stack_mem_size; + uint32_t num_in_samples; + uint32_t num_out_samples; +} iir_resampler_memory_config_t; + + + +/* +iir_resampler_io_config_t + +iir_resampler_io_config_t is the library structure encapsulating library I/O configuration + +in_channels: [IN]: input tx channels +out_channels: [IN]: output channels +in_sample_rate: [IN]: input sampling frequency +out_sample_rate: [IN]: output sampling frequency +frame_length_ms: [IN]: frame length (ms) + +*/ +typedef struct iir_resampler_io_config_t { + uint32_t in_channels; + uint32_t out_channels; + uint32_t in_sample_rate; + uint32_t out_sample_rate; + uint32_t frame_length_ms; + uint32_t bytes_per_sample; +} iir_resampler_io_config_t; + + +/* +iir_resampler_delay_config_t + +iir_resampler_delay_config_t is the library structure encapsulating group_delay_samples + +group_delay_samples: [OUT]: number of samples by which the group is delayed by + +*/ +typedef struct iir_resampler_delay_config_t { + uint32 group_delay_samples_x1000; +} iir_resampler_delay_config_t; + + +/* +iir_resampler_t + +iir_resampler_t is the library instance definition. + +IN: Memory for iir_resampler_t is used in iir_resampler_set(...),iir_resampler_get(...),iir_resampler_process(...) +OUT: Library provides pointer to iir_resampler_t in iir_resampler_init(...) +*/ +typedef int8_t iir_resampler_t; + +/************************************ Functions *****************************/ + +/* +iir_resampler_get_req + +iir_resampler_get_req, signature of the structure is fixed as shown. This api provides +the memory and other requirements (if any) of the library based on the +configuration provided. + +This function is expected to be called before init is invoked. + +req_config_id: [IN]: configuration structure ID for querying requirements, defines the payload pointed by iir_resampler_instance_memory_config_t + Supports: IIR_RESAMPLER_INSTANCE_MEM + +req_config_ptr: [IN/OUT]: requirements structure pointer in which the memory requirement that is calculated is updated and returned + +req_config_size: [IN]: size of structure pointed by req_config_ptr in which the requirements are updated + +config_id: [IN]: requirements structure ID for querying requirements, defines the payload pointed by config_ptr + Supports: IIR_RESAMPLER_IO_CONFIG + +config_ptr: [IN]: req configuration structure pointer for iir_resampler to be used by library to calculate the requirement + +config_size: [IN]: size of structure pointed by config_ptr which has the configuration details for which the memory is calculated + +Return: +result - IIR_RESAMPLER_RESULT +*/ +IIR_RESAMPLER_RESULT iir_resampler_get_req( + uint32_t req_config_id, + int8_t* req_config_ptr, + uint32_t req_config_size, + uint32_t config_id, + int8_t* config_ptr, + uint32_t config_size +); + + +/* +iir_resampler_init + +iir_resampler_init signature of the structure is fixed as shown. +This api +- Allocates static memory only to library instance +- Outputs iir_resampler's instance pointer iir_resampler_lib_ptr + +This function is expected to be called before iir_resampler_process is invoked. + +iir_resampler_lib_ptr : [OUT]: pointer to the pointer pointing to the iir_resampler library instance +req_config_id : [IN]: structure ID that defines the payload pointed by req_config_ptr +req_config_ptr : [IN]: pointer to static config for initializing the iir_resampler +req_config_size: [IN]: size of the structure pointed by config_ptr +static_mem_ptr : [IN]: pointer to the iir_resampler instance memory +static_mem_size: [IN]: size of instance memory that has been allocated for the iir_resampler + +Return: +result - IIR_RESAMPLER_RESULT +*/ +IIR_RESAMPLER_RESULT iir_resampler_init( + iir_resampler_t** iir_resampler_lib_ptr, + uint32_t req_config_id, + int8_t* req_config_ptr, + uint32_t req_config_size, + int8_t* static_mem_ptr, + uint32_t static_mem_size +); + +/* +iir_resampler_process + +iir_resampler_lib_ptr: [IN]: pointer to the iir_resampler public library structure +output_data_ptr: [OUT]: caller provides the address of output_data that needs to be filled by the library(callee) +input_data_ptr: [IN]: caller provides the address of input_data that contains the data of input channels +output_data_size: [IN]: output data structure size +input_data_size: [IN]: input data structure size +output_data_id: [IN]: structure ID associated with the output data + Supports: IIR_RESAMPLER_BUF_PTR +input_data_id: [IN]: structure ID associated with the input data + Supports: IIR_RESAMPLER_BUF_PTR +Return: +result - IIR_RESAMPLER_RESULT +*/ +IIR_RESAMPLER_RESULT iir_resampler_process( + iir_resampler_t* iir_resampler_lib_ptr, + int8_t** output_data_ptr, + int8_t** input_data_ptr, + uint32_t output_data_size, + uint32_t input_data_size, + uint32_t output_data_id, + uint32_t input_data_id +); + +/* +iir_resampler_set + +- This api sets data to the library + +iir_resampler_lib_ptr: [IN]: pointer to the iir_resampler public library structure +set_struct_id: [IN]: this structure ID defines the structure in set_struct_ptr +set_struct_ptr: [IN]: data pointer of the parameter buffer that is to be set to internal library +set_struct_size [IN]: caller fills; size of the structure pointed by set_struct_ptr; It also indicates memory in bytes that will be filled by the caller +Return: +result - IIR_RESAMPLER_RESULT +*/ +IIR_RESAMPLER_RESULT iir_resampler_set( + iir_resampler_t* iir_resampler_lib_ptr, + IIR_RESAMPLER_STRUCT_IDS set_struct_id, + int8_t* set_struct_ptr, + uint32_t set_struct_size +); + +/* +iir_resampler_get + +- This api gets data from library +- Returns data identified with get_struct_id which is filled by caller +- Callee fills data at the address pointed by get_struct_ptr +- Callee updates get_struct_filled_size + +iir_resampler_lib_ptr: [IN]: pointer to the iir_resampler public library structure +get_struct_id: [IN]: this structre ID defines the structure in get_struct_ptr + Supports: PARAM_ID_IIR_RESAMPELR_VERSION + PARAM_ID_IIR_RESAMPLER_DELAY +get_struct_ptr: [IN/OUT]: data pointer of the parameter buffer in which the values read from internal library will be stored +get_struct_size: [IN]: caller fills; get_struct_size i.e. memory in bytes available for callee to fill(note has to be properly type casted for the size) +*get_struct_filled_size:[IN/OUT]: Caller passes address; callee fills with memory in bytes read from internal library + +Return: +result - IIR_RESAMPLER_RESULT +*/ +IIR_RESAMPLER_RESULT iir_resampler_get( + iir_resampler_t* iir_resampler_lib_ptr, + IIR_RESAMPLER_STRUCT_IDS get_struct_id, + int8_t* get_struct_ptr, + uint32_t get_struct_size, + uint32_t* get_struct_filled_size +); + +/* +iir_resampler_get_upsample_factor +-this api is used to get up sample factor from lib which is used to calculate bandwidth. +iir_resampler_lib_ptr: [IN]: pointer to the iir_resampler public library structure +up_sample_factor: [OUT]: uint32_t variable returns up_sample_factor +*/ +uint32_t iir_resampler_get_upsample_factor(iir_resampler_t* iir_resampler_lib_ptr); +#ifdef __cplusplus +} +#endif + +#endif//#ifndef __IIR_RESAMPER_H__ diff --git a/modules/processing/resamplers/iir_resampler/inc/iir_rs_lib.h b/modules/processing/resamplers/iir_resampler/inc/iir_rs_lib.h new file mode 100644 index 0000000..a3b7864 --- /dev/null +++ b/modules/processing/resamplers/iir_resampler/inc/iir_rs_lib.h @@ -0,0 +1,100 @@ +/*========================================================================= +Copyright (c) Qualcomm Innovation Center, Inc. All Rights Reserved. +SPDX-License-Identifier: BSD-3-Clause +========================================================================= */ + +#ifndef IIR_RS_LIB_H +#define IIR_RS_LIB_H + +/*------------------------------------------------------------------------ + * Include files + * -----------------------------------------------------------------------*/ + +#ifndef CAPI_UNIT_TEST +#include "shared_lib_api.h" +#else +#include "Elite_intf_extns_change_media_fmt.h" +#include "capi.h" +#endif + +#include "posal.h" +#include "iir_resampler.h" +#include "module_cmn_api.h" + + + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +/*------------------------------------------------------------------------ + * Macros, Defines, Type declarations + * -----------------------------------------------------------------------*/ +//#define IIR_RS_DBG_TAP + +#define IIR_RS_8K_SAMPLING_RATE (8000) +#define IIR_RS_16K_SAMPLING_RATE (16000) +#define IIR_RS_24K_SAMPLING_RATE (24000) +#define IIR_RS_32K_SAMPLING_RATE (32000) +#define IIR_RS_48K_SAMPLING_RATE (48000) +#define IIR_RS_96K_SAMPLING_RATE (96000) +#define IIR_RS_192K_SAMPLING_RATE (192000) +#define IIR_RS_384K_SAMPLING_RATE (384000) + + +#define IIR_RS_FRAME_LEN_10_MS (10) +#define IIR_RS_FRAME_LEN_20_MS (20) +#define IIR_RS_FRAME_LEN_1_MS (1) +#define IIR_RS_FRAME_LEN_MAX_MS (IIR_RS_FRAME_LEN_20_MS + IIR_RS_FRAME_LEN_1_MS) + +#define IIR_RESAMPLER_ROUNDTO8(x) ((((uint32_t)(x) + 7) >> 3) << 3) + +// To print all lib status messages +//#define IIR_RESAMPLER_DEBUG_MSG 1 + +// Max number of ports supported by one instance of resampler CAPI +#define MAX_NUM_PORTS (1) +/*------------------------------------------------------------------------ + * Structure definitions + * -----------------------------------------------------------------------*/ +typedef struct iir_rs_lib_instance_t +{ + iir_resampler_io_config_t lib_io_config; // resampler io config + iir_resampler_memory_config_t lib_mem_config; // resampler mem config + iir_resampler_t * lib_mem_ptr; // resampler mem instance +} iir_rs_lib_instance_t; + + + +typedef struct iir_rs_lib_t +{ + uint32_t num_ports; // active port numbers + iir_rs_lib_instance_t lib_instance_per_port_ptr[MAX_NUM_PORTS]; // pointer to lib instance per port +} iir_rs_lib_t; + +/*------------------------------------------------------------------------ + * Function definitions + * -----------------------------------------------------------------------*/ +void iir_rs_lib_deinit(iir_rs_lib_t *iir_rs_ptr); + +ar_result_t iir_rs_process(iir_rs_lib_t *iir_rs_ptr, int8** input_data_ptr, int8** output_data_ptr,uint32 num_in_samples, uint32 num_out_samples); + +ar_result_t iir_rs_get_param(iir_rs_lib_t *iir_rs_ptr, uint32_t param_id, int8_t *param_data_ptr, uint32_t param_size); + +ar_result_t iir_rs_lib_clear_algo_memory(iir_rs_lib_t *iir_rs_ptr); + +ar_result_t iir_rs_lib_allocate_memory(iir_rs_lib_t * iir_rs_ptr, uint32_t inp_sampling_rate, uint32_t out_sampling_rate, + uint32_t num_channels, uint32_t bits_per_sample, uint32_t frame_length_ms, uint32_t heap_id); + +uint32_t iir_rs_lib_get_kpps(iir_rs_lib_t *iir_rs_ptr, uint32_t input_samp_rate, uint32_t output_samp_rate); + +uint32_t iir_rs_lib_get_delay(iir_rs_lib_t *iir_rs_ptr, uint32_t input_samp_rate, uint32_t output_samp_rate); + +uint32_t iir_rs_lib_get_bw(iir_rs_lib_t *iir_rs_ptr, uint32_t input_samp_rate); + + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif // iir_rs_lib_H