提交mqtt相关内容 3162/4 3162/head
authorhcj <1224667075@qq.com>
Fri, 6 Feb 2026 02:16:11 +0000 (10:16 +0800)
committerhcj <1224667075@qq.com>
Fri, 6 Feb 2026 02:16:11 +0000 (10:16 +0800)
81 files changed:
code/sdk/application/brandy/brandy_standard/service_init.c
code/sdk/build/config/target_config/brandy/config.py
code/sdk/middleware/utils/fs/vfs/CMakeLists.txt
code/sdk/zhihui/MQTT/MQTT_server_mian.c
code/sdk/zhihui/MQTT/uapi_mqtt_at.c
code/sdk/zhihui/leiniao/imsl_ln/LN_MQTT/ln_UDP.c
code/sdk/zhihui/leiniao/imsl_ln/LN_MQTT/ln_mqtt_api.c
code/sdk/zhihui/leiniao/imsl_ln/LN_MQTT/ln_mqtt_api.h
code/sdk/zhihui/leiniao/imsl_ln/LN_MQTT/ln_mqtt_api_at.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api_websocket.c
code/sdk/zhihui/test_opus_frame_to_pcm/CMakeLists.txt
code/sdk/zhihui/test_opus_frame_to_pcm/ENCODER_SUPPORT.md [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/Makefile
code/sdk/zhihui/test_opus_frame_to_pcm/Makefile.libopus
code/sdk/zhihui/test_opus_frame_to_pcm/celt/celt_encoder.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/copy_encoder_sources.ps1 [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/copy_encoder_sources.sh [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/copy_missing_headers.sh [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/include/opus_frame_to_pcm.h
code/sdk/zhihui/test_opus_frame_to_pcm/silk/NLSF_VQ.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/NLSF_del_dec_quant.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/NLSF_encode.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/NSQ.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/NSQ_del_dec.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/VAD.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/VQ_WMat_EC.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/ana_filt_bank_1.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/check_control_input.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/control_SNR.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/control_audio_bandwidth.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/control_codec.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/enc_API.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/encode_indices.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/encode_pulses.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/LTP_analysis_filter_FIX.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/LTP_scale_ctrl_FIX.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/apply_sine_window_FIX.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/autocorr_FIX.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/burg_modified_FIX.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/burg_modified_FIX_sse4_1.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/corrMatrix_FIX.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/encode_frame_FIX.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/find_LPC_FIX.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/find_LTP_FIX.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/find_pitch_lags_FIX.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/find_pred_coefs_FIX.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/k2a_FIX.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/k2a_Q16_FIX.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/main_FIX.h [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/noise_shape_analysis_FIX.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/pitch_analysis_core_FIX.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/process_gains_FIX.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/regularize_correlations_FIX.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/residual_energy16_FIX.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/residual_energy_FIX.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/schur64_FIX.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/schur_FIX.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/structs_FIX.h [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/vector_ops_FIX.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/vector_ops_FIX_sse4_1.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/warped_autocorrelation_FIX.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/warped_autocorrelation_FIX_arm.h [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/warped_autocorrelation_FIX_mipsr1.h [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/warped_autocorrelation_FIX_neon_intr.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/float/SigProc_FLP.h [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/float/main_FLP.h [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/float/structs_FLP.h [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/init_encoder.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/process_NLSFs.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/quant_LTP_gains.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/stereo_LR_to_MS.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/stereo_encode_pred.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/silk/stereo_quant_pred.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/src/opus_pcm_to_frame.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/src_lib/analysis.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/src_lib/mlp.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/src_lib/mlp_data.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/src_lib/opus_encoder.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/src_lib/opus_multistream_encoder.c [new file with mode: 0644]
code/sdk/zhihui/test_opus_frame_to_pcm/src_lib/opus_projection_encoder.c [new file with mode: 0644]

index 6ed3503..5061be5 100644 (file)
-\r
-/*\r
- * Copyright (c) CompanyNameMagicTag 2021-2022. All rights reserved.\r
- * Description: Application core board init function for standard\r
- * Author:\r
- * Create:\r
- */\r
-\r
-#ifdef __LITEOS__\r
-#include "partition.h"\r
-#endif\r
-#include "pm.h"\r
-#include "cpu_utils.h"\r
-#include "log_common.h"\r
-#include "log_uart.h"\r
-#include "exception.h"\r
-#include "log_oml_exception.h"\r
-#ifdef HSO_SUPPORT\r
-#include "dfx_system_init.h"\r
-#endif\r
-#include "debug_print.h"\r
-#ifdef SUPPORT_AT_GRAPHIC\r
-#include "graphic_cmd_manager.h"\r
-#endif\r
-#ifdef SUPPORT_AT_JSINSTALL\r
-#include "ohosfwk_at_process.h"\r
-#endif\r
-#ifdef SUPPORT_AT_APP\r
-#include "app_cmd_manager.h"\r
-#endif\r
-#ifdef SUPPORT_AT_AUDIO\r
-#include "audio_cmd_manager.h"\r
-#endif\r
-#ifdef SUPPORT_AT_BT_MANAGER\r
-#include "bt_manager_cmd_manager.h"\r
-#endif\r
-#ifdef SUPPORT_AT_MEDIA\r
-#include "media_cmd_manager.h"\r
-#endif\r
-#ifdef SUPPORT_AT_PPG\r
-#include "ppg_at_cmd.h"\r
-#endif\r
-\r
-#ifdef SUPPORT_AT_VIBRATOR\r
-#include "vibrator_at_cmd.h"\r
-#endif\r
-\r
-#ifdef SUPPORT_AT_HX3507\r
-#include "hx3507_at_cmd.h"\r
-#endif\r
-\r
-#ifdef SUPPORT_AT_BATTERY\r
-#include "battery_at_cmd.h"\r
-#endif\r
-\r
-#ifdef SUPPORT_AT_LIS2DOC\r
-#include "lis2doc_at_cmd.h"\r
-#endif\r
-\r
-#ifdef SUPPORT_AT_CYWEE\r
-#include "cywee_at_cmd.h"\r
-#endif\r
-\r
-#ifdef SUPPORT_AT_MAG\r
-#include "mag_at_cmd.h"\r
-#endif\r
-\r
-#ifdef SUPPORT_AT_CUSTOMUTILS\r
-#include "custom_utils_at_cmd.h"\r
-#endif\r
-#ifdef SUPPORT_AT_CAT1\r
-#include "cat1_at_cmd.h"\r
-#endif\r
-#ifdef SUPPORT_AT_PPP\r
-#include "ppp_at_cmd.h"\r
-#endif\r
-#include "log_file.h"\r
-#ifdef CONFIG_OTA_UPDATE_SUPPORT\r
-#include "upg.h"\r
-#endif\r
-#ifdef SUPPORT_AT_HX3508\r
-#include "hx3508_at_cmd.h"\r
-#endif\r
-#ifdef SUPPORT_AT_LQBP002TA\r
-#include "lqbp_at_cmd.h"\r
-#endif\r
-#ifdef SUPPORT_AT_CHARGE\r
-#include "charge_at_cmd.h"\r
-#endif\r
-#ifdef SUPPORT_AT_CROWN\r
-#include "crown_at_cmd.h"\r
-#endif\r
-#ifdef SUPPORT_AT_MIXO\r
-#include "mixo_at_cmd.h"\r
-#endif\r
-#ifdef SUPPORT_AT_GYRO\r
-#include "gyro_at_cmd.h"\r
-#endif\r
-#ifdef SUPPORT_AT_BP\r
-#include "bp_at_cmd.h"\r
-#endif\r
-#ifdef SUPPORT_AT_LEINIAO_API\r
-#include "leiniao_api_at.h"\r
-#endif\r
-#ifdef SUPPORT_ZHIHUI_AI_SERVICE\r
-#include "ai_at_cmd.h"\r
-#endif\r
-#ifdef SUPPORT_ZHIHUI_AI_OLD\r
-#include "ai_old_at_cmd.h"\r
-#endif\r
-\r
-#ifdef SUPPORT_ZHIHUI_AI_NEW\r
-#include "ai_V2_5_at_cmd.h"\r
-#endif\r
-\r
-#ifdef ENABLE_VAD_SERVER\r
-#include "imsl_at_cmd.h"\r
-#endif\r
-\r
-#ifdef SUPPORT_ZHIHUI_DMP\r
-#include "dmp_api_at.h"\r
-#endif\r
-\r
-static void register_at_service(void)\r
-{\r
-#ifdef SUPPORT_AT_GRAPHIC\r
-    uint32_t graphic_cmd_count;\r
-    at_cmd_table_t *graphic_table = get_graphic_cmd_table(&graphic_cmd_count);\r
-    at_cmd_add_moudle(graphic_table, graphic_cmd_count);\r
-#endif\r
-#ifdef SUPPORT_AT_JSINSTALL\r
-    uint32_t ohosfwk_cmd_count;\r
-    at_cmd_table_t *ohosfwk_table = get_ohosfwk_cmd_table(&ohosfwk_cmd_count);\r
-    at_cmd_add_moudle(ohosfwk_table, ohosfwk_cmd_count);\r
-#endif\r
-#ifdef SUPPORT_AT_APP\r
-    uint32_t app_cmd_count;\r
-    at_cmd_table_t *app_table = get_app_cmd_table(&app_cmd_count);\r
-    at_cmd_add_moudle(app_table, app_cmd_count);\r
-#endif\r
-#ifdef SUPPORT_AT_AUDIO\r
-    uint32_t audio_cmd_count;\r
-    at_cmd_table_t *audio_table = get_audio_cmd_table(&audio_cmd_count);\r
-    at_cmd_add_moudle(audio_table, audio_cmd_count);\r
-#endif\r
-#ifdef SUPPORT_AT_BT_MANAGER\r
-    uint32_t bt_manager_cmd_count;\r
-    at_cmd_table_t *bt_manager_table = get_bt_manager_cmd_table(&bt_manager_cmd_count);\r
-    at_cmd_add_moudle(bt_manager_table, bt_manager_cmd_count);\r
-#endif\r
-#ifdef SUPPORT_AT_MEDIA\r
-    uint32_t media_cmd_count;\r
-    at_cmd_table_t *media_table = get_media_cmd_table(&media_cmd_count);\r
-    at_cmd_add_moudle(media_table, media_cmd_count);\r
-#endif\r
-\r
-#ifdef SUPPORT_AT_PPG\r
-    uint32_t hx3695_cmd_count;\r
-    at_cmd_table_t *hx3695_table = get_ppg_cmd_table(&hx3695_cmd_count);\r
-    at_cmd_add_moudle(hx3695_table, hx3695_cmd_count);\r
-#endif\r
-\r
-#ifdef SUPPORT_AT_VIBRATOR\r
-    uint32_t vibrator_cmd_count;\r
-    at_cmd_table_t *vibrator_table = get_vibrator_cmd_table(&vibrator_cmd_count);\r
-    at_cmd_add_moudle(vibrator_table, vibrator_cmd_count);\r
-#endif\r
-\r
-#ifdef SUPPORT_AT_HX3507\r
-    uint32_t hx3507_cmd_count;\r
-    at_cmd_table_t *hx3507_table = get_hx3507_cmd_table(&hx3507_cmd_count);\r
-    at_cmd_add_moudle(hx3507_table, hx3507_cmd_count);\r
-#endif\r
-\r
-#ifdef SUPPORT_AT_BATTERY\r
-    uint32_t battery_cmd_count;\r
-    at_cmd_table_t *battery_table = get_battery_cmd_table(&battery_cmd_count);\r
-    at_cmd_add_moudle(battery_table, battery_cmd_count);\r
-#endif\r
-\r
-#ifdef SUPPORT_AT_CUSTOMUTILS\r
-    uint32_t custom_utils_cmd_count;\r
-    at_cmd_table_t *custom_utils_table = get_custom_utils_cmd_table(&custom_utils_cmd_count);\r
-    at_cmd_add_moudle(custom_utils_table, custom_utils_cmd_count);\r
-#endif\r
-#ifdef SUPPORT_AT_CAT1\r
-    uint32_t cat1_cmd_count;\r
-    at_cmd_table_t *cat1_table = get_cat1_cmd_table(&cat1_cmd_count);\r
-    at_cmd_add_moudle(cat1_table, cat1_cmd_count);\r
-#endif\r
-\r
-#ifdef SUPPORT_AT_PPP\r
-    uint32_t ppp_cmd_count;\r
-    at_cmd_table_t *ppp_table = get_ppp_cmd_table(&ppp_cmd_count);\r
-    at_cmd_add_moudle(ppp_table, ppp_cmd_count);\r
-#endif\r
-\r
-#ifdef SUPPORT_ZHIHUI_AI_OLD\r
-    uint32_t ai_old_cmd_count;\r
-    at_cmd_table_t *ai_old_table = get_ai_old_cmd_table(&ai_old_cmd_count);\r
-    at_cmd_add_moudle(ai_old_table, ai_old_cmd_count);\r
-#endif\r
-\r
-#ifdef SUPPORT_ZHIHUI_AI_NEW\r
-    uint32_t ai_new_cmd_count;\r
-    at_cmd_table_t *ai_new_table = get_ai_2_5_cmd_table(&ai_new_cmd_count);\r
-    at_cmd_add_moudle(ai_new_table, ai_new_cmd_count);\r
-#endif\r
-\r
-#ifdef SUPPORT_AT_LIS2DOC\r
-    uint32_t lis2doc_cmd_count;\r
-    at_cmd_table_t *lis2doc_table = get_lis2doc_cmd_table(&lis2doc_cmd_count);\r
-    at_cmd_add_moudle(lis2doc_table, lis2doc_cmd_count);\r
-#endif\r
-\r
-#ifdef SUPPORT_AT_CYWEE\r
-    uint32_t cywee_cmd_count;\r
-    at_cmd_table_t *cywee_table = get_cywee_cmd_table(&cywee_cmd_count);\r
-    at_cmd_add_moudle(cywee_table, cywee_cmd_count);\r
-#endif\r
-\r
-#ifdef SUPPORT_AT_MAG\r
-    uint32_t mag_cmd_count;\r
-    at_cmd_table_t *mag_table = get_mag_cmd_table(&mag_cmd_count);\r
-    at_cmd_add_moudle(mag_table, mag_cmd_count);\r
-#endif\r
-\r
-#ifdef SUPPORT_AT_RTCTEST\r
-    uint32_t rtc_cmd_count;\r
-    at_cmd_table_t *rtc_table = get_rtc_cmd_table(&rtc_cmd_count);\r
-    at_cmd_add_moudle(rtc_table, rtc_cmd_count);\r
-#endif\r
-#ifdef SUPPORT_AT_HX3508\r
-    uint32_t hx3508_cmd_count;\r
-    at_cmd_table_t *hx3508_table = get_hx3508_cmd_table(&hx3508_cmd_count);\r
-    at_cmd_add_moudle(hx3508_table, hx3508_cmd_count);\r
-#endif\r
-#ifdef SUPPORT_AT_LQBP002TA\r
-    uint32_t lqbp002ta_cmd_count;\r
-    at_cmd_table_t *lqbp002ta_table = get_lqbp002ta_cmd_table(&lqbp002ta_cmd_count);\r
-    at_cmd_add_moudle(lqbp002ta_table, lqbp002ta_cmd_count);\r
-#endif\r
-#ifdef SUPPORT_AT_CHARGE\r
-    uint32_t charge_cmd_count;\r
-    at_cmd_table_t *charge_table = get_charge_cmd_table(&charge_cmd_count);\r
-    at_cmd_add_moudle(charge_table, charge_cmd_count);    \r
-#endif\r
-#ifdef SUPPORT_AT_CROWN\r
-    uint32_t crown_cmd_count;\r
-    at_cmd_table_t *crown_table = get_crown_cmd_table(&crown_cmd_count);\r
-    at_cmd_add_moudle(crown_table, crown_cmd_count);    \r
-#endif\r
-#ifdef SUPPORT_AT_MIXO\r
-    uint32_t mixo_cmd_count;\r
-    at_cmd_table_t *mixo_table = get_mixo_cmd_table(&mixo_cmd_count);\r
-    at_cmd_add_moudle(mixo_table, mixo_cmd_count);\r
-#endif\r
-#ifdef SUPPORT_AT_GYRO\r
-    uint32_t gyro_cmd_count;\r
-    at_cmd_table_t *gyro_table = get_gyro_cmd_table(&gyro_cmd_count);\r
-    at_cmd_add_moudle(gyro_table, gyro_cmd_count);\r
-#endif\r
-#ifdef SUPPORT_AT_BP\r
-    uint32_t bp_cmd_count;\r
-    at_cmd_table_t *bp_table = get_bp_cmd_table(&bp_cmd_count);\r
-    at_cmd_add_moudle(bp_table, bp_cmd_count);\r
-#endif\r
-#ifdef SUPPORT_AT_LEINIAO_API\r
-    uint32_t leiniao_api_cmd_count;\r
-    at_cmd_table_t *leiniao_api_table = get_leiniao_api_cmd_table(&leiniao_api_cmd_count);\r
-    at_cmd_add_moudle(leiniao_api_table, leiniao_api_cmd_count);\r
-#endif\r
-\r
-#ifdef SUPPORT_ZHIHUI_AI_SERVICE\r
-    uint32_t ai_cmd_count;\r
-    at_cmd_table_t *ai_table = get_ai_cmd_table(&ai_cmd_count);\r
-    at_cmd_add_moudle(ai_table, ai_cmd_count);\r
-#endif\r
-\r
-#ifdef ENABLE_VAD_SERVER\r
-    uint32_t imsl_vad_cmd_count;\r
-    at_cmd_table_t *imsl_vad_table = get_imsl_vad_at_cmd_table(&imsl_vad_cmd_count);\r
-    at_cmd_add_moudle(imsl_vad_table, imsl_vad_cmd_count);\r
-#endif\r
-\r
-\r
-#ifdef SUPPORT_ZHIHUI_DMP\r
-    uint32_t dmp_api_cmd_count;\r
-    at_cmd_table_t *dmp_api_table = get_dmp_api_cmd_table(&dmp_api_cmd_count);\r
-    at_cmd_add_moudle(dmp_api_table, dmp_api_cmd_count);\r
-#endif\r
-\r
-}\r
-\r
-#ifdef CONFIG_OTA_UPDATE_SUPPORT\r
-static void upg_putc(const char c)\r
-{\r
-    printf("%c", c);\r
-}\r
-#endif\r
-\r
-void service_init(void)\r
-{\r
-#if defined(FREERTOS_TEST)\r
-    // freertos demo test\r
-    extern int main_full(void);\r
-    (void)main_full();\r
-    for (;;) {\r
-    }\r
-#endif\r
-\r
-    /* dfx init step 1: log init */\r
-    log_init();\r
-    log_init_after_rtos();\r
-    log_uart_init_after_rtos();\r
-\r
-    cpu_utils_init();\r
-    hal_register_exception_dump_callback(log_exception_dump);\r
-    uapi_partition_init();\r
-    PRINT("partition init success"NEWLINE);\r
-\r
-#ifdef CONFIG_OTA_UPDATE_SUPPORT\r
-    upg_func_t upg_func = {0};\r
-    upg_func.malloc = malloc;\r
-    upg_func.free = free;\r
-    upg_func.serial_putc = upg_putc;\r
-    (void)uapi_upg_init(&upg_func);\r
-    PRINT("upg init success\r\n");\r
-#endif\r
-    crypto_drv_init();\r
-    uapi_nv_init();\r
-#ifdef SUPPORT_AT_BT_MANAGER\r
-    set_calibration_from_nv();\r
-#endif\r
-    /* dfx init step 2: dfx init */\r
-#ifdef HSO_SUPPORT\r
-    dfx_system_init();\r
-#endif\r
-    /* power manager init */\r
-    uapi_pm_init();\r
-#ifdef SUPPORT_POWER_MANAGER\r
-    uapi_power_display_init();\r
-#endif\r
-\r
-    register_at_service();\r
-}\r
+
+/*
+ * Copyright (c) CompanyNameMagicTag 2021-2022. All rights reserved.
+ * Description: Application core board init function for standard
+ * Author:
+ * Create:
+ */
+
+#ifdef __LITEOS__
+#include "partition.h"
+#endif
+#include "pm.h"
+#include "cpu_utils.h"
+#include "log_common.h"
+#include "log_uart.h"
+#include "exception.h"
+#include "log_oml_exception.h"
+#ifdef HSO_SUPPORT
+#include "dfx_system_init.h"
+#endif
+#include "debug_print.h"
+#ifdef SUPPORT_AT_GRAPHIC
+#include "graphic_cmd_manager.h"
+#endif
+#ifdef SUPPORT_AT_JSINSTALL
+#include "ohosfwk_at_process.h"
+#endif
+#ifdef SUPPORT_AT_APP
+#include "app_cmd_manager.h"
+#endif
+#ifdef SUPPORT_AT_AUDIO
+#include "audio_cmd_manager.h"
+#endif
+#ifdef SUPPORT_AT_BT_MANAGER
+#include "bt_manager_cmd_manager.h"
+#endif
+#ifdef SUPPORT_AT_MEDIA
+#include "media_cmd_manager.h"
+#endif
+#ifdef SUPPORT_AT_PPG
+#include "ppg_at_cmd.h"
+#endif
+
+#ifdef SUPPORT_AT_VIBRATOR
+#include "vibrator_at_cmd.h"
+#endif
+
+#ifdef SUPPORT_AT_HX3507
+#include "hx3507_at_cmd.h"
+#endif
+
+#ifdef SUPPORT_AT_BATTERY
+#include "battery_at_cmd.h"
+#endif
+
+#ifdef SUPPORT_AT_LIS2DOC
+#include "lis2doc_at_cmd.h"
+#endif
+
+#ifdef SUPPORT_AT_CYWEE
+#include "cywee_at_cmd.h"
+#endif
+
+#ifdef SUPPORT_AT_MAG
+#include "mag_at_cmd.h"
+#endif
+
+#ifdef SUPPORT_AT_CUSTOMUTILS
+#include "custom_utils_at_cmd.h"
+#endif
+#ifdef SUPPORT_AT_CAT1
+#include "cat1_at_cmd.h"
+#endif
+#ifdef SUPPORT_AT_PPP
+#include "ppp_at_cmd.h"
+#endif
+#include "log_file.h"
+#ifdef CONFIG_OTA_UPDATE_SUPPORT
+#include "upg.h"
+#endif
+#ifdef SUPPORT_AT_HX3508
+#include "hx3508_at_cmd.h"
+#endif
+#ifdef SUPPORT_AT_LQBP002TA
+#include "lqbp_at_cmd.h"
+#endif
+#ifdef SUPPORT_AT_CHARGE
+#include "charge_at_cmd.h"
+#endif
+#ifdef SUPPORT_AT_CROWN
+#include "crown_at_cmd.h"
+#endif
+#ifdef SUPPORT_AT_MIXO
+#include "mixo_at_cmd.h"
+#endif
+#ifdef SUPPORT_AT_GYRO
+#include "gyro_at_cmd.h"
+#endif
+#ifdef SUPPORT_AT_BP
+#include "bp_at_cmd.h"
+#endif
+#ifdef SUPPORT_AT_LEINIAO_API
+#include "leiniao_api_at.h"
+#include "uapi_mqtt_at.h"
+#include "ln_mqtt_api_at.h"
+#endif
+#ifdef SUPPORT_ZHIHUI_AI_SERVICE
+#include "ai_at_cmd.h"
+#endif
+#ifdef SUPPORT_ZHIHUI_AI_OLD
+#include "ai_old_at_cmd.h"
+#endif
+
+#ifdef SUPPORT_ZHIHUI_AI_NEW
+#include "ai_V2_5_at_cmd.h"
+#endif
+
+#ifdef ENABLE_VAD_SERVER
+#include "imsl_at_cmd.h"
+#endif
+
+#ifdef SUPPORT_ZHIHUI_DMP
+#include "dmp_api_at.h"
+#endif
+
+#ifdef SUPPORT_AT_AI_API
+#include "ai_api_at.h"
+#endif
+
+static void register_at_service(void)
+{
+#ifdef SUPPORT_AT_GRAPHIC
+    uint32_t graphic_cmd_count;
+    at_cmd_table_t *graphic_table = get_graphic_cmd_table(&graphic_cmd_count);
+    at_cmd_add_moudle(graphic_table, graphic_cmd_count);
+#endif
+#ifdef SUPPORT_AT_JSINSTALL
+    uint32_t ohosfwk_cmd_count;
+    at_cmd_table_t *ohosfwk_table = get_ohosfwk_cmd_table(&ohosfwk_cmd_count);
+    at_cmd_add_moudle(ohosfwk_table, ohosfwk_cmd_count);
+#endif
+#ifdef SUPPORT_AT_APP
+    uint32_t app_cmd_count;
+    at_cmd_table_t *app_table = get_app_cmd_table(&app_cmd_count);
+    at_cmd_add_moudle(app_table, app_cmd_count);
+#endif
+#ifdef SUPPORT_AT_AUDIO
+    uint32_t audio_cmd_count;
+    at_cmd_table_t *audio_table = get_audio_cmd_table(&audio_cmd_count);
+    at_cmd_add_moudle(audio_table, audio_cmd_count);
+#endif
+#ifdef SUPPORT_AT_BT_MANAGER
+    uint32_t bt_manager_cmd_count;
+    at_cmd_table_t *bt_manager_table = get_bt_manager_cmd_table(&bt_manager_cmd_count);
+    at_cmd_add_moudle(bt_manager_table, bt_manager_cmd_count);
+#endif
+#ifdef SUPPORT_AT_MEDIA
+    uint32_t media_cmd_count;
+    at_cmd_table_t *media_table = get_media_cmd_table(&media_cmd_count);
+    at_cmd_add_moudle(media_table, media_cmd_count);
+#endif
+
+#ifdef SUPPORT_AT_PPG
+    uint32_t hx3695_cmd_count;
+    at_cmd_table_t *hx3695_table = get_ppg_cmd_table(&hx3695_cmd_count);
+    at_cmd_add_moudle(hx3695_table, hx3695_cmd_count);
+#endif
+
+#ifdef SUPPORT_AT_VIBRATOR
+    uint32_t vibrator_cmd_count;
+    at_cmd_table_t *vibrator_table = get_vibrator_cmd_table(&vibrator_cmd_count);
+    at_cmd_add_moudle(vibrator_table, vibrator_cmd_count);
+#endif
+
+#ifdef SUPPORT_AT_HX3507
+    uint32_t hx3507_cmd_count;
+    at_cmd_table_t *hx3507_table = get_hx3507_cmd_table(&hx3507_cmd_count);
+    at_cmd_add_moudle(hx3507_table, hx3507_cmd_count);
+#endif
+
+#ifdef SUPPORT_AT_BATTERY
+    uint32_t battery_cmd_count;
+    at_cmd_table_t *battery_table = get_battery_cmd_table(&battery_cmd_count);
+    at_cmd_add_moudle(battery_table, battery_cmd_count);
+#endif
+
+#ifdef SUPPORT_AT_CUSTOMUTILS
+    uint32_t custom_utils_cmd_count;
+    at_cmd_table_t *custom_utils_table = get_custom_utils_cmd_table(&custom_utils_cmd_count);
+    at_cmd_add_moudle(custom_utils_table, custom_utils_cmd_count);
+#endif
+#ifdef SUPPORT_AT_CAT1
+    uint32_t cat1_cmd_count;
+    at_cmd_table_t *cat1_table = get_cat1_cmd_table(&cat1_cmd_count);
+    at_cmd_add_moudle(cat1_table, cat1_cmd_count);
+#endif
+
+#ifdef SUPPORT_AT_PPP
+    uint32_t ppp_cmd_count;
+    at_cmd_table_t *ppp_table = get_ppp_cmd_table(&ppp_cmd_count);
+    at_cmd_add_moudle(ppp_table, ppp_cmd_count);
+#endif
+
+#ifdef SUPPORT_ZHIHUI_AI_OLD
+    uint32_t ai_old_cmd_count;
+    at_cmd_table_t *ai_old_table = get_ai_old_cmd_table(&ai_old_cmd_count);
+    at_cmd_add_moudle(ai_old_table, ai_old_cmd_count);
+#endif
+
+#ifdef SUPPORT_ZHIHUI_AI_NEW
+    uint32_t ai_new_cmd_count;
+    at_cmd_table_t *ai_new_table = get_ai_2_5_cmd_table(&ai_new_cmd_count);
+    at_cmd_add_moudle(ai_new_table, ai_new_cmd_count);
+#endif
+
+#ifdef SUPPORT_AT_LIS2DOC
+    uint32_t lis2doc_cmd_count;
+    at_cmd_table_t *lis2doc_table = get_lis2doc_cmd_table(&lis2doc_cmd_count);
+    at_cmd_add_moudle(lis2doc_table, lis2doc_cmd_count);
+#endif
+
+#ifdef SUPPORT_AT_CYWEE
+    uint32_t cywee_cmd_count;
+    at_cmd_table_t *cywee_table = get_cywee_cmd_table(&cywee_cmd_count);
+    at_cmd_add_moudle(cywee_table, cywee_cmd_count);
+#endif
+
+#ifdef SUPPORT_AT_MAG
+    uint32_t mag_cmd_count;
+    at_cmd_table_t *mag_table = get_mag_cmd_table(&mag_cmd_count);
+    at_cmd_add_moudle(mag_table, mag_cmd_count);
+#endif
+
+#ifdef SUPPORT_AT_RTCTEST
+    uint32_t rtc_cmd_count;
+    at_cmd_table_t *rtc_table = get_rtc_cmd_table(&rtc_cmd_count);
+    at_cmd_add_moudle(rtc_table, rtc_cmd_count);
+#endif
+#ifdef SUPPORT_AT_HX3508
+    uint32_t hx3508_cmd_count;
+    at_cmd_table_t *hx3508_table = get_hx3508_cmd_table(&hx3508_cmd_count);
+    at_cmd_add_moudle(hx3508_table, hx3508_cmd_count);
+#endif
+#ifdef SUPPORT_AT_LQBP002TA
+    uint32_t lqbp002ta_cmd_count;
+    at_cmd_table_t *lqbp002ta_table = get_lqbp002ta_cmd_table(&lqbp002ta_cmd_count);
+    at_cmd_add_moudle(lqbp002ta_table, lqbp002ta_cmd_count);
+#endif
+#ifdef SUPPORT_AT_CHARGE
+    uint32_t charge_cmd_count;
+    at_cmd_table_t *charge_table = get_charge_cmd_table(&charge_cmd_count);
+    at_cmd_add_moudle(charge_table, charge_cmd_count);    
+#endif
+#ifdef SUPPORT_AT_CROWN
+    uint32_t crown_cmd_count;
+    at_cmd_table_t *crown_table = get_crown_cmd_table(&crown_cmd_count);
+    at_cmd_add_moudle(crown_table, crown_cmd_count);    
+#endif
+#ifdef SUPPORT_AT_MIXO
+    uint32_t mixo_cmd_count;
+    at_cmd_table_t *mixo_table = get_mixo_cmd_table(&mixo_cmd_count);
+    at_cmd_add_moudle(mixo_table, mixo_cmd_count);
+#endif
+#ifdef SUPPORT_AT_GYRO
+    uint32_t gyro_cmd_count;
+    at_cmd_table_t *gyro_table = get_gyro_cmd_table(&gyro_cmd_count);
+    at_cmd_add_moudle(gyro_table, gyro_cmd_count);
+#endif
+#ifdef SUPPORT_AT_BP
+    uint32_t bp_cmd_count;
+    at_cmd_table_t *bp_table = get_bp_cmd_table(&bp_cmd_count);
+    at_cmd_add_moudle(bp_table, bp_cmd_count);
+#endif
+#ifdef SUPPORT_AT_LEINIAO_API
+    uint32_t leiniao_api_cmd_count;
+    at_cmd_table_t *leiniao_api_table = get_leiniao_api_cmd_table(&leiniao_api_cmd_count);
+    at_cmd_add_moudle(leiniao_api_table, leiniao_api_cmd_count);
+    
+    uint32_t uapi_mqtt_at_cmd_count;
+    at_cmd_table_t *uapi_mqtt_at_table = get_uapi_mqtt_at_cmd_table(&uapi_mqtt_at_cmd_count);
+    at_cmd_add_moudle(uapi_mqtt_at_table, uapi_mqtt_at_cmd_count);
+#ifdef SUPPORT_LN_MQTT_SERVICE
+    uint32_t ln_mqtt_at_cmd_count;
+    at_cmd_table_t *ln_mqtt_at_table = get_ln_mqtt_at_cmd_table(&ln_mqtt_at_cmd_count);
+    at_cmd_add_moudle(ln_mqtt_at_table, ln_mqtt_at_cmd_count);
+#endif
+
+#endif
+
+#ifdef SUPPORT_ZHIHUI_AI_SERVICE
+    uint32_t ai_cmd_count;
+    at_cmd_table_t *ai_table = get_ai_cmd_table(&ai_cmd_count);
+    at_cmd_add_moudle(ai_table, ai_cmd_count);
+#endif
+
+#ifdef ENABLE_VAD_SERVER
+    uint32_t imsl_vad_cmd_count;
+    at_cmd_table_t *imsl_vad_table = get_imsl_vad_at_cmd_table(&imsl_vad_cmd_count);
+    at_cmd_add_moudle(imsl_vad_table, imsl_vad_cmd_count);
+#endif
+
+
+#ifdef SUPPORT_ZHIHUI_DMP
+    uint32_t dmp_api_cmd_count;
+    at_cmd_table_t *dmp_api_table = get_dmp_api_cmd_table(&dmp_api_cmd_count);
+    at_cmd_add_moudle(dmp_api_table, dmp_api_cmd_count);
+#endif
+
+// #ifdef SUPPORT_AT_AI_API
+//     uint32_t ai_api_cmd_count;
+//     at_cmd_table_t *ai_api_table = get_ai_api_cmd_table(&ai_api_cmd_count);
+//     at_cmd_add_moudle(ai_api_table, ai_api_cmd_count);
+// #endif
+
+}
+
+#ifdef CONFIG_OTA_UPDATE_SUPPORT
+static void upg_putc(const char c)
+{
+    printf("%c", c);
+}
+#endif
+
+void service_init(void)
+{
+#if defined(FREERTOS_TEST)
+    // freertos demo test
+    extern int main_full(void);
+    (void)main_full();
+    for (;;) {
+    }
+#endif
+
+    /* dfx init step 1: log init */
+    log_init();
+    log_init_after_rtos();
+    log_uart_init_after_rtos();
+
+    cpu_utils_init();
+    hal_register_exception_dump_callback(log_exception_dump);
+    uapi_partition_init();
+    PRINT("partition init success"NEWLINE);
+
+#ifdef CONFIG_OTA_UPDATE_SUPPORT
+    upg_func_t upg_func = {0};
+    upg_func.malloc = malloc;
+    upg_func.free = free;
+    upg_func.serial_putc = upg_putc;
+    (void)uapi_upg_init(&upg_func);
+    PRINT("upg init success\r\n");
+#endif
+    crypto_drv_init();
+    uapi_nv_init();
+#ifdef SUPPORT_AT_BT_MANAGER
+    set_calibration_from_nv();
+#endif
+    /* dfx init step 2: dfx init */
+#ifdef HSO_SUPPORT
+    dfx_system_init();
+#endif
+    /* power manager init */
+    uapi_pm_init();
+#ifdef SUPPORT_POWER_MANAGER
+    uapi_power_display_init();
+#endif
+
+    register_at_service();
+}
index 124949c..605ee07 100644 (file)
@@ -1293,6 +1293,7 @@ target = {
             # 'CYWEE_MAC_ACTIVE',                      
             #'SUPPORT_HID_SERVICE',
             # 'SUPPORT_BAIDU_MAP',
+            # 'SUPPORT_LN_MQTT_SERVICE',
             'CFG_FLASH_SIZE=16',
             'CONFIG_NORFLASH_SIZE=16',
             'SINGLE_USER_PARTITION',
@@ -1331,6 +1332,7 @@ target = {
                          #cat1 相关配置
                          #有方需要开启  PPP
                         #   "ppp",
+                        # "mqtt_service",
 
                         #EG810 需要开启 cat1Service 和 cat1Test
                         "cat1Service",
@@ -1352,6 +1354,7 @@ target = {
                         #VAD 相关配置
                         "imsl",
                         "minimp3",
+                        # "opus_frame_to_pcm",
                         #   "CustomUIcomponentTool",
                           "nativeabilityfwk",
                           "custom_utils",
index 1604770..163619e 100644 (file)
@@ -102,6 +102,7 @@ set(PUBLIC_HEADER
     ${ROOT_DIR}/kernel/dpal/device/include
     ${ROOT_DIR}/kernel/osal/include
     ${FREERTOSTOPDIR}/sys/dev/mmc
+    ${ROOT_DIR}/open_source/openharmony/third_party/lwip/lwip_adapter/liteos/src/include
 )
 
     aux_source_directory(${FS_PATH}/yaffs2/os_adapt/adapt_freertos PUBLIC_HEADER)
index 771ce0a..9a9bb95 100644 (file)
@@ -144,7 +144,7 @@ static void mqtt_queue_init(void)
 static void mqtt_connection_status_cb(void *arg, int status)
 {
     (void)arg;
-    MQTT_DEBUG("Connection status callback: %d\n", status);
+    MQTT_ERROR("Connection status callback: %d\n", status);
     
     // 更新连接状态码
     g_mqtt_connection_status = status;
index fbd4a77..b98910b 100644 (file)
@@ -81,6 +81,25 @@ static void mqtt_at_message_cb(void *arg, const char *topic, const uint8_t *data
     }
 }
 
+/* OTA-发送设备信息到服务器:AT^LN_OTA_SEND_DEVICE_INFO */
+// static uint32_t ln_ota_send_device_info_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
+// {
+//     (void)para;
+//     (void)paraLen;
+//     (void)strCommonCmd;
+    
+//     printf("[LN][OTA_SEND_DEVICE_INFO]\n");
+    
+//     // 直接调用封装函数,使用默认URL(NULL表示使用默认URL)
+//     int ret = ln_ota_send_device_info_json(NULL, NULL);
+//     if (ret == 0) {
+//         printf("+LN_OTA_SEND_DEVICE_INFO: OK\n");
+//         return ERRCODE_SUCC;
+//     } else {
+//         printf("+LN_OTA_SEND_DEVICE_INFO: FAIL\n");
+//         return ERRCODE_FAIL;
+//     }
+// }
 
 /* MQTT-初始化:AT^LN_MQTT_INIT */
 static uint32_t uapi_mqtt_init_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd)
@@ -177,7 +196,7 @@ static uint32_t uapi_mqtt_AudioCaptureSample_at(uint8_t *para, uint32_t paraLen,
     (void)paraLen;
     (void)strCommonCmd;
     
-    const char *defaultArgs[6] = {"01", "opus", "16000", "1", "16", "user/userdata/test.ogg"};
+    const char *defaultArgs[6] = {"01", "sbc", "16000", "1", "16", "user/userdata/test.sbc"};
 
     int ret = AudioCaptureSample(6, defaultArgs);
     LOS_Msleep(1000*10);
index 074c677..bab9283 100644 (file)
 #include "opus_frame_to_pcm.h"
 #include "ai_media.h"
 
-/* 双流接口开关:定义 USE_DUAL_STREAM_INTERFACE 则使用 old_Start_AIVoice 双流接口,否则使用 MQTT_StartRecord */
-/* 可在 CMakeLists.txt 或编译选项中定义,例如:-DUSE_DUAL_STREAM_INTERFACE=1 */
-#ifndef USE_DUAL_STREAM_INTERFACE
-#define USE_DUAL_STREAM_INTERFACE 0
-#endif
-
 /* OGG 封装相关前向声明(不透明句柄,避免依赖 ogg.h) */
 /* OGG 上下文句柄(不透明指针,实际类型在 ai_media_service_mqtt.cpp 中定义) */
 typedef void* MqttOggContextHandle;
@@ -107,10 +101,33 @@ static uint32_t g_ogg_out_of_order_count = 0;  // 连续错序计数器(用于
 #define OGG_MAX_OUT_OF_ORDER_COUNT 5  // 最大连续错序次数,超过后跳过一包
 #define AUDIO_SEND_MAX_DATA_SIZE 0x800  // 最大音频数据大小(与 LN_MQTT_RECORD_MAX_FRAME 一致)
 
+/* 音频编码类型:入队时 data 的格式 */
+#define AUDIO_SEND_ENCODING_OPUS 0  // 已是 Opus,直接发送
+#define AUDIO_SEND_ENCODING_PCM  1  // PCM,发送前用 opus_pcm_to_frame_encode_packet 编码为 Opus
+
+/* PCM 编码默认参数(encoding==PCM 时使用):输入为 16kHz 单声道、16-bit 小端 */
+#define AUDIO_SEND_PCM_SAMPLE_RATE  16000  /* 采样率 16kHz */
+#define AUDIO_SEND_PCM_CHANNELS     1      /* 单声道 */
+#define AUDIO_SEND_PCM_BITRATE     64000
+#define AUDIO_SEND_PCM_APPLICATION 2049  /* Opus: OPUS_APPLICATION_AUDIO,必须为 2048/2049/2051 之一 */
+#define AUDIO_SEND_PCM_FRAME_MS    60  // 每帧 60ms,切割固定长度再编码
+/* 60ms 对应字节数:sample_rate * frame_ms/1000 * channels * 2(bytes/sample) */
+#define AUDIO_SEND_PCM_FRAME_BYTES ((uint32_t)((AUDIO_SEND_PCM_SAMPLE_RATE) * (AUDIO_SEND_PCM_FRAME_MS) / 1000 * (AUDIO_SEND_PCM_CHANNELS) * 2))
+#define AUDIO_SEND_PCM_FRAME_SAMPLES ((int)((AUDIO_SEND_PCM_SAMPLE_RATE) * (AUDIO_SEND_PCM_FRAME_MS) / 1000))
+/* 动态分配容量:帧数可调以增大缓存(约 6s @ 60ms/帧) */
+#define AUDIO_SEND_PCM_CACHE_ALLOC_FRAMES 100
+#define AUDIO_SEND_PCM_CACHE_ALLOC_SIZE   ((AUDIO_SEND_PCM_CACHE_ALLOC_FRAMES) * (AUDIO_SEND_PCM_FRAME_BYTES) + (AUDIO_SEND_MAX_DATA_SIZE))
+
+/* PCM 发送缓存:动态分配,新数据追加,满 60ms 切割编码,余量保留 */
+static uint8_t *g_pcm_send_cache = NULL;
+static uint32_t g_pcm_send_cache_capacity = 0;  /* 已分配容量(字节) */
+static uint32_t g_pcm_send_cache_size = 0;      /* 当前有效数据长度 */
+
 /* 音频数据消息结构体 */
 typedef struct {
     uint8_t *data;    // 音频数据指针(动态分配)
-    uint32_t size;    // 数据大小
+    uint32_t size;    // 数据大小(字节)
+    uint8_t  encoding; // 0=Opus,1=PCM(发送前编码为 Opus)
 } audio_send_msg_t;
 
 static unsigned long g_audio_send_queue = 0xFFFFFFFF;  // 消息队列句柄
@@ -1077,6 +1094,28 @@ static int ln_udp_dual_stream_handle_packet(uint32_t sequence, const uint8_t *da
 }
 #endif /* USE_DUAL_STREAM_INTERFACE */
 
+/* 接收线程内固定时间间隔(秒)后重新创建 socket,0 表示不按时间重建 */
+#define UDP_RECV_RECREATE_INTERVAL_SEC 0
+
+/**
+ * @brief 仅创建并配置 UDP socket(不启动线程),供创建与接收线程内重建复用
+ * @return socket fd,失败返回 -1
+ */
+static int ln_udp_create_socket_fd_only(void)
+{
+    ln_mqtt_udp_info_t udp_info;
+    if (ln_mqtt_api_get_udp_info(&udp_info) != 0 || !udp_info.valid) {
+        return -1;
+    }
+    int fd = lwip_socket(AF_INET, SOCK_DGRAM, 0);
+    if (fd < 0) {
+        return -1;
+    }
+    int opt = 1;
+    lwip_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+    return fd;
+}
+
 /**
  * @brief UDP接收线程
  */
@@ -1086,9 +1125,11 @@ static void ln_udp_recv_thread(void *arg)
     uint8_t recv_buffer[UDP_RECV_BUFFER_SIZE];
     struct sockaddr_in server_addr;
     socklen_t addr_len = sizeof(server_addr);
-    
+    uint32_t last_recreate_tick = 0;
+    uint32_t recreate_interval_ticks = 0;
+
     printf("[LN_UDP] Receive thread started\n");
-    
+
     // 获取服务器地址信息
     ln_mqtt_udp_info_t udp_info;
     if (ln_mqtt_api_get_udp_info(&udp_info) != 0 || !udp_info.valid) {
@@ -1096,40 +1137,68 @@ static void ln_udp_recv_thread(void *arg)
         g_udp_thread_running = false;
         return;
     }
-    
+
     // 设置服务器地址
     memset(&server_addr, 0, sizeof(server_addr));
     server_addr.sin_family = AF_INET;
     server_addr.sin_port = lwip_htons(udp_info.port);
     server_addr.sin_addr.s_addr = inet_addr(udp_info.server);
 
-    
-    // printf("[LN_UDP] Ready to receive UDP packets from %s:%d\n", udp_info.server, udp_info.port);
-    
+    if (UDP_RECV_RECREATE_INTERVAL_SEC > 0) {
+        recreate_interval_ticks = (uint32_t)osKernelGetTickFreq() * UDP_RECV_RECREATE_INTERVAL_SEC;
+        last_recreate_tick = osKernelGetTickCount();
+    }
+
     while (g_udp_thread_running && g_udp_socket >= 0) {
+        printf("g_udp_thread_running %d g_udp_socket %d\r\n",g_udp_thread_running,g_udp_socket);
+        // 固定时间间隔到达则重新创建 socket
+        if (UDP_RECV_RECREATE_INTERVAL_SEC > 0 && recreate_interval_ticks > 0) {
+            uint32_t now = osKernelGetTickCount();
+            if ((now - last_recreate_tick) >= recreate_interval_ticks) {
+                printf("[LN_UDP] Recreate interval reached (%u s), recreating socket\n", (unsigned)UDP_RECV_RECREATE_INTERVAL_SEC);
+                lwip_close(g_udp_socket);
+                g_udp_socket = ln_udp_create_socket_fd_only();
+                if (g_udp_socket < 0) {
+                    printf("[LN_UDP] Failed to recreate socket: %s\n", strerror(errno));
+                    break;
+                }
+                /* 重新获取 UDP 地址信息(服务端地址/端口可能变化) */
+                if (ln_mqtt_api_get_udp_info(&udp_info) != 0 || !udp_info.valid) {
+                    printf("[LN_UDP] UDP info not available after recreate, keep last address\n");
+                } else {
+                    memset(&server_addr, 0, sizeof(server_addr));
+                    server_addr.sin_family = AF_INET;
+                    server_addr.sin_port = lwip_htons(udp_info.port);
+                    server_addr.sin_addr.s_addr = inet_addr(udp_info.server);
+                    addr_len = sizeof(server_addr);
+                }
+                last_recreate_tick = osKernelGetTickCount();
+            }
+        }
+
         // 接收UDP数据(阻塞,超时1秒)
         fd_set read_fds;
         struct timeval timeout;
-        
+
         FD_ZERO(&read_fds);
         FD_SET(g_udp_socket, &read_fds);
         timeout.tv_sec = 1;
         timeout.tv_usec = 0;
-        
+
         int select_ret = lwip_select(g_udp_socket + 1, &read_fds, NULL, NULL, &timeout);
         if (select_ret > 0 && FD_ISSET(g_udp_socket, &read_fds)) {
             // 有数据可读
             ssize_t recv_len = lwip_recvfrom(g_udp_socket, recv_buffer, sizeof(recv_buffer) - 1, 0,
                                              (struct sockaddr *)&server_addr, &addr_len);
-            
+
             if (recv_len > 0) {
-                printf("[LN_UDP] Received %d bytes\r\n", 
+                printf("[LN_UDP] Received %d bytes\r\n",
                        (int)recv_len
                     );
-                
+
                 // 处理接收到的数据(解包、解密)
                 ln_udp_process_received_data(recv_buffer, (uint16_t)recv_len);
-                
+
             } else if (recv_len == 0) {
                 printf("[LN_UDP] Connection closed by peer\n");
                 break;
@@ -1139,6 +1208,7 @@ static void ln_udp_recv_thread(void *arg)
                 }
             }
         } else if (select_ret < 0) {
+            printf("select_ret %d\r\n",select_ret);
             if (errno != EINTR) {
                 printf("[LN_UDP] Select error: %s\n", strerror(errno));
                 break;
@@ -1146,7 +1216,7 @@ static void ln_udp_recv_thread(void *arg)
         }
         // select_ret == 0 表示超时,继续循环
     }
-    
+
     printf("[LN_UDP] Receive thread exited\n");
     g_udp_thread_running = false;
 }
@@ -1170,28 +1240,20 @@ int ln_udp_create_socket(void)
     }
     
     printf("[LN_UDP] Creating UDP socket for server: %s:%d\n", udp_info.server, udp_info.port);
-    
+
     // 如果socket已存在,先关闭
     if (g_udp_socket >= 0) {
         printf("[LN_UDP] Closing existing UDP socket\n");
         ln_udp_close_socket();
     }
-    
-    // 创建UDP套接字
-    g_udp_socket = lwip_socket(AF_INET, SOCK_DGRAM, 0);
+
+    // 创建UDP套接字(复用仅创建 fd 的逻辑)
+    g_udp_socket = ln_udp_create_socket_fd_only();
     if (g_udp_socket < 0) {
         printf("[LN_UDP] Failed to create UDP socket: %s\n", strerror(errno));
         return -1;
     }
-    
-    // 设置socket选项(允许地址重用)
-    int opt = 1;
-    lwip_setsockopt(g_udp_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
-    
-    // 设置为非阻塞模式(可选,用于select超时)
-    // int flags = lwip_fcntl(g_udp_socket, F_GETFL, 0);
-    // lwip_fcntl(g_udp_socket, F_SETFL, flags | O_NONBLOCK);
-    
+
     printf("[LN_UDP] UDP socket created successfully, fd=%d\n", g_udp_socket);
     
     // 创建接收线程
@@ -1578,6 +1640,50 @@ static void create_audio_send_queue(void)
     }
 }
 
+/**
+ * @brief 发送单帧音频数据(打包/加密 + 发送)
+ * @note 复用原有 ln_udp_pack_audio_data 加密逻辑
+ * @return 0 成功,-1 失败
+ */
+static int ln_udp_send_audio_frame(int sock, const uint8_t *audio_data, uint32_t audio_size)
+{
+    if (audio_data == NULL || audio_size == 0 || audio_size > AUDIO_SEND_MAX_DATA_SIZE) {
+        return -1;
+    }
+
+    // 打包(加密+UDP头)
+    uint8_t send_buf[UDP_PACKET_HEADER_SIZE + AUDIO_SEND_MAX_DATA_SIZE];
+    uint16_t out_len = 0;
+    if (ln_udp_pack_audio_data(audio_data, (uint16_t)audio_size, send_buf,
+                               (uint16_t)sizeof(send_buf), &out_len) != 0) {
+        printf("[LN_UDP] Failed to pack audio frame\n");
+        return -1;
+    }
+
+    // 获取UDP信息(用于发送)
+    ln_mqtt_udp_info_t udp_info;
+    if (ln_mqtt_api_get_udp_info(&udp_info) != 0 || !udp_info.valid) {
+        printf("[LN_UDP] UDP info not available, drop audio frame\n");
+        return -1;
+    }
+
+    // 发送到服务器
+    struct sockaddr_in server_addr;
+    memset(&server_addr, 0, sizeof(server_addr));
+    server_addr.sin_family = AF_INET;
+    server_addr.sin_port = lwip_htons(udp_info.port);
+    server_addr.sin_addr.s_addr = inet_addr(udp_info.server);
+
+    ssize_t sent = lwip_sendto(sock, send_buf, out_len, 0,
+                               (struct sockaddr *)&server_addr, sizeof(server_addr));
+    if (sent < 0) {
+        printf("[LN_UDP] UDP sendto failed: %s\n", strerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
 /**
  * @brief 音频数据发送线程
  */
@@ -1622,37 +1728,72 @@ static void ln_udp_audio_send_thread(void *arg)
             continue;
         }
         
-        // 打包(加密+UDP头)
-        uint8_t send_buf[UDP_PACKET_HEADER_SIZE + AUDIO_SEND_MAX_DATA_SIZE];
-        uint16_t out_len = 0;
-        if (ln_udp_pack_audio_data(msg.data, (uint16_t)msg.size, send_buf, 
-                                   (uint16_t)sizeof(send_buf), &out_len) != 0) {
-            printf("[LN_UDP] Failed to pack audio frame\n");
+        const uint8_t *send_data = msg.data;
+        uint32_t send_size = msg.size;
+        if (msg.encoding == AUDIO_SEND_ENCODING_PCM) {
+            /* PCM:追加到缓存,按 60ms 切割编码,余量保留 */
+            if (g_pcm_send_cache == NULL) {
+                printf("[LN_UDP] PCM cache not allocated, drop %u bytes\n", msg.size);
+                free(msg.data);
+                continue;
+            }
+            while (g_pcm_send_cache_size + msg.size > g_pcm_send_cache_capacity &&
+                   g_pcm_send_cache_size >= AUDIO_SEND_PCM_FRAME_BYTES) {
+                /* 缓存满时丢弃最旧一帧,避免持续溢出 */
+                g_pcm_send_cache_size -= AUDIO_SEND_PCM_FRAME_BYTES;
+                memmove(g_pcm_send_cache, g_pcm_send_cache + AUDIO_SEND_PCM_FRAME_BYTES, g_pcm_send_cache_size);
+            }
+            if (g_pcm_send_cache_size + msg.size > g_pcm_send_cache_capacity) {
+                printf("[LN_UDP] PCM cache full, drop %u bytes (cache=%u, cap=%u)\n", msg.size, g_pcm_send_cache_size, g_pcm_send_cache_capacity);
+                free(msg.data);
+                continue;
+            }
+            memcpy(g_pcm_send_cache + g_pcm_send_cache_size, msg.data, msg.size);
+            g_pcm_send_cache_size += msg.size;
             free(msg.data);
+            msg.data = NULL;
+
+            /* 每次取 60ms 固定长度编码并发送,多余数据留在缓存 */
+            uint8_t opus_buf[1500];
+
+            while (g_pcm_send_cache_size >= AUDIO_SEND_PCM_FRAME_BYTES) {
+                int encoded_len = 0;
+                printf("[LN_UDP] PCM send cache size: %d\n", g_pcm_send_cache_size);
+                for (int i = 0; i < AUDIO_SEND_PCM_FRAME_BYTES; i++) {
+                    printf("%02x ", g_pcm_send_cache[i]);
+                }
+                printf("\n");
+                
+                if (opus_pcm_to_frame_encode_packet(
+                        (const short *)g_pcm_send_cache, AUDIO_SEND_PCM_FRAME_SAMPLES,
+                        AUDIO_SEND_PCM_SAMPLE_RATE, AUDIO_SEND_PCM_CHANNELS,
+                        AUDIO_SEND_PCM_BITRATE, AUDIO_SEND_PCM_APPLICATION,
+                        opus_buf, (int)sizeof(opus_buf), &encoded_len) != 0) {
+                    printf("[LN_UDP] PCM to Opus encode failed, drop 60ms frame\n");
+                    break;
+                }
+                printf("[LN_UDP] Opus encoded len: %d\n", encoded_len); 
+                printf("[LN_UDP] Opus encoded data: ");
+                for (int i = 0; i < encoded_len; i++) {
+                    printf("%02x ", opus_buf[i]);
+                }
+                printf("\n");
+                if (ln_udp_send_audio_frame(sock, opus_buf, (uint32_t)encoded_len) != 0) {
+                    break;
+                }
+                /* 移除已编码的一帧,保留余量 */
+                g_pcm_send_cache_size -= AUDIO_SEND_PCM_FRAME_BYTES;
+                memmove(g_pcm_send_cache, g_pcm_send_cache + AUDIO_SEND_PCM_FRAME_BYTES, g_pcm_send_cache_size);
+            }
             continue;
         }
         
-        // 获取UDP信息(用于发送)
-        ln_mqtt_udp_info_t udp_info;
-        if (ln_mqtt_api_get_udp_info(&udp_info) != 0 || !udp_info.valid) {
-            printf("[LN_UDP] UDP info not available, drop audio frame\n");
+        (void)send_data;
+        if (ln_udp_send_audio_frame(sock, send_data, send_size) != 0) {
             free(msg.data);
             continue;
         }
         
-        // 发送到服务器
-        struct sockaddr_in server_addr;
-        memset(&server_addr, 0, sizeof(server_addr));
-        server_addr.sin_family = AF_INET;
-        server_addr.sin_port = lwip_htons(udp_info.port);
-        server_addr.sin_addr.s_addr = inet_addr(udp_info.server);
-        
-        ssize_t sent = lwip_sendto(sock, send_buf, out_len, 0, 
-                                   (struct sockaddr *)&server_addr, sizeof(server_addr));
-        if (sent < 0) {
-            printf("[LN_UDP] UDP sendto failed: %s\n", strerror(errno));
-        }
-        
         // 释放音频数据内存
         free(msg.data);
         msg.data = NULL;
@@ -1668,10 +1809,27 @@ static void ln_udp_audio_send_thread(void *arg)
  */
 int ln_udp_audio_send_init(void)
 {
+    // 动态分配 PCM 发送缓存(若未分配或需扩容可在此统一申请)
+    if (g_pcm_send_cache == NULL) {
+        g_pcm_send_cache = (uint8_t *)malloc(AUDIO_SEND_PCM_CACHE_ALLOC_SIZE);
+        if (g_pcm_send_cache == NULL) {
+            printf("[LN_UDP] Failed to allocate PCM send cache (%u bytes)\n", (unsigned)AUDIO_SEND_PCM_CACHE_ALLOC_SIZE);
+            return -1;
+        }
+        g_pcm_send_cache_capacity = AUDIO_SEND_PCM_CACHE_ALLOC_SIZE;
+        g_pcm_send_cache_size = 0;
+        printf("[LN_UDP] PCM send cache allocated: %u bytes\n", g_pcm_send_cache_capacity);
+    }
+
     // 创建消息队列
     create_audio_send_queue();
     if (g_audio_send_queue == 0xFFFFFFFF) {
         printf("[LN_UDP] Failed to create audio send queue\n");
+        if (g_pcm_send_cache != NULL) {
+            free(g_pcm_send_cache);
+            g_pcm_send_cache = NULL;
+            g_pcm_send_cache_capacity = 0;
+        }
         return -1;
     }
     
@@ -1680,7 +1838,7 @@ int ln_udp_audio_send_init(void)
         app_task_definition_ext_t send_task = {
             .attr = {
                 .name = "ln_udp_audio_send",
-                .stack_size = 0x2000,
+                .stack_size = 0x8000,
                 .priority = osPriorityNormal
             },
             .func = ln_udp_audio_send_thread,
@@ -1708,6 +1866,12 @@ int ln_udp_audio_send_init(void)
  */
 void ln_udp_audio_send_deinit(void)
 {
+    g_pcm_send_cache_size = 0;
+    g_pcm_send_cache_capacity = 0;
+    if (g_pcm_send_cache != NULL) {
+        free(g_pcm_send_cache);
+        g_pcm_send_cache = NULL;
+    }
     // 停止发送线程
     if (g_audio_send_thread_running) {
         printf("[LN_UDP] Stopping audio send thread\n");
@@ -1718,6 +1882,7 @@ void ln_udp_audio_send_deinit(void)
         audio_send_msg_t wake_msg = {0};
         wake_msg.data = NULL;
         wake_msg.size = 0;
+        wake_msg.encoding = AUDIO_SEND_ENCODING_OPUS;
         osal_msg_queue_write_copy(g_audio_send_queue, (void *)&wake_msg, sizeof(audio_send_msg_t), 0);
         
         // 等待线程退出(最多等待2秒)
@@ -1759,8 +1924,9 @@ void ln_udp_audio_send_deinit(void)
  * @param size 音频数据大小
  * @return 0 成功,-1 失败
  * @note 函数内部会动态分配内存并复制数据,发送线程负责释放内存
+ * @param encoding AUDIO_SEND_ENCODING_OPUS(0)=已是 Opus;AUDIO_SEND_ENCODING_PCM(1)=PCM,发送前编码为 Opus
  */
-int ln_udp_audio_send_enqueue(const uint8_t *data, uint32_t size)
+int ln_udp_audio_send_enqueue(const uint8_t *data, uint32_t size, uint8_t encoding)
 {
     if (!data || size == 0 || size > AUDIO_SEND_MAX_DATA_SIZE) {
         printf("[LN_UDP] Invalid parameters for audio send enqueue\n");
@@ -1784,6 +1950,7 @@ int ln_udp_audio_send_enqueue(const uint8_t *data, uint32_t size)
     audio_send_msg_t msg;
     msg.data = data_copy;
     msg.size = size;
+    msg.encoding = (encoding != AUDIO_SEND_ENCODING_PCM) ? AUDIO_SEND_ENCODING_OPUS : AUDIO_SEND_ENCODING_PCM;
     
     // 写入队列(非阻塞,如果队列满则丢弃并释放内存)
     int32_t ret = osal_msg_queue_write_copy(g_audio_send_queue, (void *)&msg, sizeof(audio_send_msg_t), 0);
index 6638bd5..ecbb174 100644 (file)
 #include "lwip/sockets.h"
 #include "lwip/inet.h"
 #include "lwip/netdb.h"
+#include <time.h>
+#if defined(ENABLE_VAD_SERVER) && ENABLE_VAD_SERVER
+#include "vad_server.h"
+#include "errcode.h"
+#include "cmsis_os2.h"
+#endif
 
 #define LN_MQTT_SEND_TOPIC "device-server-enhanced"
 
+/* 测试性录音数据保存到文件:1=启用,0=禁用。启用后每次开始录音创建新文件,停止时关闭。 */
+#ifndef LN_MQTT_RECORD_SAVE_TO_FILE_TEST
+#define LN_MQTT_RECORD_SAVE_TO_FILE_TEST 1
+#endif
+
 /* MQTT API 全局状态 */
 static bool g_ln_mqtt_service_started = false;
 
 /* UDP信息全局变量 */
 static ln_mqtt_udp_info_t g_udp_info = {0};
+static uint8_t g_audio_format_input = SBC;
 
 /* hello 消息中的 session_id(用于后续 listen 控制消息) */
 static char g_ln_mqtt_session_id[64] = {0};
@@ -32,6 +44,9 @@ static bool g_ln_mqtt_session_id_valid = false;
 /* 音频初始化状态标志位 */
 static volatile bool g_mqtt_audio_initialized = false;
 
+/* 消息处理回调函数(参考 ln_ws_start,可为 NULL) */
+static ws_message_callback_t_ln g_mqtt_message_callback = NULL;
+
 int ln_mqtt_api_send_hello(void);
 
 // --- MQTT 录音(来自 ai_media_service_mqtt.cpp,无对应头文件时在此声明) ---
@@ -53,11 +68,11 @@ extern int ln_udp_pack_audio_data(const uint8_t *audio_data, uint16_t audio_len,
                                   uint8_t *buffer, uint16_t buffer_size, uint16_t *out_len);
 extern int ln_udp_audio_send_init(void);
 extern void ln_udp_audio_send_deinit(void);
-extern int ln_udp_audio_send_enqueue(const uint8_t *data, uint32_t size);
+extern int ln_udp_audio_send_enqueue(const uint8_t *data, uint32_t size, uint8_t encoding);
 
 #define LN_UDP_PACKET_HEADER_SIZE 16
 #define LN_MQTT_RECORD_MAX_FRAME   0x800
-#define LN_MQTT_RECORD_BATCH_SIZE  5  // 每5包数据合并后入队
+#define LN_MQTT_RECORD_BATCH_SIZE  2  // 每5包数据合并后入队
 
 /* 双流接口开关:定义 USE_DUAL_STREAM_INTERFACE 则使用 old_Start_AIVoice 双流接口,否则使用 MQTT_StartRecord */
 /* 可在 CMakeLists.txt 或编译选项中定义,例如:-DUSE_DUAL_STREAM_INTERFACE=1 */
@@ -79,6 +94,251 @@ static ln_mqtt_record_cache_t g_record_cache = {0};
 static volatile bool g_recording_upload_started = false;
 #endif /* USE_DUAL_STREAM_INTERFACE */
 
+#if defined(ENABLE_VAD_SERVER) && ENABLE_VAD_SERVER
+/* VAD 服务器状态标志位(参考 ln_api_websocket.c) */
+static volatile bool g_mqtt_vad_server_enabled = false;
+static volatile bool g_mqtt_vad_server_initialized = false;
+/* VAD 冷却时间(参考 ln_ws,避免频繁触发对话) */
+static uint32_t g_mqtt_vad_cooldown_start_tick = 0;
+static volatile bool g_mqtt_vad_cooldown_active = false;
+static uint32_t g_mqtt_vad_cooldown_duration_ms = 3000;  /* 10秒 */
+
+static bool ln_mqtt_is_vad_cooldown_active(void);
+static void ln_mqtt_start_vad_cooldown(void);
+#endif /* ENABLE_VAD_SERVER */
+
+static void ln_mqtt_record_data_cb(const uint8_t* data, uint32_t size, void* userData);
+
+#if defined(ENABLE_VAD_SERVER) && ENABLE_VAD_SERVER
+/**
+ * @brief 检查 VAD 冷却是否激活(参考 ln_ws_is_vad_cooldown_active)
+ */
+static bool ln_mqtt_is_vad_cooldown_active(void)
+{
+    if (!g_mqtt_vad_cooldown_active) {
+        return false;
+    }
+    uint32_t current_tick = osKernelGetTickCount();
+    uint32_t elapsed_ms = current_tick - g_mqtt_vad_cooldown_start_tick;
+    if (elapsed_ms >= g_mqtt_vad_cooldown_duration_ms) {
+        g_mqtt_vad_cooldown_active = false;
+        printf("[LN_MQTT_API] VAD cooldown period ended\n");
+        return false;
+    }
+    return true;
+}
+
+/**
+ * @brief 开始 VAD 冷却计时(参考 ln_ws_start_vad_cooldown)
+ */
+static void ln_mqtt_start_vad_cooldown(void)
+{
+    g_mqtt_vad_cooldown_start_tick = osKernelGetTickCount();
+    g_mqtt_vad_cooldown_active = true;
+    printf("[LN_MQTT_API] VAD cooldown started, duration: %u ms\n", g_mqtt_vad_cooldown_duration_ms);
+}
+
+/**
+ * @brief VAD 状态回调(参考 vad_status_callback_ln_ws,含冷却机制)
+ * @param status 1=检测到说话开始,2=检测到说话结束
+ */
+static void vad_status_callback_ln_mqtt(int status)
+{
+    printf("[LN_MQTT_API] VAD status callback: %d\n", status);
+    if (!g_mqtt_vad_server_enabled) {
+        return;
+    }
+    if (status == 1) {
+        if (ln_mqtt_is_vad_cooldown_active()) {
+            printf("[LN_MQTT_API] VAD cooldown active, ignoring start request\n");
+            return;
+        }
+        (void)ln_mqtt_api_send_listen_state(true);
+    } else if (status == 2) {
+        (void)ln_mqtt_api_send_listen_state(false);
+        /* 对话结束后启动冷却,避免频繁触发 */
+        ln_mqtt_start_vad_cooldown();
+    }
+}
+#endif /* ENABLE_VAD_SERVER */
+
+/**
+ * @brief MQTT 音频辅助初始化(参考 ln_ws_audio_init,独立可调用)
+ * @note 仅在 USE_DUAL_STREAM_INTERFACE 时初始化 old_Start_AIVoice 双流接口
+ */
+void ln_mqtt_audio_init(void)
+{
+#if USE_DUAL_STREAM_INTERFACE
+    if (g_mqtt_audio_initialized) {
+        printf("[LN_MQTT_API] Audio already initialized, skipping duplicate initialization\n");
+        return;
+    }
+
+    printf("[LN_MQTT_API] Initializing audio with old_Start_AIVoice dual stream interface\n");
+
+    if (AI_IsVoiceServiceRunning()) {
+        printf("[LN_MQTT_API] AI voice service already running, stopping it first\n");
+        StopAIVoice();
+    }
+
+    AIVoiceAudioParams inputParams = {
+        .audioFormat =g_audio_format_input ,
+        .channelCount = 1,
+        .sampleRate = 16000
+    };
+
+    AIVoiceAudioParams outputParams = {
+        .audioFormat = PCM,
+        .channelCount = 1,
+        .sampleRate = 16000
+    };
+
+#if defined(ENABLE_VAD_SERVER) && ENABLE_VAD_SERVER
+    AIVoiceAudioParams audioParams_in_vad = {
+        .audioFormat = PCM,
+        .channelCount = 1,
+        .sampleRate = 16000
+    };
+#endif
+
+    int32_t start_ret = old_Start_AIVoice(
+        (AIVoiceDataCallback)ln_mqtt_record_data_cb,
+        NULL,
+        &inputParams,
+        &outputParams,
+#if defined(ENABLE_VAD_SERVER) && ENABLE_VAD_SERVER
+        NULL,  /* VAD 上行流(PCM) */
+#else
+        NULL,   /* 第二个上行流参数(NULL 表示不启用双流) */
+#endif
+        false
+    );
+
+    if (start_ret != 0) {
+        printf("[LN_MQTT_API] WARNING: old_Start_AIVoice failed: %d\n", (int)start_ret);
+        return;
+    }
+
+    g_mqtt_audio_initialized = true;
+    printf("[LN_MQTT_API] old_Start_AIVoice initialized successfully (SBC, 16kHz, mono)\n");
+
+#if defined(ENABLE_VAD_SERVER) && ENABLE_VAD_SERVER
+    /* 初始化 VAD 服务器(参考 ln_api_websocket.c ln_ws_audio_init) */
+    g_mqtt_vad_server_enabled = true;
+    if (!g_mqtt_vad_server_initialized) {
+        printf("[LN_MQTT_API] Initializing VAD server...\n");
+        errcode_t ret = vad_server_init(vad_status_callback_ln_mqtt);
+        if (ret == ERRCODE_SUCC) {
+            g_mqtt_vad_server_initialized = true;
+            printf("[LN_MQTT_API] VAD server initialized successfully\n");
+            if (g_mqtt_message_callback != NULL) {
+                g_mqtt_message_callback(NULL, WS_CMD_VAD_INITIALIZED);
+            }
+        } else {
+            printf("[LN_MQTT_API] Failed to initialize VAD server: %d\n", (int)ret);
+        }
+    }
+#endif /* ENABLE_VAD_SERVER */
+
+    #ifdef SUPPORT_CAT1_SERVICE
+    cat1_api_keep_wakeup(1);
+    cat1_api_wakeup_cat1(1);
+    
+    #endif
+    uapi_cat1_add_cat1_vote();
+
+#else
+    (void)0;  /* 非双流模式使用 MQTT_StartRecord,无需预初始化 */
+#endif /* USE_DUAL_STREAM_INTERFACE */
+}
+
+/**
+ * @brief MQTT 音频辅助反初始化(参考 ln_ws_audio_deinit)
+ */
+void ln_mqtt_audio_deinit(void)
+{
+#if USE_DUAL_STREAM_INTERFACE
+    if (!g_mqtt_audio_initialized) {
+        return;
+    }
+
+#if defined(ENABLE_VAD_SERVER) && ENABLE_VAD_SERVER
+    if (g_mqtt_vad_server_initialized) {
+        printf("[LN_MQTT_API] Deinitializing VAD server...\n");
+        errcode_t ret = vad_server_deinit();
+        if (ret == ERRCODE_SUCC) {
+            g_mqtt_vad_server_initialized = false;
+            printf("[LN_MQTT_API] VAD server deinitialized successfully\n");
+        } else {
+            printf("[LN_MQTT_API] Failed to deinitialize VAD server: %d\n", (int)ret);
+        }
+    }
+    g_mqtt_vad_server_enabled = false;
+#endif /* ENABLE_VAD_SERVER */
+
+    g_recording_upload_started = false;
+    if (AI_IsVoiceServiceRunning()) {
+        (void)StopAIVoice();
+        printf("[LN_MQTT_API] AI voice service stopped\n");
+    }
+    g_mqtt_audio_initialized = false;
+    printf("[LN_MQTT_API] Audio deinitialized\n");
+
+    #ifdef SUPPORT_CAT1_SERVICE
+    cat1_api_keep_wakeup(0);
+    cat1_api_wakeup_cat1(0);    
+    
+    #endif
+    uapi_cat1_remove_cat1_vote();
+#else
+    (void)0;
+#endif /* USE_DUAL_STREAM_INTERFACE */
+}
+
+/**
+ * @brief 获取 MQTT 音频初始化状态
+ */
+bool ln_mqtt_is_audio_initialized(void)
+{
+    return g_mqtt_audio_initialized;
+}
+
+#if LN_MQTT_RECORD_SAVE_TO_FILE_TEST
+static FILE *g_record_test_file = NULL;
+
+static void ln_mqtt_record_test_file_open(void)
+{
+    if (g_record_test_file != NULL) {
+        return;  /* 已打开 */
+    }
+    time_t now = time(NULL);
+    struct tm *tm_info = (now >= 0) ? localtime(&now) : NULL;
+    char filename[96];
+    if (tm_info) {
+        snprintf(filename, sizeof(filename), "/user/userdata/ln_mqtt_record_%04d%02d%02d_%02d%02d%02d.sbc",
+                 tm_info->tm_year + 1900, tm_info->tm_mon + 1, tm_info->tm_mday,
+                 tm_info->tm_hour, tm_info->tm_min, tm_info->tm_sec);
+    } else {
+        snprintf(filename, sizeof(filename), "/user/userdata/ln_mqtt_record_test.raw");
+    }
+    g_record_test_file = fopen(filename, "wb");
+    if (g_record_test_file) {
+        printf("[LN_MQTT_API] Test save file opened: %s\n", filename);
+    } else {
+        printf("[LN_MQTT_API] Failed to open test save file: %s\n", filename);
+    }
+}
+
+static void ln_mqtt_record_test_file_close(void)
+{
+    if (g_record_test_file) {
+        fclose(g_record_test_file);
+        g_record_test_file = NULL;
+        printf("[LN_MQTT_API] Test save file closed\n");
+    }
+}
+#endif /* LN_MQTT_RECORD_SAVE_TO_FILE_TEST */
+
 static void ln_mqtt_record_data_cb(const uint8_t* data, uint32_t size, void* userData)
 {
     (void)userData;
@@ -90,17 +350,55 @@ static void ln_mqtt_record_data_cb(const uint8_t* data, uint32_t size, void* use
         return;
     }
 
+#if defined(ENABLE_VAD_SERVER) && ENABLE_VAD_SERVER && USE_DUAL_STREAM_INTERFACE
+    /* VAD 音频送入(须在 early return 之前,VAD 需持续收流以检测唤醒) */
+    if (g_mqtt_vad_server_enabled && g_mqtt_vad_server_initialized && vad_server_is_running() ) {
+        if(g_audio_format_input == PCM)
+        {
+            // printf("vad_server_send_audio size %d\r\n",size);
+            (void)vad_server_send_audio(VAD_AUDIO_FORMAT_PCM, data, size, false);
+        }
+        else if(AI_GetSecondStreamInStatus())
+        {
+            uint8_t *pcm_data=(uint8_t *)malloc(1024*3);
+            if(pcm_data)        
+            {
+                uint32_t len=1024*3;
+                AI_SecondStreamInObtainBuffer(pcm_data, &len);
+                printf("vad_server_send_audio size %d\r\n",len);
+                (void)vad_server_send_audio(VAD_AUDIO_FORMAT_PCM, pcm_data, len, false);
+                free(pcm_data);
+            }
+            printf("vad_server_send_audio size %d\r\n",size);
+            (void)vad_server_send_audio(VAD_AUDIO_FORMAT_PCM, data, size, false);
+        }
+      
+    }
+#endif /* ENABLE_VAD_SERVER && USE_DUAL_STREAM_INTERFACE */
+
 #if USE_DUAL_STREAM_INTERFACE
-    // 如果录音上传未开始,只缓存数据,不处理上传
+    /* 如果录音上传未开始,只做 VAD,不处理上传 */
     if (!g_recording_upload_started) {
-        // 可以在这里缓存数据,等待开始标志位设置后再处理
-        // 目前先直接返回,不处理数据
         return;
     }
 #endif /* USE_DUAL_STREAM_INTERFACE */
 
     printf("ln_mqtt_record_data_cb size %d\r\n",size);
 
+#if LN_MQTT_RECORD_SAVE_TO_FILE_TEST
+    /* 测试性保存到文件:首次写入时打开,之后追加 */
+    if (g_record_test_file == NULL) {
+        ln_mqtt_record_test_file_open();
+    }
+    if (g_record_test_file != NULL && size > 0) {
+        size_t n = fwrite(data, 1, (size_t)size, g_record_test_file);
+        if (n != (size_t)size) {
+            printf("[LN_MQTT_API] Test file write incomplete: %zu/%u\n", n, (unsigned)size);
+        }
+    }
+#endif /* LN_MQTT_RECORD_SAVE_TO_FILE_TEST */
+
+#ifdef USE_RECORD_CACHE
     // 如果缓冲区未分配,先分配
     if (g_record_cache.buffer == NULL) {
         uint32_t initial_size = LN_MQTT_RECORD_MAX_FRAME * LN_MQTT_RECORD_BATCH_SIZE;
@@ -121,7 +419,7 @@ static void ln_mqtt_record_data_cb(const uint8_t* data, uint32_t size, void* use
                g_record_cache.current_size, size, g_record_cache.buffer_size);
         // 如果缓存已满,强制刷新当前缓存
         if (g_record_cache.current_size > 0) {
-            if (ln_udp_audio_send_enqueue(g_record_cache.buffer, g_record_cache.current_size) != 0) {
+            if (ln_udp_audio_send_enqueue(g_record_cache.buffer, g_record_cache.current_size, AUDIO_SEND_ENCODING_OPUS) != 0) {
                 printf("[LN_MQTT_API] Failed to enqueue cached audio data\n");
             }
             g_record_cache.current_size = 0;
@@ -136,7 +434,8 @@ static void ln_mqtt_record_data_cb(const uint8_t* data, uint32_t size, void* use
 
     // 如果累积到5包,将缓存数据入队
     if (g_record_cache.packet_count >= LN_MQTT_RECORD_BATCH_SIZE) {
-        if (ln_udp_audio_send_enqueue(g_record_cache.buffer, g_record_cache.current_size) != 0) {
+        if (ln_udp_audio_send_enqueue(g_record_cache.buffer, g_record_cache.current_size, AUDIO_SEND_ENCODING_OPUS) != 0)
+         {
             printf("[LN_MQTT_API] Failed to enqueue batched audio data, drop frame\n");
         } else {
             printf("[LN_MQTT_API] Enqueued %u packets, total size: %u bytes\n",
@@ -146,8 +445,16 @@ static void ln_mqtt_record_data_cb(const uint8_t* data, uint32_t size, void* use
         g_record_cache.current_size = 0;
         g_record_cache.packet_count = 0;
     }
-
-    msleep(10);
+#else
+    if (ln_udp_audio_send_enqueue(data, size, AUDIO_SEND_ENCODING_OPUS) != 0) {
+        printf("[LN_MQTT_API] Failed to enqueue audio data, drop frame\n");
+    } else {
+        printf("[LN_MQTT_API] Enqueued %u bytes of audio data\n", size);
+    }
+#endif
+#if USE_DUAL_STREAM_INTERFACE
+    msleep(50);
+#endif
 }
 
 /**
@@ -341,6 +648,10 @@ static int ln_mqtt_handle_hello_message(const char *data)
     } else {
         printf("[LN_MQTT_API] No UDP information in hello message\n");
     }
+
+    if (g_mqtt_message_callback != NULL) {
+        g_mqtt_message_callback(NULL, WS_CMD_CONNECTED);
+    }
     
     cJSON_Delete(root);
     return 0;
@@ -396,6 +707,9 @@ static void ln_mqtt_internal_connect_cb(void *arg, int status)
     }
     else
     {
+        if (g_mqtt_message_callback != NULL) {
+            g_mqtt_message_callback(NULL, WS_CMD_DISCONNECTED);
+        }
         extern void ln_udp_close_socket(void);
         ln_udp_close_socket();
     }
@@ -437,14 +751,20 @@ static void ln_mqtt_internal_subscribe_cb(void *arg, const char *topic, uint8_t
 
 /**
  * @brief 启动MQTT服务(启动线程、自动连接等)
+ * @param callback 消息处理回调函数,参考 ln_ws_start,可为 NULL
  * @return 0 成功,-1 失败
  */
-int ln_mqtt_api_start_service(void)
+int ln_mqtt_api_start_service(ws_message_callback_t_ln callback)
 {
     if (g_ln_mqtt_service_started) {
         WEARABLE_LOGW(WEARABLE_LOG_MODULE_APP, "[LN_MQTT_API] Service already started");
         return 0;
     }
+
+    g_mqtt_message_callback = callback;
+    if (callback != NULL) {
+        printf("[LN_MQTT_API] Message callback registered\n");
+    }
     
     printf("[LN_MQTT_API] Starting MQTT service\n");
     
@@ -468,58 +788,10 @@ int ln_mqtt_api_start_service(void)
         printf("[LN_MQTT_API] WARNING: Failed to init audio send queue/thread\n");
         // 不影响服务启动,继续执行
     }
-    
-#if USE_DUAL_STREAM_INTERFACE
-    // 使用 old_Start_AIVoice 接口时,在服务初始化时就启动音频
-    printf("[LN_MQTT_API] Initializing audio with old_Start_AIVoice dual stream interface\n");
-    
-    // 检查 AI 语音服务是否已经在运行(如果已运行则先停止)
-    if (AI_IsVoiceServiceRunning()) {
-        printf("[LN_MQTT_API] AI voice service already running, stopping it first\n");
-        StopAIVoice();
-    }
-    
-    // 设置第二个上行流参数:SBC 格式,16kHz,单声道
-    AIVoiceAudioParams secondStreamParams = {
-        .audioFormat = SBC,        // SBC 格式(对应 AudioCodecFormat)
-        .channelCount = 1,         // 单声道
-        .sampleRate = 16000        // 16kHz 采样率
-    };
-    
-    // 输入流参数(主输入流)
-    AIVoiceAudioParams inputParams = {
-        .audioFormat = SBC,
-        .channelCount = 1,
-        .sampleRate = 16000
-    };
 
+    // 音频辅助初始化(双流模式下启动 old_Start_AIVoice)
+    // ln_mqtt_audio_init();
 
-    
-    // 输出流参数(设置为NULL,因为我们只关心录音输入)
-    AIVoiceAudioParams outputParams = {
-        .audioFormat = PCM,
-        .channelCount = 1,
-        .sampleRate = 16000
-    };
-    
-    // 使用 old_Start_AIVoice 启动,传入第二个上行流参数启用双流
-    int32_t start_ret = old_Start_AIVoice(
-        (AIVoiceDataCallback)ln_mqtt_record_data_cb,  // 数据回调函数(类型转换)
-        NULL,                                          // 用户数据
-        &inputParams,                                  // 输入流参数(主输入流)
-        &outputParams,                                  // 输出流参数(NULL使用默认)
-        NULL,                          // 第二个上行流参数(启用双流)
-        false                                          // 不使用OGG封装
-    );
-    
-    if (start_ret != 0) {
-        printf("[LN_MQTT_API] WARNING: old_Start_AIVoice failed during service init: %d\n", (int)start_ret);
-        // 不影响服务启动,继续执行
-    } else {
-        printf("[LN_MQTT_API] old_Start_AIVoice initialized successfully (SBC, 16kHz, mono)\n");
-    }
-#endif /* USE_DUAL_STREAM_INTERFACE */
-    
     g_ln_mqtt_service_started = true;
     printf("[LN_MQTT_API] MQTT service started successfully\n");
     
@@ -545,7 +817,7 @@ void ln_mqtt_api_stop_service(void)
         // 如果有剩余数据,先刷新
         if (g_record_cache.current_size > 0) {
             printf("[LN_MQTT_API] Flushing remaining cached audio data before service stop\n");
-            if (ln_udp_audio_send_enqueue(g_record_cache.buffer, g_record_cache.current_size) != 0) {
+            if (ln_udp_audio_send_enqueue(g_record_cache.buffer, g_record_cache.current_size, AUDIO_SEND_ENCODING_OPUS) != 0) {
                 printf("[LN_MQTT_API] Failed to enqueue remaining cached audio data\n");
             }
         }
@@ -556,18 +828,15 @@ void ln_mqtt_api_stop_service(void)
         g_record_cache.packet_count = 0;
         printf("[LN_MQTT_API] Cache buffer freed\n");
     }
-    
-#if USE_DUAL_STREAM_INTERFACE
-    // 清理双流接口资源
-    // 清除录音上传标志位
-    g_recording_upload_started = false;
-    // 停止 AI 语音服务(在服务初始化时已启动)
-    if (AI_IsVoiceServiceRunning()) {
-        (void)StopAIVoice();
-        printf("[LN_MQTT_API] AI voice service stopped\n");
+
+    // 音频辅助反初始化(双流模式下停止 AI 语音服务)
+    ln_mqtt_audio_deinit();
+
+    if (g_mqtt_message_callback != NULL) {
+        g_mqtt_message_callback(NULL, WS_CMD_DISCONNECTED);
     }
-#endif /* USE_DUAL_STREAM_INTERFACE */
-    
+    g_mqtt_message_callback = NULL;
+
     // 停止MQTT主线程(会自动断开连接和清理资源)
     uapi_mqtt_main_stop();
     
@@ -724,7 +993,15 @@ int ln_mqtt_api_send_hello(void)
         cJSON_Delete(root);
         return -1;
     }
-    cJSON_AddStringToObject(audio_params, "format", "sbc");
+    if(g_audio_format_input == PCM )
+    {
+        cJSON_AddStringToObject(audio_params, "format", "pcm");
+    }
+    else if(g_audio_format_input == SBC)
+    {
+        cJSON_AddStringToObject(audio_params, "format", "sbc");
+    }
+
     cJSON_AddNumberToObject(audio_params, "sample_rate", 16000);
     cJSON_AddNumberToObject(audio_params, "channels", 1);
     cJSON_AddNumberToObject(audio_params, "frame_duration", 60);
@@ -785,6 +1062,11 @@ int ln_mqtt_api_send_listen_state(bool start)
         return -1;
     }
 
+    if(start==false && g_recording_upload_started==false)
+    {
+        start=true;
+    }
+
     // 构建JSON数据
     cJSON *root = cJSON_CreateObject();
     if (!root) {
@@ -843,6 +1125,9 @@ int ln_mqtt_api_send_listen_state(bool start)
         
         // 设置录音上传标志位,开始处理并上传音频数据
         g_recording_upload_started = true;
+        if (g_mqtt_message_callback != NULL) {
+            g_mqtt_message_callback(NULL, WS_CMD_AUDIO_RECORDING_START);
+        }
         printf("[LN_MQTT_API] Dual stream recording started, upload enabled\n");
 #else
         // 使用原有 MQTT_StartRecord 接口
@@ -856,6 +1141,9 @@ int ln_mqtt_api_send_listen_state(bool start)
         if (rec_ret != 0) {
             printf("[LN_MQTT_API] WARNING: MQTT_StartRecord failed: %d\n", (int)rec_ret);
         } else {
+            if (g_mqtt_message_callback != NULL) {
+                g_mqtt_message_callback(NULL, WS_CMD_AUDIO_RECORDING_START);
+            }
             printf("[LN_MQTT_API] MQTT_StartRecord started with SBC format (16kHz, mono)\n");
         }
 #endif /* USE_DUAL_STREAM_INTERFACE */
@@ -864,7 +1152,7 @@ int ln_mqtt_api_send_listen_state(bool start)
         if (g_record_cache.buffer != NULL && g_record_cache.current_size > 0) {
             printf("[LN_MQTT_API] Flushing remaining cached audio data: %u bytes, %u packets\n",
                    g_record_cache.current_size, g_record_cache.packet_count);
-            if (ln_udp_audio_send_enqueue(g_record_cache.buffer, g_record_cache.current_size) != 0) {
+            if (ln_udp_audio_send_enqueue(g_record_cache.buffer, g_record_cache.current_size, AUDIO_SEND_ENCODING_OPUS) != 0) {
                 printf("[LN_MQTT_API] Failed to enqueue remaining cached audio data\n");
             }
             g_record_cache.current_size = 0;
@@ -877,12 +1165,22 @@ int ln_mqtt_api_send_listen_state(bool start)
             g_record_cache.buffer_size = 0;
             printf("[LN_MQTT_API] Cache buffer freed\n");
         }
-        
+
+#if LN_MQTT_RECORD_SAVE_TO_FILE_TEST
+        ln_mqtt_record_test_file_close();
+#endif /* LN_MQTT_RECORD_SAVE_TO_FILE_TEST */
+
 #if USE_DUAL_STREAM_INTERFACE
         // 清除录音上传标志位,停止处理并上传音频数据
         g_recording_upload_started = false;
+        if (g_mqtt_message_callback != NULL) {
+            g_mqtt_message_callback(NULL, WS_CMD_AUDIO_RECORDING_END);
+        }
         printf("[LN_MQTT_API] Dual stream recording stopped, upload disabled (AI voice service still running)\n");
 #else
+        if (g_mqtt_message_callback != NULL) {
+            g_mqtt_message_callback(NULL, WS_CMD_AUDIO_RECORDING_END);
+        }
         // 停止原有 MQTT_StartRecord 接口
         (void)MQTT_StopRecord();
 #endif /* USE_DUAL_STREAM_INTERFACE */
index 277d292..b0518dc 100644 (file)
@@ -4,6 +4,7 @@
 #include <stdint.h>
 #include <stdbool.h>
 #include "uapi_Mqtt_api.h"
+#include "ln_api.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -16,11 +17,12 @@ extern "C" {
 
 /**
  * @brief 启动MQTT服务(启动线程、自动连接等)
+ * @param callback 消息处理回调函数,参考 ln_ws_start,可为 NULL 使用默认处理
  * @note 启动服务时会自动进行初始化,无需单独调用初始化函数
  * @note 服务模式下使用内部回调,自动处理连接状态和消息接收
  * @return 0 成功,-1 失败
  */
-int ln_mqtt_api_start_service(void);
+int ln_mqtt_api_start_service(ws_message_callback_t_ln callback);
 
 /**
  * @brief 停止MQTT服务(停止线程、断开连接等)
@@ -96,13 +98,18 @@ int ln_udp_audio_send_init(void);
  */
 void ln_udp_audio_send_deinit(void);
 
+/* 音频发送编码类型:与 ln_UDP.c 中 AUDIO_SEND_ENCODING_* 一致 */
+#define AUDIO_SEND_ENCODING_OPUS 0  /* 已是 Opus,直接发送 */
+#define AUDIO_SEND_ENCODING_PCM  1  /* PCM,发送前编码为 Opus */
+
 /**
  * @brief 将音频数据放入发送队列(供录音回调函数调用)
  * @param data 音频数据指针
- * @param size 音频数据大小
+ * @param size 音频数据大小(字节)
+ * @param encoding AUDIO_SEND_ENCODING_OPUS(0)=已是 Opus;AUDIO_SEND_ENCODING_PCM(1)=PCM,发送前编码为 Opus
  * @return 0 成功,-1 失败
  */
-int ln_udp_audio_send_enqueue(const uint8_t *data, uint32_t size);
+int ln_udp_audio_send_enqueue(const uint8_t *data, uint32_t size, uint8_t encoding);
 
 
 #ifdef __cplusplus
index d7a8990..862a372 100644 (file)
@@ -150,7 +150,7 @@ static uint32_t ln_mqtt_start_service_at(uint8_t *para, uint32_t paraLen, char *
     (void)strCommonCmd;\r
 \r
     printf("[LN_MQTT_AT] Starting MQTT service...\n");\r
-    int ret = ln_mqtt_api_start_service();\r
+    int ret = ln_mqtt_api_start_service(NULL);\r
     if (ret == 0) {\r
         printf("+LN_MQTT_START_SERVICE: OK\n");\r
         return ERRCODE_SUCC;\r
index 2183140..dccb58e 100644 (file)
@@ -1,4 +1,7 @@
 #include "ln_api.h"
+#ifdef SUPPORT_LN_MQTT_SERVICE
+#include "ln_mqtt_api.h"
+#endif
 /* for https/http client */
 #include "netstack/http/include/httpclient.h"
 /* for HMAC-SHA256 */
@@ -1496,6 +1499,10 @@ errcode_t uapi_ln_ws_start(void)
 // 雷鸟http服务初始化
 errcode_t uapi_ln_http_server_init(void)
 {
+    #ifdef SUPPORT_LN_MQTT_SERVICE
+    ln_mqtt_api_start_service(ln_ws_process_message);
+    return ERRCODE_SUCC;
+    #endif
     printf("uapi_ln_http_server_init !\r\n");
 
     ln_send_taskcfg.create_queue_handle = create_ln_http_send_queue;
index 96e867f..776e475 100644 (file)
@@ -801,6 +801,15 @@ int ln_ws_send_audio_data(const uint8_t *audio_data, uint32_t audio_size, bool f
 // 开始新的音频聊天
 int ln_ws_start_audio_chat(ln_audio_chat_command_t command_type)
 {
+    #ifdef SUPPORT_LN_MQTT_SERVICE
+
+    ln_mqtt_api_send_listen_state(true);
+
+    return 0;
+
+
+    #else
+
     // 检查音频是否已初始化
     if (!g_audio_initialized) {
         LN_WS_ERROR("Audio not initialized, please call ln_ws_audio_init() first\n");
@@ -845,12 +854,18 @@ int ln_ws_start_audio_chat(ln_audio_chat_command_t command_type)
     
     LN_WS_DEBUG("Audio chat started: %s (command: %s)\n", 
            g_audio_chat_ctx.chat_id, command_name);
+    #endif
     return 0;
 }
 
 // 结束当前音频聊天
 int ln_ws_end_audio_chat(void)
 {
+
+    #ifdef SUPPORT_LN_MQTT_SERVICE
+    ln_mqtt_api_send_listen_state(false);
+    return 0;
+    #else
     // 检查音频是否已初始化
     if (!g_audio_initialized) {
         LN_WS_ERROR("Audio not initialized, please call ln_ws_audio_init() first\n");
@@ -874,6 +889,7 @@ int ln_ws_end_audio_chat(void)
     g_audio_chat_ending = true;
     
     return 0;
+    #endif
 }
 
 // 获取当前聊天ID
@@ -1410,6 +1426,11 @@ void vad_status_callback_ln_ws(int status)
 
 void ln_ws_audio_init(bool is_vad_server_enabled)
 {
+
+    #ifdef SUPPORT_LN_MQTT_SERVICE
+    ln_mqtt_audio_init();
+    #else
+   
     // is_vad_server_enabled=false;
     // 检查是否已经初始化
     if (g_audio_initialized) {
@@ -1511,6 +1532,8 @@ void ln_ws_audio_init(bool is_vad_server_enabled)
         }
     } 
 #endif
+
+#endif
 }
 
 
@@ -2009,7 +2032,12 @@ int ln_ws_stop(void)
 /* 获取当前连接状态 */
 int ln_ws_get_connection_state(void)
 {
+    #ifdef SUPPORT_LN_MQTT_SERVICE
+    // return ln_mqtt_api_get_connection_state();
+    return WS_STATE_CONNECTED;
+    #else
     return g_ws_connected;
+    #endif
 }
 
 /* 获取连接状态字符串 */
index 60a1d6a..c571978 100644 (file)
@@ -9,6 +9,7 @@ set(COMPONENT_NAME "opus_frame_to_pcm")
 # 应用主程序
 set(SOURCES
     ${CMAKE_CURRENT_SOURCE_DIR}/src/opus_frame_to_pcm.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/src/opus_pcm_to_frame.c
 )
 
 # 将 opus 库的 .c 源文件全部链接进来(替代原有静态库)
@@ -16,16 +17,22 @@ file(GLOB OPUS_SRC_LIB "${CMAKE_CURRENT_SOURCE_DIR}/src_lib/*.c")
 file(GLOB OPUS_CELT    "${CMAKE_CURRENT_SOURCE_DIR}/celt/*.c")
 file(GLOB OPUS_SILK    "${CMAKE_CURRENT_SOURCE_DIR}/silk/*.c")
 file(GLOB OPUS_SILK_FLOAT "${CMAKE_CURRENT_SOURCE_DIR}/silk/float/*.c")
+file(GLOB OPUS_SILK_FIXED "${CMAKE_CURRENT_SOURCE_DIR}/silk/fixed/*.c")
 
 # 仅 x86 平台编译 SIMD 优化源;非 x86(如 ARM/Cortex/RISC-V)不包含,使用 C 通用实现
 if(CMAKE_SYSTEM_PROCESSOR MATCHES "[xX]86|i686|AMD64|x86_64")
   file(GLOB OPUS_CELT_X86  "${CMAKE_CURRENT_SOURCE_DIR}/celt/x86/*.c")
   file(GLOB OPUS_SILK_X86  "${CMAKE_CURRENT_SOURCE_DIR}/silk/x86/*.c")
-  list(APPEND SOURCES ${OPUS_SRC_LIB} ${OPUS_CELT} ${OPUS_CELT_X86} ${OPUS_SILK} ${OPUS_SILK_FLOAT} ${OPUS_SILK_X86})
+  # x86 使用 fixed 的 C/SSE4.1 实现,排除 ARM NEON 文件
+  list(REMOVE_ITEM OPUS_SILK_FIXED "${CMAKE_CURRENT_SOURCE_DIR}/silk/fixed/warped_autocorrelation_FIX_neon_intr.c")
+  list(APPEND SOURCES ${OPUS_SRC_LIB} ${OPUS_CELT} ${OPUS_CELT_X86} ${OPUS_SILK} ${OPUS_SILK_FLOAT} ${OPUS_SILK_FIXED} ${OPUS_SILK_X86})
 else()
-  # 非 x86 平台:排除 x86 特定的 AVX2 文件
+  # 非 x86 平台:排除 float 的 AVX2、fixed 的 SSE4.1/NEON 架构特定文件
   list(REMOVE_ITEM OPUS_SILK_FLOAT "${CMAKE_CURRENT_SOURCE_DIR}/silk/float/inner_product_FLP_avx2.c")
-  list(APPEND SOURCES ${OPUS_SRC_LIB} ${OPUS_CELT} ${OPUS_SILK} ${OPUS_SILK_FLOAT})
+  list(REMOVE_ITEM OPUS_SILK_FIXED "${CMAKE_CURRENT_SOURCE_DIR}/silk/fixed/vector_ops_FIX_sse4_1.c")
+  list(REMOVE_ITEM OPUS_SILK_FIXED "${CMAKE_CURRENT_SOURCE_DIR}/silk/fixed/burg_modified_FIX_sse4_1.c")
+  list(REMOVE_ITEM OPUS_SILK_FIXED "${CMAKE_CURRENT_SOURCE_DIR}/silk/fixed/warped_autocorrelation_FIX_neon_intr.c")
+  list(APPEND SOURCES ${OPUS_SRC_LIB} ${OPUS_CELT} ${OPUS_SILK} ${OPUS_SILK_FLOAT} ${OPUS_SILK_FIXED})
 endif()
 
 # 头文件目录
@@ -38,6 +45,7 @@ set(PRIVATE_HEADER
     ${CMAKE_CURRENT_SOURCE_DIR}/src_lib
     ${CMAKE_CURRENT_SOURCE_DIR}/silk
     ${CMAKE_CURRENT_SOURCE_DIR}/silk/float
+    ${CMAKE_CURRENT_SOURCE_DIR}/silk/fixed
     ${CMAKE_CURRENT_SOURCE_DIR}/celt
 )
 
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/ENCODER_SUPPORT.md b/code/sdk/zhihui/test_opus_frame_to_pcm/ENCODER_SUPPORT.md
new file mode 100644 (file)
index 0000000..baf3c22
--- /dev/null
@@ -0,0 +1,160 @@
+# Opus 编码器支持说明\r
+\r
+本文档说明如何在 `test_opus_frame_to_pcm` 目录中添加编码器支持。\r
+\r
+## 概述\r
+\r
+默认情况下,`test_opus_frame_to_pcm` 目录只包含解码器相关的源码,用于构建最小化的解码库。如果需要编码功能,需要额外复制编码器相关的源文件。\r
+\r
+## 快速开始\r
+\r
+### 1. 复制解码器源码(如果还没有)\r
+\r
+首先确保已经复制了解码器源码:\r
+\r
+**Linux/macOS:**\r
+```bash\r
+cd test_opus_frame_to_pcm\r
+./copy_sources.sh\r
+```\r
+\r
+**Windows PowerShell:**\r
+```powershell\r
+cd test_opus_frame_to_pcm\r
+.\copy_sources.ps1  # 如果有的话,或者使用 Git Bash 运行 copy_sources.sh\r
+```\r
+\r
+### 2. 复制编码器源码\r
+\r
+运行编码器源码复制脚本:\r
+\r
+**Linux/macOS:**\r
+```bash\r
+./copy_encoder_sources.sh\r
+```\r
+\r
+**Windows PowerShell:**\r
+```powershell\r
+.\copy_encoder_sources.ps1\r
+```\r
+\r
+### 3. 编译库\r
+\r
+使用更新后的 Makefile 编译包含编码器的库:\r
+\r
+```bash\r
+make -f Makefile.libopus clean\r
+make -f Makefile.libopus\r
+```\r
+\r
+这将生成包含编码器和解码器功能的 `libopus_min.a`。\r
+\r
+### 4. 测试编码功能\r
+\r
+编译编码工具并测试:\r
+\r
+```bash\r
+make clean\r
+make\r
+```\r
+\r
+然后测试编码功能:\r
+\r
+```bash\r
+# 编码:PCM -> Opus\r
+./opus_frame_to_pcm -e input.pcm -o output.opus -r 16000 -c 1 -b 64000 -a 1\r
+\r
+# 或使用独立的编码工具\r
+./opus_pcm_to_frame input.pcm -o output.opus -r 16000 -c 1 -b 64000 -a 1\r
+```\r
+\r
+## 添加的编码器源文件\r
+\r
+### Opus API 层(6 个文件)\r
+- `src/opus_encoder.c` - Opus 编码器主实现\r
+- `src/opus_multistream_encoder.c` - 多流编码器\r
+- `src/opus_projection_encoder.c` - 投影编码器\r
+- `src/analysis.c` - 编码分析(VAD 和带宽检测)\r
+- `src/mlp.c` - MLP 神经网络\r
+- `src/mlp_data.c` - MLP 数据\r
+\r
+### CELT 编码器(1 个文件)\r
+- `celt/celt_encoder.c` - CELT 编码器核心\r
+\r
+### SILK 编码器(约 30 个文件)\r
+- **核心编码文件(8 个)**\r
+  - `silk/enc_API.c`\r
+  - `silk/init_encoder.c`\r
+  - `silk/encode_indices.c`\r
+  - `silk/encode_pulses.c`\r
+  - `silk/NLSF_encode.c`\r
+  - `silk/NLSF_VQ.c`\r
+  - `silk/NLSF_del_dec_quant.c`\r
+  - `silk/process_NLSFs.c`\r
+\r
+- **立体声编码(3 个)**\r
+  - `silk/stereo_LR_to_MS.c`\r
+  - `silk/stereo_encode_pred.c`\r
+  - `silk/stereo_quant_pred.c`\r
+\r
+- **NSQ(噪声整形量化)(2 个)**\r
+  - `silk/NSQ.c`\r
+  - `silk/NSQ_del_dec.c`\r
+\r
+- **控制和分析(8 个)**\r
+  - `silk/quant_LTP_gains.c`\r
+  - `silk/VQ_WMat_EC.c`\r
+  - `silk/VAD.c`\r
+  - `silk/control_audio_bandwidth.c`\r
+  - `silk/control_SNR.c`\r
+  - `silk/control_codec.c`\r
+  - `silk/check_control_input.c`\r
+  - `silk/ana_filt_bank_1.c`\r
+\r
+- **浮点实现(1 个)**\r
+  - `silk/float/encode_frame_FLP.c`\r
+\r
+- **定点实现(约 20 个)**\r
+  - `silk/fixed/*.c` 中的所有编码相关文件\r
+\r
+## 库大小对比\r
+\r
+- **仅解码器**: 约 500-600 KB\r
+- **编码器+解码器**: 约 800-1000 KB\r
+\r
+编码器会增加约 300-400 KB 的库大小。\r
+\r
+## 注意事项\r
+\r
+1. **必须先复制解码器源码**:编码器脚本假设解码器源码已经存在。\r
+\r
+2. **Makefile 已更新**:`Makefile.libopus` 已经更新以支持编码器源文件。如果只想要解码器,可以注释掉编码器相关的源文件定义。\r
+\r
+3. **编译时间**:包含编码器后,编译时间会显著增加(约 2-3 倍)。\r
+\r
+4. **依赖关系**:编码器需要 `entenc.c`(熵编码器),这个文件在解码器构建中已经包含。\r
+\r
+## 故障排除\r
+\r
+### 编译错误:找不到编码器源文件\r
+\r
+确保已经运行了 `copy_encoder_sources.sh` 或 `copy_encoder_sources.ps1`。\r
+\r
+### 链接错误:未定义的编码器函数\r
+\r
+检查 `Makefile.libopus` 中是否包含了所有编码器源文件。\r
+\r
+### 运行时错误:编码失败\r
+\r
+检查输入 PCM 文件的格式:\r
+- 必须是 16 位 PCM\r
+- 小端序(little-endian)\r
+- 正确的采样率和声道数\r
+\r
+## 相关文件\r
+\r
+- `copy_encoder_sources.sh` - Linux/macOS 编码器源码复制脚本\r
+- `copy_encoder_sources.ps1` - Windows PowerShell 编码器源码复制脚本\r
+- `Makefile.libopus` - 库构建 Makefile(已更新支持编码器)\r
+- `src/opus_pcm_to_frame.c` - 独立的编码工具\r
+- `src/opus_frame_to_pcm.c` - 统一的编解码工具(支持 `-e` 选项)\r
index 88f4c14..ca6315f 100644 (file)
@@ -9,17 +9,20 @@ LDFLAGS = -L. -lopus -lm
 SRC_DIR = src\r
 INCLUDE_DIR = include\r
 \r
-# Target executable\r
+# Target executables\r
 TARGET = opus_frame_to_pcm\r
+TARGET_ENCODE = opus_pcm_to_frame\r
 \r
-# Source file\r
+# Source files\r
 SOURCES = $(SRC_DIR)/opus_frame_to_pcm.c\r
+SOURCES_ENCODE = $(SRC_DIR)/opus_pcm_to_frame.c\r
 \r
 # Object files\r
 OBJECTS = $(SOURCES:.c=.o)\r
+OBJECTS_ENCODE = $(SOURCES_ENCODE:.c=.o)\r
 \r
 # Default target\r
-all: $(TARGET)\r
+all: $(TARGET) $(TARGET_ENCODE)\r
 \r
 # Build executable\r
 LIBOPUS ?= libopus_min.a\r
@@ -28,28 +31,47 @@ $(TARGET): $(OBJECTS) $(LIBOPUS)
        # Ensure we link against the chosen archive name\r
        @if [ "$(LIBOPUS)" != "libopus.a" ]; then \\r
                rm -f libopus.a; \\r
-               ln -s "$(LIBOPUS)" libopus.a; \\r
+               if command -v ln >/dev/null 2>&1; then \\r
+                       ln -s "$(LIBOPUS)" libopus.a; \\r
+               else \\r
+                       cp "$(LIBOPUS)" libopus.a; \\r
+               fi; \\r
        fi\r
        $(CC) $(OBJECTS) -o $(TARGET) $(LDFLAGS)\r
 \r
+$(TARGET_ENCODE): $(OBJECTS_ENCODE) $(LIBOPUS)\r
+       # Ensure we link against the chosen archive name\r
+       @if [ "$(LIBOPUS)" != "libopus.a" ]; then \\r
+               rm -f libopus.a; \\r
+               if command -v ln >/dev/null 2>&1; then \\r
+                       ln -s "$(LIBOPUS)" libopus.a; \\r
+               else \\r
+                       cp "$(LIBOPUS)" libopus.a; \\r
+               fi; \\r
+       fi\r
+       $(CC) $(OBJECTS_ENCODE) -o $(TARGET_ENCODE) $(LDFLAGS)\r
+\r
 # Compile source files\r
 $(SRC_DIR)/%.o: $(SRC_DIR)/%.c\r
        $(CC) $(CFLAGS) -c $< -o $@\r
 \r
-# Check if libopus.a exists\r
+# Check if libopus.a exists and build it if needed\r
 $(LIBOPUS):\r
        @if [ ! -f $(LIBOPUS) ]; then \\r
-               echo "Error: libopus.a not found!"; \\r
-               echo "Please create $(LIBOPUS) first."; \\r
-               echo "Recommended (PowerShell):"; \\r
-               echo "  ./build_min_libopus.ps1"; \\r
-               echo "Fallback: copy the full libopus.a as $(LIBOPUS) (NOT recommended for size validation)."; \\r
-               exit 1; \\r
+               echo "Error: $(LIBOPUS) not found!"; \\r
+               echo "Building $(LIBOPUS) using Makefile.libopus..."; \\r
+               $(MAKE) -f Makefile.libopus $(LIBOPUS) || ( \\r
+                       echo "Build failed. Please ensure:"; \\r
+                       echo "  1. Source files are copied (run copy_sources.sh)"; \\r
+                       echo "  2. Encoder sources are copied if needed (run copy_encoder_sources.sh)"; \\r
+                       echo "  3. config.h exists in this directory"; \\r
+                       exit 1 \\r
+               ); \\r
        fi\r
 \r
 # Clean build artifacts\r
 clean:\r
-       rm -f $(OBJECTS) $(TARGET)\r
+       rm -f $(OBJECTS) $(OBJECTS_ENCODE) $(TARGET) $(TARGET_ENCODE)\r
 \r
 # Test with a sample file (if available)\r
 test: $(TARGET)\r
@@ -60,4 +82,10 @@ test: $(TARGET)
                echo "No test_frame.opus found. Please provide a test file."; \\r
        fi\r
 \r
-.PHONY: all clean test\r
+.PHONY: all clean test encode-only decode-only\r
+\r
+# Build only decode tool\r
+decode-only: $(TARGET)\r
+\r
+# Build only encode tool\r
+encode-only: $(TARGET_ENCODE)\r
index 9b16a4b..28f89fd 100644 (file)
@@ -1,5 +1,5 @@
-# Makefile for building minimal Opus decoder library (libopus_min.a)\r
-# This builds a decoder-only library from source files\r
+# Makefile for building minimal Opus library (libopus_min.a)\r
+# This builds a library with both encoder and decoder support\r
 \r
 CC = gcc\r
 AR = ar\r
@@ -23,7 +23,7 @@ SRC_LIB_DIR = src_lib
 CELT_DIR = celt\r
 SILK_DIR = silk\r
 \r
-# Opus API layer sources (decoder only)\r
+# Opus API layer sources (decoder only, encoder added if available)\r
 OPUS_SOURCES = \\r
        $(SRC_LIB_DIR)/opus.c \\r
        $(SRC_LIB_DIR)/opus_decoder.c \\r
@@ -33,7 +33,21 @@ OPUS_SOURCES = \
        $(SRC_LIB_DIR)/repacketizer.c \\r
        $(SRC_LIB_DIR)/mapping_matrix.c\r
 \r
-# CELT decoder sources\r
+# Add encoder sources if they exist\r
+ifeq ($(wildcard $(SRC_LIB_DIR)/opus_encoder.c),$(SRC_LIB_DIR)/opus_encoder.c)\r
+OPUS_SOURCES += \\r
+       $(SRC_LIB_DIR)/opus_encoder.c \\r
+       $(SRC_LIB_DIR)/opus_multistream_encoder.c \\r
+       $(SRC_LIB_DIR)/opus_projection_encoder.c \\r
+       $(SRC_LIB_DIR)/analysis.c \\r
+       $(SRC_LIB_DIR)/mlp.c \\r
+       $(SRC_LIB_DIR)/mlp_data.c\r
+ENCODER_SUPPORT = 1\r
+else\r
+ENCODER_SUPPORT = 0\r
+endif\r
+\r
+# CELT decoder sources (encoder added if available)\r
 CELT_SOURCES = \\r
        $(CELT_DIR)/bands.c \\r
        $(CELT_DIR)/celt.c \\r
@@ -53,6 +67,11 @@ CELT_SOURCES = \
        $(CELT_DIR)/rate.c \\r
        $(CELT_DIR)/vq.c\r
 \r
+# Add CELT encoder if available\r
+ifeq ($(wildcard $(CELT_DIR)/celt_encoder.c),$(CELT_DIR)/celt_encoder.c)\r
+CELT_SOURCES += $(CELT_DIR)/celt_encoder.c\r
+endif\r
+\r
 # CELT x86 optimization (if available)\r
 CELT_X86_SOURCES = \\r
        $(CELT_DIR)/x86/x86cpu.c \\r
@@ -75,6 +94,20 @@ SILK_CORE_SOURCES = \
        $(SILK_DIR)/decode_pulses.c \\r
        $(SILK_DIR)/decoder_set_fs.c\r
 \r
+# SILK encoder sources (core) - only if available\r
+SILK_ENCODER_CORE_SOURCES = \r
+ifeq ($(wildcard $(SILK_DIR)/enc_API.c),$(SILK_DIR)/enc_API.c)\r
+SILK_ENCODER_CORE_SOURCES += \\r
+       $(SILK_DIR)/enc_API.c \\r
+       $(SILK_DIR)/init_encoder.c \\r
+       $(SILK_DIR)/encode_indices.c \\r
+       $(SILK_DIR)/encode_pulses.c \\r
+       $(SILK_DIR)/NLSF_encode.c \\r
+       $(SILK_DIR)/NLSF_VQ.c \\r
+       $(SILK_DIR)/NLSF_del_dec_quant.c \\r
+       $(SILK_DIR)/process_NLSFs.c\r
+endif\r
+\r
 # SILK signal processing (shared)\r
 SILK_SIGNAL_SOURCES = \\r
        $(SILK_DIR)/CNG.c \\r
@@ -104,12 +137,20 @@ SILK_NLSF_SOURCES = \
        $(SILK_DIR)/table_LSF_cos.c \\r
        $(SILK_DIR)/LPC_inv_pred_gain.c\r
 \r
-# SILK stereo processing\r
+# SILK stereo processing (decoder, encoder added if available)\r
 SILK_STEREO_SOURCES = \\r
        $(SILK_DIR)/stereo_MS_to_LR.c \\r
        $(SILK_DIR)/stereo_decode_pred.c \\r
        $(SILK_DIR)/stereo_find_predictor.c\r
 \r
+# Add encoder stereo processing if available\r
+ifeq ($(wildcard $(SILK_DIR)/stereo_LR_to_MS.c),$(SILK_DIR)/stereo_LR_to_MS.c)\r
+SILK_STEREO_SOURCES += \\r
+       $(SILK_DIR)/stereo_LR_to_MS.c \\r
+       $(SILK_DIR)/stereo_encode_pred.c \\r
+       $(SILK_DIR)/stereo_quant_pred.c\r
+endif\r
+\r
 # SILK tables\r
 SILK_TABLE_SOURCES = \\r
        $(SILK_DIR)/tables_gain.c \\r
@@ -137,6 +178,28 @@ SILK_HELPER_SOURCES = \
        $(SILK_DIR)/sum_sqr_shift.c \\r
        $(SILK_DIR)/gain_quant.c\r
 \r
+# SILK encoder NSQ (Noise Shaping Quantization) - only if available\r
+SILK_ENCODER_NSQ_SOURCES = \r
+ifeq ($(wildcard $(SILK_DIR)/NSQ.c),$(SILK_DIR)/NSQ.c)\r
+SILK_ENCODER_NSQ_SOURCES += \\r
+       $(SILK_DIR)/NSQ.c \\r
+       $(SILK_DIR)/NSQ_del_dec.c\r
+endif\r
+\r
+# SILK encoder control and analysis - only if available\r
+SILK_ENCODER_CONTROL_SOURCES = \r
+ifeq ($(wildcard $(SILK_DIR)/VAD.c),$(SILK_DIR)/VAD.c)\r
+SILK_ENCODER_CONTROL_SOURCES += \\r
+       $(SILK_DIR)/quant_LTP_gains.c \\r
+       $(SILK_DIR)/VQ_WMat_EC.c \\r
+       $(SILK_DIR)/VAD.c \\r
+       $(SILK_DIR)/control_audio_bandwidth.c \\r
+       $(SILK_DIR)/control_SNR.c \\r
+       $(SILK_DIR)/control_codec.c \\r
+       $(SILK_DIR)/check_control_input.c \\r
+       $(SILK_DIR)/ana_filt_bank_1.c\r
+endif\r
+\r
 # SILK float implementation (default, comment out if using fixed-point)\r
 SILK_FLOAT_SOURCES = \\r
        $(SILK_DIR)/float/apply_sine_window_FLP.c \\r
@@ -167,6 +230,40 @@ SILK_FLOAT_SOURCES = \
        $(SILK_DIR)/float/schur_FLP.c \\r
        $(SILK_DIR)/float/sort_FLP.c\r
 \r
+# Add encode_frame_FLP.c if available\r
+ifeq ($(wildcard $(SILK_DIR)/float/encode_frame_FLP.c),$(SILK_DIR)/float/encode_frame_FLP.c)\r
+SILK_FLOAT_SOURCES += $(SILK_DIR)/float/encode_frame_FLP.c\r
+endif\r
+\r
+# SILK fixed-point implementation (encoder) - only if available\r
+SILK_FIXED_SOURCES = \r
+ifeq ($(wildcard $(SILK_DIR)/fixed/encode_frame_FIX.c),$(SILK_DIR)/fixed/encode_frame_FIX.c)\r
+SILK_FIXED_SOURCES += \\r
+       $(SILK_DIR)/fixed/apply_sine_window_FIX.c \\r
+       $(SILK_DIR)/fixed/autocorr_FIX.c \\r
+       $(SILK_DIR)/fixed/burg_modified_FIX.c \\r
+       $(SILK_DIR)/fixed/corrMatrix_FIX.c \\r
+       $(SILK_DIR)/fixed/encode_frame_FIX.c \\r
+       $(SILK_DIR)/fixed/find_LPC_FIX.c \\r
+       $(SILK_DIR)/fixed/find_LTP_FIX.c \\r
+       $(SILK_DIR)/fixed/find_pitch_lags_FIX.c \\r
+       $(SILK_DIR)/fixed/find_pred_coefs_FIX.c \\r
+       $(SILK_DIR)/fixed/k2a_FIX.c \\r
+       $(SILK_DIR)/fixed/k2a_Q16_FIX.c \\r
+       $(SILK_DIR)/fixed/LTP_analysis_filter_FIX.c \\r
+       $(SILK_DIR)/fixed/LTP_scale_ctrl_FIX.c \\r
+       $(SILK_DIR)/fixed/noise_shape_analysis_FIX.c \\r
+       $(SILK_DIR)/fixed/pitch_analysis_core_FIX.c \\r
+       $(SILK_DIR)/fixed/process_gains_FIX.c \\r
+       $(SILK_DIR)/fixed/regularize_correlations_FIX.c \\r
+       $(SILK_DIR)/fixed/residual_energy_FIX.c \\r
+       $(SILK_DIR)/fixed/residual_energy16_FIX.c \\r
+       $(SILK_DIR)/fixed/schur_FIX.c \\r
+       $(SILK_DIR)/fixed/schur64_FIX.c \\r
+       $(SILK_DIR)/fixed/vector_ops_FIX.c \\r
+       $(SILK_DIR)/fixed/warped_autocorrelation_FIX.c\r
+endif\r
+\r
 # SILK x86 optimization (if available)\r
 SILK_X86_SOURCES = \\r
        $(SILK_DIR)/x86/x86_silk_map.c\r
@@ -177,12 +274,16 @@ ALL_SOURCES = \
        $(CELT_SOURCES) \\r
        $(CELT_X86_SOURCES) \\r
        $(SILK_CORE_SOURCES) \\r
+       $(SILK_ENCODER_CORE_SOURCES) \\r
        $(SILK_SIGNAL_SOURCES) \\r
        $(SILK_NLSF_SOURCES) \\r
        $(SILK_STEREO_SOURCES) \\r
        $(SILK_TABLE_SOURCES) \\r
        $(SILK_HELPER_SOURCES) \\r
+       $(SILK_ENCODER_NSQ_SOURCES) \\r
+       $(SILK_ENCODER_CONTROL_SOURCES) \\r
        $(SILK_FLOAT_SOURCES) \\r
+       $(SILK_FIXED_SOURCES) \\r
        $(SILK_X86_SOURCES)\r
 \r
 # Object files\r
@@ -194,6 +295,11 @@ all: $(LIBRARY)
 # Build library\r
 $(LIBRARY): $(OBJECTS)\r
        @echo "Creating $(LIBRARY)..."\r
+       @if [ "$(ENCODER_SUPPORT)" = "1" ]; then \\r
+               echo "Building with encoder support"; \\r
+       else \\r
+               echo "Building decoder-only library"; \\r
+       fi\r
        $(AR) rcs $(LIBRARY) $(OBJECTS)\r
        $(RANLIB) $(LIBRARY)\r
        @echo "Library created: $(LIBRARY)"\r
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/celt/celt_encoder.c b/code/sdk/zhihui/test_opus_frame_to_pcm/celt/celt_encoder.c
new file mode 100644 (file)
index 0000000..ab97ed6
--- /dev/null
@@ -0,0 +1,3161 @@
+/* Copyright (c) 2007-2008 CSIRO
+   Copyright (c) 2007-2010 Xiph.Org Foundation
+   Copyright (c) 2008 Gregory Maxwell
+   Written by Jean-Marc Valin and Gregory Maxwell */
+/*
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+   - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+   - Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+   OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#define CELT_ENCODER_C
+
+#include "cpu_support.h"
+#include "os_support.h"
+#include "mdct.h"
+#include <math.h>
+#include "celt.h"
+#include "pitch.h"
+#include "bands.h"
+#include "modes.h"
+#include "entcode.h"
+#include "quant_bands.h"
+#include "rate.h"
+#include "stack_alloc.h"
+#include "mathops.h"
+#include "float_cast.h"
+#include <stdarg.h>
+#include "celt_lpc.h"
+#include "vq.h"
+
+
+#ifndef M_PI
+#define M_PI 3.141592653
+#endif
+
+
+/** Encoder state
+ @brief Encoder state
+ */
+struct OpusCustomEncoder {
+   const OpusCustomMode *mode;     /**< Mode used by the encoder */
+   int channels;
+   int stream_channels;
+
+   int force_intra;
+   int clip;
+   int disable_pf;
+   int complexity;
+   int upsample;
+   int start, end;
+
+   opus_int32 bitrate;
+   int vbr;
+   int signalling;
+   int constrained_vbr;      /* If zero, VBR can do whatever it likes with the rate */
+   int loss_rate;
+   int lsb_depth;
+   int lfe;
+   int disable_inv;
+   int arch;
+#ifdef ENABLE_QEXT
+   int enable_qext;
+   int qext_scale;
+#endif
+
+   /* Everything beyond this point gets cleared on a reset */
+#define ENCODER_RESET_START rng
+
+   opus_uint32 rng;
+   int spread_decision;
+   opus_val32 delayedIntra;
+   int tonal_average;
+   int lastCodedBands;
+   int hf_average;
+   int tapset_decision;
+
+   int prefilter_period;
+   opus_val16 prefilter_gain;
+   int prefilter_tapset;
+#ifdef RESYNTH
+   int prefilter_period_old;
+   opus_val16 prefilter_gain_old;
+   int prefilter_tapset_old;
+#endif
+   int consec_transient;
+   AnalysisInfo analysis;
+   SILKInfo silk_info;
+
+   opus_val32 preemph_memE[2];
+   opus_val32 preemph_memD[2];
+
+   /* VBR-related parameters */
+   opus_int32 vbr_reservoir;
+   opus_int32 vbr_drift;
+   opus_int32 vbr_offset;
+   opus_int32 vbr_count;
+   opus_val32 overlap_max;
+   opus_val16 stereo_saving;
+   int intensity;
+   celt_glog *energy_mask;
+   celt_glog spec_avg;
+
+#ifdef RESYNTH
+#ifdef ENABLE_QEXT
+   /* +MAX_PERIOD/2 to make space for overlap */
+   celt_sig syn_mem[2][2*DEC_PITCH_BUF_SIZE+MAX_PERIOD];
+#else
+   /* +MAX_PERIOD/2 to make space for overlap */
+   celt_sig syn_mem[2][DEC_PITCH_BUF_SIZE+MAX_PERIOD/2];
+#endif
+#endif
+
+   celt_sig in_mem[1]; /* Size = channels*mode->overlap */
+   /* celt_sig prefilter_mem[],  Size = channels*COMBFILTER_MAXPERIOD */
+   /* celt_glog oldBandE[],     Size = channels*mode->nbEBands */
+   /* celt_glog oldLogE[],      Size = channels*mode->nbEBands */
+   /* celt_glog oldLogE2[],     Size = channels*mode->nbEBands */
+   /* celt_glog energyError[],  Size = channels*mode->nbEBands */
+};
+
+int celt_encoder_get_size(int channels)
+{
+#ifdef ENABLE_QEXT
+   CELTMode *mode = opus_custom_mode_create(96000, 1920, NULL);
+#else
+   CELTMode *mode = opus_custom_mode_create(48000, 960, NULL);
+#endif
+   return opus_custom_encoder_get_size(mode, channels);
+}
+
+OPUS_CUSTOM_NOSTATIC int opus_custom_encoder_get_size(const CELTMode *mode, int channels)
+{
+   int extra=0;
+   int size;
+#ifdef ENABLE_QEXT
+   int qext_scale;
+   extra = channels*NB_QEXT_BANDS*sizeof(celt_glog);
+   if (mode->Fs == 96000 && (mode->shortMdctSize==240 || mode->shortMdctSize==180)) {
+      qext_scale = 2;
+   } else qext_scale = 1;
+#endif
+   size = sizeof(struct CELTEncoder)
+         + (channels*mode->overlap-1)*sizeof(celt_sig)    /* celt_sig in_mem[channels*mode->overlap]; */
+         + channels*QEXT_SCALE(COMBFILTER_MAXPERIOD)*sizeof(celt_sig) /* celt_sig prefilter_mem[channels*COMBFILTER_MAXPERIOD]; */
+         + 4*channels*mode->nbEBands*sizeof(celt_glog)    /* celt_glog oldBandE[channels*mode->nbEBands]; */
+                                                          /* celt_glog oldLogE[channels*mode->nbEBands]; */
+                                                          /* celt_glog oldLogE2[channels*mode->nbEBands]; */
+                                                          /* celt_glog energyError[channels*mode->nbEBands]; */
+         + extra;
+   return size;
+}
+
+#if defined(CUSTOM_MODES) || defined(ENABLE_OPUS_CUSTOM_API)
+CELTEncoder *opus_custom_encoder_create(const CELTMode *mode, int channels, int *error)
+{
+   int ret;
+   CELTEncoder *st = (CELTEncoder *)opus_alloc(opus_custom_encoder_get_size(mode, channels));
+   /* init will handle the NULL case */
+   ret = opus_custom_encoder_init(st, mode, channels);
+   if (ret != OPUS_OK)
+   {
+      opus_custom_encoder_destroy(st);
+      st = NULL;
+   }
+   if (error)
+      *error = ret;
+   return st;
+}
+#endif /* CUSTOM_MODES */
+
+static int opus_custom_encoder_init_arch(CELTEncoder *st, const CELTMode *mode,
+                                         int channels, int arch)
+{
+   if (channels < 0 || channels > 2)
+      return OPUS_BAD_ARG;
+
+   if (st==NULL || mode==NULL)
+      return OPUS_ALLOC_FAIL;
+
+   OPUS_CLEAR((char*)st, opus_custom_encoder_get_size(mode, channels));
+
+   st->mode = mode;
+   st->stream_channels = st->channels = channels;
+
+   st->upsample = 1;
+   st->start = 0;
+   st->end = st->mode->effEBands;
+   st->signalling = 1;
+   st->arch = arch;
+
+   st->constrained_vbr = 1;
+   st->clip = 1;
+
+   st->bitrate = OPUS_BITRATE_MAX;
+   st->vbr = 0;
+   st->force_intra  = 0;
+   st->complexity = 5;
+   st->lsb_depth=24;
+
+#ifdef ENABLE_QEXT
+   if (st->mode->Fs == 96000 && (mode->shortMdctSize==240 || mode->shortMdctSize==180)) st->qext_scale = 2;
+   else st->qext_scale = 1;
+#endif
+
+   opus_custom_encoder_ctl(st, OPUS_RESET_STATE);
+
+   return OPUS_OK;
+}
+
+#if defined(CUSTOM_MODES) || defined(ENABLE_OPUS_CUSTOM_API)
+int opus_custom_encoder_init(CELTEncoder *st, const CELTMode *mode, int channels)
+{
+   return opus_custom_encoder_init_arch(st, mode, channels, opus_select_arch());
+}
+#endif
+
+int celt_encoder_init(CELTEncoder *st, opus_int32 sampling_rate, int channels,
+                      int arch)
+{
+   int ret;
+#ifdef ENABLE_QEXT
+   if (sampling_rate==96000) {
+      st->upsample = 1;
+      return opus_custom_encoder_init_arch(st,
+              opus_custom_mode_create(96000, 1920, NULL), channels, arch);
+   }
+#endif
+   ret = opus_custom_encoder_init_arch(st,
+           opus_custom_mode_create(48000, 960, NULL), channels, arch);
+   if (ret != OPUS_OK)
+      return ret;
+   st->upsample = resampling_factor(sampling_rate);
+   return OPUS_OK;
+}
+
+#if defined(CUSTOM_MODES) || defined(ENABLE_OPUS_CUSTOM_API)
+void opus_custom_encoder_destroy(CELTEncoder *st)
+{
+   opus_free(st);
+}
+#endif /* CUSTOM_MODES */
+
+
+static int transient_analysis(const opus_val32 * OPUS_RESTRICT in, int len, int C,
+                              opus_val16 *tf_estimate, int *tf_chan, int allow_weak_transients,
+                              int *weak_transient, opus_val16 tone_freq, opus_val32 toneishness)
+{
+   int i;
+   VARDECL(opus_val16, tmp);
+   opus_val32 mem0,mem1;
+   int is_transient = 0;
+   opus_int32 mask_metric = 0;
+   int c;
+   opus_val16 tf_max;
+   int len2;
+   /* Forward masking: 6.7 dB/ms. */
+#ifdef FIXED_POINT
+   int forward_shift = 4;
+#else
+   opus_val16 forward_decay = QCONST16(.0625f,15);
+#endif
+   /* Table of 6*64/x, trained on real data to minimize the average error */
+   static const unsigned char inv_table[128] = {
+         255,255,156,110, 86, 70, 59, 51, 45, 40, 37, 33, 31, 28, 26, 25,
+          23, 22, 21, 20, 19, 18, 17, 16, 16, 15, 15, 14, 13, 13, 12, 12,
+          12, 12, 11, 11, 11, 10, 10, 10,  9,  9,  9,  9,  9,  9,  8,  8,
+           8,  8,  8,  7,  7,  7,  7,  7,  7,  6,  6,  6,  6,  6,  6,  6,
+           6,  6,  6,  6,  6,  6,  6,  6,  6,  5,  5,  5,  5,  5,  5,  5,
+           5,  5,  5,  5,  5,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
+           4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  3,  3,
+           3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  2,
+   };
+   SAVE_STACK;
+#ifdef FIXED_POINT
+   int in_shift = IMAX(0, celt_ilog2(1+celt_maxabs32(in, C*len))-14);
+#endif
+   ALLOC(tmp, len, opus_val16);
+
+   *weak_transient = 0;
+   /* For lower bitrates, let's be more conservative and have a forward masking
+      decay of 3.3 dB/ms. This avoids having to code transients at very low
+      bitrate (mostly for hybrid), which can result in unstable energy and/or
+      partial collapse. */
+   if (allow_weak_transients)
+   {
+#ifdef FIXED_POINT
+      forward_shift = 5;
+#else
+      forward_decay = QCONST16(.03125f,15);
+#endif
+   }
+   len2=len/2;
+   for (c=0;c<C;c++)
+   {
+      opus_val32 mean;
+      opus_int32 unmask=0;
+      opus_val32 norm;
+      opus_val16 maxE;
+      mem0=0;
+      mem1=0;
+      /* High-pass filter: (1 - 2*z^-1 + z^-2) / (1 - z^-1 + .5*z^-2) */
+      for (i=0;i<len;i++)
+      {
+#ifndef FIXED_POINT
+         float mem00;
+#endif
+         opus_val32 x,y;
+         x = SHR32(in[i+c*len],in_shift);
+         y = ADD32(mem0, x);
+#ifdef FIXED_POINT
+         mem0 = mem1 + y - SHL32(x,1);
+         mem1 = x - SHR32(y,1);
+#else
+         /* Original code:
+         mem0 = mem1 + y - 2*x;
+         mem1 = x - .5f*y;
+         Modified code to shorten dependency chains: */
+         mem00=mem0;
+         mem0 = mem0 - x + .5f*mem1;
+         mem1 =  x - mem00;
+#endif
+         tmp[i] = SROUND16(y, 2);
+         /*printf("%f ", tmp[i]);*/
+      }
+      /*printf("\n");*/
+      /* First few samples are bad because we don't propagate the memory */
+      OPUS_CLEAR(tmp, 12);
+
+#ifdef FIXED_POINT
+      /* Normalize tmp to max range */
+      {
+         int shift=0;
+         shift = 14-celt_ilog2(MAX16(1, celt_maxabs16(tmp, len)));
+         if (shift!=0)
+         {
+            for (i=0;i<len;i++)
+               tmp[i] = SHL16(tmp[i], shift);
+         }
+      }
+#endif
+
+      mean=0;
+      mem0=0;
+      /* Grouping by two to reduce complexity */
+      /* Forward pass to compute the post-echo threshold*/
+      for (i=0;i<len2;i++)
+      {
+         opus_val32 x2 = PSHR32(MULT16_16(tmp[2*i],tmp[2*i]) + MULT16_16(tmp[2*i+1],tmp[2*i+1]),4);
+         mean += PSHR32(x2, 12);
+#ifdef FIXED_POINT
+         /* FIXME: Use PSHR16() instead */
+         mem0 = mem0 + PSHR32(x2-mem0,forward_shift);
+         tmp[i] = PSHR32(mem0, 12);
+#else
+         mem0 = x2 + (1.f-forward_decay)*mem0;
+         tmp[i] = forward_decay*mem0;
+#endif
+      }
+
+      mem0=0;
+      maxE=0;
+      /* Backward pass to compute the pre-echo threshold */
+      for (i=len2-1;i>=0;i--)
+      {
+         /* Backward masking: 13.9 dB/ms. */
+#ifdef FIXED_POINT
+         /* FIXME: Use PSHR16() instead */
+         mem0 = mem0 + PSHR32(SHL32(tmp[i],4)-mem0,3);
+         tmp[i] = PSHR32(mem0, 4);
+         maxE = MAX16(maxE, tmp[i]);
+#else
+         mem0 = tmp[i] + 0.875f*mem0;
+         tmp[i] = 0.125f*mem0;
+         maxE = MAX16(maxE, 0.125f*mem0);
+#endif
+      }
+      /*for (i=0;i<len2;i++)printf("%f ", tmp[i]/mean);printf("\n");*/
+
+      /* Compute the ratio of the "frame energy" over the harmonic mean of the energy.
+         This essentially corresponds to a bitrate-normalized temporal noise-to-mask
+         ratio */
+
+      /* As a compromise with the old transient detector, frame energy is the
+         geometric mean of the energy and half the max */
+#ifdef FIXED_POINT
+      /* Costs two sqrt() to avoid overflows */
+      mean = MULT16_16(celt_sqrt(mean), celt_sqrt(MULT16_16(maxE,len2>>1)));
+#else
+      mean = celt_sqrt(mean * maxE*.5*len2);
+#endif
+      /* Inverse of the mean energy in Q15+6 */
+      norm = SHL32(EXTEND32(len2),6+14)/ADD32(EPSILON,SHR32(mean,1));
+      /* Compute harmonic mean discarding the unreliable boundaries
+         The data is smooth, so we only take 1/4th of the samples */
+      unmask=0;
+      /* We should never see NaNs here. If we find any, then something really bad happened and we better abort
+         before it does any damage later on. If these asserts are disabled (no hardening), then the table
+         lookup a few lines below (id = ...) is likely to crash dur to an out-of-bounds read. DO NOT FIX
+         that crash on NaN since it could result in a worse issue later on. */
+      celt_assert(!celt_isnan(tmp[0]));
+      celt_assert(!celt_isnan(norm));
+      for (i=12;i<len2-5;i+=4)
+      {
+         int id;
+#ifdef FIXED_POINT
+         id = MAX32(0,MIN32(127,MULT16_32_Q15(tmp[i]+EPSILON,norm))); /* Do not round to nearest */
+#else
+         id = (int)MAX32(0,MIN32(127,floor(64*norm*(tmp[i]+EPSILON)))); /* Do not round to nearest */
+#endif
+         unmask += inv_table[id];
+      }
+      /*printf("%d\n", unmask);*/
+      /* Normalize, compensate for the 1/4th of the sample and the factor of 6 in the inverse table */
+      unmask = 64*unmask*4/(6*(len2-17));
+      if (unmask>mask_metric)
+      {
+         *tf_chan = c;
+         mask_metric = unmask;
+      }
+   }
+   is_transient = mask_metric>200;
+   /* Prevent the transient detector from confusing the partial cycle of a
+      very low frequency tone with a transient. */
+   if (toneishness > QCONST32(.98f, 29) && tone_freq < QCONST16(0.026f, 13))
+   {
+      is_transient = 0;
+      mask_metric = 0;
+   }
+   /* For low bitrates, define "weak transients" that need to be
+      handled differently to avoid partial collapse. */
+   if (allow_weak_transients && is_transient && mask_metric<600) {
+      is_transient = 0;
+      *weak_transient = 1;
+   }
+   /* Arbitrary metric for VBR boost */
+   tf_max = MAX16(0,celt_sqrt(27*mask_metric)-42);
+   /* *tf_estimate = 1 + MIN16(1, sqrt(MAX16(0, tf_max-30))/20); */
+   *tf_estimate = celt_sqrt(MAX32(0, SHL32(MULT16_16(QCONST16(0.0069,14),MIN16(163,tf_max)),14)-QCONST32(0.139,28)));
+   /*printf("%d %f\n", tf_max, mask_metric);*/
+   RESTORE_STACK;
+#ifdef FUZZING
+   is_transient = rand()&0x1;
+#endif
+   /*printf("%d %f %d\n", is_transient, (float)*tf_estimate, tf_max);*/
+   return is_transient;
+}
+
+/* Looks for sudden increases of energy to decide whether we need to patch
+   the transient decision */
+static int patch_transient_decision(celt_glog *newE, celt_glog *oldE, int nbEBands,
+      int start, int end, int C)
+{
+   int i, c;
+   opus_val32 mean_diff=0;
+   celt_glog spread_old[26];
+   /* Apply an aggressive (-6 dB/Bark) spreading function to the old frame to
+      avoid false detection caused by irrelevant bands */
+   if (C==1)
+   {
+      spread_old[start] = oldE[start];
+      for (i=start+1;i<end;i++)
+         spread_old[i] = MAXG(spread_old[i-1]-GCONST(1.0f), oldE[i]);
+   } else {
+      spread_old[start] = MAXG(oldE[start],oldE[start+nbEBands]);
+      for (i=start+1;i<end;i++)
+         spread_old[i] = MAXG(spread_old[i-1]-GCONST(1.0f),
+                               MAXG(oldE[i],oldE[i+nbEBands]));
+   }
+   for (i=end-2;i>=start;i--)
+      spread_old[i] = MAXG(spread_old[i], spread_old[i+1]-GCONST(1.0f));
+   /* Compute mean increase */
+   c=0; do {
+      for (i=IMAX(2,start);i<end-1;i++)
+      {
+         opus_val16 x1, x2;
+         x1 = MAXG(0, newE[i + c*nbEBands]);
+         x2 = MAXG(0, spread_old[i]);
+         mean_diff = ADD32(mean_diff, MAXG(0, SUB32(x1, x2)));
+      }
+   } while (++c<C);
+   mean_diff = DIV32(mean_diff, C*(end-1-IMAX(2,start)));
+   /*printf("%f %f %d\n", mean_diff, max_diff, count);*/
+   return mean_diff > GCONST(1.f);
+}
+
+/** Apply window and compute the MDCT for all sub-frames and
+    all channels in a frame */
+static void compute_mdcts(const CELTMode *mode, int shortBlocks, celt_sig * OPUS_RESTRICT in,
+                          celt_sig * OPUS_RESTRICT out, int C, int CC, int LM, int upsample,
+                          int arch)
+{
+   const int overlap = mode->overlap;
+   int N;
+   int B;
+   int shift;
+   int i, b, c;
+   if (shortBlocks)
+   {
+      B = shortBlocks;
+      N = mode->shortMdctSize;
+      shift = mode->maxLM;
+   } else {
+      B = 1;
+      N = mode->shortMdctSize<<LM;
+      shift = mode->maxLM-LM;
+   }
+   c=0; do {
+      for (b=0;b<B;b++)
+      {
+         /* Interleaving the sub-frames while doing the MDCTs */
+         clt_mdct_forward(&mode->mdct, in+c*(B*N+overlap)+b*N,
+                          &out[b+c*N*B], mode->window, overlap, shift, B,
+                          arch);
+      }
+   } while (++c<CC);
+   if (CC==2&&C==1)
+   {
+      for (i=0;i<B*N;i++)
+         out[i] = ADD32(HALF32(out[i]), HALF32(out[B*N+i]));
+   }
+   if (upsample != 1)
+   {
+      c=0; do
+      {
+         int bound = B*N/upsample;
+         for (i=0;i<bound;i++)
+            out[c*B*N+i] *= upsample;
+         OPUS_CLEAR(&out[c*B*N+bound], B*N-bound);
+      } while (++c<C);
+   }
+}
+
+
+void celt_preemphasis(const opus_res * OPUS_RESTRICT pcmp, celt_sig * OPUS_RESTRICT inp,
+                        int N, int CC, int upsample, const opus_val16 *coef, celt_sig *mem, int clip)
+{
+   int i;
+   opus_val16 coef0;
+   celt_sig m;
+   int Nu;
+
+   coef0 = coef[0];
+   m = *mem;
+
+   /* Fast path for the normal 48kHz case and no clipping */
+   if (coef[1] == 0 && upsample == 1 && !clip)
+   {
+      for (i=0;i<N;i++)
+      {
+         celt_sig x;
+         x = RES2SIG(pcmp[CC*i]);
+         /* Apply pre-emphasis */
+         inp[i] = x - m;
+         m = MULT16_32_Q15(coef0, x);
+      }
+      *mem = m;
+      return;
+   }
+
+   Nu = N/upsample;
+   if (upsample!=1)
+   {
+      OPUS_CLEAR(inp, N);
+   }
+   for (i=0;i<Nu;i++)
+      inp[i*upsample] = RES2SIG(pcmp[CC*i]);
+
+#ifndef FIXED_POINT
+   if (clip)
+   {
+      /* Clip input to avoid encoding non-portable files */
+      for (i=0;i<Nu;i++)
+         inp[i*upsample] = MAX32(-65536.f, MIN32(65536.f,inp[i*upsample]));
+   }
+#elif defined(ENABLE_RES24)
+   if (clip)
+   {
+      /* Clip input to avoid encoding non-portable files */
+      for (i=0;i<Nu;i++)
+         inp[i*upsample] = MAX32(-(65536<<SIG_SHIFT), MIN32(65536<<SIG_SHIFT,inp[i*upsample]));
+   }
+#else
+   (void)clip; /* Avoids a warning about clip being unused. */
+#endif
+#if defined(CUSTOM_MODES) || defined(ENABLE_OPUS_CUSTOM_API) || defined(ENABLE_QEXT)
+   if (coef[1] != 0)
+   {
+      opus_val16 coef1 = coef[1];
+#if defined(FIXED_POINT) && defined(ENABLE_QEXT)
+      /* If we need the extra precision, we use the fact that coef[3] is exact to do a Newton-Raphson
+         iteration and get us more precision on coef[2]. */
+      opus_val32 coef2_q30 = SHL32(coef[2], 18) + PSHR32(MULT16_16(QCONST32(1.f, 25) - MULT16_16(coef[3], coef[2]), coef[2]), 7);
+      celt_assert(SIG_SHIFT == 12);
+#else
+      opus_val16 coef2 = coef[2];
+#endif
+      for (i=0;i<N;i++)
+      {
+         celt_sig x, tmp;
+         x = inp[i];
+         /* Apply pre-emphasis */
+#if defined(FIXED_POINT) && defined(ENABLE_QEXT)
+         tmp = SHL32(MULT32_32_Q31(coef2_q30, x), 1);
+#else
+         tmp = SHL32(MULT16_32_Q15(coef2, x), 15-SIG_SHIFT);
+#endif
+         inp[i] = tmp + m;
+         m = MULT16_32_Q15(coef1, inp[i]) - MULT16_32_Q15(coef0, tmp);
+      }
+   } else
+#endif
+   {
+      for (i=0;i<N;i++)
+      {
+         celt_sig x;
+         x = inp[i];
+         /* Apply pre-emphasis */
+         inp[i] = x - m;
+         m = MULT16_32_Q15(coef0, x);
+      }
+   }
+   *mem = m;
+}
+
+
+
+static opus_val32 l1_metric(const celt_norm *tmp, int N, int LM, opus_val16 bias)
+{
+   int i;
+   opus_val32 L1;
+   L1 = 0;
+   for (i=0;i<N;i++)
+      L1 += EXTEND32(ABS16(SHR32(tmp[i], NORM_SHIFT-14)));
+   /* When in doubt, prefer good freq resolution */
+   L1 = MAC16_32_Q15(L1, LM*bias, L1);
+   return L1;
+
+}
+
+static int tf_analysis(const CELTMode *m, int len, int isTransient,
+      int *tf_res, int lambda, celt_norm *X, int N0, int LM,
+      opus_val16 tf_estimate, int tf_chan, int *importance)
+{
+   int i;
+   VARDECL(int, metric);
+   int cost0;
+   int cost1;
+   VARDECL(int, path0);
+   VARDECL(int, path1);
+   VARDECL(celt_norm, tmp);
+   VARDECL(celt_norm, tmp_1);
+   int sel;
+   int selcost[2];
+   int tf_select=0;
+   opus_val16 bias;
+
+   SAVE_STACK;
+   bias = MULT16_16_Q14(QCONST16(.04f,15), MAX16(-QCONST16(.25f,14), QCONST16(.5f,14)-tf_estimate));
+   /*printf("%f ", bias);*/
+
+   ALLOC(metric, len, int);
+   ALLOC(tmp, (m->eBands[len]-m->eBands[len-1])<<LM, celt_norm);
+   ALLOC(tmp_1, (m->eBands[len]-m->eBands[len-1])<<LM, celt_norm);
+   ALLOC(path0, len, int);
+   ALLOC(path1, len, int);
+
+   for (i=0;i<len;i++)
+   {
+      int k, N;
+      int narrow;
+      opus_val32 L1, best_L1;
+      int best_level=0;
+      N = (m->eBands[i+1]-m->eBands[i])<<LM;
+      /* band is too narrow to be split down to LM=-1 */
+      narrow = (m->eBands[i+1]-m->eBands[i])==1;
+      OPUS_COPY(tmp, &X[tf_chan*N0 + (m->eBands[i]<<LM)], N);
+      /* Just add the right channel if we're in stereo */
+      /*if (C==2)
+         for (j=0;j<N;j++)
+            tmp[j] = ADD16(SHR16(tmp[j], 1),SHR16(X[N0+j+(m->eBands[i]<<LM)], 1));*/
+      L1 = l1_metric(tmp, N, isTransient ? LM : 0, bias);
+      best_L1 = L1;
+      /* Check the -1 case for transients */
+      if (isTransient && !narrow)
+      {
+         OPUS_COPY(tmp_1, tmp, N);
+         haar1(tmp_1, N>>LM, 1<<LM);
+         L1 = l1_metric(tmp_1, N, LM+1, bias);
+         if (L1<best_L1)
+         {
+            best_L1 = L1;
+            best_level = -1;
+         }
+      }
+      /*printf ("%f ", L1);*/
+      for (k=0;k<LM+!(isTransient||narrow);k++)
+      {
+         int B;
+
+         if (isTransient)
+            B = (LM-k-1);
+         else
+            B = k+1;
+
+         haar1(tmp, N>>k, 1<<k);
+
+         L1 = l1_metric(tmp, N, B, bias);
+
+         if (L1 < best_L1)
+         {
+            best_L1 = L1;
+            best_level = k+1;
+         }
+      }
+      /*printf ("%d ", isTransient ? LM-best_level : best_level);*/
+      /* metric is in Q1 to be able to select the mid-point (-0.5) for narrower bands */
+      if (isTransient)
+         metric[i] = 2*best_level;
+      else
+         metric[i] = -2*best_level;
+      /* For bands that can't be split to -1, set the metric to the half-way point to avoid
+         biasing the decision */
+      if (narrow && (metric[i]==0 || metric[i]==-2*LM))
+         metric[i]-=1;
+      /*printf("%d ", metric[i]/2 + (!isTransient)*LM);*/
+   }
+   /*printf("\n");*/
+   /* Search for the optimal tf resolution, including tf_select */
+   tf_select = 0;
+   for (sel=0;sel<2;sel++)
+   {
+      cost0 = importance[0]*abs(metric[0]-2*tf_select_table[LM][4*isTransient+2*sel+0]);
+      cost1 = importance[0]*abs(metric[0]-2*tf_select_table[LM][4*isTransient+2*sel+1]) + (isTransient ? 0 : lambda);
+      for (i=1;i<len;i++)
+      {
+         int curr0, curr1;
+         curr0 = IMIN(cost0, cost1 + lambda);
+         curr1 = IMIN(cost0 + lambda, cost1);
+         cost0 = curr0 + importance[i]*abs(metric[i]-2*tf_select_table[LM][4*isTransient+2*sel+0]);
+         cost1 = curr1 + importance[i]*abs(metric[i]-2*tf_select_table[LM][4*isTransient+2*sel+1]);
+      }
+      cost0 = IMIN(cost0, cost1);
+      selcost[sel]=cost0;
+   }
+   /* For now, we're conservative and only allow tf_select=1 for transients.
+    * If tests confirm it's useful for non-transients, we could allow it. */
+   if (selcost[1]<selcost[0] && isTransient)
+      tf_select=1;
+   cost0 = importance[0]*abs(metric[0]-2*tf_select_table[LM][4*isTransient+2*tf_select+0]);
+   cost1 = importance[0]*abs(metric[0]-2*tf_select_table[LM][4*isTransient+2*tf_select+1]) + (isTransient ? 0 : lambda);
+   /* Viterbi forward pass */
+   for (i=1;i<len;i++)
+   {
+      int curr0, curr1;
+      int from0, from1;
+
+      from0 = cost0;
+      from1 = cost1 + lambda;
+      if (from0 < from1)
+      {
+         curr0 = from0;
+         path0[i]= 0;
+      } else {
+         curr0 = from1;
+         path0[i]= 1;
+      }
+
+      from0 = cost0 + lambda;
+      from1 = cost1;
+      if (from0 < from1)
+      {
+         curr1 = from0;
+         path1[i]= 0;
+      } else {
+         curr1 = from1;
+         path1[i]= 1;
+      }
+      cost0 = curr0 + importance[i]*abs(metric[i]-2*tf_select_table[LM][4*isTransient+2*tf_select+0]);
+      cost1 = curr1 + importance[i]*abs(metric[i]-2*tf_select_table[LM][4*isTransient+2*tf_select+1]);
+   }
+   tf_res[len-1] = cost0 < cost1 ? 0 : 1;
+   /* Viterbi backward pass to check the decisions */
+   for (i=len-2;i>=0;i--)
+   {
+      if (tf_res[i+1] == 1)
+         tf_res[i] = path1[i+1];
+      else
+         tf_res[i] = path0[i+1];
+   }
+   /*printf("%d %f\n", *tf_sum, tf_estimate);*/
+   RESTORE_STACK;
+#ifdef FUZZING
+   tf_select = rand()&0x1;
+   tf_res[0] = rand()&0x1;
+   for (i=1;i<len;i++)
+      tf_res[i] = tf_res[i-1] ^ ((rand()&0xF) == 0);
+#endif
+   return tf_select;
+}
+
+static void tf_encode(int start, int end, int isTransient, int *tf_res, int LM, int tf_select, ec_enc *enc)
+{
+   int curr, i;
+   int tf_select_rsv;
+   int tf_changed;
+   int logp;
+   opus_uint32 budget;
+   opus_uint32 tell;
+   budget = enc->storage*8;
+   tell = ec_tell(enc);
+   logp = isTransient ? 2 : 4;
+   /* Reserve space to code the tf_select decision. */
+   tf_select_rsv = LM>0 && tell+logp+1 <= budget;
+   budget -= tf_select_rsv;
+   curr = tf_changed = 0;
+   for (i=start;i<end;i++)
+   {
+      if (tell+logp<=budget)
+      {
+         ec_enc_bit_logp(enc, tf_res[i] ^ curr, logp);
+         tell = ec_tell(enc);
+         curr = tf_res[i];
+         tf_changed |= curr;
+      }
+      else
+         tf_res[i] = curr;
+      logp = isTransient ? 4 : 5;
+   }
+   /* Only code tf_select if it would actually make a difference. */
+   if (tf_select_rsv &&
+         tf_select_table[LM][4*isTransient+0+tf_changed]!=
+         tf_select_table[LM][4*isTransient+2+tf_changed])
+      ec_enc_bit_logp(enc, tf_select, 1);
+   else
+      tf_select = 0;
+   for (i=start;i<end;i++)
+      tf_res[i] = tf_select_table[LM][4*isTransient+2*tf_select+tf_res[i]];
+   /*for(i=0;i<end;i++)printf("%d ", isTransient ? tf_res[i] : LM+tf_res[i]);printf("\n");*/
+}
+
+
+static int alloc_trim_analysis(const CELTMode *m, const celt_norm *X,
+      const celt_glog *bandLogE, int end, int LM, int C, int N0,
+      AnalysisInfo *analysis, opus_val16 *stereo_saving, opus_val16 tf_estimate,
+      int intensity, celt_glog surround_trim, opus_int32 equiv_rate, int arch)
+{
+   int i;
+   opus_val32 diff=0;
+   int c;
+   int trim_index;
+   opus_val16 trim = QCONST16(5.f, 8);
+   opus_val16 logXC, logXC2;
+   /* At low bitrate, reducing the trim seems to help. At higher bitrates, it's less
+      clear what's best, so we're keeping it as it was before, at least for now. */
+   if (equiv_rate < 64000) {
+      trim = QCONST16(4.f, 8);
+   } else if (equiv_rate < 80000) {
+      opus_int32 frac = (equiv_rate-64000) >> 10;
+      trim = QCONST16(4.f, 8) + QCONST16(1.f/16.f, 8)*frac;
+   }
+   if (C==2)
+   {
+      opus_val16 sum = 0; /* Q10 */
+      opus_val16 minXC; /* Q10 */
+      /* Compute inter-channel correlation for low frequencies */
+      for (i=0;i<8;i++)
+      {
+         opus_val32 partial;
+         partial = celt_inner_prod_norm_shift(&X[m->eBands[i]<<LM], &X[N0+(m->eBands[i]<<LM)],
+               (m->eBands[i+1]-m->eBands[i])<<LM, arch);
+         sum = ADD16(sum, EXTRACT16(SHR32(partial, 18)));
+      }
+      sum = MULT16_16_Q15(QCONST16(1.f/8, 15), sum);
+      sum = MIN16(QCONST16(1.f, 10), ABS16(sum));
+      minXC = sum;
+      for (i=8;i<intensity;i++)
+      {
+         opus_val32 partial;
+         partial = celt_inner_prod_norm_shift(&X[m->eBands[i]<<LM], &X[N0+(m->eBands[i]<<LM)],
+               (m->eBands[i+1]-m->eBands[i])<<LM, arch);
+         minXC = MIN16(minXC, ABS16(EXTRACT16(SHR32(partial, 18))));
+      }
+      minXC = MIN16(QCONST16(1.f, 10), ABS16(minXC));
+      /*printf ("%f\n", sum);*/
+      /* mid-side savings estimations based on the LF average*/
+      logXC = celt_log2(QCONST32(1.001f, 20)-MULT16_16(sum, sum));
+      /* mid-side savings estimations based on min correlation */
+      logXC2 = MAX16(HALF16(logXC), celt_log2(QCONST32(1.001f, 20)-MULT16_16(minXC, minXC)));
+#ifdef FIXED_POINT
+      /* Compensate for Q20 vs Q14 input and convert output to Q8 */
+      logXC = PSHR32(logXC-QCONST16(6.f, 10),10-8);
+      logXC2 = PSHR32(logXC2-QCONST16(6.f, 10),10-8);
+#endif
+
+      trim += MAX16(-QCONST16(4.f, 8), MULT16_16_Q15(QCONST16(.75f,15),logXC));
+      *stereo_saving = MIN16(*stereo_saving + QCONST16(0.25f, 8), -HALF16(logXC2));
+   }
+
+   /* Estimate spectral tilt */
+   c=0; do {
+      for (i=0;i<end-1;i++)
+      {
+         diff += SHR32(bandLogE[i+c*m->nbEBands], 5)*(opus_int32)(2+2*i-end);
+      }
+   } while (++c<C);
+   diff /= C*(end-1);
+   /*printf("%f\n", diff);*/
+   trim -= MAX32(-QCONST16(2.f, 8), MIN32(QCONST16(2.f, 8), SHR32(diff+QCONST32(1.f, DB_SHIFT-5),DB_SHIFT-13)/6 ));
+   trim -= SHR16(surround_trim, DB_SHIFT-8);
+   trim -= 2*SHR16(tf_estimate, 14-8);
+#ifndef DISABLE_FLOAT_API
+   if (analysis->valid)
+   {
+      trim -= MAX16(-QCONST16(2.f, 8), MIN16(QCONST16(2.f, 8),
+            (opus_val16)(QCONST16(2.f, 8)*(analysis->tonality_slope+.05f))));
+   }
+#else
+   (void)analysis;
+#endif
+
+#ifdef FIXED_POINT
+   trim_index = PSHR32(trim, 8);
+#else
+   trim_index = (int)floor(.5f+trim);
+#endif
+   trim_index = IMAX(0, IMIN(10, trim_index));
+   /*printf("%d\n", trim_index);*/
+#ifdef FUZZING
+   trim_index = rand()%11;
+#endif
+   return trim_index;
+}
+
+static int stereo_analysis(const CELTMode *m, const celt_norm *X,
+      int LM, int N0)
+{
+   int i;
+   int thetas;
+   opus_val32 sumLR = EPSILON, sumMS = EPSILON;
+
+   /* Use the L1 norm to model the entropy of the L/R signal vs the M/S signal */
+   for (i=0;i<13;i++)
+   {
+      int j;
+      for (j=m->eBands[i]<<LM;j<m->eBands[i+1]<<LM;j++)
+      {
+         opus_val32 L, R, M, S;
+         /* We cast to 32-bit first because of the -32768 case */
+         L = SHR32(X[j], NORM_SHIFT-14);
+         R = SHR32(X[N0+j], NORM_SHIFT-14);
+         M = ADD32(L, R);
+         S = SUB32(L, R);
+         sumLR = ADD32(sumLR, ADD32(ABS32(L), ABS32(R)));
+         sumMS = ADD32(sumMS, ADD32(ABS32(M), ABS32(S)));
+      }
+   }
+   sumMS = MULT16_32_Q15(QCONST16(0.707107f, 15), sumMS);
+   thetas = 13;
+   /* We don't need thetas for lower bands with LM<=1 */
+   if (LM<=1)
+      thetas -= 8;
+   return MULT16_32_Q15((m->eBands[13]<<(LM+1))+thetas, sumMS)
+         > MULT16_32_Q15(m->eBands[13]<<(LM+1), sumLR);
+}
+
+#define MSWAP(a,b) do {celt_glog tmp = a;a=b;b=tmp;} while(0)
+static celt_glog median_of_5(const celt_glog *x)
+{
+   celt_glog t0, t1, t2, t3, t4;
+   t2 = x[2];
+   if (x[0] > x[1])
+   {
+      t0 = x[1];
+      t1 = x[0];
+   } else {
+      t0 = x[0];
+      t1 = x[1];
+   }
+   if (x[3] > x[4])
+   {
+      t3 = x[4];
+      t4 = x[3];
+   } else {
+      t3 = x[3];
+      t4 = x[4];
+   }
+   if (t0 > t3)
+   {
+      MSWAP(t0, t3);
+      MSWAP(t1, t4);
+   }
+   if (t2 > t1)
+   {
+      if (t1 < t3)
+         return MING(t2, t3);
+      else
+         return MING(t4, t1);
+   } else {
+      if (t2 < t3)
+         return MING(t1, t3);
+      else
+         return MING(t2, t4);
+   }
+}
+
+static celt_glog median_of_3(const celt_glog *x)
+{
+   celt_glog t0, t1, t2;
+   if (x[0] > x[1])
+   {
+      t0 = x[1];
+      t1 = x[0];
+   } else {
+      t0 = x[0];
+      t1 = x[1];
+   }
+   t2 = x[2];
+   if (t1 < t2)
+      return t1;
+   else if (t0 < t2)
+      return t2;
+   else
+      return t0;
+}
+
+static celt_glog dynalloc_analysis(const celt_glog *bandLogE, const celt_glog *bandLogE2, const celt_glog *oldBandE,
+      int nbEBands, int start, int end, int C, int *offsets, int lsb_depth, const opus_int16 *logN,
+      int isTransient, int vbr, int constrained_vbr, const opus_int16 *eBands, int LM,
+      int effectiveBytes, opus_int32 *tot_boost_, int lfe, celt_glog *surround_dynalloc,
+      AnalysisInfo *analysis, int *importance, int *spread_weight, opus_val16 tone_freq, opus_val32 toneishness
+      ARG_QEXT(int qext_scale))
+{
+   int i, c;
+   opus_int32 tot_boost=0;
+   celt_glog maxDepth;
+   VARDECL(celt_glog, follower);
+   VARDECL(celt_glog, noise_floor);
+   VARDECL(celt_glog, bandLogE3);
+   SAVE_STACK;
+   ALLOC(follower, C*nbEBands, celt_glog);
+   ALLOC(noise_floor, C*nbEBands, celt_glog);
+   ALLOC(bandLogE3, nbEBands, celt_glog);
+   OPUS_CLEAR(offsets, nbEBands);
+   /* Dynamic allocation code */
+   maxDepth=-GCONST(31.9f);
+   for (i=0;i<end;i++)
+   {
+      /* Noise floor must take into account eMeans, the depth, the width of the bands
+         and the preemphasis filter (approx. square of bark band ID) */
+      noise_floor[i] = GCONST(0.0625f)*logN[i]
+            +GCONST(.5f)+SHL32(9-lsb_depth,DB_SHIFT)-SHL32(eMeans[i],DB_SHIFT-4)
+            +GCONST(.0062f)*(i+5)*(i+5);
+   }
+   c=0;do
+   {
+      for (i=0;i<end;i++)
+         maxDepth = MAXG(maxDepth, bandLogE[c*nbEBands+i]-noise_floor[i]);
+   } while (++c<C);
+   {
+      /* Compute a really simple masking model to avoid taking into account completely masked
+         bands when computing the spreading decision. */
+      VARDECL(celt_glog, mask);
+      VARDECL(celt_glog, sig);
+      ALLOC(mask, nbEBands, celt_glog);
+      ALLOC(sig, nbEBands, celt_glog);
+      for (i=0;i<end;i++)
+         mask[i] = bandLogE[i]-noise_floor[i];
+      if (C==2)
+      {
+         for (i=0;i<end;i++)
+            mask[i] = MAXG(mask[i], bandLogE[nbEBands+i]-noise_floor[i]);
+      }
+      OPUS_COPY(sig, mask, end);
+      for (i=1;i<end;i++)
+         mask[i] = MAXG(mask[i], mask[i-1] - GCONST(2.f));
+      for (i=end-2;i>=0;i--)
+         mask[i] = MAXG(mask[i], mask[i+1] - GCONST(3.f));
+      for (i=0;i<end;i++)
+      {
+         /* Compute SMR: Mask is never more than 72 dB below the peak and never below the noise floor.*/
+         celt_glog smr = sig[i]-MAXG(MAXG(0, maxDepth-GCONST(12.f)), mask[i]);
+         /* Clamp SMR to make sure we're not shifting by something negative or too large. */
+#ifdef FIXED_POINT
+         /* FIXME: Use PSHR16() instead */
+         int shift = -PSHR32(MAXG(-GCONST(5.f), MING(0, smr)), DB_SHIFT);
+#else
+         int shift = IMIN(5, IMAX(0, -(int)floor(.5f + smr)));
+#endif
+         spread_weight[i] = 32 >> shift;
+      }
+      /*for (i=0;i<end;i++)
+         printf("%d ", spread_weight[i]);
+      printf("\n");*/
+   }
+   /* Make sure that dynamic allocation can't make us bust the budget.
+      We enable the feature starting at 24 kb/s for 20-ms frames
+      and 96 kb/s for 2.5 ms frames.  */
+   if (effectiveBytes >= (30 + 5*LM) && !lfe)
+   {
+      int last=0;
+      c=0;do
+      {
+         celt_glog offset;
+         celt_glog tmp;
+         celt_glog *f;
+         OPUS_COPY(bandLogE3, &bandLogE2[c*nbEBands], end);
+         if (LM==0) {
+            /* For 2.5 ms frames, the first 8 bands have just one bin, so the
+               energy is highly unreliable (high variance). For that reason,
+               we take the max with the previous energy so that at least 2 bins
+               are getting used. */
+            for (i=0;i<IMIN(8,end);i++) bandLogE3[i] = MAXG(bandLogE2[c*nbEBands+i], oldBandE[c*nbEBands+i]);
+         }
+         f = &follower[c*nbEBands];
+         f[0] = bandLogE3[0];
+         for (i=1;i<end;i++)
+         {
+            /* The last band to be at least 3 dB higher than the previous one
+               is the last we'll consider. Otherwise, we run into problems on
+               bandlimited signals. */
+            if (bandLogE3[i] > bandLogE3[i-1]+GCONST(.5f))
+               last=i;
+            f[i] = MING(f[i-1]+GCONST(1.5f), bandLogE3[i]);
+         }
+         for (i=last-1;i>=0;i--)
+            f[i] = MING(f[i], MING(f[i+1]+GCONST(2.f), bandLogE3[i]));
+
+         /* Combine with a median filter to avoid dynalloc triggering unnecessarily.
+            The "offset" value controls how conservative we are -- a higher offset
+            reduces the impact of the median filter and makes dynalloc use more bits. */
+         offset = GCONST(1.f);
+         for (i=2;i<end-2;i++)
+            f[i] = MAXG(f[i], median_of_5(&bandLogE3[i-2])-offset);
+         tmp = median_of_3(&bandLogE3[0])-offset;
+         f[0] = MAXG(f[0], tmp);
+         f[1] = MAXG(f[1], tmp);
+         tmp = median_of_3(&bandLogE3[end-3])-offset;
+         f[end-2] = MAXG(f[end-2], tmp);
+         f[end-1] = MAXG(f[end-1], tmp);
+
+         for (i=0;i<end;i++)
+            f[i] = MAXG(f[i], noise_floor[i]);
+      } while (++c<C);
+      if (C==2)
+      {
+         for (i=start;i<end;i++)
+         {
+            /* Consider 24 dB "cross-talk" */
+            follower[nbEBands+i] = MAXG(follower[nbEBands+i], follower[         i]-GCONST(4.f));
+            follower[         i] = MAXG(follower[         i], follower[nbEBands+i]-GCONST(4.f));
+            follower[i] = HALF32(MAXG(0, bandLogE[i]-follower[i]) + MAXG(0, bandLogE[nbEBands+i]-follower[nbEBands+i]));
+         }
+      } else {
+         for (i=start;i<end;i++)
+         {
+            follower[i] = MAXG(0, bandLogE[i]-follower[i]);
+         }
+      }
+      for (i=start;i<end;i++)
+         follower[i] = MAXG(follower[i], surround_dynalloc[i]);
+      for (i=start;i<end;i++)
+      {
+#ifdef FIXED_POINT
+         importance[i] = PSHR32(13*celt_exp2_db(MING(follower[i], GCONST(4.f))), 16);
+#else
+         importance[i] = (int)floor(.5f+13*celt_exp2_db(MING(follower[i], GCONST(4.f))));
+#endif
+      }
+      /* For non-transient CBR/CVBR frames, halve the dynalloc contribution */
+      if ((!vbr || constrained_vbr)&&!isTransient)
+      {
+         for (i=start;i<end;i++)
+            follower[i] = HALF32(follower[i]);
+      }
+      for (i=start;i<end;i++)
+      {
+         if (i<8)
+            follower[i] *= 2;
+         if (i>=12)
+            follower[i] = HALF32(follower[i]);
+      }
+      /* Compensate for Opus' under-allocation on tones. */
+      if (toneishness > QCONST32(.98f, 29)) {
+#ifdef FIXED_POINT
+         int freq_bin = PSHR32(QEXT_SCALE((opus_val32)tone_freq)*QCONST16(120/M_PI, 9), 13+9);
+#else
+         int freq_bin = (int)floor(.5 + QEXT_SCALE(tone_freq)*120/M_PI);
+#endif
+         for (i=start;i<end;i++) {
+            if (freq_bin >= eBands[i] && freq_bin <= eBands[i+1]) follower[i] += GCONST(2.f);
+            if (freq_bin >= eBands[i]-1 && freq_bin <= eBands[i+1]+1) follower[i] += GCONST(1.f);
+            if (freq_bin >= eBands[i]-2 && freq_bin <= eBands[i+1]+2) follower[i] += GCONST(1.f);
+            if (freq_bin >= eBands[i]-3 && freq_bin <= eBands[i+1]+3) follower[i] += GCONST(.5f);
+         }
+         if (freq_bin >= eBands[end]) {
+            follower[end-1] += GCONST(2.f);
+            follower[end-2] += GCONST(1.f);
+         }
+      }
+#ifdef DISABLE_FLOAT_API
+      (void)analysis;
+#else
+      if (analysis->valid)
+      {
+         for (i=start;i<IMIN(LEAK_BANDS, end);i++)
+            follower[i] = follower[i] +  GCONST(1.f/64.f)*analysis->leak_boost[i];
+      }
+#endif
+      for (i=start;i<end;i++)
+      {
+         int width;
+         int boost;
+         int boost_bits;
+
+         follower[i] = MING(follower[i], GCONST(4));
+
+         follower[i] = SHR32(follower[i], 8);
+         width = C*(eBands[i+1]-eBands[i])<<LM;
+         if (width<6)
+         {
+            boost = (int)SHR32(follower[i],DB_SHIFT-8);
+            boost_bits = boost*width<<BITRES;
+         } else if (width > 48) {
+            boost = (int)SHR32(follower[i]*8,DB_SHIFT-8);
+            boost_bits = (boost*width<<BITRES)/8;
+         } else {
+            boost = (int)SHR32(follower[i]*width/6,DB_SHIFT-8);
+            boost_bits = boost*6<<BITRES;
+         }
+         /* For CBR and non-transient CVBR frames, limit dynalloc to 2/3 of the bits */
+         if ((!vbr || (constrained_vbr&&!isTransient))
+               && (tot_boost+boost_bits)>>BITRES>>3 > 2*effectiveBytes/3)
+         {
+            opus_int32 cap = ((2*effectiveBytes/3)<<BITRES<<3);
+            offsets[i] = cap-tot_boost;
+            tot_boost = cap;
+            break;
+         } else {
+            offsets[i] = boost;
+            tot_boost += boost_bits;
+         }
+      }
+   } else {
+      for (i=start;i<end;i++)
+         importance[i] = 13;
+   }
+   *tot_boost_ = tot_boost;
+   RESTORE_STACK;
+   return maxDepth;
+}
+
+#ifdef FIXED_POINT
+void normalize_tone_input(opus_val16 *x, int len) {
+   opus_val32 ac0=len;
+   int i;
+   int shift;
+   for (i=0;i<len;i++) {
+      ac0 = ADD32(ac0, SHR32(MULT16_16(x[i], x[i]), 10));
+   }
+   shift = 5 - (28-celt_ilog2(ac0))/2;
+   if (shift > 0) {
+      for (i=0;i<len;i++) {
+         x[i] = PSHR32(x[i], shift);
+      }
+   }
+}
+int acos_approx(opus_val32 x) {
+   opus_val16 x14;
+   opus_val32 tmp;
+   int flip = x<0;
+   x = abs(x);
+   x14 = x>>15;
+   tmp = (762*x14>>14)-3308;
+   tmp = (tmp*x14>>14)+25726;
+   tmp = tmp*celt_sqrt(IMAX(0, (1<<30) - (x<<1)))>>16;
+   if (flip) tmp = 25736 - tmp;
+   return tmp;
+}
+#endif
+
+/* Compute the LPC coefficients using a least-squares fit for both forward and backward prediction. */
+static int tone_lpc(const opus_val16 *x, int len, int delay, opus_val32 *lpc) {
+   int i;
+   opus_val32 r00=0, r01=0, r11=0, r02=0, r12=0, r22=0;
+   opus_val32 edges;
+   opus_val32 num0, num1, den;
+   celt_assert(len > 2*delay);
+   /* Compute correlations as if using the forward prediction covariance method. */
+   for (i=0;i<len-2*delay;i++) {
+      r00 += MULT16_16(x[i],x[i]);
+      r01 += MULT16_16(x[i],x[i+delay]);
+      r02 += MULT16_16(x[i],x[i+2*delay]);
+   }
+   edges = 0;
+   for (i=0;i<delay;i++) edges += MULT16_16(x[len+i-2*delay],x[len+i-2*delay]) - MULT16_16(x[i],x[i]);
+   r11 = r00+edges;
+   edges = 0;
+   for (i=0;i<delay;i++) edges += MULT16_16(x[len+i-delay],x[len+i-delay]) - MULT16_16(x[i+delay],x[i+delay]);
+   r22 = r11+edges;
+   edges = 0;
+   for (i=0;i<delay;i++) edges += MULT16_16(x[len+i-2*delay],x[len+i-delay]) - MULT16_16(x[i],x[i+delay]);
+   r12 = r01+edges;
+   /* Reverse and sum to get the backward contribution. */
+   {
+      opus_val32 R00, R01, R11, R02, R12, R22;
+      R00 = r00 + r22;
+      R01 = r01 + r12;
+      R11 = 2*r11;
+      R02 = 2*r02;
+      R12 = r12 + r01;
+      R22 = r00 + r22;
+      r00 = R00;
+      r01 = R01;
+      r11 = R11;
+      r02 = R02;
+      r12 = R12;
+      r22 = R22;
+   }
+   /* Solve A*x=b, where A=[r00, r01; r01, r11] and b=[r02; r12]. */
+   den = MULT32_32_Q31(r00,r11) - MULT32_32_Q31(r01,r01);
+#ifdef FIXED_POINT
+   if (den <= SHR32(MULT32_32_Q31(r00,r11), 10)) return 1;
+#else
+   if (den < .001f*MULT32_32_Q31(r00,r11)) return 1;
+#endif
+   num1 = MULT32_32_Q31(r02,r11) - MULT32_32_Q31(r01,r12);
+   if (num1 >= den) lpc[1] = QCONST32(1.f, 29);
+   else if (num1 <= -den) lpc[1] = -QCONST32(1.f, 29);
+   else lpc[1] = frac_div32_q29(num1, den);
+   num0 = MULT32_32_Q31(r00,r12) - MULT32_32_Q31(r02,r01);
+   if (HALF32(num0) >= den) lpc[0] = QCONST32(1.999999f, 29);
+   else if (HALF32(num0) <= -den) lpc[0] = -QCONST32(1.999999f, 29);
+   else lpc[0] = frac_div32_q29(num0, den);
+   /*printf("%f %f\n", lpc[0], lpc[1]);*/
+   return 0;
+}
+
+/* Detects pure of nearly pure tones so we can prevent them from causing problems with the encoder. */
+static opus_val16 tone_detect(const celt_sig *in, int CC, int N, opus_val32 *toneishness, opus_int32 Fs) {
+   int i;
+   int delay = 1;
+   int fail;
+   opus_val32 lpc[2];
+   opus_val16 freq;
+   VARDECL(opus_val16, x);
+   SAVE_STACK;
+   ALLOC(x, N, opus_val16);
+   /* Shift by SIG_SHIFT+2 (+3 for stereo) to account for HF gain of the preemphasis filter. */
+   if (CC==2) {
+      for (i=0;i<N;i++) x[i] = PSHR32(ADD32(SHR32(in[i], 1), SHR32(in[i+N], 1)), SIG_SHIFT+2);
+   } else {
+      for (i=0;i<N;i++) x[i] = PSHR32(in[i], SIG_SHIFT+2);
+   }
+#ifdef FIXED_POINT
+   normalize_tone_input(x, N);
+#endif
+   fail = tone_lpc(x, N, delay, lpc);
+   /* If our LPC filter resonates too close to DC, retry the analysis with down-sampling. */
+   while (delay <= Fs/3000 && (fail || (lpc[0] > QCONST32(1.f, 29) && lpc[1] < 0))) {
+      delay *= 2;
+      fail = tone_lpc(x, N, delay, lpc);
+   }
+   /* Check that our filter has complex roots. */
+   if (!fail && MULT32_32_Q31(lpc[0],lpc[0]) + MULT32_32_Q31(QCONST32(3.999999, 29), lpc[1]) < 0) {
+      /* Squared radius of the poles. */
+      *toneishness = -lpc[1];
+#ifdef FIXED_POINT
+      freq = (acos_approx(lpc[0]>>1)+delay/2)/delay;
+#else
+      freq = acos(.5f*lpc[0])/delay;
+#endif
+   } else {
+      freq = -1;
+      *toneishness=0;
+   }
+   /*printf("%f %f %f %f\n", freq, lpc[0], lpc[1], *toneishness);*/
+   RESTORE_STACK;
+   return freq;
+}
+
+static int run_prefilter(CELTEncoder *st, celt_sig *in, celt_sig *prefilter_mem, int CC, int N,
+      int prefilter_tapset, int *pitch, opus_val16 *gain, int *qgain, int enabled, int complexity, opus_val16 tf_estimate,
+      int nbAvailableBytes, AnalysisInfo *analysis, opus_val16 tone_freq, opus_val32 toneishness ARG_QEXT(int qext_scale))
+{
+   int c;
+   VARDECL(celt_sig, _pre);
+   celt_sig *pre[2];
+   const CELTMode *mode;
+   int pitch_index;
+   opus_val16 gain1;
+   opus_val16 pf_threshold;
+   int pf_on;
+   int qg;
+   int overlap;
+   int min_period, max_period;
+   opus_val32 before[2]={0}, after[2]={0};
+   int cancel_pitch=0;
+   SAVE_STACK;
+
+   max_period = QEXT_SCALE(COMBFILTER_MAXPERIOD);
+   min_period = QEXT_SCALE(COMBFILTER_MINPERIOD);
+   mode = st->mode;
+   overlap = mode->overlap;
+   ALLOC(_pre, CC*(N+max_period), celt_sig);
+
+   pre[0] = _pre;
+   pre[1] = _pre + (N+max_period);
+
+
+   c=0; do {
+      OPUS_COPY(pre[c], prefilter_mem+c*max_period, max_period);
+      OPUS_COPY(pre[c]+max_period, in+c*(N+overlap)+overlap, N);
+   } while (++c<CC);
+
+   /* If we detect that the signal is dominated by a single tone, don't rely on the standard pitch
+      estimator, as it can become unreliable. */
+   if (enabled && toneishness > QCONST32(.99f, 29)) {
+      int multiple=1;
+      /* Using aliased version of the postfilter above 24 kHz.
+         First value is purposely slightly above pi to avoid triggering for Fs=48kHz. */
+      if (QEXT_SCALE(tone_freq) >= QCONST16(3.1416f, 13)) tone_freq = QCONST16(3.141593f, 13) - tone_freq;
+      /* If the pitch is too high for our post-filter, apply pitch doubling until
+         we can get something that fits (not ideal, but better than nothing). */
+      while (QEXT_SCALE(tone_freq) >= multiple*QCONST16(0.39f, 13)) multiple++;
+      if (QEXT_SCALE(tone_freq) > QCONST16(0.006148f, 13)) {
+#ifdef FIXED_POINT
+         pitch_index = IMIN((51472*multiple+QEXT_SCALE(tone_freq)/2)/QEXT_SCALE(tone_freq), COMBFILTER_MAXPERIOD-2);
+#else
+         pitch_index = IMIN((int)floor(.5+2.f*M_PI*multiple/QEXT_SCALE(tone_freq)), COMBFILTER_MAXPERIOD-2);
+#endif
+      } else {
+         /* If the pitch is too low, using a very high pitch will actually give us an improvement
+            due to the DC component of the filter that will be close to our tone. Again, not ideal,
+            but if we only have a single tone, it's better than nothing. */
+         pitch_index = COMBFILTER_MINPERIOD;
+      }
+      gain1 = QCONST16(.75f, 15);
+   } else if (enabled && complexity >= 5) {
+      VARDECL(opus_val16, pitch_buf);
+      ALLOC(pitch_buf, (max_period+N)>>1, opus_val16);
+
+      pitch_downsample(pre, pitch_buf, (max_period+N)>>1, CC, 2, st->arch);
+      /* Don't search for the fir last 1.5 octave of the range because
+         there's too many false-positives due to short-term correlation */
+      pitch_search(pitch_buf+(max_period>>1), pitch_buf, N,
+            max_period-3*min_period, &pitch_index,
+            st->arch);
+      pitch_index = max_period-pitch_index;
+
+      gain1 = remove_doubling(pitch_buf, max_period, min_period,
+            N, &pitch_index, st->prefilter_period, st->prefilter_gain, st->arch);
+      if (pitch_index > max_period-QEXT_SCALE(2))
+         pitch_index = max_period-QEXT_SCALE(2);
+#ifdef ENABLE_QEXT
+      pitch_index /= qext_scale;
+#endif
+      gain1 = MULT16_16_Q15(QCONST16(.7f,15),gain1);
+      /*printf("%d %d %f %f\n", pitch_change, pitch_index, gain1, st->analysis.tonality);*/
+      if (st->loss_rate>2)
+         gain1 = HALF32(gain1);
+      if (st->loss_rate>4)
+         gain1 = HALF32(gain1);
+      if (st->loss_rate>8)
+         gain1 = 0;
+   } else {
+      gain1 = 0;
+      pitch_index = COMBFILTER_MINPERIOD;
+   }
+#ifndef DISABLE_FLOAT_API
+   if (analysis->valid)
+      gain1 = (opus_val16)(gain1 * analysis->max_pitch_ratio);
+#else
+   (void)analysis;
+#endif
+   /* Gain threshold for enabling the prefilter/postfilter */
+   pf_threshold = QCONST16(.2f,15);
+
+   /* Adjusting the threshold based on rate and continuity */
+   if (abs(pitch_index-st->prefilter_period)*10>pitch_index)
+   {
+      pf_threshold += QCONST16(.2f,15);
+      /* Completely disable the prefilter on strong transients without continuity. */
+      if (tf_estimate > QCONST16(.98f, 14))
+         gain1 = 0;
+   }
+   if (nbAvailableBytes<25)
+      pf_threshold += QCONST16(.1f,15);
+   if (nbAvailableBytes<35)
+      pf_threshold += QCONST16(.1f,15);
+   if (st->prefilter_gain > QCONST16(.4f,15))
+      pf_threshold -= QCONST16(.1f,15);
+   if (st->prefilter_gain > QCONST16(.55f,15))
+      pf_threshold -= QCONST16(.1f,15);
+
+   /* Hard threshold at 0.2 */
+   pf_threshold = MAX16(pf_threshold, QCONST16(.2f,15));
+   if (gain1<pf_threshold)
+   {
+      gain1 = 0;
+      pf_on = 0;
+      qg = 0;
+   } else {
+      /*This block is not gated by a total bits check only because
+        of the nbAvailableBytes check above.*/
+      if (ABS16(gain1-st->prefilter_gain)<QCONST16(.1f,15))
+         gain1=st->prefilter_gain;
+
+#ifdef FIXED_POINT
+      qg = ((gain1+1536)>>10)/3-1;
+#else
+      qg = (int)floor(.5f+gain1*32/3)-1;
+#endif
+      qg = IMAX(0, IMIN(7, qg));
+      gain1 = QCONST16(0.09375f,15)*(qg+1);
+      pf_on = 1;
+   }
+   /*printf("%d %f\n", pitch_index, gain1);*/
+
+   c=0; do {
+      int i;
+      int offset = mode->shortMdctSize-overlap;
+      st->prefilter_period=IMAX(st->prefilter_period, COMBFILTER_MINPERIOD);
+      OPUS_COPY(in+c*(N+overlap), st->in_mem+c*(overlap), overlap);
+      for (i=0;i<N;i++) before[c] += ABS32(SHR32(in[c*(N+overlap)+overlap+i], 12));
+      if (offset)
+         comb_filter(in+c*(N+overlap)+overlap, pre[c]+max_period,
+               st->prefilter_period, st->prefilter_period, offset, -st->prefilter_gain, -st->prefilter_gain,
+               st->prefilter_tapset, st->prefilter_tapset, NULL, 0, st->arch);
+
+      comb_filter(in+c*(N+overlap)+overlap+offset, pre[c]+max_period+offset,
+            st->prefilter_period, pitch_index, N-offset, -st->prefilter_gain, -gain1,
+            st->prefilter_tapset, prefilter_tapset, mode->window, overlap, st->arch);
+      for (i=0;i<N;i++) after[c] += ABS32(SHR32(in[c*(N+overlap)+overlap+i], 12));
+   } while (++c<CC);
+
+   if (CC==2) {
+      opus_val16 thresh[2];
+      thresh[0] = MULT16_32_Q15(MULT16_16_Q15(QCONST16(.25f, 15), gain1), before[0]) + MULT16_32_Q15(QCONST16(.01f,15), before[1]);
+      thresh[1] = MULT16_32_Q15(MULT16_16_Q15(QCONST16(.25f, 15), gain1), before[1]) + MULT16_32_Q15(QCONST16(.01f,15), before[0]);
+      /* Don't use the filter if one channel gets significantly worse. */
+      if (after[0]-before[0] > thresh[0] || after[1]-before[1] > thresh[1]) cancel_pitch = 1;
+      /* Use the filter only if at least one channel gets significantly better. */
+      if (before[0]-after[0] <  thresh[0] && before[1]-after[1] < thresh[1]) cancel_pitch = 1;
+   } else {
+      /* Check that the mono channel actually got better. */
+      if (after[0] > before[0]) cancel_pitch = 1;
+   }
+   /* If needed, revert to a gain of zero. */
+   if (cancel_pitch) {
+      c=0; do {
+         int offset = mode->shortMdctSize-overlap;
+         OPUS_COPY(in+c*(N+overlap)+overlap, pre[c]+max_period, N);
+         comb_filter(in+c*(N+overlap)+overlap+offset, pre[c]+max_period+offset,
+                     st->prefilter_period, pitch_index, overlap, -st->prefilter_gain, -0,
+                     st->prefilter_tapset, prefilter_tapset, mode->window, overlap, st->arch);
+      } while (++c<CC);
+      gain1 = 0;
+      pf_on = 0;
+      qg = 0;
+   }
+
+   c=0; do {
+      OPUS_COPY(st->in_mem+c*(overlap), in+c*(N+overlap)+N, overlap);
+
+      if (N>max_period)
+      {
+         OPUS_COPY(prefilter_mem+c*max_period, pre[c]+N, max_period);
+      } else {
+         OPUS_MOVE(prefilter_mem+c*max_period, prefilter_mem+c*max_period+N, max_period-N);
+         OPUS_COPY(prefilter_mem+c*max_period+max_period-N, pre[c]+max_period, N);
+      }
+   } while (++c<CC);
+
+   RESTORE_STACK;
+   *gain = gain1;
+   *pitch = pitch_index;
+   *qgain = qg;
+   return pf_on;
+}
+
+static int compute_vbr(const CELTMode *mode, AnalysisInfo *analysis, opus_int32 base_target,
+      int LM, opus_int32 bitrate, int lastCodedBands, int C, int intensity,
+      int constrained_vbr, opus_val16 stereo_saving, int tot_boost,
+      opus_val16 tf_estimate, int pitch_change, celt_glog maxDepth,
+      int lfe, int has_surround_mask, celt_glog surround_masking,
+      celt_glog temporal_vbr ARG_QEXT(int enable_qext))
+{
+   /* The target rate in 8th bits per frame */
+   opus_int32 target;
+   int coded_bins;
+   int coded_bands;
+   opus_val16 tf_calibration;
+   int nbEBands;
+   const opus_int16 *eBands;
+
+   nbEBands = mode->nbEBands;
+   eBands = mode->eBands;
+
+   coded_bands = lastCodedBands ? lastCodedBands : nbEBands;
+   coded_bins = eBands[coded_bands]<<LM;
+   if (C==2)
+      coded_bins += eBands[IMIN(intensity, coded_bands)]<<LM;
+
+   target = base_target;
+
+   /*printf("%f %f %f %f %d %d ", st->analysis.activity, st->analysis.tonality, tf_estimate, st->stereo_saving, tot_boost, coded_bands);*/
+#ifndef DISABLE_FLOAT_API
+   if (analysis->valid && analysis->activity<.4)
+      target -= (opus_int32)((coded_bins<<BITRES)*(.4f-analysis->activity));
+#endif
+   /* Stereo savings */
+   if (C==2)
+   {
+      int coded_stereo_bands;
+      int coded_stereo_dof;
+      opus_val16 max_frac;
+      coded_stereo_bands = IMIN(intensity, coded_bands);
+      coded_stereo_dof = (eBands[coded_stereo_bands]<<LM)-coded_stereo_bands;
+      /* Maximum fraction of the bits we can save if the signal is mono. */
+      max_frac = DIV32_16(MULT16_16(QCONST16(0.8f, 15), coded_stereo_dof), coded_bins);
+      stereo_saving = MIN16(stereo_saving, QCONST16(1.f, 8));
+      /*printf("%d %d %d ", coded_stereo_dof, coded_bins, tot_boost);*/
+      target -= (opus_int32)MIN32(MULT16_32_Q15(max_frac,target),
+                      SHR32(MULT16_16(stereo_saving-QCONST16(0.1f,8),(coded_stereo_dof<<BITRES)),8));
+   }
+   /* Boost the rate according to dynalloc (minus the dynalloc average for calibration). */
+   target += tot_boost-(19<<LM);
+   /* Apply transient boost, compensating for average boost. */
+   tf_calibration = QCONST16(0.044f,14);
+   target += (opus_int32)SHL32(MULT16_32_Q15(tf_estimate-tf_calibration, target),1);
+
+#ifndef DISABLE_FLOAT_API
+   /* Apply tonality boost */
+   if (analysis->valid && !lfe)
+   {
+      opus_int32 tonal_target;
+      float tonal;
+
+      /* Tonality boost (compensating for the average). */
+      tonal = MAX16(0.f,analysis->tonality-.15f)-0.12f;
+      tonal_target = target + (opus_int32)((coded_bins<<BITRES)*1.2f*tonal);
+      if (pitch_change)
+         tonal_target +=  (opus_int32)((coded_bins<<BITRES)*.8f);
+      /*printf("%f %f ", analysis->tonality, tonal);*/
+      target = tonal_target;
+   }
+#else
+   (void)analysis;
+   (void)pitch_change;
+#endif
+
+   if (has_surround_mask&&!lfe)
+   {
+      opus_int32 surround_target = target + (opus_int32)SHR32(MULT16_16(SHR32(surround_masking,DB_SHIFT-10),coded_bins<<BITRES), 10);
+      /*printf("%f %d %d %d %d %d %d ", surround_masking, coded_bins, st->end, st->intensity, surround_target, target, st->bitrate);*/
+      target = IMAX(target/4, surround_target);
+   }
+
+   {
+      opus_int32 floor_depth;
+      int bins;
+      bins = eBands[nbEBands-2]<<LM;
+#ifdef ENABLE_QEXT
+      if (enable_qext) bins  = mode->shortMdctSize<<LM;
+#endif
+      /*floor_depth = SHR32(MULT16_16((C*bins<<BITRES),celt_log2(SHL32(MAX16(1,sample_max),13))), DB_SHIFT);*/
+      floor_depth = (opus_int32)SHR32(MULT16_32_Q15((C*bins<<BITRES),maxDepth), DB_SHIFT-15);
+      floor_depth = IMAX(floor_depth, target>>2);
+      target = IMIN(target, floor_depth);
+      /*printf("%f %d\n", maxDepth, floor_depth);*/
+   }
+
+   /* Make VBR less aggressive for constrained VBR because we can't keep a higher bitrate
+      for long. Needs tuning. */
+   if ((!has_surround_mask||lfe) && constrained_vbr)
+   {
+      target = base_target + (opus_int32)MULT16_32_Q15(QCONST16(0.67f, 15), target-base_target);
+   }
+
+   if (!has_surround_mask && tf_estimate < QCONST16(.2f, 14))
+   {
+      opus_val16 amount;
+      opus_val16 tvbr_factor;
+      amount = MULT16_16_Q15(QCONST16(.0000031f, 30), IMAX(0, IMIN(32000, 96000-bitrate)));
+      tvbr_factor = SHR32(MULT16_16(SHR32(temporal_vbr, DB_SHIFT-10), amount), 10);
+      target += (opus_int32)MULT16_32_Q15(tvbr_factor, target);
+   }
+
+   /* Don't allow more than doubling the rate */
+   target = IMIN(2*base_target, target);
+
+   return target;
+}
+
+#ifdef ENABLE_QEXT
+static void encode_qext_stereo_params(ec_enc *ec, int qext_end, int qext_intensity, int qext_dual_stereo) {
+   ec_enc_uint(ec, qext_intensity, qext_end+1);
+   if (qext_intensity != 0) ec_enc_bit_logp(ec, qext_dual_stereo, 1);
+}
+#endif
+
+int celt_encode_with_ec(CELTEncoder * OPUS_RESTRICT st, const opus_res * pcm, int frame_size, unsigned char *compressed, int nbCompressedBytes, ec_enc *enc)
+{
+   int i, c, N;
+   opus_int32 bits;
+   ec_enc _enc;
+   VARDECL(celt_sig, in);
+   VARDECL(celt_sig, freq);
+   VARDECL(celt_norm, X);
+   VARDECL(celt_ener, bandE);
+   VARDECL(celt_glog, bandLogE);
+   VARDECL(celt_glog, bandLogE2);
+   VARDECL(int, fine_quant);
+   VARDECL(celt_glog, error);
+   VARDECL(int, pulses);
+   VARDECL(int, cap);
+   VARDECL(int, offsets);
+   VARDECL(int, importance);
+   VARDECL(int, spread_weight);
+   VARDECL(int, fine_priority);
+   VARDECL(int, tf_res);
+   VARDECL(unsigned char, collapse_masks);
+   celt_sig *prefilter_mem;
+   celt_glog *oldBandE, *oldLogE, *oldLogE2, *energyError;
+   int shortBlocks=0;
+   int isTransient=0;
+   const int CC = st->channels;
+   const int C = st->stream_channels;
+   int LM, M;
+   int tf_select;
+   int nbFilledBytes, nbAvailableBytes;
+   opus_int32 min_allowed;
+   int start;
+   int end;
+   int effEnd;
+   int codedBands;
+   int alloc_trim;
+   int pitch_index=COMBFILTER_MINPERIOD;
+   opus_val16 gain1 = 0;
+   int dual_stereo=0;
+   int effectiveBytes;
+   int dynalloc_logp;
+   opus_int32 vbr_rate;
+   opus_int32 total_bits;
+   opus_int32 total_boost;
+   opus_int32 balance;
+   opus_int32 tell;
+   opus_int32 tell0_frac;
+   int prefilter_tapset=0;
+   int pf_on;
+   int anti_collapse_rsv;
+   int anti_collapse_on=0;
+   int silence=0;
+   int tf_chan = 0;
+   opus_val16 tf_estimate;
+   int pitch_change=0;
+   opus_int32 tot_boost;
+   opus_val32 sample_max;
+   celt_glog maxDepth;
+   const OpusCustomMode *mode;
+   int nbEBands;
+   int overlap;
+   const opus_int16 *eBands;
+   int secondMdct;
+   int signalBandwidth;
+   int transient_got_disabled=0;
+   celt_glog surround_masking=0;
+   celt_glog temporal_vbr=0;
+   celt_glog surround_trim = 0;
+   opus_int32 equiv_rate;
+   int hybrid;
+   int weak_transient = 0;
+   int enable_tf_analysis;
+   opus_val16 tone_freq=-1;
+   opus_val32 toneishness=0;
+   VARDECL(celt_glog, surround_dynalloc);
+   int qext_bytes=0;
+   int packet_size_cap = 1275;
+#ifdef ENABLE_QEXT
+   int qext_scale;
+   int qext_end=0;
+   int qext_intensity=0;
+   int qext_dual_stereo=0;
+   int padding_len_bytes=0;
+   unsigned char *ext_payload;
+   opus_int32 qext_bits;
+   ec_enc ext_enc;
+   VARDECL(int, extra_quant);
+   VARDECL(int, extra_pulses);
+   VARDECL(celt_glog, error_bak);
+   const CELTMode *qext_mode = NULL;
+   CELTMode qext_mode_struct;
+   celt_ener qext_bandE[2*NB_QEXT_BANDS];
+   celt_glog qext_bandLogE[2*NB_QEXT_BANDS];
+   celt_glog *qext_oldBandE=NULL;
+   celt_glog qext_error[2*NB_QEXT_BANDS];
+#endif
+   ALLOC_STACK;
+
+   mode = st->mode;
+   nbEBands = mode->nbEBands;
+   overlap = mode->overlap;
+   eBands = mode->eBands;
+   start = st->start;
+   end = st->end;
+   hybrid = start != 0;
+   tf_estimate = 0;
+   if (nbCompressedBytes<2 || pcm==NULL)
+   {
+      RESTORE_STACK;
+      return OPUS_BAD_ARG;
+   }
+
+   frame_size *= st->upsample;
+   for (LM=0;LM<=mode->maxLM;LM++)
+      if (mode->shortMdctSize<<LM==frame_size)
+         break;
+   if (LM>mode->maxLM)
+   {
+      RESTORE_STACK;
+      return OPUS_BAD_ARG;
+   }
+   M=1<<LM;
+   N = M*mode->shortMdctSize;
+
+#ifdef ENABLE_QEXT
+   qext_scale = st->qext_scale;
+   if (st->enable_qext) packet_size_cap = QEXT_PACKET_SIZE_CAP;
+#endif
+
+   prefilter_mem = st->in_mem+CC*(overlap);
+   oldBandE = (celt_glog*)(st->in_mem+CC*(overlap+QEXT_SCALE(COMBFILTER_MAXPERIOD)));
+   oldLogE = oldBandE + CC*nbEBands;
+   oldLogE2 = oldLogE + CC*nbEBands;
+   energyError = oldLogE2 + CC*nbEBands;
+
+   if (enc==NULL)
+   {
+      tell0_frac=tell=1;
+      nbFilledBytes=0;
+   } else {
+      tell0_frac=ec_tell_frac(enc);
+      tell=ec_tell(enc);
+      nbFilledBytes=(tell+4)>>3;
+   }
+
+#if defined(CUSTOM_MODES) || defined(ENABLE_OPUS_CUSTOM_API)
+   if (st->signalling && enc==NULL)
+   {
+      int tmp = (mode->effEBands-end)>>1;
+      end = st->end = IMAX(1, mode->effEBands-tmp);
+      compressed[0] = tmp<<5;
+      compressed[0] |= LM<<3;
+      compressed[0] |= (C==2)<<2;
+      /* Convert "standard mode" to Opus header */
+# ifndef ENABLE_QEXT
+      if (mode->Fs==48000 && mode->shortMdctSize==120)
+# endif
+      {
+         int c0 = toOpus(compressed[0]);
+         if (c0<0)
+         {
+            RESTORE_STACK;
+            return OPUS_BAD_ARG;
+         }
+         compressed[0] = c0;
+      }
+      compressed++;
+      nbCompressedBytes--;
+   }
+#else
+   celt_assert(st->signalling==0);
+#endif
+
+   /* Can't produce more than 1275 output bytes for the main payload, plus any QEXT extra data. */
+   nbCompressedBytes = IMIN(nbCompressedBytes,packet_size_cap);
+
+   if (st->vbr && st->bitrate!=OPUS_BITRATE_MAX)
+   {
+      vbr_rate = bitrate_to_bits(st->bitrate, mode->Fs, frame_size)<<BITRES;
+#if defined(CUSTOM_MODES) || defined(ENABLE_OPUS_CUSTOM_API)
+      if (st->signalling)
+         vbr_rate -= 8<<BITRES;
+#endif
+      effectiveBytes = vbr_rate>>(3+BITRES);
+   } else {
+      opus_int32 tmp;
+      vbr_rate = 0;
+      tmp = st->bitrate*frame_size;
+      if (tell>1)
+         tmp += tell*mode->Fs;
+      if (st->bitrate!=OPUS_BITRATE_MAX)
+      {
+         nbCompressedBytes = IMAX(2, IMIN(nbCompressedBytes,
+               (tmp+4*mode->Fs)/(8*mode->Fs)-!!st->signalling));
+         if (enc != NULL)
+            ec_enc_shrink(enc, nbCompressedBytes);
+      }
+      effectiveBytes = nbCompressedBytes - nbFilledBytes;
+   }
+   nbAvailableBytes = nbCompressedBytes - nbFilledBytes;
+   equiv_rate = ((opus_int32)nbCompressedBytes*8*50 << (3-LM)) - (40*C+20)*((400>>LM) - 50);
+   if (st->bitrate != OPUS_BITRATE_MAX)
+      equiv_rate = IMIN(equiv_rate, st->bitrate - (40*C+20)*((400>>LM) - 50));
+
+   if (enc==NULL)
+   {
+      ec_enc_init(&_enc, compressed, nbCompressedBytes);
+      enc = &_enc;
+   }
+
+   if (vbr_rate>0)
+   {
+      /* Computes the max bit-rate allowed in VBR mode to avoid violating the
+          target rate and buffering.
+         We must do this up front so that bust-prevention logic triggers
+          correctly if we don't have enough bits. */
+      if (st->constrained_vbr)
+      {
+         opus_int32 vbr_bound;
+         opus_int32 max_allowed;
+         /* We could use any multiple of vbr_rate as bound (depending on the
+             delay).
+            This is clamped to ensure we use at least two bytes if the encoder
+             was entirely empty, but to allow 0 in hybrid mode. */
+         vbr_bound = vbr_rate;
+         max_allowed = IMIN(IMAX(tell==1?2:0,
+               (vbr_rate+vbr_bound-st->vbr_reservoir)>>(BITRES+3)),
+               nbAvailableBytes);
+         if(max_allowed < nbAvailableBytes)
+         {
+            nbCompressedBytes = nbFilledBytes+max_allowed;
+            nbAvailableBytes = max_allowed;
+            ec_enc_shrink(enc, nbCompressedBytes);
+         }
+      }
+   }
+   total_bits = nbCompressedBytes*8;
+
+   effEnd = end;
+   if (effEnd > mode->effEBands)
+      effEnd = mode->effEBands;
+
+   ALLOC(in, CC*(N+overlap), celt_sig);
+
+   sample_max=MAX32(st->overlap_max, celt_maxabs_res(pcm, C*(N-overlap)/st->upsample));
+   st->overlap_max=celt_maxabs_res(pcm+C*(N-overlap)/st->upsample, C*overlap/st->upsample);
+   sample_max=MAX32(sample_max, st->overlap_max);
+#ifdef FIXED_POINT
+   silence = (sample_max==0);
+#else
+   silence = (sample_max <= (opus_val16)1/(1<<st->lsb_depth));
+#endif
+#ifdef FUZZING
+   if ((rand()&0x3F)==0)
+      silence = 1;
+#endif
+   if (tell==1)
+      ec_enc_bit_logp(enc, silence, 15);
+   else
+      silence=0;
+   if (silence)
+   {
+      /*In VBR mode there is no need to send more than the minimum. */
+      if (vbr_rate>0)
+      {
+         effectiveBytes=nbCompressedBytes=IMIN(nbCompressedBytes, nbFilledBytes+2);
+         total_bits=nbCompressedBytes*8;
+         nbAvailableBytes=2;
+         ec_enc_shrink(enc, nbCompressedBytes);
+      }
+#ifdef ENABLE_QEXT
+      else if (st->enable_qext) {
+         nbCompressedBytes = IMIN(nbCompressedBytes, 1275);
+         nbAvailableBytes = nbCompressedBytes - nbFilledBytes;
+         total_bits = nbCompressedBytes*8;
+         ec_enc_shrink(enc, nbCompressedBytes);
+      }
+#endif
+      /* Pretend we've filled all the remaining bits with zeros
+            (that's what the initialiser did anyway) */
+      tell = nbCompressedBytes*8;
+      enc->nbits_total+=tell-ec_tell(enc);
+   }
+   c=0; do {
+      int need_clip=0;
+#ifdef FIXED_POINT
+      need_clip = st->clip && sample_max>65536<<RES_SHIFT;
+#else
+      need_clip = st->clip && sample_max>65536.f;
+#endif
+      celt_preemphasis(pcm+c, in+c*(N+overlap)+overlap, N, CC, st->upsample,
+                  mode->preemph, st->preemph_memE+c, need_clip);
+      OPUS_COPY(in+c*(N+overlap), &prefilter_mem[(1+c)*QEXT_SCALE(COMBFILTER_MAXPERIOD)-overlap], overlap);
+   } while (++c<CC);
+
+
+   tone_freq = tone_detect(in, CC, N+overlap, &toneishness, mode->Fs);
+   isTransient = 0;
+   shortBlocks = 0;
+   if (st->complexity >= 1 && !st->lfe)
+   {
+      /* Reduces the likelihood of energy instability on fricatives at low bitrate
+         in hybrid mode. It seems like we still want to have real transients on vowels
+         though (small SILK quantization offset value). */
+      int allow_weak_transients = hybrid && effectiveBytes<15 && st->silk_info.signalType != 2;
+      isTransient = transient_analysis(in, N+overlap, CC,
+            &tf_estimate, &tf_chan, allow_weak_transients, &weak_transient, tone_freq, toneishness);
+   }
+   toneishness = MIN32(toneishness, QCONST32(1.f, 29)-SHL32(tf_estimate, 15));
+   /* Find pitch period and gain */
+   {
+      int enabled;
+      int qg;
+      enabled = ((st->lfe&&nbAvailableBytes>3) || nbAvailableBytes>12*C) && !hybrid && !silence && tell+16<=total_bits && !st->disable_pf;
+
+      prefilter_tapset = st->tapset_decision;
+      pf_on = run_prefilter(st, in, prefilter_mem, CC, N, prefilter_tapset, &pitch_index, &gain1, &qg, enabled, st->complexity, tf_estimate, nbAvailableBytes, &st->analysis, tone_freq, toneishness ARG_QEXT(qext_scale));
+      if ((gain1 > QCONST16(.4f,15) || st->prefilter_gain > QCONST16(.4f,15)) && (!st->analysis.valid || st->analysis.tonality > .3)
+            && (pitch_index > 1.26*st->prefilter_period || pitch_index < .79*st->prefilter_period))
+         pitch_change = 1;
+      if (pf_on==0)
+      {
+         if(!hybrid && tell+16<=total_bits)
+            ec_enc_bit_logp(enc, 0, 1);
+      } else {
+         /*This block is not gated by a total bits check only because
+           of the nbAvailableBytes check above.*/
+         int octave;
+         ec_enc_bit_logp(enc, 1, 1);
+         pitch_index += 1;
+         octave = EC_ILOG(pitch_index)-5;
+         ec_enc_uint(enc, octave, 6);
+         ec_enc_bits(enc, pitch_index-(16<<octave), 4+octave);
+         pitch_index -= 1;
+         ec_enc_bits(enc, qg, 3);
+         ec_enc_icdf(enc, prefilter_tapset, tapset_icdf, 2);
+      }
+   }
+   if (LM>0 && ec_tell(enc)+3<=total_bits)
+   {
+      if (isTransient)
+         shortBlocks = M;
+   } else {
+      isTransient = 0;
+      transient_got_disabled=1;
+   }
+
+   ALLOC(freq, CC*N, celt_sig); /**< Interleaved signal MDCTs */
+   ALLOC(bandE,nbEBands*CC, celt_ener);
+   ALLOC(bandLogE,nbEBands*CC, celt_glog);
+
+   secondMdct = shortBlocks && st->complexity>=8;
+   ALLOC(bandLogE2, C*nbEBands, celt_glog);
+   if (secondMdct)
+   {
+      compute_mdcts(mode, 0, in, freq, C, CC, LM, st->upsample, st->arch);
+      compute_band_energies(mode, freq, bandE, effEnd, C, LM, st->arch);
+      amp2Log2(mode, effEnd, end, bandE, bandLogE2, C);
+      for (c=0;c<C;c++)
+      {
+         for (i=0;i<end;i++)
+            bandLogE2[nbEBands*c+i] += HALF32(SHL32(LM, DB_SHIFT));
+      }
+   }
+
+   compute_mdcts(mode, shortBlocks, in, freq, C, CC, LM, st->upsample, st->arch);
+   /* This should catch any NaN in the CELT input. Since we're not supposed to see any (they're filtered
+      at the Opus layer), just abort. */
+   celt_assert(!celt_isnan(freq[0]) && (C==1 || !celt_isnan(freq[N])));
+   if (CC==2&&C==1)
+      tf_chan = 0;
+   compute_band_energies(mode, freq, bandE, effEnd, C, LM, st->arch);
+
+   if (st->lfe)
+   {
+      for (i=2;i<end;i++)
+      {
+         bandE[i] = IMIN(bandE[i], MULT16_32_Q15(QCONST16(1e-4f,15),bandE[0]));
+         bandE[i] = MAX32(bandE[i], EPSILON);
+      }
+   }
+   amp2Log2(mode, effEnd, end, bandE, bandLogE, C);
+
+   ALLOC(surround_dynalloc, C*nbEBands, celt_glog);
+   OPUS_CLEAR(surround_dynalloc, end);
+   /* This computes how much masking takes place between surround channels */
+   if (!hybrid&&st->energy_mask&&!st->lfe)
+   {
+      int mask_end;
+      int midband;
+      int count_dynalloc;
+      opus_val32 mask_avg=0;
+      opus_val32 diff=0;
+      int count=0;
+      mask_end = IMAX(2,st->lastCodedBands);
+      for (c=0;c<C;c++)
+      {
+         for(i=0;i<mask_end;i++)
+         {
+            celt_glog mask;
+            opus_val16 mask16;
+            mask = MAXG(MING(st->energy_mask[nbEBands*c+i],
+                   GCONST(.25f)), -GCONST(2.0f));
+            if (mask > 0)
+               mask = HALF32(mask);
+            mask16 = SHR32(mask, DB_SHIFT-10);
+            mask_avg += MULT16_16(mask16, eBands[i+1]-eBands[i]);
+            count += eBands[i+1]-eBands[i];
+            diff += MULT16_16(mask16, 1+2*i-mask_end);
+         }
+      }
+      celt_assert(count>0);
+      mask_avg = SHL32(DIV32_16(mask_avg,count), DB_SHIFT-10);
+      mask_avg += GCONST(.2f);
+      diff = SHL32(diff*6/(C*(mask_end-1)*(mask_end+1)*mask_end), DB_SHIFT-10);
+      /* Again, being conservative */
+      diff = HALF32(diff);
+      diff = MAX32(MIN32(diff, GCONST(.031f)), -GCONST(.031f));
+      /* Find the band that's in the middle of the coded spectrum */
+      for (midband=0;eBands[midband+1] < eBands[mask_end]/2;midband++);
+      count_dynalloc=0;
+      for(i=0;i<mask_end;i++)
+      {
+         opus_val32 lin;
+         celt_glog unmask;
+         lin = mask_avg + diff*(i-midband);
+         if (C==2)
+            unmask = MAXG(st->energy_mask[i], st->energy_mask[nbEBands+i]);
+         else
+            unmask = st->energy_mask[i];
+         unmask = MING(unmask, GCONST(.0f));
+         unmask -= lin;
+         if (unmask > GCONST(.25f))
+         {
+            surround_dynalloc[i] = unmask - GCONST(.25f);
+            count_dynalloc++;
+         }
+      }
+      if (count_dynalloc>=3)
+      {
+         /* If we need dynalloc in many bands, it's probably because our
+            initial masking rate was too low. */
+         mask_avg += GCONST(.25f);
+         if (mask_avg>0)
+         {
+            /* Something went really wrong in the original calculations,
+               disabling masking. */
+            mask_avg = 0;
+            diff = 0;
+            OPUS_CLEAR(surround_dynalloc, mask_end);
+         } else {
+            for(i=0;i<mask_end;i++)
+               surround_dynalloc[i] = MAXG(0, surround_dynalloc[i]-GCONST(.25f));
+         }
+      }
+      mask_avg += GCONST(.2f);
+      /* Convert to 1/64th units used for the trim */
+      surround_trim = 64*diff;
+      /*printf("%d %d ", mask_avg, surround_trim);*/
+      surround_masking = mask_avg;
+   }
+   /* Temporal VBR (but not for LFE) */
+   if (!st->lfe)
+   {
+      celt_glog follow=-QCONST32(10.0f, DB_SHIFT-5);
+      opus_val32 frame_avg=0;
+      celt_glog offset = shortBlocks?HALF32(SHL32(LM, DB_SHIFT-5)):0;
+      for(i=start;i<end;i++)
+      {
+         follow = MAXG(follow-QCONST32(1.0f, DB_SHIFT-5), SHR32(bandLogE[i],5)-offset);
+         if (C==2)
+            follow = MAXG(follow, SHR32(bandLogE[i+nbEBands],5)-offset);
+         frame_avg += follow;
+      }
+      frame_avg /= (end-start);
+      temporal_vbr = SUB32(SHL32(frame_avg, 5),st->spec_avg);
+      temporal_vbr = MING(GCONST(3.f), MAXG(-GCONST(1.5f), temporal_vbr));
+      st->spec_avg += MULT16_32_Q15(QCONST16(.02f, 15), temporal_vbr);
+   }
+   /*for (i=0;i<21;i++)
+      printf("%f ", bandLogE[i]);
+   printf("\n");*/
+
+   if (!secondMdct)
+   {
+      OPUS_COPY(bandLogE2, bandLogE, C*nbEBands);
+   }
+
+   /* Last chance to catch any transient we might have missed in the
+      time-domain analysis */
+   if (LM>0 && ec_tell(enc)+3<=total_bits && !isTransient && st->complexity>=5 && !st->lfe && !hybrid)
+   {
+      if (patch_transient_decision(bandLogE, oldBandE, nbEBands, start, end, C))
+      {
+         isTransient = 1;
+         shortBlocks = M;
+         compute_mdcts(mode, shortBlocks, in, freq, C, CC, LM, st->upsample, st->arch);
+         compute_band_energies(mode, freq, bandE, effEnd, C, LM, st->arch);
+         amp2Log2(mode, effEnd, end, bandE, bandLogE, C);
+         /* Compensate for the scaling of short vs long mdcts */
+         for (c=0;c<C;c++)
+         {
+            for (i=0;i<end;i++)
+               bandLogE2[nbEBands*c+i] += HALF32(SHL32(LM, DB_SHIFT));
+         }
+         tf_estimate = QCONST16(.2f,14);
+      }
+   }
+
+   if (LM>0 && ec_tell(enc)+3<=total_bits)
+      ec_enc_bit_logp(enc, isTransient, 3);
+
+   ALLOC(X, C*N, celt_norm);         /**< Interleaved normalised MDCTs */
+
+   /* Band normalisation */
+   normalise_bands(mode, freq, X, bandE, effEnd, C, M);
+
+   enable_tf_analysis = effectiveBytes>=15*C && !hybrid && st->complexity>=2 && !st->lfe && toneishness < QCONST32(.98f, 29);
+
+   ALLOC(offsets, nbEBands, int);
+   ALLOC(importance, nbEBands, int);
+   ALLOC(spread_weight, nbEBands, int);
+
+   maxDepth = dynalloc_analysis(bandLogE, bandLogE2, oldBandE, nbEBands, start, end, C, offsets,
+         st->lsb_depth, mode->logN, isTransient, st->vbr, st->constrained_vbr,
+         eBands, LM, effectiveBytes, &tot_boost, st->lfe, surround_dynalloc, &st->analysis, importance, spread_weight, tone_freq, toneishness ARG_QEXT(qext_scale));
+
+   ALLOC(tf_res, nbEBands, int);
+   /* Disable variable tf resolution for hybrid and at very low bitrate */
+   if (enable_tf_analysis)
+   {
+      int lambda;
+      lambda = IMAX(80, 20480/effectiveBytes + 2);
+      tf_select = tf_analysis(mode, effEnd, isTransient, tf_res, lambda, X, N, LM, tf_estimate, tf_chan, importance);
+      for (i=effEnd;i<end;i++)
+         tf_res[i] = tf_res[effEnd-1];
+   } else if (hybrid && weak_transient)
+   {
+      /* For weak transients, we rely on the fact that improving time resolution using
+         TF on a long window is imperfect and will not result in an energy collapse at
+         low bitrate. */
+      for (i=0;i<end;i++)
+         tf_res[i] = 1;
+      tf_select=0;
+   } else if (hybrid && effectiveBytes<15 && st->silk_info.signalType != 2)
+   {
+      /* For low bitrate hybrid, we force temporal resolution to 5 ms rather than 2.5 ms. */
+      for (i=0;i<end;i++)
+         tf_res[i] = 0;
+      tf_select=isTransient;
+   } else {
+      for (i=0;i<end;i++)
+         tf_res[i] = isTransient;
+      tf_select=0;
+   }
+
+   ALLOC(error, C*nbEBands, celt_glog);
+   c=0;
+   do {
+      for (i=start;i<end;i++)
+      {
+         /* When the energy is stable, slightly bias energy quantization towards
+            the previous error to make the gain more stable (a constant offset is
+            better than fluctuations). */
+         if (ABS32(SUB32(bandLogE[i+c*nbEBands], oldBandE[i+c*nbEBands])) < GCONST(2.f))
+         {
+            bandLogE[i+c*nbEBands] -= MULT16_32_Q15(QCONST16(0.25f, 15), energyError[i+c*nbEBands]);
+         }
+      }
+   } while (++c < C);
+   quant_coarse_energy(mode, start, end, effEnd, bandLogE,
+         oldBandE, total_bits, error, enc,
+         C, LM, nbAvailableBytes, st->force_intra,
+         &st->delayedIntra, st->complexity >= 4, st->loss_rate, st->lfe);
+
+   tf_encode(start, end, isTransient, tf_res, LM, tf_select, enc);
+
+   if (ec_tell(enc)+4<=total_bits)
+   {
+      if (st->lfe)
+      {
+         st->tapset_decision = 0;
+         st->spread_decision = SPREAD_NORMAL;
+      } else if (hybrid)
+      {
+         if (st->complexity == 0)
+            st->spread_decision = SPREAD_NONE;
+         else if (isTransient)
+            st->spread_decision = SPREAD_NORMAL;
+         else
+            st->spread_decision = SPREAD_AGGRESSIVE;
+      } else if (shortBlocks || st->complexity < 3 || nbAvailableBytes < 10*C)
+      {
+         if (st->complexity == 0)
+            st->spread_decision = SPREAD_NONE;
+         else
+            st->spread_decision = SPREAD_NORMAL;
+      } else {
+         /* Disable new spreading+tapset estimator until we can show it works
+            better than the old one. So far it seems like spreading_decision()
+            works best. */
+#if 0
+         if (st->analysis.valid)
+         {
+            static const opus_val16 spread_thresholds[3] = {-QCONST16(.6f, 15), -QCONST16(.2f, 15), -QCONST16(.07f, 15)};
+            static const opus_val16 spread_histeresis[3] = {QCONST16(.15f, 15), QCONST16(.07f, 15), QCONST16(.02f, 15)};
+            static const opus_val16 tapset_thresholds[2] = {QCONST16(.0f, 15), QCONST16(.15f, 15)};
+            static const opus_val16 tapset_histeresis[2] = {QCONST16(.1f, 15), QCONST16(.05f, 15)};
+            st->spread_decision = hysteresis_decision(-st->analysis.tonality, spread_thresholds, spread_histeresis, 3, st->spread_decision);
+            st->tapset_decision = hysteresis_decision(st->analysis.tonality_slope, tapset_thresholds, tapset_histeresis, 2, st->tapset_decision);
+         } else
+#endif
+         {
+            st->spread_decision = spreading_decision(mode, X,
+                  &st->tonal_average, st->spread_decision, &st->hf_average,
+                  &st->tapset_decision, pf_on&&!shortBlocks, effEnd, C, M, spread_weight);
+         }
+         /*printf("%d %d\n", st->tapset_decision, st->spread_decision);*/
+         /*printf("%f %d %f %d\n\n", st->analysis.tonality, st->spread_decision, st->analysis.tonality_slope, st->tapset_decision);*/
+      }
+      ec_enc_icdf(enc, st->spread_decision, spread_icdf, 5);
+   } else {
+      st->spread_decision = SPREAD_NORMAL;
+   }
+
+   /* For LFE, everything interesting is in the first band */
+   if (st->lfe)
+      offsets[0] = IMIN(8, effectiveBytes/3);
+   ALLOC(cap, nbEBands, int);
+   init_caps(mode,cap,LM,C);
+
+   dynalloc_logp = 6;
+   total_bits<<=BITRES;
+   total_boost = 0;
+   tell = ec_tell_frac(enc);
+   for (i=start;i<end;i++)
+   {
+      int width, quanta;
+      int dynalloc_loop_logp;
+      int boost;
+      int j;
+      width = C*(eBands[i+1]-eBands[i])<<LM;
+      /* quanta is 6 bits, but no more than 1 bit/sample
+         and no less than 1/8 bit/sample */
+      quanta = IMIN(width<<BITRES, IMAX(6<<BITRES, width));
+      dynalloc_loop_logp = dynalloc_logp;
+      boost = 0;
+      for (j = 0; tell+(dynalloc_loop_logp<<BITRES) < total_bits-total_boost
+            && boost < cap[i]; j++)
+      {
+         int flag;
+         flag = j<offsets[i];
+         ec_enc_bit_logp(enc, flag, dynalloc_loop_logp);
+         tell = ec_tell_frac(enc);
+         if (!flag)
+            break;
+         boost += quanta;
+         total_boost += quanta;
+         dynalloc_loop_logp = 1;
+      }
+      /* Making dynalloc more likely */
+      if (j)
+         dynalloc_logp = IMAX(2, dynalloc_logp-1);
+      offsets[i] = boost;
+   }
+
+   if (C==2)
+   {
+      static const opus_val16 intensity_thresholds[21]=
+      /* 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19  20  off*/
+        {  1, 2, 3, 4, 5, 6, 7, 8,16,24,36,44,50,56,62,67,72,79,88,106,134};
+      static const opus_val16 intensity_histeresis[21]=
+        {  1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 4, 5, 6,  8, 8};
+
+      /* Always use MS for 2.5 ms frames until we can do a better analysis */
+      if (LM!=0)
+         dual_stereo = stereo_analysis(mode, X, LM, N);
+
+      st->intensity = hysteresis_decision((opus_val16)(equiv_rate/1000),
+            intensity_thresholds, intensity_histeresis, 21, st->intensity);
+      st->intensity = IMIN(end,IMAX(start, st->intensity));
+   }
+
+   alloc_trim = 5;
+   if (tell+(6<<BITRES) <= total_bits - total_boost)
+   {
+      if (start > 0 || st->lfe)
+      {
+         st->stereo_saving = 0;
+         alloc_trim = 5;
+      } else {
+         alloc_trim = alloc_trim_analysis(mode, X, bandLogE,
+            end, LM, C, N, &st->analysis, &st->stereo_saving, tf_estimate,
+            st->intensity, surround_trim, equiv_rate, st->arch);
+      }
+      ec_enc_icdf(enc, alloc_trim, trim_icdf, 7);
+      tell = ec_tell_frac(enc);
+   }
+
+   /* In VBR mode the frame size must not be reduced so much that it would
+       result in the encoder running out of bits.
+      The margin of 2 bytes ensures that none of the bust-prevention logic
+       in the decoder will have triggered so far. */
+   min_allowed = ((tell+total_boost+(1<<(BITRES+3))-1)>>(BITRES+3)) + 2;
+   /* Take into account the 37 bits we need to have left in the packet to
+      signal a redundant frame in hybrid mode. Creating a shorter packet would
+      create an entropy coder desync. */
+   if (hybrid)
+      min_allowed = IMAX(min_allowed, (tell0_frac+(37<<BITRES)+total_boost+(1<<(BITRES+3))-1)>>(BITRES+3));
+   /* Variable bitrate */
+   if (vbr_rate>0)
+   {
+     opus_val16 alpha;
+     opus_int32 delta;
+     /* The target rate in 8th bits per frame */
+     opus_int32 target, base_target;
+     int lm_diff = mode->maxLM - LM;
+
+     /* Don't attempt to use more than 510 kb/s, even for frames smaller than 20 ms.
+        The CELT allocator will just not be able to use more than that anyway. */
+     nbCompressedBytes = IMIN(nbCompressedBytes,packet_size_cap>>(3-LM));
+     if (!hybrid)
+     {
+        base_target = vbr_rate - ((40*C+20)<<BITRES);
+     } else {
+        base_target = IMAX(0, vbr_rate - ((9*C+4)<<BITRES));
+     }
+
+     if (st->constrained_vbr)
+        base_target += (st->vbr_offset>>lm_diff);
+
+     if (!hybrid)
+     {
+        target = compute_vbr(mode, &st->analysis, base_target, LM, equiv_rate,
+           st->lastCodedBands, C, st->intensity, st->constrained_vbr,
+           st->stereo_saving, tot_boost, tf_estimate, pitch_change, maxDepth,
+           st->lfe, st->energy_mask!=NULL, surround_masking,
+           temporal_vbr ARG_QEXT(st->enable_qext));
+     } else {
+        target = base_target;
+        /* Tonal frames (offset<100) need more bits than noisy (offset>100) ones. */
+        if (st->silk_info.offset < 100) target += 12 << BITRES >> (3-LM);
+        if (st->silk_info.offset > 100) target -= 18 << BITRES >> (3-LM);
+        /* Boosting bitrate on transients and vowels with significant temporal
+           spikes. */
+        target += (opus_int32)MULT16_16_Q14(tf_estimate-QCONST16(.25f,14), (50<<BITRES));
+        /* If we have a strong transient, let's make sure it has enough bits to code
+           the first two bands, so that it can use folding rather than noise. */
+        if (tf_estimate > QCONST16(.7f,14))
+           target = IMAX(target, 50<<BITRES);
+     }
+     /* The current offset is removed from the target and the space used
+        so far is added*/
+     target=target+tell;
+
+     nbAvailableBytes = (target+(1<<(BITRES+2)))>>(BITRES+3);
+     nbAvailableBytes = IMAX(min_allowed,nbAvailableBytes);
+     nbAvailableBytes = IMIN(nbCompressedBytes,nbAvailableBytes);
+
+     /* By how much did we "miss" the target on that frame */
+     delta = target - vbr_rate;
+
+     target=nbAvailableBytes<<(BITRES+3);
+
+     /*If the frame is silent we don't adjust our drift, otherwise
+       the encoder will shoot to very high rates after hitting a
+       span of silence, but we do allow the bitres to refill.
+       This means that we'll undershoot our target in CVBR/VBR modes
+       on files with lots of silence. */
+     if(silence)
+     {
+       nbAvailableBytes = 2;
+       target = 2*8<<BITRES;
+       delta = 0;
+     }
+
+     if (st->vbr_count < 970)
+     {
+        st->vbr_count++;
+        alpha = celt_rcp(SHL32(EXTEND32(st->vbr_count+20),16));
+     } else
+        alpha = QCONST16(.001f,15);
+     /* How many bits have we used in excess of what we're allowed */
+     if (st->constrained_vbr)
+        st->vbr_reservoir += target - vbr_rate;
+     /*printf ("%d\n", st->vbr_reservoir);*/
+
+     /* Compute the offset we need to apply in order to reach the target */
+     if (st->constrained_vbr)
+     {
+        st->vbr_drift += (opus_int32)MULT16_32_Q15(alpha,(delta*(1<<lm_diff))-st->vbr_offset-st->vbr_drift);
+        st->vbr_offset = -st->vbr_drift;
+     }
+     /*printf ("%d\n", st->vbr_drift);*/
+
+     if (st->constrained_vbr && st->vbr_reservoir < 0)
+     {
+        /* We're under the min value -- increase rate */
+        int adjust = (-st->vbr_reservoir)/(8<<BITRES);
+        /* Unless we're just coding silence */
+        nbAvailableBytes += silence?0:adjust;
+        st->vbr_reservoir = 0;
+        /*printf ("+%d\n", adjust);*/
+     }
+     nbCompressedBytes = IMIN(nbCompressedBytes,nbAvailableBytes);
+     /*printf("%d\n", nbCompressedBytes*50*8);*/
+     /* This moves the raw bits to take into account the new compressed size */
+     ec_enc_shrink(enc, nbCompressedBytes);
+   }
+#ifdef ENABLE_QEXT
+   if (st->enable_qext) {
+      int new_compressedBytes;
+      /* Don't give any bits for the first 80 kb/s per channel. Then 80% of the excess. */
+      opus_int32 offset = bitrate_to_bits(C*80000, mode->Fs, frame_size)/8;
+      qext_bytes = IMAX(nbCompressedBytes-1275, IMAX(0, (nbCompressedBytes-offset)*4/5));
+      if (qext_bytes > 20) {
+         opus_int32 target;
+         opus_val16 scale;
+         target = ((nbCompressedBytes-qext_bytes/3)*8<<BITRES);
+         if (!vbr_rate) {
+            opus_val16 tf_estimate2;
+            target -= ((40*C+20)<<BITRES);
+            tf_estimate2 = MIN32(QCONST16(1.f, 14), 2*EXTEND32(tf_estimate));
+            target = compute_vbr(mode, &st->analysis, target, LM, equiv_rate,
+                  st->lastCodedBands, C, st->intensity, st->constrained_vbr,
+                  st->stereo_saving, tot_boost, tf_estimate2, pitch_change, maxDepth,
+                  st->lfe, st->energy_mask!=NULL, surround_masking,
+                  temporal_vbr ARG_QEXT(st->enable_qext));
+            target += tell;
+         }
+         scale = PSHR32(toneishness,14);
+         scale = Q15ONE - MULT16_16_Q15(scale, scale);
+         qext_bytes += MULT16_32_Q15(scale, (nbCompressedBytes-(target/(8<<BITRES))) - qext_bytes);
+         qext_bytes = IMAX(nbCompressedBytes-1275, IMAX(21, qext_bytes));
+      }
+      padding_len_bytes = (qext_bytes+253)/254;
+      qext_bytes = IMIN(qext_bytes, nbCompressedBytes-min_allowed-padding_len_bytes-1);
+      padding_len_bytes = (qext_bytes+253)/254;
+      if (qext_bytes > 20) {
+         new_compressedBytes = nbCompressedBytes-qext_bytes-padding_len_bytes-1;
+         ec_enc_shrink(enc, new_compressedBytes);
+         if (compressed == NULL) {
+            compressed = enc->buf;
+         }
+         compressed[-1] |= 0x03; /* Code 3 packet */
+         enc->buf += 1+padding_len_bytes;
+         OPUS_MOVE(compressed+1+padding_len_bytes, compressed, new_compressedBytes);
+         compressed[0] = 0x41; /* Set padding */
+         for (i=0;i<padding_len_bytes-1;i++) compressed[i+1] = 255;
+         compressed[padding_len_bytes] = qext_bytes%254 == 0 ? 254 : qext_bytes%254;
+         ext_payload = compressed+padding_len_bytes+1+new_compressedBytes;
+         ext_payload[0] = QEXT_EXTENSION_ID<<1;
+         ext_payload += 1;
+         qext_bytes -= 1;
+         OPUS_CLEAR(ext_payload, qext_bytes);
+         ec_enc_init(&ext_enc, ext_payload, qext_bytes);
+         nbCompressedBytes = new_compressedBytes;
+         if (end == nbEBands && (mode->Fs == 48000 ||  mode->Fs == 96000) && (mode->shortMdctSize==120*qext_scale || mode->shortMdctSize==90*qext_scale)) {
+            compute_qext_mode(&qext_mode_struct, mode);
+            qext_mode = &qext_mode_struct;
+            qext_end = (qext_scale == 2) ? NB_QEXT_BANDS : 2;
+            ec_enc_bit_logp(&ext_enc, qext_end == NB_QEXT_BANDS, 1);
+         }
+      } else {
+         ec_enc_init(&ext_enc, NULL, 0);
+         qext_bytes = 0;
+      }
+   } else {
+      ec_enc_init(&ext_enc, NULL, 0);
+   }
+#endif
+
+   /* Bit allocation */
+   ALLOC(fine_quant, nbEBands, int);
+   ALLOC(pulses, nbEBands, int);
+   ALLOC(fine_priority, nbEBands, int);
+
+   /* bits =           packet size                    - where we are - safety*/
+   bits = (((opus_int32)nbCompressedBytes*8)<<BITRES) - (opus_int32)ec_tell_frac(enc) - 1;
+   anti_collapse_rsv = isTransient&&LM>=2&&bits>=((LM+2)<<BITRES) ? (1<<BITRES) : 0;
+   bits -= anti_collapse_rsv;
+   signalBandwidth = end-1;
+#ifndef DISABLE_FLOAT_API
+   if (st->analysis.valid)
+   {
+      int min_bandwidth;
+      if (equiv_rate < (opus_int32)32000*C)
+         min_bandwidth = 13;
+      else if (equiv_rate < (opus_int32)48000*C)
+         min_bandwidth = 16;
+      else if (equiv_rate < (opus_int32)60000*C)
+         min_bandwidth = 18;
+      else  if (equiv_rate < (opus_int32)80000*C)
+         min_bandwidth = 19;
+      else
+         min_bandwidth = 20;
+      signalBandwidth = IMAX(st->analysis.bandwidth, min_bandwidth);
+   }
+#endif
+   if (st->lfe)
+      signalBandwidth = 1;
+   codedBands = clt_compute_allocation(mode, start, end, offsets, cap,
+         alloc_trim, &st->intensity, &dual_stereo, bits, &balance, pulses,
+         fine_quant, fine_priority, C, LM, enc, 1, st->lastCodedBands, signalBandwidth);
+   if (st->lastCodedBands)
+      st->lastCodedBands = IMIN(st->lastCodedBands+1,IMAX(st->lastCodedBands-1,codedBands));
+   else
+      st->lastCodedBands = codedBands;
+
+   quant_fine_energy(mode, start, end, oldBandE, error, NULL, fine_quant, enc, C);
+   OPUS_CLEAR(energyError, nbEBands*CC);
+#ifdef ENABLE_QEXT
+   if (qext_mode)
+   {
+      /* Don't bias for intra. */
+      opus_val32 qext_delayedIntra=0;
+      qext_oldBandE = energyError + CC*nbEBands;
+      compute_band_energies(qext_mode, freq, qext_bandE, qext_end, C, LM, st->arch);
+      normalise_bands(qext_mode, freq, X, qext_bandE, qext_end, C, M);
+      amp2Log2(qext_mode, qext_end, qext_end, qext_bandE, qext_bandLogE, C);
+      if (C==2) {
+         qext_intensity = qext_end;
+         qext_dual_stereo = dual_stereo;
+         encode_qext_stereo_params(&ext_enc, qext_end, qext_intensity, qext_dual_stereo);
+      }
+      quant_coarse_energy(qext_mode, 0, qext_end, qext_end, qext_bandLogE,
+               qext_oldBandE, qext_bytes*8, qext_error, &ext_enc,
+               C, LM, qext_bytes, st->force_intra,
+               &qext_delayedIntra, st->complexity >= 4, st->loss_rate, st->lfe);
+   }
+   ALLOC(extra_quant, nbEBands+NB_QEXT_BANDS, int);
+   ALLOC(extra_pulses, nbEBands+NB_QEXT_BANDS, int);
+   ALLOC(error_bak, C*nbEBands, celt_glog);
+
+   qext_bits = ((opus_int32)qext_bytes*8<<BITRES) - (opus_int32)ec_tell_frac(enc) - 1;
+   clt_compute_extra_allocation(mode, qext_mode, start, end, qext_end, bandLogE, qext_bandLogE,
+         qext_bits, extra_pulses, extra_quant, C, LM, &ext_enc, 1, tone_freq, toneishness);
+   OPUS_COPY(error_bak, error, C*nbEBands);
+   if (qext_bytes > 0) {
+      quant_fine_energy(mode, start, end, oldBandE, error, fine_quant, extra_quant, &ext_enc, C);
+   }
+#endif
+
+   /* Residual quantisation */
+   ALLOC(collapse_masks, C*nbEBands, unsigned char);
+   quant_all_bands(1, mode, start, end, X, C==2 ? X+N : NULL, collapse_masks,
+         bandE, pulses, shortBlocks, st->spread_decision,
+         dual_stereo, st->intensity, tf_res, nbCompressedBytes*(8<<BITRES)-anti_collapse_rsv,
+         balance, enc, LM, codedBands, &st->rng, st->complexity, st->arch, st->disable_inv
+         ARG_QEXT(&ext_enc) ARG_QEXT(extra_pulses)
+         ARG_QEXT(qext_bytes*(8<<BITRES)) ARG_QEXT(cap));
+
+#ifdef ENABLE_QEXT
+   if (qext_mode) {
+      VARDECL(int, zeros);
+      VARDECL(unsigned char, qext_collapse_masks);
+      ec_enc dummy_enc;
+      int ext_balance;
+      ALLOC(zeros, nbEBands, int);
+      ALLOC(qext_collapse_masks, C*NB_QEXT_BANDS, unsigned char);
+      ec_enc_init(&dummy_enc, NULL, 0);
+      OPUS_CLEAR(zeros, end);
+      ext_balance = qext_bytes*(8<<BITRES) - ec_tell_frac(&ext_enc);
+      for (i=0;i<qext_end;i++) ext_balance -= extra_pulses[nbEBands+i] + C*(extra_quant[nbEBands+1]<<BITRES);
+      quant_fine_energy(qext_mode, 0, qext_end, qext_oldBandE, qext_error, NULL, &extra_quant[nbEBands], &ext_enc, C);
+      quant_all_bands(1, qext_mode, 0, qext_end, X, C==2 ? X+N : NULL, qext_collapse_masks,
+            qext_bandE, &extra_pulses[nbEBands], shortBlocks, st->spread_decision,
+            qext_dual_stereo, qext_intensity, zeros, qext_bytes*(8<<BITRES),
+            ext_balance, &ext_enc, LM, qext_end, &st->rng, st->complexity, st->arch, st->disable_inv, &dummy_enc, zeros, 0, NULL);
+   }
+#endif
+
+   if (anti_collapse_rsv > 0)
+   {
+      anti_collapse_on = st->consec_transient<2;
+#ifdef FUZZING
+      anti_collapse_on = rand()&0x1;
+#endif
+      ec_enc_bits(enc, anti_collapse_on, 1);
+   }
+   if (qext_bytes == 0)
+      quant_energy_finalise(mode, start, end, oldBandE, error, fine_quant, fine_priority, nbCompressedBytes*8-ec_tell(enc), enc, C);
+   c=0;
+   do {
+      for (i=start;i<end;i++)
+      {
+         energyError[i+c*nbEBands] = MAXG(-GCONST(0.5f), MING(GCONST(0.5f), error[i+c*nbEBands]));
+      }
+   } while (++c < C);
+#ifdef ENABLE_QEXT
+   if (qext_bytes > 0)
+      quant_energy_finalise(mode, start, end, NULL, error_bak, fine_quant, fine_priority, nbCompressedBytes*8-ec_tell(enc), enc, C);
+#endif
+   if (silence)
+   {
+      for (i=0;i<C*nbEBands;i++)
+         oldBandE[i] = -GCONST(28.f);
+   }
+
+#ifdef RESYNTH
+   /* Re-synthesis of the coded audio if required */
+   {
+      celt_sig *out_mem[2];
+
+      if (anti_collapse_on)
+      {
+         anti_collapse(mode, X, collapse_masks, LM, C, N,
+               start, end, oldBandE, oldLogE, oldLogE2, pulses, st->rng, 1, st->arch);
+      }
+
+      c=0; do {
+         OPUS_MOVE(st->syn_mem[c], st->syn_mem[c]+N, QEXT_SCALE(DEC_PITCH_BUF_SIZE)-N+overlap/2);
+      } while (++c<CC);
+
+      c=0; do {
+         out_mem[c] = st->syn_mem[c]+QEXT_SCALE(DEC_PITCH_BUF_SIZE)-N;
+      } while (++c<CC);
+
+      celt_synthesis(mode, X, out_mem, oldBandE, start, effEnd,
+                     C, CC, isTransient, LM, st->upsample, silence, st->arch ARG_QEXT(qext_mode) ARG_QEXT(qext_oldBandE) ARG_QEXT(qext_end));
+
+      c=0; do {
+         st->prefilter_period=IMAX(st->prefilter_period, COMBFILTER_MINPERIOD);
+         st->prefilter_period_old=IMAX(st->prefilter_period_old, COMBFILTER_MINPERIOD);
+         comb_filter(out_mem[c], out_mem[c], st->prefilter_period_old, st->prefilter_period, mode->shortMdctSize,
+               st->prefilter_gain_old, st->prefilter_gain, st->prefilter_tapset_old, st->prefilter_tapset,
+               mode->window, overlap, st->arch);
+         if (LM!=0)
+            comb_filter(out_mem[c]+mode->shortMdctSize, out_mem[c]+mode->shortMdctSize, st->prefilter_period, pitch_index, N-mode->shortMdctSize,
+                  st->prefilter_gain, gain1, st->prefilter_tapset, prefilter_tapset,
+                  mode->window, overlap, st->arch);
+      } while (++c<CC);
+
+      /* We reuse freq[] as scratch space for the de-emphasis */
+      deemphasis(out_mem, (opus_res*)pcm, N, CC, st->upsample, mode->preemph, st->preemph_memD, 0);
+      st->prefilter_period_old = st->prefilter_period;
+      st->prefilter_gain_old = st->prefilter_gain;
+      st->prefilter_tapset_old = st->prefilter_tapset;
+   }
+#endif
+
+   st->prefilter_period = pitch_index;
+   st->prefilter_gain = gain1;
+   st->prefilter_tapset = prefilter_tapset;
+#ifdef RESYNTH
+   if (LM!=0)
+   {
+      st->prefilter_period_old = st->prefilter_period;
+      st->prefilter_gain_old = st->prefilter_gain;
+      st->prefilter_tapset_old = st->prefilter_tapset;
+   }
+#endif
+
+   if (CC==2&&C==1) {
+      OPUS_COPY(&oldBandE[nbEBands], oldBandE, nbEBands);
+   }
+
+   if (!isTransient)
+   {
+      OPUS_COPY(oldLogE2, oldLogE, CC*nbEBands);
+      OPUS_COPY(oldLogE, oldBandE, CC*nbEBands);
+   } else {
+      for (i=0;i<CC*nbEBands;i++)
+         oldLogE[i] = MING(oldLogE[i], oldBandE[i]);
+   }
+   /* In case start or end were to change */
+   c=0; do
+   {
+      for (i=0;i<start;i++)
+      {
+         oldBandE[c*nbEBands+i]=0;
+         oldLogE[c*nbEBands+i]=oldLogE2[c*nbEBands+i]=-GCONST(28.f);
+      }
+      for (i=end;i<nbEBands;i++)
+      {
+         oldBandE[c*nbEBands+i]=0;
+         oldLogE[c*nbEBands+i]=oldLogE2[c*nbEBands+i]=-GCONST(28.f);
+      }
+   } while (++c<CC);
+
+   if (isTransient || transient_got_disabled)
+      st->consec_transient++;
+   else
+      st->consec_transient=0;
+   st->rng = enc->rng;
+
+   /* If there's any room left (can only happen for very high rates),
+      it's already filled with zeros */
+   ec_enc_done(enc);
+#ifdef ENABLE_QEXT
+   ec_enc_done(&ext_enc);
+   if (qext_bytes > 0)
+      nbCompressedBytes += padding_len_bytes+2+qext_bytes;
+   if (qext_bytes) st->rng = st->rng ^ ext_enc.rng;
+   if (ec_get_error(&ext_enc))
+      return OPUS_INTERNAL_ERROR;
+#endif
+#if defined(CUSTOM_MODES) || defined(ENABLE_OPUS_CUSTOM_API)
+   if (st->signalling)
+      nbCompressedBytes++;
+#endif
+
+   RESTORE_STACK;
+   if (ec_get_error(enc))
+      return OPUS_INTERNAL_ERROR;
+   else
+      return nbCompressedBytes;
+}
+
+
+#if defined(CUSTOM_MODES) || defined(ENABLE_OPUS_CUSTOM_API)
+
+#if defined(FIXED_POINT) && !defined(ENABLE_RES24)
+int opus_custom_encode(CELTEncoder * OPUS_RESTRICT st, const opus_int16 * pcm, int frame_size, unsigned char *compressed, int nbCompressedBytes)
+{
+   return celt_encode_with_ec(st, pcm, frame_size, compressed, nbCompressedBytes, NULL);
+}
+#else
+int opus_custom_encode(CELTEncoder * OPUS_RESTRICT st, const opus_int16 * pcm, int frame_size, unsigned char *compressed, int nbCompressedBytes)
+{
+   int j, ret, C, N;
+   VARDECL(opus_res, in);
+   ALLOC_STACK;
+
+   if (pcm==NULL)
+      return OPUS_BAD_ARG;
+
+   C = st->channels;
+   N = frame_size;
+   ALLOC(in, C*N, opus_res);
+
+   for (j=0;j<C*N;j++)
+     in[j] = INT16TORES(pcm[j]);
+
+   ret=celt_encode_with_ec(st,in,frame_size,compressed,nbCompressedBytes, NULL);
+#ifdef RESYNTH
+   for (j=0;j<C*N;j++)
+      ((opus_int16*)pcm)[j]=RES2INT16(in[j]);
+#endif
+   RESTORE_STACK;
+   return ret;
+}
+#endif
+
+
+#if defined(FIXED_POINT) && defined(ENABLE_RES24)
+int opus_custom_encode24(CELTEncoder * OPUS_RESTRICT st, const opus_int32 * pcm, int frame_size, unsigned char *compressed, int nbCompressedBytes)
+{
+   return celt_encode_with_ec(st, pcm, frame_size, compressed, nbCompressedBytes, NULL);
+}
+#else
+int opus_custom_encode24(CELTEncoder * OPUS_RESTRICT st, const opus_int32 * pcm, int frame_size, unsigned char *compressed, int nbCompressedBytes)
+{
+   int j, ret, C, N;
+   VARDECL(opus_res, in);
+   ALLOC_STACK;
+
+   if (pcm==NULL)
+      return OPUS_BAD_ARG;
+
+   C = st->channels;
+   N = frame_size;
+   ALLOC(in, C*N, opus_res);
+
+   for (j=0;j<C*N;j++)
+     in[j] = INT24TORES(pcm[j]);
+
+   ret=celt_encode_with_ec(st,in,frame_size,compressed,nbCompressedBytes, NULL);
+#ifdef RESYNTH
+   for (j=0;j<C*N;j++)
+      ((opus_int32*)pcm)[j]=RES2INT24(in[j]);
+#endif
+   RESTORE_STACK;
+   return ret;
+}
+#endif
+
+
+#ifndef DISABLE_FLOAT_API
+
+# if !defined(FIXED_POINT)
+int opus_custom_encode_float(CELTEncoder * OPUS_RESTRICT st, const float * pcm, int frame_size, unsigned char *compressed, int nbCompressedBytes)
+{
+   return celt_encode_with_ec(st, pcm, frame_size, compressed, nbCompressedBytes, NULL);
+}
+# else
+int opus_custom_encode_float(CELTEncoder * OPUS_RESTRICT st, const float * pcm, int frame_size, unsigned char *compressed, int nbCompressedBytes)
+{
+   int j, ret, C, N;
+   VARDECL(opus_res, in);
+   ALLOC_STACK;
+
+   if (pcm==NULL)
+      return OPUS_BAD_ARG;
+
+   C = st->channels;
+   N = frame_size;
+   ALLOC(in, C*N, opus_res);
+
+   for (j=0;j<C*N;j++)
+     in[j] = FLOAT2RES(pcm[j]);
+
+   ret=celt_encode_with_ec(st,in,frame_size,compressed,nbCompressedBytes, NULL);
+#ifdef RESYNTH
+   for (j=0;j<C*N;j++)
+      ((float*)pcm)[j]=RES2FLOAT(in[j]);
+#endif
+   RESTORE_STACK;
+   return ret;
+}
+# endif
+
+#endif
+
+#endif /* CUSTOM_MODES */
+
+int opus_custom_encoder_ctl(CELTEncoder * OPUS_RESTRICT st, int request, ...)
+{
+   va_list ap;
+
+   va_start(ap, request);
+   switch (request)
+   {
+      case OPUS_SET_COMPLEXITY_REQUEST:
+      {
+         int value = va_arg(ap, opus_int32);
+         if (value<0 || value>10)
+            goto bad_arg;
+         st->complexity = value;
+      }
+      break;
+      case CELT_SET_START_BAND_REQUEST:
+      {
+         opus_int32 value = va_arg(ap, opus_int32);
+         if (value<0 || value>=st->mode->nbEBands)
+            goto bad_arg;
+         st->start = value;
+      }
+      break;
+      case CELT_SET_END_BAND_REQUEST:
+      {
+         opus_int32 value = va_arg(ap, opus_int32);
+         if (value<1 || value>st->mode->nbEBands)
+            goto bad_arg;
+         st->end = value;
+      }
+      break;
+      case CELT_SET_PREDICTION_REQUEST:
+      {
+         int value = va_arg(ap, opus_int32);
+         if (value<0 || value>2)
+            goto bad_arg;
+         st->disable_pf = value<=1;
+         st->force_intra = value==0;
+      }
+      break;
+      case OPUS_SET_PACKET_LOSS_PERC_REQUEST:
+      {
+         int value = va_arg(ap, opus_int32);
+         if (value<0 || value>100)
+            goto bad_arg;
+         st->loss_rate = value;
+      }
+      break;
+      case OPUS_SET_VBR_CONSTRAINT_REQUEST:
+      {
+         opus_int32 value = va_arg(ap, opus_int32);
+         st->constrained_vbr = value;
+      }
+      break;
+      case OPUS_SET_VBR_REQUEST:
+      {
+         opus_int32 value = va_arg(ap, opus_int32);
+         st->vbr = value;
+      }
+      break;
+      case OPUS_SET_BITRATE_REQUEST:
+      {
+         opus_int32 value = va_arg(ap, opus_int32);
+         if (value<=500 && value!=OPUS_BITRATE_MAX)
+            goto bad_arg;
+         value = IMIN(value, 750000*st->channels);
+         st->bitrate = value;
+      }
+      break;
+      case CELT_SET_CHANNELS_REQUEST:
+      {
+         opus_int32 value = va_arg(ap, opus_int32);
+         if (value<1 || value>2)
+            goto bad_arg;
+         st->stream_channels = value;
+      }
+      break;
+      case OPUS_SET_LSB_DEPTH_REQUEST:
+      {
+          opus_int32 value = va_arg(ap, opus_int32);
+          if (value<8 || value>24)
+             goto bad_arg;
+          st->lsb_depth=value;
+      }
+      break;
+      case OPUS_GET_LSB_DEPTH_REQUEST:
+      {
+          opus_int32 *value = va_arg(ap, opus_int32*);
+          *value=st->lsb_depth;
+      }
+      break;
+      case OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST:
+      {
+          opus_int32 value = va_arg(ap, opus_int32);
+          if(value<0 || value>1)
+          {
+             goto bad_arg;
+          }
+          st->disable_inv = value;
+      }
+      break;
+      case OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST:
+      {
+          opus_int32 *value = va_arg(ap, opus_int32*);
+          if (!value)
+          {
+             goto bad_arg;
+          }
+          *value = st->disable_inv;
+      }
+      break;
+#ifdef ENABLE_QEXT
+      case OPUS_SET_QEXT_REQUEST:
+      {
+          opus_int32 value = va_arg(ap, opus_int32);
+          if(value<0 || value>1)
+          {
+             goto bad_arg;
+          }
+          st->enable_qext = value;
+      }
+      break;
+      case OPUS_GET_QEXT_REQUEST:
+      {
+          opus_int32 *value = va_arg(ap, opus_int32*);
+          if (!value)
+          {
+             goto bad_arg;
+          }
+          *value = st->enable_qext;
+      }
+      break;
+#endif
+      case OPUS_RESET_STATE:
+      {
+         int i;
+         celt_glog *oldBandE, *oldLogE, *oldLogE2;
+         oldBandE = (celt_glog*)(st->in_mem+st->channels*(st->mode->overlap+QEXT_SCALE2(COMBFILTER_MAXPERIOD, st->qext_scale)));
+         oldLogE = oldBandE + st->channels*st->mode->nbEBands;
+         oldLogE2 = oldLogE + st->channels*st->mode->nbEBands;
+         OPUS_CLEAR((char*)&st->ENCODER_RESET_START,
+               opus_custom_encoder_get_size(st->mode, st->channels)-
+               ((char*)&st->ENCODER_RESET_START - (char*)st));
+         for (i=0;i<st->channels*st->mode->nbEBands;i++)
+            oldLogE[i]=oldLogE2[i]=-GCONST(28.f);
+         st->vbr_offset = 0;
+         st->delayedIntra = 1;
+         st->spread_decision = SPREAD_NORMAL;
+         st->tonal_average = 256;
+         st->hf_average = 0;
+         st->tapset_decision = 0;
+      }
+      break;
+#if defined(CUSTOM_MODES) || defined(ENABLE_OPUS_CUSTOM_API)
+      case CELT_SET_INPUT_CLIPPING_REQUEST:
+      {
+         opus_int32 value = va_arg(ap, opus_int32);
+         st->clip = value;
+      }
+      break;
+#endif
+      case CELT_SET_SIGNALLING_REQUEST:
+      {
+         opus_int32 value = va_arg(ap, opus_int32);
+         st->signalling = value;
+      }
+      break;
+      case CELT_SET_ANALYSIS_REQUEST:
+      {
+         AnalysisInfo *info = va_arg(ap, AnalysisInfo *);
+         if (info)
+            OPUS_COPY(&st->analysis, info, 1);
+      }
+      break;
+      case CELT_SET_SILK_INFO_REQUEST:
+      {
+         SILKInfo *info = va_arg(ap, SILKInfo *);
+         if (info)
+            OPUS_COPY(&st->silk_info, info, 1);
+      }
+      break;
+      case CELT_GET_MODE_REQUEST:
+      {
+         const CELTMode ** value = va_arg(ap, const CELTMode**);
+         if (value==0)
+            goto bad_arg;
+         *value=st->mode;
+      }
+      break;
+      case OPUS_GET_FINAL_RANGE_REQUEST:
+      {
+         opus_uint32 * value = va_arg(ap, opus_uint32 *);
+         if (value==0)
+            goto bad_arg;
+         *value=st->rng;
+      }
+      break;
+      case OPUS_SET_LFE_REQUEST:
+      {
+          opus_int32 value = va_arg(ap, opus_int32);
+          st->lfe = value;
+      }
+      break;
+      case OPUS_SET_ENERGY_MASK_REQUEST:
+      {
+         celt_glog *value = va_arg(ap, celt_glog*);
+          st->energy_mask = value;
+      }
+      break;
+      default:
+         goto bad_request;
+   }
+   va_end(ap);
+   return OPUS_OK;
+bad_arg:
+   va_end(ap);
+   return OPUS_BAD_ARG;
+bad_request:
+   va_end(ap);
+   return OPUS_UNIMPLEMENTED;
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/copy_encoder_sources.ps1 b/code/sdk/zhihui/test_opus_frame_to_pcm/copy_encoder_sources.ps1
new file mode 100644 (file)
index 0000000..2e1101e
--- /dev/null
@@ -0,0 +1,195 @@
+# PowerShell script to copy encoder source files in addition to decoder files\r
+# This script should be run after copy_sources.sh to add encoder support\r
+\r
+$ErrorActionPreference = "Stop"\r
+\r
+$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path\r
+$ProjectRoot = Split-Path -Parent $ScriptDir\r
+\r
+Write-Host "=========================================="\r
+Write-Host "Copying Opus Encoder Source Files"\r
+Write-Host "=========================================="\r
+Write-Host "Project root: $ProjectRoot"\r
+Write-Host "Target directory: $ScriptDir"\r
+Write-Host ""\r
+\r
+# Create directories if they don't exist\r
+$dirs = @(\r
+    "$ScriptDir\src_lib",\r
+    "$ScriptDir\celt",\r
+    "$ScriptDir\silk",\r
+    "$ScriptDir\silk\fixed"\r
+)\r
+\r
+foreach ($dir in $dirs) {\r
+    if (-not (Test-Path $dir)) {\r
+        New-Item -ItemType Directory -Path $dir -Force | Out-Null\r
+    }\r
+}\r
+\r
+Write-Host "Created directory structure"\r
+Write-Host ""\r
+\r
+# Copy Opus API layer encoder sources\r
+Write-Host "Copying Opus encoder API sources..."\r
+$OpusEncoderFiles = @(\r
+    "src\opus_encoder.c",\r
+    "src\opus_multistream_encoder.c",\r
+    "src\opus_projection_encoder.c",\r
+    "src\analysis.c",\r
+    "src\mlp.c",\r
+    "src\mlp_data.c"\r
+)\r
+\r
+foreach ($file in $OpusEncoderFiles) {\r
+    $srcPath = Join-Path $ProjectRoot $file\r
+    $dstPath = Join-Path $ScriptDir "src_lib" (Split-Path -Leaf $file)\r
+    if (Test-Path $srcPath) {\r
+        Copy-Item $srcPath $dstPath -Force\r
+        Write-Host "  Copied: $file"\r
+    } else {\r
+        Write-Host "  Warning: $file not found"\r
+    }\r
+}\r
+\r
+Write-Host ""\r
+\r
+# Copy CELT encoder sources\r
+Write-Host "Copying CELT encoder sources..."\r
+$CeltEncoderFiles = @(\r
+    "celt\celt_encoder.c"\r
+)\r
+\r
+foreach ($file in $CeltEncoderFiles) {\r
+    $srcPath = Join-Path $ProjectRoot $file\r
+    $dstPath = Join-Path $ScriptDir "celt" (Split-Path -Leaf $file)\r
+    if (Test-Path $srcPath) {\r
+        Copy-Item $srcPath $dstPath -Force\r
+        Write-Host "  Copied: $file"\r
+    } else {\r
+        Write-Host "  Warning: $file not found"\r
+    }\r
+}\r
+\r
+Write-Host ""\r
+\r
+# Copy SILK encoder core sources\r
+Write-Host "Copying SILK encoder core sources..."\r
+$SilkEncoderCoreFiles = @(\r
+    "silk\enc_API.c",\r
+    "silk\init_encoder.c",\r
+    "silk\encode_indices.c",\r
+    "silk\encode_pulses.c",\r
+    "silk\NLSF_encode.c",\r
+    "silk\NLSF_VQ.c",\r
+    "silk\NLSF_del_dec_quant.c",\r
+    "silk\process_NLSFs.c"\r
+)\r
+\r
+foreach ($file in $SilkEncoderCoreFiles) {\r
+    $srcPath = Join-Path $ProjectRoot $file\r
+    $dstPath = Join-Path $ScriptDir "silk" (Split-Path -Leaf $file)\r
+    if (Test-Path $srcPath) {\r
+        Copy-Item $srcPath $dstPath -Force\r
+        Write-Host "  Copied: $file"\r
+    } else {\r
+        Write-Host "  Warning: $file not found"\r
+    }\r
+}\r
+\r
+# Copy SILK encoder stereo processing\r
+Write-Host "Copying SILK encoder stereo processing..."\r
+$SilkEncoderStereoFiles = @(\r
+    "silk\stereo_LR_to_MS.c",\r
+    "silk\stereo_encode_pred.c",\r
+    "silk\stereo_quant_pred.c"\r
+)\r
+\r
+foreach ($file in $SilkEncoderStereoFiles) {\r
+    $srcPath = Join-Path $ProjectRoot $file\r
+    $dstPath = Join-Path $ScriptDir "silk" (Split-Path -Leaf $file)\r
+    if (Test-Path $srcPath) {\r
+        Copy-Item $srcPath $dstPath -Force\r
+        Write-Host "  Copied: $file"\r
+    } else {\r
+        Write-Host "  Warning: $file not found"\r
+    }\r
+}\r
+\r
+# Copy SILK encoder NSQ (Noise Shaping Quantization)\r
+Write-Host "Copying SILK encoder NSQ sources..."\r
+$SilkEncoderNsqFiles = @(\r
+    "silk\NSQ.c",\r
+    "silk\NSQ_del_dec.c"\r
+)\r
+\r
+foreach ($file in $SilkEncoderNsqFiles) {\r
+    $srcPath = Join-Path $ProjectRoot $file\r
+    $dstPath = Join-Path $ScriptDir "silk" (Split-Path -Leaf $file)\r
+    if (Test-Path $srcPath) {\r
+        Copy-Item $srcPath $dstPath -Force\r
+        Write-Host "  Copied: $file"\r
+    } else {\r
+        Write-Host "  Warning: $file not found"\r
+    }\r
+}\r
+\r
+# Copy SILK encoder control and analysis\r
+Write-Host "Copying SILK encoder control and analysis..."\r
+$SilkEncoderControlFiles = @(\r
+    "silk\quant_LTP_gains.c",\r
+    "silk\VQ_WMat_EC.c",\r
+    "silk\VAD.c",\r
+    "silk\control_audio_bandwidth.c",\r
+    "silk\control_SNR.c",\r
+    "silk\control_codec.c",\r
+    "silk\check_control_input.c",\r
+    "silk\ana_filt_bank_1.c"\r
+)\r
+\r
+foreach ($file in $SilkEncoderControlFiles) {\r
+    $srcPath = Join-Path $ProjectRoot $file\r
+    $dstPath = Join-Path $ScriptDir "silk" (Split-Path -Leaf $file)\r
+    if (Test-Path $srcPath) {\r
+        Copy-Item $srcPath $dstPath -Force\r
+        Write-Host "  Copied: $file"\r
+    } else {\r
+        Write-Host "  Warning: $file not found"\r
+    }\r
+}\r
+\r
+# Copy SILK fixed-point encoder implementation\r
+Write-Host "Copying SILK fixed-point encoder implementation..."\r
+$fixedDir = Join-Path $ProjectRoot "silk\fixed"\r
+$targetFixedDir = Join-Path $ScriptDir "silk\fixed"\r
+if (Test-Path $fixedDir) {\r
+    $fixedFiles = Get-ChildItem -Path $fixedDir -Filter "*.c" -File\r
+    foreach ($file in $fixedFiles) {\r
+        Copy-Item $file.FullName $targetFixedDir -Force\r
+    }\r
+    $fixedHeaders = Get-ChildItem -Path $fixedDir -Filter "*.h" -File\r
+    foreach ($file in $fixedHeaders) {\r
+        Copy-Item $file.FullName $targetFixedDir -Force\r
+    }\r
+    Write-Host "  Copied SILK fixed-point encoder sources and headers"\r
+}\r
+\r
+# Copy SILK float encoder implementation\r
+Write-Host "Copying SILK float encoder implementation..."\r
+$encodeFrameFlp = Join-Path $ProjectRoot "silk\float\encode_frame_FLP.c"\r
+$targetFlp = Join-Path $ScriptDir "silk\float\encode_frame_FLP.c"\r
+if (Test-Path $encodeFrameFlp) {\r
+    Copy-Item $encodeFrameFlp $targetFlp -Force\r
+    Write-Host "  Copied: silk\float\encode_frame_FLP.c"\r
+}\r
+\r
+Write-Host ""\r
+Write-Host "=========================================="\r
+Write-Host "Encoder source files copied successfully!"\r
+Write-Host "=========================================="\r
+Write-Host ""\r
+Write-Host "Next steps:"\r
+Write-Host "  1. Review copied files"\r
+Write-Host "  2. Run: make -f Makefile.libopus"\r
+Write-Host "  3. This will create libopus_min.a with encoder support"\r
+Write-Host ""\r
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/copy_encoder_sources.sh b/code/sdk/zhihui/test_opus_frame_to_pcm/copy_encoder_sources.sh
new file mode 100644 (file)
index 0000000..b3fb2dc
--- /dev/null
@@ -0,0 +1,166 @@
+#!/bin/bash
+# Script to copy encoder source files in addition to decoder files
+# This script should be run after copy_sources.sh to add encoder support
+
+set -e
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
+
+echo "=========================================="
+echo "Copying Opus Encoder Source Files"
+echo "=========================================="
+echo "Project root: $PROJECT_ROOT"
+echo "Target directory: $SCRIPT_DIR"
+echo ""
+
+# Create directories if they don't exist
+mkdir -p "$SCRIPT_DIR/src_lib"
+mkdir -p "$SCRIPT_DIR/celt"
+mkdir -p "$SCRIPT_DIR/silk"
+mkdir -p "$SCRIPT_DIR/silk/fixed"
+
+echo "Created directory structure"
+echo ""
+
+# Copy Opus API layer encoder sources
+echo "Copying Opus encoder API sources..."
+OPUS_ENCODER_FILES=(
+       "src/opus_encoder.c"
+       "src/opus_multistream_encoder.c"
+       "src/opus_projection_encoder.c"
+       "src/analysis.c"
+       "src/mlp.c"
+       "src/mlp_data.c"
+)
+
+for file in "${OPUS_ENCODER_FILES[@]}"; do
+       if [ -f "$PROJECT_ROOT/$file" ]; then
+               cp "$PROJECT_ROOT/$file" "$SCRIPT_DIR/src_lib/"
+               echo "  Copied: $file"
+       else
+               echo "  Warning: $file not found"
+       fi
+done
+
+echo ""
+
+# Copy CELT encoder sources
+echo "Copying CELT encoder sources..."
+CELT_ENCODER_FILES=(
+       "celt/celt_encoder.c"
+)
+
+for file in "${CELT_ENCODER_FILES[@]}"; do
+       if [ -f "$PROJECT_ROOT/$file" ]; then
+               cp "$PROJECT_ROOT/$file" "$SCRIPT_DIR/celt/"
+               echo "  Copied: $file"
+       else
+               echo "  Warning: $file not found"
+       fi
+done
+
+echo ""
+
+# Copy SILK encoder core sources
+echo "Copying SILK encoder core sources..."
+SILK_ENCODER_CORE_FILES=(
+       "silk/enc_API.c"
+       "silk/init_encoder.c"
+       "silk/encode_indices.c"
+       "silk/encode_pulses.c"
+       "silk/NLSF_encode.c"
+       "silk/NLSF_VQ.c"
+       "silk/NLSF_del_dec_quant.c"
+       "silk/process_NLSFs.c"
+)
+
+for file in "${SILK_ENCODER_CORE_FILES[@]}"; do
+       if [ -f "$PROJECT_ROOT/$file" ]; then
+               cp "$PROJECT_ROOT/$file" "$SCRIPT_DIR/silk/"
+               echo "  Copied: $file"
+       else
+               echo "  Warning: $file not found"
+       fi
+done
+
+# Copy SILK encoder stereo processing
+echo "Copying SILK encoder stereo processing..."
+SILK_ENCODER_STEREO_FILES=(
+       "silk/stereo_LR_to_MS.c"
+       "silk/stereo_encode_pred.c"
+       "silk/stereo_quant_pred.c"
+)
+
+for file in "${SILK_ENCODER_STEREO_FILES[@]}"; do
+       if [ -f "$PROJECT_ROOT/$file" ]; then
+               cp "$PROJECT_ROOT/$file" "$SCRIPT_DIR/silk/"
+               echo "  Copied: $file"
+       else
+               echo "  Warning: $file not found"
+       fi
+done
+
+# Copy SILK encoder NSQ (Noise Shaping Quantization)
+echo "Copying SILK encoder NSQ sources..."
+SILK_ENCODER_NSQ_FILES=(
+       "silk/NSQ.c"
+       "silk/NSQ_del_dec.c"
+)
+
+for file in "${SILK_ENCODER_NSQ_FILES[@]}"; do
+       if [ -f "$PROJECT_ROOT/$file" ]; then
+               cp "$PROJECT_ROOT/$file" "$SCRIPT_DIR/silk/"
+               echo "  Copied: $file"
+       else
+               echo "  Warning: $file not found"
+       fi
+done
+
+# Copy SILK encoder control and analysis
+echo "Copying SILK encoder control and analysis..."
+SILK_ENCODER_CONTROL_FILES=(
+       "silk/quant_LTP_gains.c"
+       "silk/VQ_WMat_EC.c"
+       "silk/VAD.c"
+       "silk/control_audio_bandwidth.c"
+       "silk/control_SNR.c"
+       "silk/control_codec.c"
+       "silk/check_control_input.c"
+       "silk/ana_filt_bank_1.c"
+)
+
+for file in "${SILK_ENCODER_CONTROL_FILES[@]}"; do
+       if [ -f "$PROJECT_ROOT/$file" ]; then
+               cp "$PROJECT_ROOT/$file" "$SCRIPT_DIR/silk/"
+               echo "  Copied: $file"
+       else
+               echo "  Warning: $file not found"
+       fi
+done
+
+# Copy SILK fixed-point encoder implementation
+echo "Copying SILK fixed-point encoder implementation..."
+if [ -d "$PROJECT_ROOT/silk/fixed" ]; then
+       find "$PROJECT_ROOT/silk/fixed" -name "*.c" -exec cp {} "$SCRIPT_DIR/silk/fixed/" \;
+       find "$PROJECT_ROOT/silk/fixed" -name "*.h" -exec cp {} "$SCRIPT_DIR/silk/fixed/" \;
+       echo "  Copied SILK fixed-point encoder sources and headers"
+fi
+
+# Copy SILK float encoder implementation (encode_frame_FLP.c should already exist)
+echo "Copying SILK float encoder implementation..."
+if [ -f "$PROJECT_ROOT/silk/float/encode_frame_FLP.c" ]; then
+       cp "$PROJECT_ROOT/silk/float/encode_frame_FLP.c" "$SCRIPT_DIR/silk/float/"
+       echo "  Copied: silk/float/encode_frame_FLP.c"
+fi
+
+echo ""
+echo "=========================================="
+echo "Encoder source files copied successfully!"
+echo "=========================================="
+echo ""
+echo "Next steps:"
+echo "  1. Review copied files"
+echo "  2. Run: make -f Makefile.libopus"
+echo "  3. This will create libopus_min.a with encoder support"
+echo ""
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/copy_missing_headers.sh b/code/sdk/zhihui/test_opus_frame_to_pcm/copy_missing_headers.sh
new file mode 100644 (file)
index 0000000..1ba86a3
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/bash
+# Script to copy missing header files needed for encoder support
+
+set -e
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
+
+echo "Copying missing header files..."
+
+# Copy SILK float headers
+if [ -d "$PROJECT_ROOT/silk/float" ]; then
+    find "$PROJECT_ROOT/silk/float" -name "*.h" -exec cp {} "$SCRIPT_DIR/silk/float/" \;
+    echo "  Copied SILK float headers"
+fi
+
+# Copy SILK fixed headers
+if [ -d "$PROJECT_ROOT/silk/fixed" ]; then
+    find "$PROJECT_ROOT/silk/fixed" -name "*.h" -exec cp {} "$SCRIPT_DIR/silk/fixed/" \;
+    echo "  Copied SILK fixed headers"
+fi
+
+echo "Done!"
index 2ce2b7c..e87aad4 100644 (file)
@@ -51,6 +51,42 @@ int opus_frame_to_pcm_decode_packet(const unsigned char *packet_data, int packet
 int opus_frame_to_pcm_decode_file(const char *input_file, int sample_rate, int channels,
                                   const char *output_file);
 
+/**
+ * @brief Encode PCM buffer (in-memory) to a single Opus packet. No file I/O.
+ *
+ * @param pcm                    Input: interleaved 16-bit PCM (little-endian).
+ * @param pcm_samples_per_channel Number of samples per channel (e.g. 320 for 20ms @ 16kHz).
+ * @param sample_rate            Sample rate in Hz (e.g. 16000/48000).
+ * @param channels               Channel count (1=mono, 2=stereo).
+ * @param bitrate                Target bitrate in bps (e.g. 64000).
+ * @param application            Opus application: 0=VOIP, 1=AUDIO, 2=RESTRICTED_LOWDELAY.
+ * @param out_packet             Output buffer for encoded Opus packet.
+ * @param out_packet_max_len     Capacity of out_packet in bytes.
+ * @param out_encoded_len        [out] Number of bytes written to out_packet. May be NULL.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int opus_pcm_to_frame_encode_packet(const short *pcm, int pcm_samples_per_channel,
+                                    int sample_rate, int channels,
+                                    int bitrate, int application,
+                                    unsigned char *out_packet, int out_packet_max_len,
+                                    int *out_encoded_len);
+
+/**
+ * @brief Encode PCM file to a single Opus frame file.
+ *
+ * @param input_file   Input file path containing 16-bit little-endian PCM.
+ * @param sample_rate  Sample rate in Hz (e.g. 16000/48000).
+ * @param channels    Channel count (1=mono, 2=stereo).
+ * @param bitrate     Target bitrate in bps (e.g. 64000).
+ * @param application Opus application: 0=VOIP, 1=AUDIO, 2=RESTRICTED_LOWDELAY.
+ * @param output_file Output Opus file path. If NULL, write to stdout.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int opus_pcm_to_frame_encode_file(const char *input_file, int sample_rate, int channels,
+                                  int bitrate, int application, const char *output_file);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/NLSF_VQ.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/NLSF_VQ.c
new file mode 100644 (file)
index 0000000..b83182a
--- /dev/null
@@ -0,0 +1,76 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main.h"
+
+/* Compute quantization errors for an LPC_order element input vector for a VQ codebook */
+void silk_NLSF_VQ(
+    opus_int32                  err_Q24[],                      /* O    Quantization errors [K]                     */
+    const opus_int16            in_Q15[],                       /* I    Input vectors to be quantized [LPC_order]   */
+    const opus_uint8            pCB_Q8[],                       /* I    Codebook vectors [K*LPC_order]              */
+    const opus_int16            pWght_Q9[],                     /* I    Codebook weights [K*LPC_order]              */
+    const opus_int              K,                              /* I    Number of codebook vectors                  */
+    const opus_int              LPC_order                       /* I    Number of LPCs                              */
+)
+{
+    opus_int         i, m;
+    opus_int32       diff_Q15, diffw_Q24, sum_error_Q24, pred_Q24;
+    const opus_int16 *w_Q9_ptr;
+    const opus_uint8 *cb_Q8_ptr;
+
+    celt_assert( ( LPC_order & 1 ) == 0 );
+
+    /* Loop over codebook */
+    cb_Q8_ptr = pCB_Q8;
+    w_Q9_ptr = pWght_Q9;
+    for( i = 0; i < K; i++ ) {
+        sum_error_Q24 = 0;
+        pred_Q24 = 0;
+        for( m = LPC_order-2; m >= 0; m -= 2 ) {
+            /* Compute weighted absolute predictive quantization error for index m + 1 */
+            diff_Q15 = silk_SUB_LSHIFT32( in_Q15[ m + 1 ], (opus_int32)cb_Q8_ptr[ m + 1 ], 7 ); /* range: [ -32767 : 32767 ]*/
+            diffw_Q24 = silk_SMULBB( diff_Q15, w_Q9_ptr[ m + 1 ] );
+            sum_error_Q24 = silk_ADD32( sum_error_Q24, silk_abs( silk_SUB_RSHIFT32( diffw_Q24, pred_Q24, 1 ) ) );
+            pred_Q24 = diffw_Q24;
+
+            /* Compute weighted absolute predictive quantization error for index m */
+            diff_Q15 = silk_SUB_LSHIFT32( in_Q15[ m ], (opus_int32)cb_Q8_ptr[ m ], 7 ); /* range: [ -32767 : 32767 ]*/
+            diffw_Q24 = silk_SMULBB( diff_Q15, w_Q9_ptr[ m ] );
+            sum_error_Q24 = silk_ADD32( sum_error_Q24, silk_abs( silk_SUB_RSHIFT32( diffw_Q24, pred_Q24, 1 ) ) );
+            pred_Q24 = diffw_Q24;
+
+            silk_assert( sum_error_Q24 >= 0 );
+        }
+        err_Q24[ i ] = sum_error_Q24;
+        cb_Q8_ptr += LPC_order;
+        w_Q9_ptr += LPC_order;
+    }
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/NLSF_del_dec_quant.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/NLSF_del_dec_quant.c
new file mode 100644 (file)
index 0000000..44a16ac
--- /dev/null
@@ -0,0 +1,215 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main.h"
+
+/* Delayed-decision quantizer for NLSF residuals */
+opus_int32 silk_NLSF_del_dec_quant(                             /* O    Returns RD value in Q25                     */
+    opus_int8                   indices[],                      /* O    Quantization indices [ order ]              */
+    const opus_int16            x_Q10[],                        /* I    Input [ order ]                             */
+    const opus_int16            w_Q5[],                         /* I    Weights [ order ]                           */
+    const opus_uint8            pred_coef_Q8[],                 /* I    Backward predictor coefs [ order ]          */
+    const opus_int16            ec_ix[],                        /* I    Indices to entropy coding tables [ order ]  */
+    const opus_uint8            ec_rates_Q5[],                  /* I    Rates []                                    */
+    const opus_int              quant_step_size_Q16,            /* I    Quantization step size                      */
+    const opus_int16            inv_quant_step_size_Q6,         /* I    Inverse quantization step size              */
+    const opus_int32            mu_Q20,                         /* I    R/D tradeoff                                */
+    const opus_int16            order                           /* I    Number of input values                      */
+)
+{
+    opus_int         i, j, nStates, ind_tmp, ind_min_max, ind_max_min, in_Q10, res_Q10;
+    opus_int         pred_Q10, diff_Q10, rate0_Q5, rate1_Q5;
+    opus_int16       out0_Q10, out1_Q10;
+    opus_int32       RD_tmp_Q25, min_Q25, min_max_Q25, max_min_Q25;
+    opus_int         ind_sort[         NLSF_QUANT_DEL_DEC_STATES ];
+    opus_int8        ind[              NLSF_QUANT_DEL_DEC_STATES ][ MAX_LPC_ORDER ];
+    opus_int16       prev_out_Q10[ 2 * NLSF_QUANT_DEL_DEC_STATES ];
+    opus_int32       RD_Q25[       2 * NLSF_QUANT_DEL_DEC_STATES ];
+    opus_int32       RD_min_Q25[       NLSF_QUANT_DEL_DEC_STATES ];
+    opus_int32       RD_max_Q25[       NLSF_QUANT_DEL_DEC_STATES ];
+    const opus_uint8 *rates_Q5;
+
+    opus_int out0_Q10_table[2 * NLSF_QUANT_MAX_AMPLITUDE_EXT];
+    opus_int out1_Q10_table[2 * NLSF_QUANT_MAX_AMPLITUDE_EXT];
+
+    for (i = -NLSF_QUANT_MAX_AMPLITUDE_EXT; i <= NLSF_QUANT_MAX_AMPLITUDE_EXT-1; i++)
+    {
+        out0_Q10 = silk_LSHIFT( i, 10 );
+        out1_Q10 = silk_ADD16( out0_Q10, 1024 );
+        if( i > 0 ) {
+            out0_Q10 = silk_SUB16( out0_Q10, SILK_FIX_CONST( NLSF_QUANT_LEVEL_ADJ, 10 ) );
+            out1_Q10 = silk_SUB16( out1_Q10, SILK_FIX_CONST( NLSF_QUANT_LEVEL_ADJ, 10 ) );
+        } else if( i == 0 ) {
+            out1_Q10 = silk_SUB16( out1_Q10, SILK_FIX_CONST( NLSF_QUANT_LEVEL_ADJ, 10 ) );
+        } else if( i == -1 ) {
+            out0_Q10 = silk_ADD16( out0_Q10, SILK_FIX_CONST( NLSF_QUANT_LEVEL_ADJ, 10 ) );
+        } else {
+            out0_Q10 = silk_ADD16( out0_Q10, SILK_FIX_CONST( NLSF_QUANT_LEVEL_ADJ, 10 ) );
+            out1_Q10 = silk_ADD16( out1_Q10, SILK_FIX_CONST( NLSF_QUANT_LEVEL_ADJ, 10 ) );
+        }
+        out0_Q10_table[ i + NLSF_QUANT_MAX_AMPLITUDE_EXT ] = silk_RSHIFT( silk_SMULBB( out0_Q10, quant_step_size_Q16 ), 16 );
+        out1_Q10_table[ i + NLSF_QUANT_MAX_AMPLITUDE_EXT ] = silk_RSHIFT( silk_SMULBB( out1_Q10, quant_step_size_Q16 ), 16 );
+    }
+
+    silk_assert( (NLSF_QUANT_DEL_DEC_STATES & (NLSF_QUANT_DEL_DEC_STATES-1)) == 0 );     /* must be power of two */
+
+    nStates = 1;
+    RD_Q25[ 0 ] = 0;
+    prev_out_Q10[ 0 ] = 0;
+    for( i = order - 1; i >= 0; i-- ) {
+        rates_Q5 = &ec_rates_Q5[ ec_ix[ i ] ];
+        in_Q10 = x_Q10[ i ];
+        for( j = 0; j < nStates; j++ ) {
+            pred_Q10 = silk_RSHIFT( silk_SMULBB( (opus_int16)pred_coef_Q8[ i ], prev_out_Q10[ j ] ), 8 );
+            res_Q10  = silk_SUB16( in_Q10, pred_Q10 );
+            ind_tmp  = silk_RSHIFT( silk_SMULBB( inv_quant_step_size_Q6, res_Q10 ), 16 );
+            ind_tmp  = silk_LIMIT( ind_tmp, -NLSF_QUANT_MAX_AMPLITUDE_EXT, NLSF_QUANT_MAX_AMPLITUDE_EXT-1 );
+            ind[ j ][ i ] = (opus_int8)ind_tmp;
+
+            /* compute outputs for ind_tmp and ind_tmp + 1 */
+            out0_Q10 = out0_Q10_table[ ind_tmp + NLSF_QUANT_MAX_AMPLITUDE_EXT ];
+            out1_Q10 = out1_Q10_table[ ind_tmp + NLSF_QUANT_MAX_AMPLITUDE_EXT ];
+
+            out0_Q10  = silk_ADD16( out0_Q10, pred_Q10 );
+            out1_Q10  = silk_ADD16( out1_Q10, pred_Q10 );
+            prev_out_Q10[ j           ] = out0_Q10;
+            prev_out_Q10[ j + nStates ] = out1_Q10;
+
+            /* compute RD for ind_tmp and ind_tmp + 1 */
+            if( ind_tmp + 1 >= NLSF_QUANT_MAX_AMPLITUDE ) {
+                if( ind_tmp + 1 == NLSF_QUANT_MAX_AMPLITUDE ) {
+                    rate0_Q5 = rates_Q5[ ind_tmp + NLSF_QUANT_MAX_AMPLITUDE ];
+                    rate1_Q5 = 280;
+                } else {
+                    rate0_Q5 = silk_SMLABB( 280 - 43 * NLSF_QUANT_MAX_AMPLITUDE, 43, ind_tmp );
+                    rate1_Q5 = silk_ADD16( rate0_Q5, 43 );
+                }
+            } else if( ind_tmp <= -NLSF_QUANT_MAX_AMPLITUDE ) {
+                if( ind_tmp == -NLSF_QUANT_MAX_AMPLITUDE ) {
+                    rate0_Q5 = 280;
+                    rate1_Q5 = rates_Q5[ ind_tmp + 1 + NLSF_QUANT_MAX_AMPLITUDE ];
+                } else {
+                    rate0_Q5 = silk_SMLABB( 280 - 43 * NLSF_QUANT_MAX_AMPLITUDE, -43, ind_tmp );
+                    rate1_Q5 = silk_SUB16( rate0_Q5, 43 );
+                }
+            } else {
+                rate0_Q5 = rates_Q5[ ind_tmp +     NLSF_QUANT_MAX_AMPLITUDE ];
+                rate1_Q5 = rates_Q5[ ind_tmp + 1 + NLSF_QUANT_MAX_AMPLITUDE ];
+            }
+            RD_tmp_Q25            = RD_Q25[ j ];
+            diff_Q10              = silk_SUB16( in_Q10, out0_Q10 );
+            RD_Q25[ j ]           = silk_SMLABB( silk_MLA( RD_tmp_Q25, silk_SMULBB( diff_Q10, diff_Q10 ), w_Q5[ i ] ), mu_Q20, rate0_Q5 );
+            diff_Q10              = silk_SUB16( in_Q10, out1_Q10 );
+            RD_Q25[ j + nStates ] = silk_SMLABB( silk_MLA( RD_tmp_Q25, silk_SMULBB( diff_Q10, diff_Q10 ), w_Q5[ i ] ), mu_Q20, rate1_Q5 );
+        }
+
+        if( nStates <= NLSF_QUANT_DEL_DEC_STATES/2 ) {
+            /* double number of states and copy */
+            for( j = 0; j < nStates; j++ ) {
+                ind[ j + nStates ][ i ] = ind[ j ][ i ] + 1;
+            }
+            nStates = silk_LSHIFT( nStates, 1 );
+            for( j = nStates; j < NLSF_QUANT_DEL_DEC_STATES; j++ ) {
+                ind[ j ][ i ] = ind[ j - nStates ][ i ];
+            }
+        } else {
+            /* sort lower and upper half of RD_Q25, pairwise */
+            for( j = 0; j < NLSF_QUANT_DEL_DEC_STATES; j++ ) {
+                if( RD_Q25[ j ] > RD_Q25[ j + NLSF_QUANT_DEL_DEC_STATES ] ) {
+                    RD_max_Q25[ j ]                         = RD_Q25[ j ];
+                    RD_min_Q25[ j ]                         = RD_Q25[ j + NLSF_QUANT_DEL_DEC_STATES ];
+                    RD_Q25[ j ]                             = RD_min_Q25[ j ];
+                    RD_Q25[ j + NLSF_QUANT_DEL_DEC_STATES ] = RD_max_Q25[ j ];
+                    /* swap prev_out values */
+                    out0_Q10 = prev_out_Q10[ j ];
+                    prev_out_Q10[ j ] = prev_out_Q10[ j + NLSF_QUANT_DEL_DEC_STATES ];
+                    prev_out_Q10[ j + NLSF_QUANT_DEL_DEC_STATES ] = out0_Q10;
+                    ind_sort[ j ] = j + NLSF_QUANT_DEL_DEC_STATES;
+                } else {
+                    RD_min_Q25[ j ] = RD_Q25[ j ];
+                    RD_max_Q25[ j ] = RD_Q25[ j + NLSF_QUANT_DEL_DEC_STATES ];
+                    ind_sort[ j ] = j;
+                }
+            }
+            /* compare the highest RD values of the winning half with the lowest one in the losing half, and copy if necessary */
+            /* afterwards ind_sort[] will contain the indices of the NLSF_QUANT_DEL_DEC_STATES winning RD values */
+            while( 1 ) {
+                min_max_Q25 = silk_int32_MAX;
+                max_min_Q25 = 0;
+                ind_min_max = 0;
+                ind_max_min = 0;
+                for( j = 0; j < NLSF_QUANT_DEL_DEC_STATES; j++ ) {
+                    if( min_max_Q25 > RD_max_Q25[ j ] ) {
+                        min_max_Q25 = RD_max_Q25[ j ];
+                        ind_min_max = j;
+                    }
+                    if( max_min_Q25 < RD_min_Q25[ j ] ) {
+                        max_min_Q25 = RD_min_Q25[ j ];
+                        ind_max_min = j;
+                    }
+                }
+                if( min_max_Q25 >= max_min_Q25 ) {
+                    break;
+                }
+                /* copy ind_min_max to ind_max_min */
+                ind_sort[     ind_max_min ] = ind_sort[     ind_min_max ] ^ NLSF_QUANT_DEL_DEC_STATES;
+                RD_Q25[       ind_max_min ] = RD_Q25[       ind_min_max + NLSF_QUANT_DEL_DEC_STATES ];
+                prev_out_Q10[ ind_max_min ] = prev_out_Q10[ ind_min_max + NLSF_QUANT_DEL_DEC_STATES ];
+                RD_min_Q25[   ind_max_min ] = 0;
+                RD_max_Q25[   ind_min_max ] = silk_int32_MAX;
+                silk_memcpy( ind[ ind_max_min ], ind[ ind_min_max ], MAX_LPC_ORDER * sizeof( opus_int8 ) );
+            }
+            /* increment index if it comes from the upper half */
+            for( j = 0; j < NLSF_QUANT_DEL_DEC_STATES; j++ ) {
+                ind[ j ][ i ] += silk_RSHIFT( ind_sort[ j ], NLSF_QUANT_DEL_DEC_STATES_LOG2 );
+            }
+        }
+    }
+
+    /* last sample: find winner, copy indices and return RD value */
+    ind_tmp = 0;
+    min_Q25 = silk_int32_MAX;
+    for( j = 0; j < 2 * NLSF_QUANT_DEL_DEC_STATES; j++ ) {
+        if( min_Q25 > RD_Q25[ j ] ) {
+            min_Q25 = RD_Q25[ j ];
+            ind_tmp = j;
+        }
+    }
+    for( j = 0; j < order; j++ ) {
+        indices[ j ] = ind[ ind_tmp & ( NLSF_QUANT_DEL_DEC_STATES - 1 ) ][ j ];
+        silk_assert( indices[ j ] >= -NLSF_QUANT_MAX_AMPLITUDE_EXT );
+        silk_assert( indices[ j ] <=  NLSF_QUANT_MAX_AMPLITUDE_EXT );
+    }
+    indices[ 0 ] += silk_RSHIFT( ind_tmp, NLSF_QUANT_DEL_DEC_STATES_LOG2 );
+    silk_assert( indices[ 0 ] <= NLSF_QUANT_MAX_AMPLITUDE_EXT );
+    silk_assert( min_Q25 >= 0 );
+    return min_Q25;
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/NLSF_encode.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/NLSF_encode.c
new file mode 100644 (file)
index 0000000..01ac7db
--- /dev/null
@@ -0,0 +1,124 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main.h"
+#include "stack_alloc.h"
+
+/***********************/
+/* NLSF vector encoder */
+/***********************/
+opus_int32 silk_NLSF_encode(                                    /* O    Returns RD value in Q25                     */
+          opus_int8             *NLSFIndices,                   /* I    Codebook path vector [ LPC_ORDER + 1 ]      */
+          opus_int16            *pNLSF_Q15,                     /* I/O  (Un)quantized NLSF vector [ LPC_ORDER ]     */
+    const silk_NLSF_CB_struct   *psNLSF_CB,                     /* I    Codebook object                             */
+    const opus_int16            *pW_Q2,                         /* I    NLSF weight vector [ LPC_ORDER ]            */
+    const opus_int              NLSF_mu_Q20,                    /* I    Rate weight for the RD optimization         */
+    const opus_int              nSurvivors,                     /* I    Max survivors after first stage             */
+    const opus_int              signalType                      /* I    Signal type: 0/1/2                          */
+)
+{
+    opus_int         i, s, ind1, bestIndex, prob_Q8, bits_q7;
+    opus_int32       W_tmp_Q9, ret;
+    VARDECL( opus_int32, err_Q24 );
+    VARDECL( opus_int32, RD_Q25 );
+    VARDECL( opus_int, tempIndices1 );
+    VARDECL( opus_int8, tempIndices2 );
+    opus_int16       res_Q10[      MAX_LPC_ORDER ];
+    opus_int16       NLSF_tmp_Q15[ MAX_LPC_ORDER ];
+    opus_int16       W_adj_Q5[     MAX_LPC_ORDER ];
+    opus_uint8       pred_Q8[      MAX_LPC_ORDER ];
+    opus_int16       ec_ix[        MAX_LPC_ORDER ];
+    const opus_uint8 *pCB_element, *iCDF_ptr;
+    const opus_int16 *pCB_Wght_Q9;
+    SAVE_STACK;
+
+    celt_assert( signalType >= 0 && signalType <= 2 );
+    silk_assert( NLSF_mu_Q20 <= 32767 && NLSF_mu_Q20 >= 0 );
+
+    /* NLSF stabilization */
+    silk_NLSF_stabilize( pNLSF_Q15, psNLSF_CB->deltaMin_Q15, psNLSF_CB->order );
+
+    /* First stage: VQ */
+    ALLOC( err_Q24, psNLSF_CB->nVectors, opus_int32 );
+    silk_NLSF_VQ( err_Q24, pNLSF_Q15, psNLSF_CB->CB1_NLSF_Q8, psNLSF_CB->CB1_Wght_Q9, psNLSF_CB->nVectors, psNLSF_CB->order );
+
+    /* Sort the quantization errors */
+    ALLOC( tempIndices1, nSurvivors, opus_int );
+    silk_insertion_sort_increasing( err_Q24, tempIndices1, psNLSF_CB->nVectors, nSurvivors );
+
+    ALLOC( RD_Q25, nSurvivors, opus_int32 );
+    ALLOC( tempIndices2, nSurvivors * MAX_LPC_ORDER, opus_int8 );
+
+    /* Loop over survivors */
+    for( s = 0; s < nSurvivors; s++ ) {
+        ind1 = tempIndices1[ s ];
+
+        /* Residual after first stage */
+        pCB_element = &psNLSF_CB->CB1_NLSF_Q8[ ind1 * psNLSF_CB->order ];
+        pCB_Wght_Q9 = &psNLSF_CB->CB1_Wght_Q9[ ind1 * psNLSF_CB->order ];
+        for( i = 0; i < psNLSF_CB->order; i++ ) {
+            NLSF_tmp_Q15[ i ] = silk_LSHIFT16( (opus_int16)pCB_element[ i ], 7 );
+            W_tmp_Q9 = pCB_Wght_Q9[ i ];
+            res_Q10[ i ] = (opus_int16)silk_RSHIFT( silk_SMULBB( pNLSF_Q15[ i ] - NLSF_tmp_Q15[ i ], W_tmp_Q9 ), 14 );
+            W_adj_Q5[ i ] = silk_DIV32_varQ( (opus_int32)pW_Q2[ i ], silk_SMULBB( W_tmp_Q9, W_tmp_Q9 ), 21 );
+        }
+
+        /* Unpack entropy table indices and predictor for current CB1 index */
+        silk_NLSF_unpack( ec_ix, pred_Q8, psNLSF_CB, ind1 );
+
+        /* Trellis quantizer */
+        RD_Q25[ s ] = silk_NLSF_del_dec_quant( &tempIndices2[ s * MAX_LPC_ORDER ], res_Q10, W_adj_Q5, pred_Q8, ec_ix,
+            psNLSF_CB->ec_Rates_Q5, psNLSF_CB->quantStepSize_Q16, psNLSF_CB->invQuantStepSize_Q6, NLSF_mu_Q20, psNLSF_CB->order );
+
+        /* Add rate for first stage */
+        iCDF_ptr = &psNLSF_CB->CB1_iCDF[ ( signalType >> 1 ) * psNLSF_CB->nVectors ];
+        if( ind1 == 0 ) {
+            prob_Q8 = 256 - iCDF_ptr[ ind1 ];
+        } else {
+            prob_Q8 = iCDF_ptr[ ind1 - 1 ] - iCDF_ptr[ ind1 ];
+        }
+        bits_q7 = ( 8 << 7 ) - silk_lin2log( prob_Q8 );
+        RD_Q25[ s ] = silk_SMLABB( RD_Q25[ s ], bits_q7, silk_RSHIFT( NLSF_mu_Q20, 2 ) );
+    }
+
+    /* Find the lowest rate-distortion error */
+    silk_insertion_sort_increasing( RD_Q25, &bestIndex, nSurvivors, 1 );
+
+    NLSFIndices[ 0 ] = (opus_int8)tempIndices1[ bestIndex ];
+    silk_memcpy( &NLSFIndices[ 1 ], &tempIndices2[ bestIndex * MAX_LPC_ORDER ], psNLSF_CB->order * sizeof( opus_int8 ) );
+
+    /* Decode */
+    silk_NLSF_decode( pNLSF_Q15, NLSFIndices, psNLSF_CB );
+
+    ret = RD_Q25[ 0 ];
+    RESTORE_STACK;
+    return ret;
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/NSQ.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/NSQ.c
new file mode 100644 (file)
index 0000000..4ffdf3d
--- /dev/null
@@ -0,0 +1,437 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main.h"
+#include "stack_alloc.h"
+#include "NSQ.h"
+
+
+static OPUS_INLINE void silk_nsq_scale_states(
+    const silk_encoder_state *psEncC,           /* I    Encoder State                   */
+    silk_nsq_state      *NSQ,                   /* I/O  NSQ state                       */
+    const opus_int16    x16[],                  /* I    input                           */
+    opus_int32          x_sc_Q10[],             /* O    input scaled with 1/Gain        */
+    const opus_int16    sLTP[],                 /* I    re-whitened LTP state in Q0     */
+    opus_int32          sLTP_Q15[],             /* O    LTP state matching scaled input */
+    opus_int            subfr,                  /* I    subframe number                 */
+    const opus_int      LTP_scale_Q14,          /* I                                    */
+    const opus_int32    Gains_Q16[ MAX_NB_SUBFR ], /* I                                 */
+    const opus_int      pitchL[ MAX_NB_SUBFR ], /* I    Pitch lag                       */
+    const opus_int      signal_type             /* I    Signal type                     */
+);
+
+#if !defined(OPUS_X86_MAY_HAVE_SSE4_1)
+static OPUS_INLINE void silk_noise_shape_quantizer(
+    silk_nsq_state      *NSQ,                   /* I/O  NSQ state                       */
+    opus_int            signalType,             /* I    Signal type                     */
+    const opus_int32    x_sc_Q10[],             /* I                                    */
+    opus_int8           pulses[],               /* O                                    */
+    opus_int16          xq[],                   /* O                                    */
+    opus_int32          sLTP_Q15[],             /* I/O  LTP state                       */
+    const opus_int16    a_Q12[],                /* I    Short term prediction coefs     */
+    const opus_int16    b_Q14[],                /* I    Long term prediction coefs      */
+    const opus_int16    AR_shp_Q13[],           /* I    Noise shaping AR coefs          */
+    opus_int            lag,                    /* I    Pitch lag                       */
+    opus_int32          HarmShapeFIRPacked_Q14, /* I                                    */
+    opus_int            Tilt_Q14,               /* I    Spectral tilt                   */
+    opus_int32          LF_shp_Q14,             /* I                                    */
+    opus_int32          Gain_Q16,               /* I                                    */
+    opus_int            Lambda_Q10,             /* I                                    */
+    opus_int            offset_Q10,             /* I                                    */
+    opus_int            length,                 /* I    Input length                    */
+    opus_int            shapingLPCOrder,        /* I    Noise shaping AR filter order   */
+    opus_int            predictLPCOrder,        /* I    Prediction filter order         */
+    int                 arch                    /* I    Architecture                    */
+);
+#endif
+
+void silk_NSQ_c
+(
+    const silk_encoder_state    *psEncC,                                      /* I    Encoder State                   */
+    silk_nsq_state              *NSQ,                                         /* I/O  NSQ state                       */
+    SideInfoIndices             *psIndices,                                   /* I/O  Quantization Indices            */
+    const opus_int16            x16[],                                        /* I    Input                           */
+    opus_int8                   pulses[],                                     /* O    Quantized pulse signal          */
+    const opus_int16            *PredCoef_Q12,                                /* I    Short term prediction coefs     */
+    const opus_int16            LTPCoef_Q14[ LTP_ORDER * MAX_NB_SUBFR ],      /* I    Long term prediction coefs      */
+    const opus_int16            AR_Q13[ MAX_NB_SUBFR * MAX_SHAPE_LPC_ORDER ], /* I    Noise shaping coefs             */
+    const opus_int              HarmShapeGain_Q14[ MAX_NB_SUBFR ],            /* I    Long term shaping coefs         */
+    const opus_int              Tilt_Q14[ MAX_NB_SUBFR ],                     /* I    Spectral tilt                   */
+    const opus_int32            LF_shp_Q14[ MAX_NB_SUBFR ],                   /* I    Low frequency shaping coefs     */
+    const opus_int32            Gains_Q16[ MAX_NB_SUBFR ],                    /* I    Quantization step sizes         */
+    const opus_int              pitchL[ MAX_NB_SUBFR ],                       /* I    Pitch lags                      */
+    const opus_int              Lambda_Q10,                                   /* I    Rate/distortion tradeoff        */
+    const opus_int              LTP_scale_Q14                                 /* I    LTP state scaling               */
+)
+{
+    opus_int            k, lag, start_idx, LSF_interpolation_flag;
+    const opus_int16    *A_Q12, *B_Q14, *AR_shp_Q13;
+    opus_int16          *pxq;
+    VARDECL( opus_int32, sLTP_Q15 );
+    VARDECL( opus_int16, sLTP );
+    opus_int32          HarmShapeFIRPacked_Q14;
+    opus_int            offset_Q10;
+    VARDECL( opus_int32, x_sc_Q10 );
+    SAVE_STACK;
+
+    NSQ->rand_seed = psIndices->Seed;
+
+    /* Set unvoiced lag to the previous one, overwrite later for voiced */
+    lag = NSQ->lagPrev;
+
+    silk_assert( NSQ->prev_gain_Q16 != 0 );
+
+    offset_Q10 = silk_Quantization_Offsets_Q10[ psIndices->signalType >> 1 ][ psIndices->quantOffsetType ];
+
+    if( psIndices->NLSFInterpCoef_Q2 == 4 ) {
+        LSF_interpolation_flag = 0;
+    } else {
+        LSF_interpolation_flag = 1;
+    }
+
+    ALLOC( sLTP_Q15, psEncC->ltp_mem_length + psEncC->frame_length, opus_int32 );
+    ALLOC( sLTP, psEncC->ltp_mem_length + psEncC->frame_length, opus_int16 );
+    ALLOC( x_sc_Q10, psEncC->subfr_length, opus_int32 );
+    /* Set up pointers to start of sub frame */
+    NSQ->sLTP_shp_buf_idx = psEncC->ltp_mem_length;
+    NSQ->sLTP_buf_idx     = psEncC->ltp_mem_length;
+    pxq                   = &NSQ->xq[ psEncC->ltp_mem_length ];
+    for( k = 0; k < psEncC->nb_subfr; k++ ) {
+        A_Q12      = &PredCoef_Q12[ (( k >> 1 ) | ( 1 - LSF_interpolation_flag )) * MAX_LPC_ORDER ];
+        B_Q14      = &LTPCoef_Q14[ k * LTP_ORDER ];
+        AR_shp_Q13 = &AR_Q13[ k * MAX_SHAPE_LPC_ORDER ];
+
+        /* Noise shape parameters */
+        silk_assert( HarmShapeGain_Q14[ k ] >= 0 );
+        HarmShapeFIRPacked_Q14  =                          silk_RSHIFT( HarmShapeGain_Q14[ k ], 2 );
+        HarmShapeFIRPacked_Q14 |= silk_LSHIFT( (opus_int32)silk_RSHIFT( HarmShapeGain_Q14[ k ], 1 ), 16 );
+
+        NSQ->rewhite_flag = 0;
+        if( psIndices->signalType == TYPE_VOICED ) {
+            /* Voiced */
+            lag = pitchL[ k ];
+
+            /* Re-whitening */
+            if( ( k & ( 3 - silk_LSHIFT( LSF_interpolation_flag, 1 ) ) ) == 0 ) {
+                /* Rewhiten with new A coefs */
+                start_idx = psEncC->ltp_mem_length - lag - psEncC->predictLPCOrder - LTP_ORDER / 2;
+                celt_assert( start_idx > 0 );
+
+                silk_LPC_analysis_filter( &sLTP[ start_idx ], &NSQ->xq[ start_idx + k * psEncC->subfr_length ],
+                    A_Q12, psEncC->ltp_mem_length - start_idx, psEncC->predictLPCOrder, psEncC->arch );
+
+                NSQ->rewhite_flag = 1;
+                NSQ->sLTP_buf_idx = psEncC->ltp_mem_length;
+            }
+        }
+
+        silk_nsq_scale_states( psEncC, NSQ, x16, x_sc_Q10, sLTP, sLTP_Q15, k, LTP_scale_Q14, Gains_Q16, pitchL, psIndices->signalType );
+
+        silk_noise_shape_quantizer( NSQ, psIndices->signalType, x_sc_Q10, pulses, pxq, sLTP_Q15, A_Q12, B_Q14,
+            AR_shp_Q13, lag, HarmShapeFIRPacked_Q14, Tilt_Q14[ k ], LF_shp_Q14[ k ], Gains_Q16[ k ], Lambda_Q10,
+            offset_Q10, psEncC->subfr_length, psEncC->shapingLPCOrder, psEncC->predictLPCOrder, psEncC->arch );
+
+        x16    += psEncC->subfr_length;
+        pulses += psEncC->subfr_length;
+        pxq    += psEncC->subfr_length;
+    }
+
+    /* Update lagPrev for next frame */
+    NSQ->lagPrev = pitchL[ psEncC->nb_subfr - 1 ];
+
+    /* Save quantized speech and noise shaping signals */
+    silk_memmove( NSQ->xq,           &NSQ->xq[           psEncC->frame_length ], psEncC->ltp_mem_length * sizeof( opus_int16 ) );
+    silk_memmove( NSQ->sLTP_shp_Q14, &NSQ->sLTP_shp_Q14[ psEncC->frame_length ], psEncC->ltp_mem_length * sizeof( opus_int32 ) );
+    RESTORE_STACK;
+}
+
+/******************************/
+/* silk_noise_shape_quantizer */
+/******************************/
+
+#if !defined(OPUS_X86_MAY_HAVE_SSE4_1)
+static OPUS_INLINE
+#endif
+void silk_noise_shape_quantizer(
+    silk_nsq_state      *NSQ,                   /* I/O  NSQ state                       */
+    opus_int            signalType,             /* I    Signal type                     */
+    const opus_int32    x_sc_Q10[],             /* I                                    */
+    opus_int8           pulses[],               /* O                                    */
+    opus_int16          xq[],                   /* O                                    */
+    opus_int32          sLTP_Q15[],             /* I/O  LTP state                       */
+    const opus_int16    a_Q12[],                /* I    Short term prediction coefs     */
+    const opus_int16    b_Q14[],                /* I    Long term prediction coefs      */
+    const opus_int16    AR_shp_Q13[],           /* I    Noise shaping AR coefs          */
+    opus_int            lag,                    /* I    Pitch lag                       */
+    opus_int32          HarmShapeFIRPacked_Q14, /* I                                    */
+    opus_int            Tilt_Q14,               /* I    Spectral tilt                   */
+    opus_int32          LF_shp_Q14,             /* I                                    */
+    opus_int32          Gain_Q16,               /* I                                    */
+    opus_int            Lambda_Q10,             /* I                                    */
+    opus_int            offset_Q10,             /* I                                    */
+    opus_int            length,                 /* I    Input length                    */
+    opus_int            shapingLPCOrder,        /* I    Noise shaping AR filter order   */
+    opus_int            predictLPCOrder,        /* I    Prediction filter order         */
+    int                 arch                    /* I    Architecture                    */
+)
+{
+    opus_int     i;
+    opus_int32   LTP_pred_Q13, LPC_pred_Q10, n_AR_Q12, n_LTP_Q13;
+    opus_int32   n_LF_Q12, r_Q10, rr_Q10, q1_Q0, q1_Q10, q2_Q10, rd1_Q20, rd2_Q20;
+    opus_int32   exc_Q14, LPC_exc_Q14, xq_Q14, Gain_Q10;
+    opus_int32   tmp1, tmp2, sLF_AR_shp_Q14;
+    opus_int32   *psLPC_Q14, *shp_lag_ptr, *pred_lag_ptr;
+#ifdef silk_short_prediction_create_arch_coef
+    opus_int32   a_Q12_arch[MAX_LPC_ORDER];
+#endif
+
+    shp_lag_ptr  = &NSQ->sLTP_shp_Q14[ NSQ->sLTP_shp_buf_idx - lag + HARM_SHAPE_FIR_TAPS / 2 ];
+    pred_lag_ptr = &sLTP_Q15[ NSQ->sLTP_buf_idx - lag + LTP_ORDER / 2 ];
+    Gain_Q10     = silk_RSHIFT( Gain_Q16, 6 );
+
+    /* Set up short term AR state */
+    psLPC_Q14 = &NSQ->sLPC_Q14[ NSQ_LPC_BUF_LENGTH - 1 ];
+
+#ifdef silk_short_prediction_create_arch_coef
+    silk_short_prediction_create_arch_coef(a_Q12_arch, a_Q12, predictLPCOrder);
+#endif
+
+    for( i = 0; i < length; i++ ) {
+        /* Generate dither */
+        NSQ->rand_seed = silk_RAND( NSQ->rand_seed );
+
+        /* Short-term prediction */
+        LPC_pred_Q10 = silk_noise_shape_quantizer_short_prediction(psLPC_Q14, a_Q12, a_Q12_arch, predictLPCOrder, arch);
+
+        /* Long-term prediction */
+        if( signalType == TYPE_VOICED ) {
+            /* Unrolled loop */
+            /* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */
+            LTP_pred_Q13 = 2;
+            LTP_pred_Q13 = silk_SMLAWB( LTP_pred_Q13, pred_lag_ptr[  0 ], b_Q14[ 0 ] );
+            LTP_pred_Q13 = silk_SMLAWB( LTP_pred_Q13, pred_lag_ptr[ -1 ], b_Q14[ 1 ] );
+            LTP_pred_Q13 = silk_SMLAWB( LTP_pred_Q13, pred_lag_ptr[ -2 ], b_Q14[ 2 ] );
+            LTP_pred_Q13 = silk_SMLAWB( LTP_pred_Q13, pred_lag_ptr[ -3 ], b_Q14[ 3 ] );
+            LTP_pred_Q13 = silk_SMLAWB( LTP_pred_Q13, pred_lag_ptr[ -4 ], b_Q14[ 4 ] );
+            pred_lag_ptr++;
+        } else {
+            LTP_pred_Q13 = 0;
+        }
+
+        /* Noise shape feedback */
+        celt_assert( ( shapingLPCOrder & 1 ) == 0 );   /* check that order is even */
+        n_AR_Q12 = silk_NSQ_noise_shape_feedback_loop(&NSQ->sDiff_shp_Q14, NSQ->sAR2_Q14, AR_shp_Q13, shapingLPCOrder, arch);
+
+        n_AR_Q12 = silk_SMLAWB( n_AR_Q12, NSQ->sLF_AR_shp_Q14, Tilt_Q14 );
+
+        n_LF_Q12 = silk_SMULWB( NSQ->sLTP_shp_Q14[ NSQ->sLTP_shp_buf_idx - 1 ], LF_shp_Q14 );
+        n_LF_Q12 = silk_SMLAWT( n_LF_Q12, NSQ->sLF_AR_shp_Q14, LF_shp_Q14 );
+
+        celt_assert( lag > 0 || signalType != TYPE_VOICED );
+
+        /* Combine prediction and noise shaping signals */
+        tmp1 = silk_SUB32_ovflw( silk_LSHIFT32( LPC_pred_Q10, 2 ), n_AR_Q12 );  /* Q12 */
+        tmp1 = silk_SUB32_ovflw( tmp1, n_LF_Q12 );                              /* Q12 */
+        if( lag > 0 ) {
+            /* Symmetric, packed FIR coefficients */
+            n_LTP_Q13 = silk_SMULWB( silk_ADD_SAT32( shp_lag_ptr[ 0 ], shp_lag_ptr[ -2 ] ), HarmShapeFIRPacked_Q14 );
+            n_LTP_Q13 = silk_SMLAWT( n_LTP_Q13, shp_lag_ptr[ -1 ],                      HarmShapeFIRPacked_Q14 );
+            n_LTP_Q13 = silk_LSHIFT( n_LTP_Q13, 1 );
+            shp_lag_ptr++;
+
+            tmp2 = silk_SUB32( LTP_pred_Q13, n_LTP_Q13 );                       /* Q13 */
+            tmp1 = silk_ADD32_ovflw( tmp2, silk_LSHIFT32( tmp1, 1 ) );          /* Q13 */
+            tmp1 = silk_RSHIFT_ROUND( tmp1, 3 );                                /* Q10 */
+        } else {
+            tmp1 = silk_RSHIFT_ROUND( tmp1, 2 );                                /* Q10 */
+        }
+
+        r_Q10 = silk_SUB32( x_sc_Q10[ i ], tmp1 );                              /* residual error Q10 */
+
+        /* Flip sign depending on dither */
+        if( NSQ->rand_seed < 0 ) {
+            r_Q10 = -r_Q10;
+        }
+        r_Q10 = silk_LIMIT_32( r_Q10, -(31 << 10), 30 << 10 );
+
+        /* Find two quantization level candidates and measure their rate-distortion */
+        q1_Q10 = silk_SUB32( r_Q10, offset_Q10 );
+        q1_Q0 = silk_RSHIFT( q1_Q10, 10 );
+        if (Lambda_Q10 > 2048) {
+            /* For aggressive RDO, the bias becomes more than one pulse. */
+            int rdo_offset = Lambda_Q10/2 - 512;
+            if (q1_Q10 > rdo_offset) {
+                q1_Q0 = silk_RSHIFT( q1_Q10 - rdo_offset, 10 );
+            } else if (q1_Q10 < -rdo_offset) {
+                q1_Q0 = silk_RSHIFT( q1_Q10 + rdo_offset, 10 );
+            } else if (q1_Q10 < 0) {
+                q1_Q0 = -1;
+            } else {
+                q1_Q0 = 0;
+            }
+        }
+        if( q1_Q0 > 0 ) {
+            q1_Q10  = silk_SUB32( silk_LSHIFT( q1_Q0, 10 ), QUANT_LEVEL_ADJUST_Q10 );
+            q1_Q10  = silk_ADD32( q1_Q10, offset_Q10 );
+            q2_Q10  = silk_ADD32( q1_Q10, 1024 );
+            rd1_Q20 = silk_SMULBB( q1_Q10, Lambda_Q10 );
+            rd2_Q20 = silk_SMULBB( q2_Q10, Lambda_Q10 );
+        } else if( q1_Q0 == 0 ) {
+            q1_Q10  = offset_Q10;
+            q2_Q10  = silk_ADD32( q1_Q10, 1024 - QUANT_LEVEL_ADJUST_Q10 );
+            rd1_Q20 = silk_SMULBB( q1_Q10, Lambda_Q10 );
+            rd2_Q20 = silk_SMULBB( q2_Q10, Lambda_Q10 );
+        } else if( q1_Q0 == -1 ) {
+            q2_Q10  = offset_Q10;
+            q1_Q10  = silk_SUB32( q2_Q10, 1024 - QUANT_LEVEL_ADJUST_Q10 );
+            rd1_Q20 = silk_SMULBB( -q1_Q10, Lambda_Q10 );
+            rd2_Q20 = silk_SMULBB(  q2_Q10, Lambda_Q10 );
+        } else {            /* Q1_Q0 < -1 */
+            q1_Q10  = silk_ADD32( silk_LSHIFT( q1_Q0, 10 ), QUANT_LEVEL_ADJUST_Q10 );
+            q1_Q10  = silk_ADD32( q1_Q10, offset_Q10 );
+            q2_Q10  = silk_ADD32( q1_Q10, 1024 );
+            rd1_Q20 = silk_SMULBB( -q1_Q10, Lambda_Q10 );
+            rd2_Q20 = silk_SMULBB( -q2_Q10, Lambda_Q10 );
+        }
+        rr_Q10  = silk_SUB32( r_Q10, q1_Q10 );
+        rd1_Q20 = silk_SMLABB( rd1_Q20, rr_Q10, rr_Q10 );
+        rr_Q10  = silk_SUB32( r_Q10, q2_Q10 );
+        rd2_Q20 = silk_SMLABB( rd2_Q20, rr_Q10, rr_Q10 );
+
+        if( rd2_Q20 < rd1_Q20 ) {
+            q1_Q10 = q2_Q10;
+        }
+
+        pulses[ i ] = (opus_int8)silk_RSHIFT_ROUND( q1_Q10, 10 );
+
+        /* Excitation */
+        exc_Q14 = silk_LSHIFT( q1_Q10, 4 );
+        if ( NSQ->rand_seed < 0 ) {
+           exc_Q14 = -exc_Q14;
+        }
+
+        /* Add predictions */
+        LPC_exc_Q14 = silk_ADD_LSHIFT32( exc_Q14, LTP_pred_Q13, 1 );
+        xq_Q14      = silk_ADD32_ovflw( LPC_exc_Q14, silk_LSHIFT32( LPC_pred_Q10, 4 ) );
+
+        /* Scale XQ back to normal level before saving */
+        xq[ i ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( silk_SMULWW( xq_Q14, Gain_Q10 ), 8 ) );
+
+        /* Update states */
+        psLPC_Q14++;
+        *psLPC_Q14 = xq_Q14;
+        NSQ->sDiff_shp_Q14 = silk_SUB32_ovflw( xq_Q14, silk_LSHIFT32( x_sc_Q10[ i ], 4 ) );
+        sLF_AR_shp_Q14 = silk_SUB32_ovflw( NSQ->sDiff_shp_Q14, silk_LSHIFT32( n_AR_Q12, 2 ) );
+        NSQ->sLF_AR_shp_Q14 = sLF_AR_shp_Q14;
+
+        NSQ->sLTP_shp_Q14[ NSQ->sLTP_shp_buf_idx ] = silk_SUB32_ovflw(sLF_AR_shp_Q14, silk_LSHIFT32(n_LF_Q12, 2));
+        sLTP_Q15[ NSQ->sLTP_buf_idx ] = silk_LSHIFT( LPC_exc_Q14, 1 );
+        NSQ->sLTP_shp_buf_idx++;
+        NSQ->sLTP_buf_idx++;
+
+        /* Make dither dependent on quantized signal */
+        NSQ->rand_seed = silk_ADD32_ovflw( NSQ->rand_seed, pulses[ i ] );
+    }
+
+    /* Update LPC synth buffer */
+    silk_memcpy( NSQ->sLPC_Q14, &NSQ->sLPC_Q14[ length ], NSQ_LPC_BUF_LENGTH * sizeof( opus_int32 ) );
+}
+
+static OPUS_INLINE void silk_nsq_scale_states(
+    const silk_encoder_state *psEncC,           /* I    Encoder State                   */
+    silk_nsq_state      *NSQ,                   /* I/O  NSQ state                       */
+    const opus_int16    x16[],                  /* I    input                           */
+    opus_int32          x_sc_Q10[],             /* O    input scaled with 1/Gain        */
+    const opus_int16    sLTP[],                 /* I    re-whitened LTP state in Q0     */
+    opus_int32          sLTP_Q15[],             /* O    LTP state matching scaled input */
+    opus_int            subfr,                  /* I    subframe number                 */
+    const opus_int      LTP_scale_Q14,          /* I                                    */
+    const opus_int32    Gains_Q16[ MAX_NB_SUBFR ], /* I                                 */
+    const opus_int      pitchL[ MAX_NB_SUBFR ], /* I    Pitch lag                       */
+    const opus_int      signal_type             /* I    Signal type                     */
+)
+{
+    opus_int   i, lag;
+    opus_int32 gain_adj_Q16, inv_gain_Q31, inv_gain_Q26;
+
+    lag          = pitchL[ subfr ];
+    inv_gain_Q31 = silk_INVERSE32_varQ( silk_max( Gains_Q16[ subfr ], 1 ), 47 );
+    silk_assert( inv_gain_Q31 != 0 );
+
+    /* Scale input */
+    inv_gain_Q26 = silk_RSHIFT_ROUND( inv_gain_Q31, 5 );
+    for( i = 0; i < psEncC->subfr_length; i++ ) {
+        x_sc_Q10[ i ] = silk_SMULWW( x16[ i ], inv_gain_Q26 );
+    }
+
+    /* After rewhitening the LTP state is un-scaled, so scale with inv_gain_Q16 */
+    if( NSQ->rewhite_flag ) {
+        if( subfr == 0 ) {
+            /* Do LTP downscaling */
+            inv_gain_Q31 = silk_LSHIFT( silk_SMULWB( inv_gain_Q31, LTP_scale_Q14 ), 2 );
+        }
+        for( i = NSQ->sLTP_buf_idx - lag - LTP_ORDER / 2; i < NSQ->sLTP_buf_idx; i++ ) {
+            silk_assert( i < MAX_FRAME_LENGTH );
+            sLTP_Q15[ i ] = silk_SMULWB( inv_gain_Q31, sLTP[ i ] );
+        }
+    }
+
+    /* Adjust for changing gain */
+    if( Gains_Q16[ subfr ] != NSQ->prev_gain_Q16 ) {
+        gain_adj_Q16 =  silk_DIV32_varQ( NSQ->prev_gain_Q16, Gains_Q16[ subfr ], 16 );
+
+        /* Scale long-term shaping state */
+        for( i = NSQ->sLTP_shp_buf_idx - psEncC->ltp_mem_length; i < NSQ->sLTP_shp_buf_idx; i++ ) {
+            NSQ->sLTP_shp_Q14[ i ] = silk_SMULWW( gain_adj_Q16, NSQ->sLTP_shp_Q14[ i ] );
+        }
+
+        /* Scale long-term prediction state */
+        if( signal_type == TYPE_VOICED && NSQ->rewhite_flag == 0 ) {
+            for( i = NSQ->sLTP_buf_idx - lag - LTP_ORDER / 2; i < NSQ->sLTP_buf_idx; i++ ) {
+                sLTP_Q15[ i ] = silk_SMULWW( gain_adj_Q16, sLTP_Q15[ i ] );
+            }
+        }
+
+        NSQ->sLF_AR_shp_Q14 = silk_SMULWW( gain_adj_Q16, NSQ->sLF_AR_shp_Q14 );
+        NSQ->sDiff_shp_Q14 = silk_SMULWW( gain_adj_Q16, NSQ->sDiff_shp_Q14 );
+
+        /* Scale short-term prediction and shaping states */
+        for( i = 0; i < NSQ_LPC_BUF_LENGTH; i++ ) {
+            NSQ->sLPC_Q14[ i ] = silk_SMULWW( gain_adj_Q16, NSQ->sLPC_Q14[ i ] );
+        }
+        for( i = 0; i < MAX_SHAPE_LPC_ORDER; i++ ) {
+            NSQ->sAR2_Q14[ i ] = silk_SMULWW( gain_adj_Q16, NSQ->sAR2_Q14[ i ] );
+        }
+
+        /* Save inverse gain */
+        NSQ->prev_gain_Q16 = Gains_Q16[ subfr ];
+    }
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/NSQ_del_dec.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/NSQ_del_dec.c
new file mode 100644 (file)
index 0000000..31ac11a
--- /dev/null
@@ -0,0 +1,730 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main.h"
+#include "stack_alloc.h"
+#include "NSQ.h"
+
+
+typedef struct {
+    opus_int32 sLPC_Q14[ MAX_SUB_FRAME_LENGTH + NSQ_LPC_BUF_LENGTH ];
+    opus_int32 RandState[ DECISION_DELAY ];
+    opus_int32 Q_Q10[     DECISION_DELAY ];
+    opus_int32 Xq_Q14[    DECISION_DELAY ];
+    opus_int32 Pred_Q15[  DECISION_DELAY ];
+    opus_int32 Shape_Q14[ DECISION_DELAY ];
+    opus_int32 sAR2_Q14[ MAX_SHAPE_LPC_ORDER ];
+    opus_int32 LF_AR_Q14;
+    opus_int32 Diff_Q14;
+    opus_int32 Seed;
+    opus_int32 SeedInit;
+    opus_int32 RD_Q10;
+} NSQ_del_dec_struct;
+
+typedef struct {
+    opus_int32 Q_Q10;
+    opus_int32 RD_Q10;
+    opus_int32 xq_Q14;
+    opus_int32 LF_AR_Q14;
+    opus_int32 Diff_Q14;
+    opus_int32 sLTP_shp_Q14;
+    opus_int32 LPC_exc_Q14;
+} NSQ_sample_struct;
+
+typedef NSQ_sample_struct  NSQ_sample_pair[ 2 ];
+
+static OPUS_INLINE void silk_nsq_del_dec_scale_states(
+    const silk_encoder_state *psEncC,               /* I    Encoder State                       */
+    silk_nsq_state      *NSQ,                       /* I/O  NSQ state                           */
+    NSQ_del_dec_struct  psDelDec[],                 /* I/O  Delayed decision states             */
+    const opus_int16    x16[],                      /* I    Input                               */
+    opus_int32          x_sc_Q10[],                 /* O    Input scaled with 1/Gain in Q10     */
+    const opus_int16    sLTP[],                     /* I    Re-whitened LTP state in Q0         */
+    opus_int32          sLTP_Q15[],                 /* O    LTP state matching scaled input     */
+    opus_int            subfr,                      /* I    Subframe number                     */
+    opus_int            nStatesDelayedDecision,     /* I    Number of del dec states            */
+    const opus_int      LTP_scale_Q14,              /* I    LTP state scaling                   */
+    const opus_int32    Gains_Q16[ MAX_NB_SUBFR ],  /* I                                        */
+    const opus_int      pitchL[ MAX_NB_SUBFR ],     /* I    Pitch lag                           */
+    const opus_int      signal_type,                /* I    Signal type                         */
+    const opus_int      decisionDelay               /* I    Decision delay                      */
+);
+
+/******************************************/
+/* Noise shape quantizer for one subframe */
+/******************************************/
+static OPUS_INLINE void silk_noise_shape_quantizer_del_dec(
+    silk_nsq_state      *NSQ,                   /* I/O  NSQ state                           */
+    NSQ_del_dec_struct  psDelDec[],             /* I/O  Delayed decision states             */
+    opus_int            signalType,             /* I    Signal type                         */
+    const opus_int32    x_Q10[],                /* I                                        */
+    opus_int8           pulses[],               /* O                                        */
+    opus_int16          xq[],                   /* O                                        */
+    opus_int32          sLTP_Q15[],             /* I/O  LTP filter state                    */
+    opus_int32          delayedGain_Q10[],      /* I/O  Gain delay buffer                   */
+    const opus_int16    a_Q12[],                /* I    Short term prediction coefs         */
+    const opus_int16    b_Q14[],                /* I    Long term prediction coefs          */
+    const opus_int16    AR_shp_Q13[],           /* I    Noise shaping coefs                 */
+    opus_int            lag,                    /* I    Pitch lag                           */
+    opus_int32          HarmShapeFIRPacked_Q14, /* I                                        */
+    opus_int            Tilt_Q14,               /* I    Spectral tilt                       */
+    opus_int32          LF_shp_Q14,             /* I                                        */
+    opus_int32          Gain_Q16,               /* I                                        */
+    opus_int            Lambda_Q10,             /* I                                        */
+    opus_int            offset_Q10,             /* I                                        */
+    opus_int            length,                 /* I    Input length                        */
+    opus_int            subfr,                  /* I    Subframe number                     */
+    opus_int            shapingLPCOrder,        /* I    Shaping LPC filter order            */
+    opus_int            predictLPCOrder,        /* I    Prediction filter order             */
+    opus_int            warping_Q16,            /* I                                        */
+    opus_int            nStatesDelayedDecision, /* I    Number of states in decision tree   */
+    opus_int            *smpl_buf_idx,          /* I/O  Index to newest samples in buffers  */
+    opus_int            decisionDelay,          /* I                                        */
+    int                 arch                    /* I                                        */
+);
+
+void silk_NSQ_del_dec_c(
+    const silk_encoder_state    *psEncC,                                      /* I    Encoder State                   */
+    silk_nsq_state              *NSQ,                                         /* I/O  NSQ state                       */
+    SideInfoIndices             *psIndices,                                   /* I/O  Quantization Indices            */
+    const opus_int16            x16[],                                        /* I    Input                           */
+    opus_int8                   pulses[],                                     /* O    Quantized pulse signal          */
+    const opus_int16            *PredCoef_Q12,                                /* I    Short term prediction coefs     */
+    const opus_int16            LTPCoef_Q14[ LTP_ORDER * MAX_NB_SUBFR ],      /* I    Long term prediction coefs      */
+    const opus_int16            AR_Q13[ MAX_NB_SUBFR * MAX_SHAPE_LPC_ORDER ], /* I    Noise shaping coefs             */
+    const opus_int              HarmShapeGain_Q14[ MAX_NB_SUBFR ],            /* I    Long term shaping coefs         */
+    const opus_int              Tilt_Q14[ MAX_NB_SUBFR ],                     /* I    Spectral tilt                   */
+    const opus_int32            LF_shp_Q14[ MAX_NB_SUBFR ],                   /* I    Low frequency shaping coefs     */
+    const opus_int32            Gains_Q16[ MAX_NB_SUBFR ],                    /* I    Quantization step sizes         */
+    const opus_int              pitchL[ MAX_NB_SUBFR ],                       /* I    Pitch lags                      */
+    const opus_int              Lambda_Q10,                                   /* I    Rate/distortion tradeoff        */
+    const opus_int              LTP_scale_Q14                                 /* I    LTP state scaling               */
+)
+{
+    opus_int            i, k, lag, start_idx, LSF_interpolation_flag, Winner_ind, subfr;
+    opus_int            last_smple_idx, smpl_buf_idx, decisionDelay;
+    const opus_int16    *A_Q12, *B_Q14, *AR_shp_Q13;
+    opus_int16          *pxq;
+    VARDECL( opus_int32, sLTP_Q15 );
+    VARDECL( opus_int16, sLTP );
+    opus_int32          HarmShapeFIRPacked_Q14;
+    opus_int            offset_Q10;
+    opus_int32          RDmin_Q10, Gain_Q10;
+    VARDECL( opus_int32, x_sc_Q10 );
+    VARDECL( opus_int32, delayedGain_Q10 );
+    VARDECL( NSQ_del_dec_struct, psDelDec );
+    NSQ_del_dec_struct  *psDD;
+    SAVE_STACK;
+
+    /* Set unvoiced lag to the previous one, overwrite later for voiced */
+    lag = NSQ->lagPrev;
+
+    silk_assert( NSQ->prev_gain_Q16 != 0 );
+
+    /* Initialize delayed decision states */
+    ALLOC( psDelDec, psEncC->nStatesDelayedDecision, NSQ_del_dec_struct );
+    silk_memset( psDelDec, 0, psEncC->nStatesDelayedDecision * sizeof( NSQ_del_dec_struct ) );
+    for( k = 0; k < psEncC->nStatesDelayedDecision; k++ ) {
+        psDD                 = &psDelDec[ k ];
+        psDD->Seed           = ( k + psIndices->Seed ) & 3;
+        psDD->SeedInit       = psDD->Seed;
+        psDD->RD_Q10         = 0;
+        psDD->LF_AR_Q14      = NSQ->sLF_AR_shp_Q14;
+        psDD->Diff_Q14       = NSQ->sDiff_shp_Q14;
+        psDD->Shape_Q14[ 0 ] = NSQ->sLTP_shp_Q14[ psEncC->ltp_mem_length - 1 ];
+        silk_memcpy( psDD->sLPC_Q14, NSQ->sLPC_Q14, NSQ_LPC_BUF_LENGTH * sizeof( opus_int32 ) );
+        silk_memcpy( psDD->sAR2_Q14, NSQ->sAR2_Q14, sizeof( NSQ->sAR2_Q14 ) );
+    }
+
+    offset_Q10   = silk_Quantization_Offsets_Q10[ psIndices->signalType >> 1 ][ psIndices->quantOffsetType ];
+    smpl_buf_idx = 0; /* index of oldest samples */
+
+    decisionDelay = silk_min_int( DECISION_DELAY, psEncC->subfr_length );
+
+    /* For voiced frames limit the decision delay to lower than the pitch lag */
+    if( psIndices->signalType == TYPE_VOICED ) {
+        for( k = 0; k < psEncC->nb_subfr; k++ ) {
+            decisionDelay = silk_min_int( decisionDelay, pitchL[ k ] - LTP_ORDER / 2 - 1 );
+        }
+    } else {
+        if( lag > 0 ) {
+            decisionDelay = silk_min_int( decisionDelay, lag - LTP_ORDER / 2 - 1 );
+        }
+    }
+
+    if( psIndices->NLSFInterpCoef_Q2 == 4 ) {
+        LSF_interpolation_flag = 0;
+    } else {
+        LSF_interpolation_flag = 1;
+    }
+
+    ALLOC( sLTP_Q15, psEncC->ltp_mem_length + psEncC->frame_length, opus_int32 );
+    ALLOC( sLTP, psEncC->ltp_mem_length + psEncC->frame_length, opus_int16 );
+    ALLOC( x_sc_Q10, psEncC->subfr_length, opus_int32 );
+    ALLOC( delayedGain_Q10, DECISION_DELAY, opus_int32 );
+    /* Set up pointers to start of sub frame */
+    pxq                   = &NSQ->xq[ psEncC->ltp_mem_length ];
+    NSQ->sLTP_shp_buf_idx = psEncC->ltp_mem_length;
+    NSQ->sLTP_buf_idx     = psEncC->ltp_mem_length;
+    subfr = 0;
+    for( k = 0; k < psEncC->nb_subfr; k++ ) {
+        A_Q12      = &PredCoef_Q12[ ( ( k >> 1 ) | ( 1 - LSF_interpolation_flag ) ) * MAX_LPC_ORDER ];
+        B_Q14      = &LTPCoef_Q14[ k * LTP_ORDER           ];
+        AR_shp_Q13 = &AR_Q13[     k * MAX_SHAPE_LPC_ORDER ];
+
+        /* Noise shape parameters */
+        silk_assert( HarmShapeGain_Q14[ k ] >= 0 );
+        HarmShapeFIRPacked_Q14  =                          silk_RSHIFT( HarmShapeGain_Q14[ k ], 2 );
+        HarmShapeFIRPacked_Q14 |= silk_LSHIFT( (opus_int32)silk_RSHIFT( HarmShapeGain_Q14[ k ], 1 ), 16 );
+
+        NSQ->rewhite_flag = 0;
+        if( psIndices->signalType == TYPE_VOICED ) {
+            /* Voiced */
+            lag = pitchL[ k ];
+
+            /* Re-whitening */
+            if( ( k & ( 3 - silk_LSHIFT( LSF_interpolation_flag, 1 ) ) ) == 0 ) {
+                if( k == 2 ) {
+                    /* RESET DELAYED DECISIONS */
+                    /* Find winner */
+                    RDmin_Q10 = psDelDec[ 0 ].RD_Q10;
+                    Winner_ind = 0;
+                    for( i = 1; i < psEncC->nStatesDelayedDecision; i++ ) {
+                        if( psDelDec[ i ].RD_Q10 < RDmin_Q10 ) {
+                            RDmin_Q10 = psDelDec[ i ].RD_Q10;
+                            Winner_ind = i;
+                        }
+                    }
+                    for( i = 0; i < psEncC->nStatesDelayedDecision; i++ ) {
+                        if( i != Winner_ind ) {
+                            psDelDec[ i ].RD_Q10 += ( silk_int32_MAX >> 4 );
+                            silk_assert( psDelDec[ i ].RD_Q10 >= 0 );
+                        }
+                    }
+
+                    /* Copy final part of signals from winner state to output and long-term filter states */
+                    psDD = &psDelDec[ Winner_ind ];
+                    last_smple_idx = smpl_buf_idx + decisionDelay;
+                    for( i = 0; i < decisionDelay; i++ ) {
+                        last_smple_idx = ( last_smple_idx - 1 ) % DECISION_DELAY;
+                        if( last_smple_idx < 0 ) last_smple_idx += DECISION_DELAY;
+                        pulses[   i - decisionDelay ] = (opus_int8)silk_RSHIFT_ROUND( psDD->Q_Q10[ last_smple_idx ], 10 );
+                        pxq[ i - decisionDelay ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND(
+                            silk_SMULWW( psDD->Xq_Q14[ last_smple_idx ], Gains_Q16[ 1 ] ), 14 ) );
+                        NSQ->sLTP_shp_Q14[ NSQ->sLTP_shp_buf_idx - decisionDelay + i ] = psDD->Shape_Q14[ last_smple_idx ];
+                    }
+
+                    subfr = 0;
+                }
+
+                /* Rewhiten with new A coefs */
+                start_idx = psEncC->ltp_mem_length - lag - psEncC->predictLPCOrder - LTP_ORDER / 2;
+                celt_assert( start_idx > 0 );
+
+                silk_LPC_analysis_filter( &sLTP[ start_idx ], &NSQ->xq[ start_idx + k * psEncC->subfr_length ],
+                    A_Q12, psEncC->ltp_mem_length - start_idx, psEncC->predictLPCOrder, psEncC->arch );
+
+                NSQ->sLTP_buf_idx = psEncC->ltp_mem_length;
+                NSQ->rewhite_flag = 1;
+            }
+        }
+
+        silk_nsq_del_dec_scale_states( psEncC, NSQ, psDelDec, x16, x_sc_Q10, sLTP, sLTP_Q15, k,
+            psEncC->nStatesDelayedDecision, LTP_scale_Q14, Gains_Q16, pitchL, psIndices->signalType, decisionDelay );
+
+        silk_noise_shape_quantizer_del_dec( NSQ, psDelDec, psIndices->signalType, x_sc_Q10, pulses, pxq, sLTP_Q15,
+            delayedGain_Q10, A_Q12, B_Q14, AR_shp_Q13, lag, HarmShapeFIRPacked_Q14, Tilt_Q14[ k ], LF_shp_Q14[ k ],
+            Gains_Q16[ k ], Lambda_Q10, offset_Q10, psEncC->subfr_length, subfr++, psEncC->shapingLPCOrder,
+            psEncC->predictLPCOrder, psEncC->warping_Q16, psEncC->nStatesDelayedDecision, &smpl_buf_idx, decisionDelay, psEncC->arch );
+
+        x16    += psEncC->subfr_length;
+        pulses += psEncC->subfr_length;
+        pxq    += psEncC->subfr_length;
+    }
+
+    /* Find winner */
+    RDmin_Q10 = psDelDec[ 0 ].RD_Q10;
+    Winner_ind = 0;
+    for( k = 1; k < psEncC->nStatesDelayedDecision; k++ ) {
+        if( psDelDec[ k ].RD_Q10 < RDmin_Q10 ) {
+            RDmin_Q10 = psDelDec[ k ].RD_Q10;
+            Winner_ind = k;
+        }
+    }
+
+    /* Copy final part of signals from winner state to output and long-term filter states */
+    psDD = &psDelDec[ Winner_ind ];
+    psIndices->Seed = psDD->SeedInit;
+    last_smple_idx = smpl_buf_idx + decisionDelay;
+    Gain_Q10 = silk_RSHIFT32( Gains_Q16[ psEncC->nb_subfr - 1 ], 6 );
+    for( i = 0; i < decisionDelay; i++ ) {
+        last_smple_idx = ( last_smple_idx - 1 ) % DECISION_DELAY;
+        if( last_smple_idx < 0 ) last_smple_idx += DECISION_DELAY;
+
+        pulses[   i - decisionDelay ] = (opus_int8)silk_RSHIFT_ROUND( psDD->Q_Q10[ last_smple_idx ], 10 );
+        pxq[ i - decisionDelay ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND(
+            silk_SMULWW( psDD->Xq_Q14[ last_smple_idx ], Gain_Q10 ), 8 ) );
+        NSQ->sLTP_shp_Q14[ NSQ->sLTP_shp_buf_idx - decisionDelay + i ] = psDD->Shape_Q14[ last_smple_idx ];
+    }
+    silk_memcpy( NSQ->sLPC_Q14, &psDD->sLPC_Q14[ psEncC->subfr_length ], NSQ_LPC_BUF_LENGTH * sizeof( opus_int32 ) );
+    silk_memcpy( NSQ->sAR2_Q14, psDD->sAR2_Q14, sizeof( psDD->sAR2_Q14 ) );
+
+    /* Update states */
+    NSQ->sLF_AR_shp_Q14 = psDD->LF_AR_Q14;
+    NSQ->sDiff_shp_Q14  = psDD->Diff_Q14;
+    NSQ->lagPrev        = pitchL[ psEncC->nb_subfr - 1 ];
+
+    /* Save quantized speech signal */
+    silk_memmove( NSQ->xq,           &NSQ->xq[           psEncC->frame_length ], psEncC->ltp_mem_length * sizeof( opus_int16 ) );
+    silk_memmove( NSQ->sLTP_shp_Q14, &NSQ->sLTP_shp_Q14[ psEncC->frame_length ], psEncC->ltp_mem_length * sizeof( opus_int32 ) );
+    RESTORE_STACK;
+}
+
+/******************************************/
+/* Noise shape quantizer for one subframe */
+/******************************************/
+#ifndef OVERRIDE_silk_noise_shape_quantizer_del_dec
+static OPUS_INLINE void silk_noise_shape_quantizer_del_dec(
+    silk_nsq_state      *NSQ,                   /* I/O  NSQ state                           */
+    NSQ_del_dec_struct  psDelDec[],             /* I/O  Delayed decision states             */
+    opus_int            signalType,             /* I    Signal type                         */
+    const opus_int32    x_Q10[],                /* I                                        */
+    opus_int8           pulses[],               /* O                                        */
+    opus_int16          xq[],                   /* O                                        */
+    opus_int32          sLTP_Q15[],             /* I/O  LTP filter state                    */
+    opus_int32          delayedGain_Q10[],      /* I/O  Gain delay buffer                   */
+    const opus_int16    a_Q12[],                /* I    Short term prediction coefs         */
+    const opus_int16    b_Q14[],                /* I    Long term prediction coefs          */
+    const opus_int16    AR_shp_Q13[],           /* I    Noise shaping coefs                 */
+    opus_int            lag,                    /* I    Pitch lag                           */
+    opus_int32          HarmShapeFIRPacked_Q14, /* I                                        */
+    opus_int            Tilt_Q14,               /* I    Spectral tilt                       */
+    opus_int32          LF_shp_Q14,             /* I                                        */
+    opus_int32          Gain_Q16,               /* I                                        */
+    opus_int            Lambda_Q10,             /* I                                        */
+    opus_int            offset_Q10,             /* I                                        */
+    opus_int            length,                 /* I    Input length                        */
+    opus_int            subfr,                  /* I    Subframe number                     */
+    opus_int            shapingLPCOrder,        /* I    Shaping LPC filter order            */
+    opus_int            predictLPCOrder,        /* I    Prediction filter order             */
+    opus_int            warping_Q16,            /* I                                        */
+    opus_int            nStatesDelayedDecision, /* I    Number of states in decision tree   */
+    opus_int            *smpl_buf_idx,          /* I/O  Index to newest samples in buffers  */
+    opus_int            decisionDelay,          /* I                                        */
+    int                 arch                    /* I                                        */
+)
+{
+    opus_int     i, j, k, Winner_ind, RDmin_ind, RDmax_ind, last_smple_idx;
+    opus_int32   Winner_rand_state;
+    opus_int32   LTP_pred_Q14, LPC_pred_Q14, n_AR_Q14, n_LTP_Q14;
+    opus_int32   n_LF_Q14, r_Q10, rr_Q10, rd1_Q10, rd2_Q10, RDmin_Q10, RDmax_Q10;
+    opus_int32   q1_Q0, q1_Q10, q2_Q10, exc_Q14, LPC_exc_Q14, xq_Q14, Gain_Q10;
+    opus_int32   tmp1, tmp2, sLF_AR_shp_Q14;
+    opus_int32   *pred_lag_ptr, *shp_lag_ptr, *psLPC_Q14;
+#ifdef silk_short_prediction_create_arch_coef
+    opus_int32   a_Q12_arch[MAX_LPC_ORDER];
+#endif
+
+    VARDECL( NSQ_sample_pair, psSampleState );
+    NSQ_del_dec_struct *psDD;
+    NSQ_sample_struct  *psSS;
+    SAVE_STACK;
+
+    celt_assert( nStatesDelayedDecision > 0 );
+    ALLOC( psSampleState, nStatesDelayedDecision, NSQ_sample_pair );
+
+    shp_lag_ptr  = &NSQ->sLTP_shp_Q14[ NSQ->sLTP_shp_buf_idx - lag + HARM_SHAPE_FIR_TAPS / 2 ];
+    pred_lag_ptr = &sLTP_Q15[ NSQ->sLTP_buf_idx - lag + LTP_ORDER / 2 ];
+    Gain_Q10     = silk_RSHIFT( Gain_Q16, 6 );
+
+#ifdef silk_short_prediction_create_arch_coef
+    silk_short_prediction_create_arch_coef(a_Q12_arch, a_Q12, predictLPCOrder);
+#endif
+
+    for( i = 0; i < length; i++ ) {
+        /* Perform common calculations used in all states */
+
+        /* Long-term prediction */
+        if( signalType == TYPE_VOICED ) {
+            /* Unrolled loop */
+            /* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */
+            LTP_pred_Q14 = 2;
+            LTP_pred_Q14 = silk_SMLAWB( LTP_pred_Q14, pred_lag_ptr[  0 ], b_Q14[ 0 ] );
+            LTP_pred_Q14 = silk_SMLAWB( LTP_pred_Q14, pred_lag_ptr[ -1 ], b_Q14[ 1 ] );
+            LTP_pred_Q14 = silk_SMLAWB( LTP_pred_Q14, pred_lag_ptr[ -2 ], b_Q14[ 2 ] );
+            LTP_pred_Q14 = silk_SMLAWB( LTP_pred_Q14, pred_lag_ptr[ -3 ], b_Q14[ 3 ] );
+            LTP_pred_Q14 = silk_SMLAWB( LTP_pred_Q14, pred_lag_ptr[ -4 ], b_Q14[ 4 ] );
+            LTP_pred_Q14 = silk_LSHIFT( LTP_pred_Q14, 1 );                          /* Q13 -> Q14 */
+            pred_lag_ptr++;
+        } else {
+            LTP_pred_Q14 = 0;
+        }
+
+        /* Long-term shaping */
+        if( lag > 0 ) {
+            /* Symmetric, packed FIR coefficients */
+            n_LTP_Q14 = silk_SMULWB( silk_ADD_SAT32( shp_lag_ptr[ 0 ], shp_lag_ptr[ -2 ] ), HarmShapeFIRPacked_Q14 );
+            n_LTP_Q14 = silk_SMLAWT( n_LTP_Q14, shp_lag_ptr[ -1 ], HarmShapeFIRPacked_Q14 );
+            n_LTP_Q14 = silk_SUB_LSHIFT32( LTP_pred_Q14, n_LTP_Q14, 2 );            /* Q12 -> Q14 */
+            shp_lag_ptr++;
+        } else {
+            n_LTP_Q14 = 0;
+        }
+
+        for( k = 0; k < nStatesDelayedDecision; k++ ) {
+            /* Delayed decision state */
+            psDD = &psDelDec[ k ];
+
+            /* Sample state */
+            psSS = psSampleState[ k ];
+
+            /* Generate dither */
+            psDD->Seed = silk_RAND( psDD->Seed );
+
+            /* Pointer used in short term prediction and shaping */
+            psLPC_Q14 = &psDD->sLPC_Q14[ NSQ_LPC_BUF_LENGTH - 1 + i ];
+            /* Short-term prediction */
+            LPC_pred_Q14 = silk_noise_shape_quantizer_short_prediction(psLPC_Q14, a_Q12, a_Q12_arch, predictLPCOrder, arch);
+            LPC_pred_Q14 = silk_LSHIFT( LPC_pred_Q14, 4 );                              /* Q10 -> Q14 */
+
+            /* Noise shape feedback */
+            celt_assert( ( shapingLPCOrder & 1 ) == 0 );   /* check that order is even */
+            /* Output of lowpass section */
+            tmp2 = silk_SMLAWB( psDD->Diff_Q14, psDD->sAR2_Q14[ 0 ], warping_Q16 );
+            /* Output of allpass section */
+            tmp1 = silk_SMLAWB( psDD->sAR2_Q14[ 0 ], silk_SUB32_ovflw(psDD->sAR2_Q14[ 1 ], tmp2), warping_Q16 );
+            psDD->sAR2_Q14[ 0 ] = tmp2;
+            n_AR_Q14 = silk_RSHIFT( shapingLPCOrder, 1 );
+            n_AR_Q14 = silk_SMLAWB( n_AR_Q14, tmp2, AR_shp_Q13[ 0 ] );
+            /* Loop over allpass sections */
+            for( j = 2; j < shapingLPCOrder; j += 2 ) {
+                /* Output of allpass section */
+                tmp2 = silk_SMLAWB( psDD->sAR2_Q14[ j - 1 ], silk_SUB32_ovflw(psDD->sAR2_Q14[ j + 0 ], tmp1), warping_Q16 );
+                psDD->sAR2_Q14[ j - 1 ] = tmp1;
+                n_AR_Q14 = silk_SMLAWB( n_AR_Q14, tmp1, AR_shp_Q13[ j - 1 ] );
+                /* Output of allpass section */
+                tmp1 = silk_SMLAWB( psDD->sAR2_Q14[ j + 0 ], silk_SUB32_ovflw(psDD->sAR2_Q14[ j + 1 ], tmp2), warping_Q16 );
+                psDD->sAR2_Q14[ j + 0 ] = tmp2;
+                n_AR_Q14 = silk_SMLAWB( n_AR_Q14, tmp2, AR_shp_Q13[ j ] );
+            }
+            psDD->sAR2_Q14[ shapingLPCOrder - 1 ] = tmp1;
+            n_AR_Q14 = silk_SMLAWB( n_AR_Q14, tmp1, AR_shp_Q13[ shapingLPCOrder - 1 ] );
+
+            n_AR_Q14 = silk_LSHIFT( n_AR_Q14, 1 );                                      /* Q11 -> Q12 */
+            n_AR_Q14 = silk_SMLAWB( n_AR_Q14, psDD->LF_AR_Q14, Tilt_Q14 );              /* Q12 */
+            n_AR_Q14 = silk_LSHIFT( n_AR_Q14, 2 );                                      /* Q12 -> Q14 */
+
+            n_LF_Q14 = silk_SMULWB( psDD->Shape_Q14[ *smpl_buf_idx ], LF_shp_Q14 );     /* Q12 */
+            n_LF_Q14 = silk_SMLAWT( n_LF_Q14, psDD->LF_AR_Q14, LF_shp_Q14 );            /* Q12 */
+            n_LF_Q14 = silk_LSHIFT( n_LF_Q14, 2 );                                      /* Q12 -> Q14 */
+
+            /* Input minus prediction plus noise feedback                       */
+            /* r = x[ i ] - LTP_pred - LPC_pred + n_AR + n_Tilt + n_LF + n_LTP  */
+            tmp1 = silk_ADD_SAT32( n_AR_Q14, n_LF_Q14 );                                /* Q14 */
+            tmp2 = silk_ADD32_ovflw( n_LTP_Q14, LPC_pred_Q14 );                         /* Q13 */
+            tmp1 = silk_SUB_SAT32( tmp2, tmp1 );                                        /* Q13 */
+            tmp1 = silk_RSHIFT_ROUND( tmp1, 4 );                                        /* Q10 */
+
+            r_Q10 = silk_SUB32( x_Q10[ i ], tmp1 );                                     /* residual error Q10 */
+
+            /* Flip sign depending on dither */
+            if ( psDD->Seed < 0 ) {
+                r_Q10 = -r_Q10;
+            }
+            r_Q10 = silk_LIMIT_32( r_Q10, -(31 << 10), 30 << 10 );
+
+            /* Find two quantization level candidates and measure their rate-distortion */
+            q1_Q10 = silk_SUB32( r_Q10, offset_Q10 );
+            q1_Q0 = silk_RSHIFT( q1_Q10, 10 );
+            if (Lambda_Q10 > 2048) {
+                /* For aggressive RDO, the bias becomes more than one pulse. */
+                int rdo_offset = Lambda_Q10/2 - 512;
+                if (q1_Q10 > rdo_offset) {
+                    q1_Q0 = silk_RSHIFT( q1_Q10 - rdo_offset, 10 );
+                } else if (q1_Q10 < -rdo_offset) {
+                    q1_Q0 = silk_RSHIFT( q1_Q10 + rdo_offset, 10 );
+                } else if (q1_Q10 < 0) {
+                    q1_Q0 = -1;
+                } else {
+                    q1_Q0 = 0;
+                }
+            }
+            if( q1_Q0 > 0 ) {
+                q1_Q10  = silk_SUB32( silk_LSHIFT( q1_Q0, 10 ), QUANT_LEVEL_ADJUST_Q10 );
+                q1_Q10  = silk_ADD32( q1_Q10, offset_Q10 );
+                q2_Q10  = silk_ADD32( q1_Q10, 1024 );
+                rd1_Q10 = silk_SMULBB( q1_Q10, Lambda_Q10 );
+                rd2_Q10 = silk_SMULBB( q2_Q10, Lambda_Q10 );
+            } else if( q1_Q0 == 0 ) {
+                q1_Q10  = offset_Q10;
+                q2_Q10  = silk_ADD32( q1_Q10, 1024 - QUANT_LEVEL_ADJUST_Q10 );
+                rd1_Q10 = silk_SMULBB( q1_Q10, Lambda_Q10 );
+                rd2_Q10 = silk_SMULBB( q2_Q10, Lambda_Q10 );
+            } else if( q1_Q0 == -1 ) {
+                q2_Q10  = offset_Q10;
+                q1_Q10  = silk_SUB32( q2_Q10, 1024 - QUANT_LEVEL_ADJUST_Q10 );
+                rd1_Q10 = silk_SMULBB( -q1_Q10, Lambda_Q10 );
+                rd2_Q10 = silk_SMULBB(  q2_Q10, Lambda_Q10 );
+            } else {            /* q1_Q0 < -1 */
+                q1_Q10  = silk_ADD32( silk_LSHIFT( q1_Q0, 10 ), QUANT_LEVEL_ADJUST_Q10 );
+                q1_Q10  = silk_ADD32( q1_Q10, offset_Q10 );
+                q2_Q10  = silk_ADD32( q1_Q10, 1024 );
+                rd1_Q10 = silk_SMULBB( -q1_Q10, Lambda_Q10 );
+                rd2_Q10 = silk_SMULBB( -q2_Q10, Lambda_Q10 );
+            }
+            rr_Q10  = silk_SUB32( r_Q10, q1_Q10 );
+            rd1_Q10 = silk_RSHIFT( silk_SMLABB( rd1_Q10, rr_Q10, rr_Q10 ), 10 );
+            rr_Q10  = silk_SUB32( r_Q10, q2_Q10 );
+            rd2_Q10 = silk_RSHIFT( silk_SMLABB( rd2_Q10, rr_Q10, rr_Q10 ), 10 );
+
+            if( rd1_Q10 < rd2_Q10 ) {
+                psSS[ 0 ].RD_Q10 = silk_ADD32( psDD->RD_Q10, rd1_Q10 );
+                psSS[ 1 ].RD_Q10 = silk_ADD32( psDD->RD_Q10, rd2_Q10 );
+                psSS[ 0 ].Q_Q10  = q1_Q10;
+                psSS[ 1 ].Q_Q10  = q2_Q10;
+            } else {
+                psSS[ 0 ].RD_Q10 = silk_ADD32( psDD->RD_Q10, rd2_Q10 );
+                psSS[ 1 ].RD_Q10 = silk_ADD32( psDD->RD_Q10, rd1_Q10 );
+                psSS[ 0 ].Q_Q10  = q2_Q10;
+                psSS[ 1 ].Q_Q10  = q1_Q10;
+            }
+
+            /* Update states for best quantization */
+
+            /* Quantized excitation */
+            exc_Q14 = silk_LSHIFT32( psSS[ 0 ].Q_Q10, 4 );
+            if ( psDD->Seed < 0 ) {
+                exc_Q14 = -exc_Q14;
+            }
+
+            /* Add predictions */
+            LPC_exc_Q14 = silk_ADD32( exc_Q14, LTP_pred_Q14 );
+            xq_Q14      = silk_ADD32_ovflw( LPC_exc_Q14, LPC_pred_Q14 );
+
+            /* Update states */
+            psSS[ 0 ].Diff_Q14     = silk_SUB32_ovflw( xq_Q14, silk_LSHIFT32( x_Q10[ i ], 4 ) );
+            sLF_AR_shp_Q14         = silk_SUB32_ovflw( psSS[ 0 ].Diff_Q14, n_AR_Q14 );
+            psSS[ 0 ].sLTP_shp_Q14 = silk_SUB_SAT32( sLF_AR_shp_Q14, n_LF_Q14 );
+            psSS[ 0 ].LF_AR_Q14    = sLF_AR_shp_Q14;
+            psSS[ 0 ].LPC_exc_Q14  = LPC_exc_Q14;
+            psSS[ 0 ].xq_Q14       = xq_Q14;
+
+            /* Update states for second best quantization */
+
+            /* Quantized excitation */
+            exc_Q14 = silk_LSHIFT32( psSS[ 1 ].Q_Q10, 4 );
+            if ( psDD->Seed < 0 ) {
+                exc_Q14 = -exc_Q14;
+            }
+
+            /* Add predictions */
+            LPC_exc_Q14 = silk_ADD32( exc_Q14, LTP_pred_Q14 );
+            xq_Q14      = silk_ADD32_ovflw( LPC_exc_Q14, LPC_pred_Q14 );
+
+            /* Update states */
+            psSS[ 1 ].Diff_Q14     = silk_SUB32_ovflw( xq_Q14, silk_LSHIFT32( x_Q10[ i ], 4 ) );
+            sLF_AR_shp_Q14         = silk_SUB32_ovflw( psSS[ 1 ].Diff_Q14, n_AR_Q14 );
+            psSS[ 1 ].sLTP_shp_Q14 = silk_SUB_SAT32( sLF_AR_shp_Q14, n_LF_Q14 );
+            psSS[ 1 ].LF_AR_Q14    = sLF_AR_shp_Q14;
+            psSS[ 1 ].LPC_exc_Q14  = LPC_exc_Q14;
+            psSS[ 1 ].xq_Q14       = xq_Q14;
+        }
+
+        *smpl_buf_idx  = ( *smpl_buf_idx - 1 ) % DECISION_DELAY;
+        if( *smpl_buf_idx < 0 ) *smpl_buf_idx += DECISION_DELAY;
+        last_smple_idx = ( *smpl_buf_idx + decisionDelay ) % DECISION_DELAY;
+
+        /* Find winner */
+        RDmin_Q10 = psSampleState[ 0 ][ 0 ].RD_Q10;
+        Winner_ind = 0;
+        for( k = 1; k < nStatesDelayedDecision; k++ ) {
+            if( psSampleState[ k ][ 0 ].RD_Q10 < RDmin_Q10 ) {
+                RDmin_Q10  = psSampleState[ k ][ 0 ].RD_Q10;
+                Winner_ind = k;
+            }
+        }
+
+        /* Increase RD values of expired states */
+        Winner_rand_state = psDelDec[ Winner_ind ].RandState[ last_smple_idx ];
+        for( k = 0; k < nStatesDelayedDecision; k++ ) {
+            if( psDelDec[ k ].RandState[ last_smple_idx ] != Winner_rand_state ) {
+                psSampleState[ k ][ 0 ].RD_Q10 = silk_ADD32( psSampleState[ k ][ 0 ].RD_Q10, silk_int32_MAX >> 4 );
+                psSampleState[ k ][ 1 ].RD_Q10 = silk_ADD32( psSampleState[ k ][ 1 ].RD_Q10, silk_int32_MAX >> 4 );
+                silk_assert( psSampleState[ k ][ 0 ].RD_Q10 >= 0 );
+            }
+        }
+
+        /* Find worst in first set and best in second set */
+        RDmax_Q10  = psSampleState[ 0 ][ 0 ].RD_Q10;
+        RDmin_Q10  = psSampleState[ 0 ][ 1 ].RD_Q10;
+        RDmax_ind = 0;
+        RDmin_ind = 0;
+        for( k = 1; k < nStatesDelayedDecision; k++ ) {
+            /* find worst in first set */
+            if( psSampleState[ k ][ 0 ].RD_Q10 > RDmax_Q10 ) {
+                RDmax_Q10  = psSampleState[ k ][ 0 ].RD_Q10;
+                RDmax_ind = k;
+            }
+            /* find best in second set */
+            if( psSampleState[ k ][ 1 ].RD_Q10 < RDmin_Q10 ) {
+                RDmin_Q10  = psSampleState[ k ][ 1 ].RD_Q10;
+                RDmin_ind = k;
+            }
+        }
+
+        /* Replace a state if best from second set outperforms worst in first set */
+        if( RDmin_Q10 < RDmax_Q10 ) {
+            silk_memcpy( ( (opus_int32 *)&psDelDec[ RDmax_ind ] ) + i,
+                         ( (opus_int32 *)&psDelDec[ RDmin_ind ] ) + i, sizeof( NSQ_del_dec_struct ) - i * sizeof( opus_int32) );
+            silk_memcpy( &psSampleState[ RDmax_ind ][ 0 ], &psSampleState[ RDmin_ind ][ 1 ], sizeof( NSQ_sample_struct ) );
+        }
+
+        /* Write samples from winner to output and long-term filter states */
+        psDD = &psDelDec[ Winner_ind ];
+        if( subfr > 0 || i >= decisionDelay ) {
+            pulses[  i - decisionDelay ] = (opus_int8)silk_RSHIFT_ROUND( psDD->Q_Q10[ last_smple_idx ], 10 );
+            xq[ i - decisionDelay ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND(
+                silk_SMULWW( psDD->Xq_Q14[ last_smple_idx ], delayedGain_Q10[ last_smple_idx ] ), 8 ) );
+            NSQ->sLTP_shp_Q14[ NSQ->sLTP_shp_buf_idx - decisionDelay ] = psDD->Shape_Q14[ last_smple_idx ];
+            sLTP_Q15[          NSQ->sLTP_buf_idx     - decisionDelay ] = psDD->Pred_Q15[  last_smple_idx ];
+        }
+        NSQ->sLTP_shp_buf_idx++;
+        NSQ->sLTP_buf_idx++;
+
+        /* Update states */
+        for( k = 0; k < nStatesDelayedDecision; k++ ) {
+            psDD                                     = &psDelDec[ k ];
+            psSS                                     = &psSampleState[ k ][ 0 ];
+            psDD->LF_AR_Q14                          = psSS->LF_AR_Q14;
+            psDD->Diff_Q14                           = psSS->Diff_Q14;
+            psDD->sLPC_Q14[ NSQ_LPC_BUF_LENGTH + i ] = psSS->xq_Q14;
+            psDD->Xq_Q14[    *smpl_buf_idx ]         = psSS->xq_Q14;
+            psDD->Q_Q10[     *smpl_buf_idx ]         = psSS->Q_Q10;
+            psDD->Pred_Q15[  *smpl_buf_idx ]         = silk_LSHIFT32( psSS->LPC_exc_Q14, 1 );
+            psDD->Shape_Q14[ *smpl_buf_idx ]         = psSS->sLTP_shp_Q14;
+            psDD->Seed                               = silk_ADD32_ovflw( psDD->Seed, silk_RSHIFT_ROUND( psSS->Q_Q10, 10 ) );
+            psDD->RandState[ *smpl_buf_idx ]         = psDD->Seed;
+            psDD->RD_Q10                             = psSS->RD_Q10;
+        }
+        delayedGain_Q10[     *smpl_buf_idx ]         = Gain_Q10;
+    }
+    /* Update LPC states */
+    for( k = 0; k < nStatesDelayedDecision; k++ ) {
+        psDD = &psDelDec[ k ];
+        silk_memcpy( psDD->sLPC_Q14, &psDD->sLPC_Q14[ length ], NSQ_LPC_BUF_LENGTH * sizeof( opus_int32 ) );
+    }
+    RESTORE_STACK;
+}
+#endif /* OVERRIDE_silk_noise_shape_quantizer_del_dec */
+
+static OPUS_INLINE void silk_nsq_del_dec_scale_states(
+    const silk_encoder_state *psEncC,               /* I    Encoder State                       */
+    silk_nsq_state      *NSQ,                       /* I/O  NSQ state                           */
+    NSQ_del_dec_struct  psDelDec[],                 /* I/O  Delayed decision states             */
+    const opus_int16    x16[],                      /* I    Input                               */
+    opus_int32          x_sc_Q10[],                 /* O    Input scaled with 1/Gain in Q10     */
+    const opus_int16    sLTP[],                     /* I    Re-whitened LTP state in Q0         */
+    opus_int32          sLTP_Q15[],                 /* O    LTP state matching scaled input     */
+    opus_int            subfr,                      /* I    Subframe number                     */
+    opus_int            nStatesDelayedDecision,     /* I    Number of del dec states            */
+    const opus_int      LTP_scale_Q14,              /* I    LTP state scaling                   */
+    const opus_int32    Gains_Q16[ MAX_NB_SUBFR ],  /* I                                        */
+    const opus_int      pitchL[ MAX_NB_SUBFR ],     /* I    Pitch lag                           */
+    const opus_int      signal_type,                /* I    Signal type                         */
+    const opus_int      decisionDelay               /* I    Decision delay                      */
+)
+{
+    opus_int            i, k, lag;
+    opus_int32          gain_adj_Q16, inv_gain_Q31, inv_gain_Q26;
+    NSQ_del_dec_struct  *psDD;
+
+    lag          = pitchL[ subfr ];
+    inv_gain_Q31 = silk_INVERSE32_varQ( silk_max( Gains_Q16[ subfr ], 1 ), 47 );
+    silk_assert( inv_gain_Q31 != 0 );
+
+    /* Scale input */
+    inv_gain_Q26 = silk_RSHIFT_ROUND( inv_gain_Q31, 5 );
+    for( i = 0; i < psEncC->subfr_length; i++ ) {
+        x_sc_Q10[ i ] = silk_SMULWW( x16[ i ], inv_gain_Q26 );
+    }
+
+    /* After rewhitening the LTP state is un-scaled, so scale with inv_gain_Q16 */
+    if( NSQ->rewhite_flag ) {
+        if( subfr == 0 ) {
+            /* Do LTP downscaling */
+            inv_gain_Q31 = silk_LSHIFT( silk_SMULWB( inv_gain_Q31, LTP_scale_Q14 ), 2 );
+        }
+        for( i = NSQ->sLTP_buf_idx - lag - LTP_ORDER / 2; i < NSQ->sLTP_buf_idx; i++ ) {
+            silk_assert( i < MAX_FRAME_LENGTH );
+            sLTP_Q15[ i ] = silk_SMULWB( inv_gain_Q31, sLTP[ i ] );
+        }
+    }
+
+    /* Adjust for changing gain */
+    if( Gains_Q16[ subfr ] != NSQ->prev_gain_Q16 ) {
+        gain_adj_Q16 =  silk_DIV32_varQ( NSQ->prev_gain_Q16, Gains_Q16[ subfr ], 16 );
+
+        /* Scale long-term shaping state */
+        for( i = NSQ->sLTP_shp_buf_idx - psEncC->ltp_mem_length; i < NSQ->sLTP_shp_buf_idx; i++ ) {
+            NSQ->sLTP_shp_Q14[ i ] = silk_SMULWW( gain_adj_Q16, NSQ->sLTP_shp_Q14[ i ] );
+        }
+
+        /* Scale long-term prediction state */
+        if( signal_type == TYPE_VOICED && NSQ->rewhite_flag == 0 ) {
+            for( i = NSQ->sLTP_buf_idx - lag - LTP_ORDER / 2; i < NSQ->sLTP_buf_idx - decisionDelay; i++ ) {
+                sLTP_Q15[ i ] = silk_SMULWW( gain_adj_Q16, sLTP_Q15[ i ] );
+            }
+        }
+
+        for( k = 0; k < nStatesDelayedDecision; k++ ) {
+            psDD = &psDelDec[ k ];
+
+            /* Scale scalar states */
+            psDD->LF_AR_Q14 = silk_SMULWW( gain_adj_Q16, psDD->LF_AR_Q14 );
+            psDD->Diff_Q14 = silk_SMULWW( gain_adj_Q16, psDD->Diff_Q14 );
+
+            /* Scale short-term prediction and shaping states */
+            for( i = 0; i < NSQ_LPC_BUF_LENGTH; i++ ) {
+                psDD->sLPC_Q14[ i ] = silk_SMULWW( gain_adj_Q16, psDD->sLPC_Q14[ i ] );
+            }
+            for( i = 0; i < MAX_SHAPE_LPC_ORDER; i++ ) {
+                psDD->sAR2_Q14[ i ] = silk_SMULWW( gain_adj_Q16, psDD->sAR2_Q14[ i ] );
+            }
+            for( i = 0; i < DECISION_DELAY; i++ ) {
+                psDD->Pred_Q15[  i ] = silk_SMULWW( gain_adj_Q16, psDD->Pred_Q15[  i ] );
+                psDD->Shape_Q14[ i ] = silk_SMULWW( gain_adj_Q16, psDD->Shape_Q14[ i ] );
+            }
+        }
+
+        /* Save inverse gain */
+        NSQ->prev_gain_Q16 = Gains_Q16[ subfr ];
+    }
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/VAD.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/VAD.c
new file mode 100644 (file)
index 0000000..d0cda52
--- /dev/null
@@ -0,0 +1,360 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main.h"
+#include "stack_alloc.h"
+
+/* Silk VAD noise level estimation */
+# if !defined(OPUS_X86_MAY_HAVE_SSE4_1)
+static OPUS_INLINE void silk_VAD_GetNoiseLevels(
+    const opus_int32             pX[ VAD_N_BANDS ], /* I    subband energies                            */
+    silk_VAD_state              *psSilk_VAD         /* I/O  Pointer to Silk VAD state                   */
+);
+#endif
+
+/**********************************/
+/* Initialization of the Silk VAD */
+/**********************************/
+opus_int silk_VAD_Init(                                         /* O    Return value, 0 if success                  */
+    silk_VAD_state              *psSilk_VAD                     /* I/O  Pointer to Silk VAD state                   */
+)
+{
+    opus_int b, ret = 0;
+
+    /* reset state memory */
+    silk_memset( psSilk_VAD, 0, sizeof( silk_VAD_state ) );
+
+    /* init noise levels */
+    /* Initialize array with approx pink noise levels (psd proportional to inverse of frequency) */
+    for( b = 0; b < VAD_N_BANDS; b++ ) {
+        psSilk_VAD->NoiseLevelBias[ b ] = silk_max_32( silk_DIV32_16( VAD_NOISE_LEVELS_BIAS, b + 1 ), 1 );
+    }
+
+    /* Initialize state */
+    for( b = 0; b < VAD_N_BANDS; b++ ) {
+        psSilk_VAD->NL[ b ]     = silk_MUL( 100, psSilk_VAD->NoiseLevelBias[ b ] );
+        psSilk_VAD->inv_NL[ b ] = silk_DIV32( silk_int32_MAX, psSilk_VAD->NL[ b ] );
+    }
+    psSilk_VAD->counter = 15;
+
+    /* init smoothed energy-to-noise ratio*/
+    for( b = 0; b < VAD_N_BANDS; b++ ) {
+        psSilk_VAD->NrgRatioSmth_Q8[ b ] = 100 * 256;       /* 100 * 256 --> 20 dB SNR */
+    }
+
+    return( ret );
+}
+
+/* Weighting factors for tilt measure */
+static const opus_int32 tiltWeights[ VAD_N_BANDS ] = { 30000, 6000, -12000, -12000 };
+
+/***************************************/
+/* Get the speech activity level in Q8 */
+/***************************************/
+opus_int silk_VAD_GetSA_Q8_c(                                   /* O    Return value, 0 if success                  */
+    silk_encoder_state          *psEncC,                        /* I/O  Encoder state                               */
+    const opus_int16            pIn[]                           /* I    PCM input                                   */
+)
+{
+    opus_int   SA_Q15, pSNR_dB_Q7, input_tilt;
+    opus_int   decimated_framelength1, decimated_framelength2;
+    opus_int   decimated_framelength;
+    opus_int   dec_subframe_length, dec_subframe_offset, SNR_Q7, i, b, s;
+    opus_int32 sumSquared, smooth_coef_Q16;
+    opus_int16 HPstateTmp;
+    VARDECL( opus_int16, X );
+    opus_int32 Xnrg[ VAD_N_BANDS ];
+    opus_int32 NrgToNoiseRatio_Q8[ VAD_N_BANDS ];
+    opus_int32 speech_nrg, x_tmp;
+    opus_int   X_offset[ VAD_N_BANDS ];
+    opus_int   ret = 0;
+    silk_VAD_state *psSilk_VAD = &psEncC->sVAD;
+    SAVE_STACK;
+
+    /* Safety checks */
+    silk_assert( VAD_N_BANDS == 4 );
+    celt_assert( MAX_FRAME_LENGTH >= psEncC->frame_length );
+    celt_assert( psEncC->frame_length <= 512 );
+    celt_assert( psEncC->frame_length == 8 * silk_RSHIFT( psEncC->frame_length, 3 ) );
+
+    /***********************/
+    /* Filter and Decimate */
+    /***********************/
+    decimated_framelength1 = silk_RSHIFT( psEncC->frame_length, 1 );
+    decimated_framelength2 = silk_RSHIFT( psEncC->frame_length, 2 );
+    decimated_framelength = silk_RSHIFT( psEncC->frame_length, 3 );
+    /* Decimate into 4 bands:
+       0       L      3L       L              3L                             5L
+               -      --       -              --                             --
+               8       8       2               4                              4
+
+       [0-1 kHz| temp. |1-2 kHz|    2-4 kHz    |            4-8 kHz           |
+
+       They're arranged to allow the minimal ( frame_length / 4 ) extra
+       scratch space during the downsampling process */
+    X_offset[ 0 ] = 0;
+    X_offset[ 1 ] = decimated_framelength + decimated_framelength2;
+    X_offset[ 2 ] = X_offset[ 1 ] + decimated_framelength;
+    X_offset[ 3 ] = X_offset[ 2 ] + decimated_framelength2;
+    ALLOC( X, X_offset[ 3 ] + decimated_framelength1, opus_int16 );
+
+    /* 0-8 kHz to 0-4 kHz and 4-8 kHz */
+    silk_ana_filt_bank_1( pIn, &psSilk_VAD->AnaState[  0 ],
+        X, &X[ X_offset[ 3 ] ], psEncC->frame_length );
+
+    /* 0-4 kHz to 0-2 kHz and 2-4 kHz */
+    silk_ana_filt_bank_1( X, &psSilk_VAD->AnaState1[ 0 ],
+        X, &X[ X_offset[ 2 ] ], decimated_framelength1 );
+
+    /* 0-2 kHz to 0-1 kHz and 1-2 kHz */
+    silk_ana_filt_bank_1( X, &psSilk_VAD->AnaState2[ 0 ],
+        X, &X[ X_offset[ 1 ] ], decimated_framelength2 );
+
+    /*********************************************/
+    /* HP filter on lowest band (differentiator) */
+    /*********************************************/
+    X[ decimated_framelength - 1 ] = silk_RSHIFT( X[ decimated_framelength - 1 ], 1 );
+    HPstateTmp = X[ decimated_framelength - 1 ];
+    for( i = decimated_framelength - 1; i > 0; i-- ) {
+        X[ i - 1 ]  = silk_RSHIFT( X[ i - 1 ], 1 );
+        X[ i ]     -= X[ i - 1 ];
+    }
+    X[ 0 ] -= psSilk_VAD->HPstate;
+    psSilk_VAD->HPstate = HPstateTmp;
+
+    /*************************************/
+    /* Calculate the energy in each band */
+    /*************************************/
+    for( b = 0; b < VAD_N_BANDS; b++ ) {
+        /* Find the decimated framelength in the non-uniformly divided bands */
+        decimated_framelength = silk_RSHIFT( psEncC->frame_length, silk_min_int( VAD_N_BANDS - b, VAD_N_BANDS - 1 ) );
+
+        /* Split length into subframe lengths */
+        dec_subframe_length = silk_RSHIFT( decimated_framelength, VAD_INTERNAL_SUBFRAMES_LOG2 );
+        dec_subframe_offset = 0;
+
+        /* Compute energy per sub-frame */
+        /* initialize with summed energy of last subframe */
+        Xnrg[ b ] = psSilk_VAD->XnrgSubfr[ b ];
+        for( s = 0; s < VAD_INTERNAL_SUBFRAMES; s++ ) {
+            sumSquared = 0;
+            for( i = 0; i < dec_subframe_length; i++ ) {
+                /* The energy will be less than dec_subframe_length * ( silk_int16_MIN / 8 ) ^ 2.            */
+                /* Therefore we can accumulate with no risk of overflow (unless dec_subframe_length > 128)  */
+                x_tmp = silk_RSHIFT(
+                    X[ X_offset[ b ] + i + dec_subframe_offset ], 3 );
+                sumSquared = silk_SMLABB( sumSquared, x_tmp, x_tmp );
+
+                /* Safety check */
+                silk_assert( sumSquared >= 0 );
+            }
+
+            /* Add/saturate summed energy of current subframe */
+            if( s < VAD_INTERNAL_SUBFRAMES - 1 ) {
+                Xnrg[ b ] = silk_ADD_POS_SAT32( Xnrg[ b ], sumSquared );
+            } else {
+                /* Look-ahead subframe */
+                Xnrg[ b ] = silk_ADD_POS_SAT32( Xnrg[ b ], silk_RSHIFT( sumSquared, 1 ) );
+            }
+
+            dec_subframe_offset += dec_subframe_length;
+        }
+        psSilk_VAD->XnrgSubfr[ b ] = sumSquared;
+    }
+
+    /********************/
+    /* Noise estimation */
+    /********************/
+    silk_VAD_GetNoiseLevels( &Xnrg[ 0 ], psSilk_VAD );
+
+    /***********************************************/
+    /* Signal-plus-noise to noise ratio estimation */
+    /***********************************************/
+    sumSquared = 0;
+    input_tilt = 0;
+    for( b = 0; b < VAD_N_BANDS; b++ ) {
+        speech_nrg = Xnrg[ b ] - psSilk_VAD->NL[ b ];
+        if( speech_nrg > 0 ) {
+            /* Divide, with sufficient resolution */
+            if( ( Xnrg[ b ] & 0xFF800000 ) == 0 ) {
+                NrgToNoiseRatio_Q8[ b ] = silk_DIV32( silk_LSHIFT( Xnrg[ b ], 8 ), psSilk_VAD->NL[ b ] + 1 );
+            } else {
+                NrgToNoiseRatio_Q8[ b ] = silk_DIV32( Xnrg[ b ], silk_RSHIFT( psSilk_VAD->NL[ b ], 8 ) + 1 );
+            }
+
+            /* Convert to log domain */
+            SNR_Q7 = silk_lin2log( NrgToNoiseRatio_Q8[ b ] ) - 8 * 128;
+
+            /* Sum-of-squares */
+            sumSquared = silk_SMLABB( sumSquared, SNR_Q7, SNR_Q7 );          /* Q14 */
+
+            /* Tilt measure */
+            if( speech_nrg < ( (opus_int32)1 << 20 ) ) {
+                /* Scale down SNR value for small subband speech energies */
+                SNR_Q7 = silk_SMULWB( silk_LSHIFT( silk_SQRT_APPROX( speech_nrg ), 6 ), SNR_Q7 );
+            }
+            input_tilt = silk_SMLAWB( input_tilt, tiltWeights[ b ], SNR_Q7 );
+        } else {
+            NrgToNoiseRatio_Q8[ b ] = 256;
+        }
+    }
+
+    /* Mean-of-squares */
+    sumSquared = silk_DIV32_16( sumSquared, VAD_N_BANDS ); /* Q14 */
+
+    /* Root-mean-square approximation, scale to dBs, and write to output pointer */
+    pSNR_dB_Q7 = (opus_int16)( 3 * silk_SQRT_APPROX( sumSquared ) ); /* Q7 */
+
+    /*********************************/
+    /* Speech Probability Estimation */
+    /*********************************/
+    SA_Q15 = silk_sigm_Q15( silk_SMULWB( VAD_SNR_FACTOR_Q16, pSNR_dB_Q7 ) - VAD_NEGATIVE_OFFSET_Q5 );
+
+    /**************************/
+    /* Frequency Tilt Measure */
+    /**************************/
+    psEncC->input_tilt_Q15 = silk_LSHIFT( silk_sigm_Q15( input_tilt ) - 16384, 1 );
+
+    /**************************************************/
+    /* Scale the sigmoid output based on power levels */
+    /**************************************************/
+    speech_nrg = 0;
+    for( b = 0; b < VAD_N_BANDS; b++ ) {
+        /* Accumulate signal-without-noise energies, higher frequency bands have more weight */
+        speech_nrg += ( b + 1 ) * silk_RSHIFT( Xnrg[ b ] - psSilk_VAD->NL[ b ], 4 );
+    }
+
+    if( psEncC->frame_length == 20 * psEncC->fs_kHz ) {
+        speech_nrg = silk_RSHIFT32( speech_nrg, 1 );
+    }
+    /* Power scaling */
+    if( speech_nrg <= 0 ) {
+        SA_Q15 = silk_RSHIFT( SA_Q15, 1 );
+    } else if( speech_nrg < 16384 ) {
+        speech_nrg = silk_LSHIFT32( speech_nrg, 16 );
+
+        /* square-root */
+        speech_nrg = silk_SQRT_APPROX( speech_nrg );
+        SA_Q15 = silk_SMULWB( 32768 + speech_nrg, SA_Q15 );
+    }
+
+    /* Copy the resulting speech activity in Q8 */
+    psEncC->speech_activity_Q8 = silk_min_int( silk_RSHIFT( SA_Q15, 7 ), silk_uint8_MAX );
+
+    /***********************************/
+    /* Energy Level and SNR estimation */
+    /***********************************/
+    /* Smoothing coefficient */
+    smooth_coef_Q16 = silk_SMULWB( VAD_SNR_SMOOTH_COEF_Q18, silk_SMULWB( (opus_int32)SA_Q15, SA_Q15 ) );
+
+    if( psEncC->frame_length == 10 * psEncC->fs_kHz ) {
+        smooth_coef_Q16 >>= 1;
+    }
+
+    for( b = 0; b < VAD_N_BANDS; b++ ) {
+        /* compute smoothed energy-to-noise ratio per band */
+        psSilk_VAD->NrgRatioSmth_Q8[ b ] = silk_SMLAWB( psSilk_VAD->NrgRatioSmth_Q8[ b ],
+            NrgToNoiseRatio_Q8[ b ] - psSilk_VAD->NrgRatioSmth_Q8[ b ], smooth_coef_Q16 );
+
+        /* signal to noise ratio in dB per band */
+        SNR_Q7 = 3 * ( silk_lin2log( psSilk_VAD->NrgRatioSmth_Q8[b] ) - 8 * 128 );
+        /* quality = sigmoid( 0.25 * ( SNR_dB - 16 ) ); */
+        psEncC->input_quality_bands_Q15[ b ] = silk_sigm_Q15( silk_RSHIFT( SNR_Q7 - 16 * 128, 4 ) );
+    }
+
+    RESTORE_STACK;
+    return( ret );
+}
+
+/**************************/
+/* Noise level estimation */
+/**************************/
+# if  !defined(OPUS_X86_MAY_HAVE_SSE4_1)
+static OPUS_INLINE
+#endif
+void silk_VAD_GetNoiseLevels(
+    const opus_int32            pX[ VAD_N_BANDS ],  /* I    subband energies                            */
+    silk_VAD_state              *psSilk_VAD         /* I/O  Pointer to Silk VAD state                   */
+)
+{
+    opus_int   k;
+    opus_int32 nl, nrg, inv_nrg;
+    opus_int   coef, min_coef;
+
+    /* Initially faster smoothing */
+    if( psSilk_VAD->counter < 1000 ) { /* 1000 = 20 sec */
+        min_coef = silk_DIV32_16( silk_int16_MAX, silk_RSHIFT( psSilk_VAD->counter, 4 ) + 1 );
+        /* Increment frame counter */
+        psSilk_VAD->counter++;
+    } else {
+        min_coef = 0;
+    }
+
+    for( k = 0; k < VAD_N_BANDS; k++ ) {
+        /* Get old noise level estimate for current band */
+        nl = psSilk_VAD->NL[ k ];
+        silk_assert( nl >= 0 );
+
+        /* Add bias */
+        nrg = silk_ADD_POS_SAT32( pX[ k ], psSilk_VAD->NoiseLevelBias[ k ] );
+        silk_assert( nrg > 0 );
+
+        /* Invert energies */
+        inv_nrg = silk_DIV32( silk_int32_MAX, nrg );
+        silk_assert( inv_nrg >= 0 );
+
+        /* Less update when subband energy is high */
+        if( nrg > silk_LSHIFT( nl, 3 ) ) {
+            coef = VAD_NOISE_LEVEL_SMOOTH_COEF_Q16 >> 3;
+        } else if( nrg < nl ) {
+            coef = VAD_NOISE_LEVEL_SMOOTH_COEF_Q16;
+        } else {
+            coef = silk_SMULWB( silk_SMULWW( inv_nrg, nl ), VAD_NOISE_LEVEL_SMOOTH_COEF_Q16 << 1 );
+        }
+
+        /* Initially faster smoothing */
+        coef = silk_max_int( coef, min_coef );
+
+        /* Smooth inverse energies */
+        psSilk_VAD->inv_NL[ k ] = silk_SMLAWB( psSilk_VAD->inv_NL[ k ], inv_nrg - psSilk_VAD->inv_NL[ k ], coef );
+        silk_assert( psSilk_VAD->inv_NL[ k ] >= 0 );
+
+        /* Compute noise level by inverting again */
+        nl = silk_DIV32( silk_int32_MAX, psSilk_VAD->inv_NL[ k ] );
+        silk_assert( nl >= 0 );
+
+        /* Limit noise levels (guarantee 7 bits of head room) */
+        nl = silk_min( nl, 0x00FFFFFF );
+
+        /* Store as part of state */
+        psSilk_VAD->NL[ k ] = nl;
+    }
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/VQ_WMat_EC.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/VQ_WMat_EC.c
new file mode 100644 (file)
index 0000000..245a7e4
--- /dev/null
@@ -0,0 +1,131 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main.h"
+
+/* Entropy constrained matrix-weighted VQ, hard-coded to 5-element vectors, for a single input data vector */
+void silk_VQ_WMat_EC_c(
+    opus_int8                   *ind,                           /* O    index of best codebook vector               */
+    opus_int32                  *res_nrg_Q15,                   /* O    best residual energy                        */
+    opus_int32                  *rate_dist_Q8,                  /* O    best total bitrate                          */
+    opus_int                    *gain_Q7,                       /* O    sum of absolute LTP coefficients            */
+    const opus_int32            *XX_Q17,                        /* I    correlation matrix                          */
+    const opus_int32            *xX_Q17,                        /* I    correlation vector                          */
+    const opus_int8             *cb_Q7,                         /* I    codebook                                    */
+    const opus_uint8            *cb_gain_Q7,                    /* I    codebook effective gain                     */
+    const opus_uint8            *cl_Q5,                         /* I    code length for each codebook vector        */
+    const opus_int              subfr_len,                      /* I    number of samples per subframe              */
+    const opus_int32            max_gain_Q7,                    /* I    maximum sum of absolute LTP coefficients    */
+    const opus_int              L                               /* I    number of vectors in codebook               */
+)
+{
+    opus_int   k, gain_tmp_Q7;
+    const opus_int8 *cb_row_Q7;
+    opus_int32 neg_xX_Q24[ 5 ];
+    opus_int32 sum1_Q15, sum2_Q24;
+    opus_int32 bits_res_Q8, bits_tot_Q8;
+
+    /* Negate and convert to new Q domain */
+    neg_xX_Q24[ 0 ] = -silk_LSHIFT32( xX_Q17[ 0 ], 7 );
+    neg_xX_Q24[ 1 ] = -silk_LSHIFT32( xX_Q17[ 1 ], 7 );
+    neg_xX_Q24[ 2 ] = -silk_LSHIFT32( xX_Q17[ 2 ], 7 );
+    neg_xX_Q24[ 3 ] = -silk_LSHIFT32( xX_Q17[ 3 ], 7 );
+    neg_xX_Q24[ 4 ] = -silk_LSHIFT32( xX_Q17[ 4 ], 7 );
+
+    /* Loop over codebook */
+    *rate_dist_Q8 = silk_int32_MAX;
+    *res_nrg_Q15 = silk_int32_MAX;
+    cb_row_Q7 = cb_Q7;
+    /* If things go really bad, at least *ind is set to something safe. */
+    *ind = 0;
+    for( k = 0; k < L; k++ ) {
+        opus_int32 penalty;
+        gain_tmp_Q7 = cb_gain_Q7[k];
+        /* Weighted rate */
+        /* Quantization error: 1 - 2 * xX * cb + cb' * XX * cb */
+        sum1_Q15 = SILK_FIX_CONST( 1.001, 15 );
+
+        /* Penalty for too large gain */
+        penalty = silk_LSHIFT32( silk_max( silk_SUB32( gain_tmp_Q7, max_gain_Q7 ), 0 ), 11 );
+
+        /* first row of XX_Q17 */
+        sum2_Q24 = silk_MLA( neg_xX_Q24[ 0 ], XX_Q17[  1 ], cb_row_Q7[ 1 ] );
+        sum2_Q24 = silk_MLA( sum2_Q24,        XX_Q17[  2 ], cb_row_Q7[ 2 ] );
+        sum2_Q24 = silk_MLA( sum2_Q24,        XX_Q17[  3 ], cb_row_Q7[ 3 ] );
+        sum2_Q24 = silk_MLA( sum2_Q24,        XX_Q17[  4 ], cb_row_Q7[ 4 ] );
+        sum2_Q24 = silk_LSHIFT32( sum2_Q24, 1 );
+        sum2_Q24 = silk_MLA( sum2_Q24,        XX_Q17[  0 ], cb_row_Q7[ 0 ] );
+        sum1_Q15 = silk_SMLAWB( sum1_Q15,        sum2_Q24,  cb_row_Q7[ 0 ] );
+
+        /* second row of XX_Q17 */
+        sum2_Q24 = silk_MLA( neg_xX_Q24[ 1 ], XX_Q17[  7 ], cb_row_Q7[ 2 ] );
+        sum2_Q24 = silk_MLA( sum2_Q24,        XX_Q17[  8 ], cb_row_Q7[ 3 ] );
+        sum2_Q24 = silk_MLA( sum2_Q24,        XX_Q17[  9 ], cb_row_Q7[ 4 ] );
+        sum2_Q24 = silk_LSHIFT32( sum2_Q24, 1 );
+        sum2_Q24 = silk_MLA( sum2_Q24,        XX_Q17[  6 ], cb_row_Q7[ 1 ] );
+        sum1_Q15 = silk_SMLAWB( sum1_Q15,        sum2_Q24,  cb_row_Q7[ 1 ] );
+
+        /* third row of XX_Q17 */
+        sum2_Q24 = silk_MLA( neg_xX_Q24[ 2 ], XX_Q17[ 13 ], cb_row_Q7[ 3 ] );
+        sum2_Q24 = silk_MLA( sum2_Q24,        XX_Q17[ 14 ], cb_row_Q7[ 4 ] );
+        sum2_Q24 = silk_LSHIFT32( sum2_Q24, 1 );
+        sum2_Q24 = silk_MLA( sum2_Q24,        XX_Q17[ 12 ], cb_row_Q7[ 2 ] );
+        sum1_Q15 = silk_SMLAWB( sum1_Q15,        sum2_Q24,  cb_row_Q7[ 2 ] );
+
+        /* fourth row of XX_Q17 */
+        sum2_Q24 = silk_MLA( neg_xX_Q24[ 3 ], XX_Q17[ 19 ], cb_row_Q7[ 4 ] );
+        sum2_Q24 = silk_LSHIFT32( sum2_Q24, 1 );
+        sum2_Q24 = silk_MLA( sum2_Q24,        XX_Q17[ 18 ], cb_row_Q7[ 3 ] );
+        sum1_Q15 = silk_SMLAWB( sum1_Q15,        sum2_Q24,  cb_row_Q7[ 3 ] );
+
+        /* last row of XX_Q17 */
+        sum2_Q24 = silk_LSHIFT32( neg_xX_Q24[ 4 ], 1 );
+        sum2_Q24 = silk_MLA( sum2_Q24,        XX_Q17[ 24 ], cb_row_Q7[ 4 ] );
+        sum1_Q15 = silk_SMLAWB( sum1_Q15,        sum2_Q24,  cb_row_Q7[ 4 ] );
+
+        /* find best */
+        if( sum1_Q15 >= 0 ) {
+            /* Translate residual energy to bits using high-rate assumption (6 dB ==> 1 bit/sample) */
+            bits_res_Q8 = silk_SMULBB( subfr_len, silk_lin2log( sum1_Q15 + penalty) - (15 << 7) );
+            /* In the following line we reduce the codelength component by half ("-1"); seems to slightly improve quality */
+            bits_tot_Q8 = silk_ADD_LSHIFT32( bits_res_Q8, cl_Q5[ k ], 3-1 );
+            if( bits_tot_Q8 <= *rate_dist_Q8 ) {
+                *rate_dist_Q8 = bits_tot_Q8;
+                *res_nrg_Q15 = sum1_Q15 + penalty;
+                *ind = (opus_int8)k;
+                *gain_Q7 = gain_tmp_Q7;
+            }
+        }
+
+        /* Go to next cbk vector */
+        cb_row_Q7 += LTP_ORDER;
+    }
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/ana_filt_bank_1.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/ana_filt_bank_1.c
new file mode 100644 (file)
index 0000000..3907f8c
--- /dev/null
@@ -0,0 +1,74 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "SigProc_FIX.h"
+
+/* Coefficients for 2-band filter bank based on first-order allpass filters */
+static const opus_int16 A_fb1_20 = 5394 << 1;
+static const opus_int16 A_fb1_21 = -24290; /* (opus_int16)(20623 << 1) */
+
+/* Split signal into two decimated bands using first-order allpass filters */
+void silk_ana_filt_bank_1(
+    const opus_int16            *in,                /* I    Input signal [N]                                            */
+    opus_int32                  *S,                 /* I/O  State vector [2]                                            */
+    opus_int16                  *outL,              /* O    Low band [N/2]                                              */
+    opus_int16                  *outH,              /* O    High band [N/2]                                             */
+    const opus_int32            N                   /* I    Number of input samples                                     */
+)
+{
+    opus_int      k, N2 = silk_RSHIFT( N, 1 );
+    opus_int32    in32, X, Y, out_1, out_2;
+
+    /* Internal variables and state are in Q10 format */
+    for( k = 0; k < N2; k++ ) {
+        /* Convert to Q10 */
+        in32 = silk_LSHIFT( (opus_int32)in[ 2 * k ], 10 );
+
+        /* All-pass section for even input sample */
+        Y      = silk_SUB32( in32, S[ 0 ] );
+        X      = silk_SMLAWB( Y, Y, A_fb1_21 );
+        out_1  = silk_ADD32( S[ 0 ], X );
+        S[ 0 ] = silk_ADD32( in32, X );
+
+        /* Convert to Q10 */
+        in32 = silk_LSHIFT( (opus_int32)in[ 2 * k + 1 ], 10 );
+
+        /* All-pass section for odd input sample, and add to output of previous section */
+        Y      = silk_SUB32( in32, S[ 1 ] );
+        X      = silk_SMULWB( Y, A_fb1_20 );
+        out_2  = silk_ADD32( S[ 1 ], X );
+        S[ 1 ] = silk_ADD32( in32, X );
+
+        /* Add/subtract, convert back to int16 and store to output */
+        outL[ k ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( silk_ADD32( out_2, out_1 ), 11 ) );
+        outH[ k ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( silk_SUB32( out_2, out_1 ), 11 ) );
+    }
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/check_control_input.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/check_control_input.c
new file mode 100644 (file)
index 0000000..b4e96e7
--- /dev/null
@@ -0,0 +1,109 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main.h"
+#include "control.h"
+#include "errors.h"
+
+/* Check encoder control struct */
+opus_int check_control_input(
+    silk_EncControlStruct        *encControl                    /* I    Control structure                           */
+)
+{
+    celt_assert( encControl != NULL );
+
+    if( ( ( encControl->API_sampleRate            !=  8000 ) &&
+          ( encControl->API_sampleRate            != 12000 ) &&
+          ( encControl->API_sampleRate            != 16000 ) &&
+          ( encControl->API_sampleRate            != 24000 ) &&
+          ( encControl->API_sampleRate            != 32000 ) &&
+          ( encControl->API_sampleRate            != 44100 ) &&
+#ifdef ENABLE_QEXT
+          ( encControl->API_sampleRate            != 96000 ) &&
+#endif
+          ( encControl->API_sampleRate            != 48000 ) ) ||
+        ( ( encControl->desiredInternalSampleRate !=  8000 ) &&
+          ( encControl->desiredInternalSampleRate != 12000 ) &&
+          ( encControl->desiredInternalSampleRate != 16000 ) ) ||
+        ( ( encControl->maxInternalSampleRate     !=  8000 ) &&
+          ( encControl->maxInternalSampleRate     != 12000 ) &&
+          ( encControl->maxInternalSampleRate     != 16000 ) ) ||
+        ( ( encControl->minInternalSampleRate     !=  8000 ) &&
+          ( encControl->minInternalSampleRate     != 12000 ) &&
+          ( encControl->minInternalSampleRate     != 16000 ) ) ||
+          ( encControl->minInternalSampleRate > encControl->desiredInternalSampleRate ) ||
+          ( encControl->maxInternalSampleRate < encControl->desiredInternalSampleRate ) ||
+          ( encControl->minInternalSampleRate > encControl->maxInternalSampleRate ) ) {
+        celt_assert( 0 );
+        return SILK_ENC_FS_NOT_SUPPORTED;
+    }
+    if( encControl->payloadSize_ms != 10 &&
+        encControl->payloadSize_ms != 20 &&
+        encControl->payloadSize_ms != 40 &&
+        encControl->payloadSize_ms != 60 ) {
+        celt_assert( 0 );
+        return SILK_ENC_PACKET_SIZE_NOT_SUPPORTED;
+    }
+    if( encControl->packetLossPercentage < 0 || encControl->packetLossPercentage > 100 ) {
+        celt_assert( 0 );
+        return SILK_ENC_INVALID_LOSS_RATE;
+    }
+    if( encControl->useDTX < 0 || encControl->useDTX > 1 ) {
+        celt_assert( 0 );
+        return SILK_ENC_INVALID_DTX_SETTING;
+    }
+    if( encControl->useCBR < 0 || encControl->useCBR > 1 ) {
+        celt_assert( 0 );
+        return SILK_ENC_INVALID_CBR_SETTING;
+    }
+    if( encControl->useInBandFEC < 0 || encControl->useInBandFEC > 1 ) {
+        celt_assert( 0 );
+        return SILK_ENC_INVALID_INBAND_FEC_SETTING;
+    }
+    if( encControl->nChannelsAPI < 1 || encControl->nChannelsAPI > ENCODER_NUM_CHANNELS ) {
+        celt_assert( 0 );
+        return SILK_ENC_INVALID_NUMBER_OF_CHANNELS_ERROR;
+    }
+    if( encControl->nChannelsInternal < 1 || encControl->nChannelsInternal > ENCODER_NUM_CHANNELS ) {
+        celt_assert( 0 );
+        return SILK_ENC_INVALID_NUMBER_OF_CHANNELS_ERROR;
+    }
+    if( encControl->nChannelsInternal > encControl->nChannelsAPI ) {
+        celt_assert( 0 );
+        return SILK_ENC_INVALID_NUMBER_OF_CHANNELS_ERROR;
+    }
+    if( encControl->complexity < 0 || encControl->complexity > 10 ) {
+        celt_assert( 0 );
+        return SILK_ENC_INVALID_COMPLEXITY_SETTING;
+    }
+
+    return SILK_NO_ERROR;
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/control_SNR.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/control_SNR.c
new file mode 100644 (file)
index 0000000..455acad
--- /dev/null
@@ -0,0 +1,113 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main.h"
+#include "tuning_parameters.h"
+
+/* These tables hold SNR values divided by 21 (so they fit in 8 bits)
+   for different target bitrates spaced at 400 bps interval. The first
+   10 values are omitted (0-4 kb/s) because they're all zeros.
+   These tables were obtained by running different SNRs through the
+   encoder and measuring the active bitrate. */
+static const unsigned char silk_TargetRate_NB_21[117 - 10] = {
+                                              0, 15, 39, 52, 61, 68,
+     74, 79, 84, 88, 92, 95, 99,102,105,108,111,114,117,119,122,124,
+    126,129,131,133,135,137,139,142,143,145,147,149,151,153,155,157,
+    158,160,162,163,165,167,168,170,171,173,174,176,177,179,180,182,
+    183,185,186,187,189,190,192,193,194,196,197,199,200,201,203,204,
+    205,207,208,209,211,212,213,215,216,217,219,220,221,223,224,225,
+    227,228,230,231,232,234,235,236,238,239,241,242,243,245,246,248,
+    249,250,252,253,255
+};
+
+static const unsigned char silk_TargetRate_MB_21[165 - 10] = {
+                                              0,  0, 28, 43, 52, 59,
+     65, 70, 74, 78, 81, 85, 87, 90, 93, 95, 98,100,102,105,107,109,
+    111,113,115,116,118,120,122,123,125,127,128,130,131,133,134,136,
+    137,138,140,141,143,144,145,147,148,149,151,152,153,154,156,157,
+    158,159,160,162,163,164,165,166,167,168,169,171,172,173,174,175,
+    176,177,178,179,180,181,182,183,184,185,186,187,188,188,189,190,
+    191,192,193,194,195,196,197,198,199,200,201,202,203,203,204,205,
+    206,207,208,209,210,211,212,213,214,214,215,216,217,218,219,220,
+    221,222,223,224,224,225,226,227,228,229,230,231,232,233,234,235,
+    236,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,
+    251,252,253,254,255
+};
+
+static const unsigned char silk_TargetRate_WB_21[201 - 10] = {
+                                              0,  0,  0,  8, 29, 41,
+     49, 56, 62, 66, 70, 74, 77, 80, 83, 86, 88, 91, 93, 95, 97, 99,
+    101,103,105,107,108,110,112,113,115,116,118,119,121,122,123,125,
+    126,127,129,130,131,132,134,135,136,137,138,140,141,142,143,144,
+    145,146,147,148,149,150,151,152,153,154,156,157,158,159,159,160,
+    161,162,163,164,165,166,167,168,169,170,171,171,172,173,174,175,
+    176,177,177,178,179,180,181,181,182,183,184,185,185,186,187,188,
+    189,189,190,191,192,192,193,194,195,195,196,197,198,198,199,200,
+    200,201,202,203,203,204,205,206,206,207,208,209,209,210,211,211,
+    212,213,214,214,215,216,216,217,218,219,219,220,221,221,222,223,
+    224,224,225,226,226,227,228,229,229,230,231,232,232,233,234,234,
+    235,236,237,237,238,239,240,240,241,242,243,243,244,245,246,246,
+    247,248,249,249,250,251,252,253,255
+};
+
+/* Control SNR of residual quantizer */
+opus_int silk_control_SNR(
+    silk_encoder_state          *psEncC,                        /* I/O  Pointer to Silk encoder state               */
+    opus_int32                  TargetRate_bps                  /* I    Target max bitrate (bps)                    */
+)
+{
+    int id;
+    int bound;
+    const unsigned char *snr_table;
+
+    psEncC->TargetRate_bps = TargetRate_bps;
+    if( psEncC->nb_subfr == 2 ) {
+        TargetRate_bps -= 2000 + psEncC->fs_kHz/16;
+    }
+    if( psEncC->fs_kHz == 8 ) {
+        bound = sizeof(silk_TargetRate_NB_21);
+        snr_table = silk_TargetRate_NB_21;
+    } else if( psEncC->fs_kHz == 12 ) {
+        bound = sizeof(silk_TargetRate_MB_21);
+        snr_table = silk_TargetRate_MB_21;
+    } else {
+        bound = sizeof(silk_TargetRate_WB_21);
+        snr_table = silk_TargetRate_WB_21;
+    }
+    id = (TargetRate_bps+200)/400;
+    id = silk_min(id - 10, bound-1);
+    if( id <= 0 ) {
+        psEncC->SNR_dB_Q7 = 0;
+    } else {
+        psEncC->SNR_dB_Q7 = snr_table[id]*21;
+    }
+    return SILK_NO_ERROR;
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/control_audio_bandwidth.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/control_audio_bandwidth.c
new file mode 100644 (file)
index 0000000..f6d22d8
--- /dev/null
@@ -0,0 +1,132 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main.h"
+#include "tuning_parameters.h"
+
+/* Control internal sampling rate */
+opus_int silk_control_audio_bandwidth(
+    silk_encoder_state          *psEncC,                        /* I/O  Pointer to Silk encoder state               */
+    silk_EncControlStruct       *encControl                     /* I    Control structure                           */
+)
+{
+    opus_int   fs_kHz;
+    opus_int   orig_kHz;
+    opus_int32 fs_Hz;
+
+    orig_kHz = psEncC->fs_kHz;
+    /* Handle a bandwidth-switching reset where we need to be aware what the last sampling rate was. */
+    if( orig_kHz == 0 ) {
+        orig_kHz = psEncC->sLP.saved_fs_kHz;
+    }
+    fs_kHz = orig_kHz;
+    fs_Hz = silk_SMULBB( fs_kHz, 1000 );
+    if( fs_Hz == 0 ) {
+        /* Encoder has just been initialized */
+        fs_Hz  = silk_min( psEncC->desiredInternal_fs_Hz, psEncC->API_fs_Hz );
+        fs_kHz = silk_DIV32_16( fs_Hz, 1000 );
+    } else if( fs_Hz > psEncC->API_fs_Hz || fs_Hz > psEncC->maxInternal_fs_Hz || fs_Hz < psEncC->minInternal_fs_Hz ) {
+        /* Make sure internal rate is not higher than external rate or maximum allowed, or lower than minimum allowed */
+        fs_Hz  = psEncC->API_fs_Hz;
+        fs_Hz  = silk_min( fs_Hz, psEncC->maxInternal_fs_Hz );
+        fs_Hz  = silk_max( fs_Hz, psEncC->minInternal_fs_Hz );
+        fs_kHz = silk_DIV32_16( fs_Hz, 1000 );
+    } else {
+        /* State machine for the internal sampling rate switching */
+        if( psEncC->sLP.transition_frame_no >= TRANSITION_FRAMES ) {
+            /* Stop transition phase */
+            psEncC->sLP.mode = 0;
+        }
+        if( psEncC->allow_bandwidth_switch || encControl->opusCanSwitch ) {
+            /* Check if we should switch down */
+            if( silk_SMULBB( orig_kHz, 1000 ) > psEncC->desiredInternal_fs_Hz )
+            {
+                /* Switch down */
+                if( psEncC->sLP.mode == 0 ) {
+                    /* New transition */
+                    psEncC->sLP.transition_frame_no = TRANSITION_FRAMES;
+
+                    /* Reset transition filter state */
+                    silk_memset( psEncC->sLP.In_LP_State, 0, sizeof( psEncC->sLP.In_LP_State ) );
+                }
+                if( encControl->opusCanSwitch ) {
+                    /* Stop transition phase */
+                    psEncC->sLP.mode = 0;
+
+                    /* Switch to a lower sample frequency */
+                    fs_kHz = orig_kHz == 16 ? 12 : 8;
+                } else {
+                   if( psEncC->sLP.transition_frame_no <= 0 ) {
+                       encControl->switchReady = 1;
+                       /* Make room for redundancy */
+                       encControl->maxBits -= encControl->maxBits * 5 / ( encControl->payloadSize_ms + 5 );
+                   } else {
+                       /* Direction: down (at double speed) */
+                       psEncC->sLP.mode = -2;
+                   }
+                }
+            }
+            else
+            /* Check if we should switch up */
+            if( silk_SMULBB( orig_kHz, 1000 ) < psEncC->desiredInternal_fs_Hz )
+            {
+                /* Switch up */
+                if( encControl->opusCanSwitch ) {
+                    /* Switch to a higher sample frequency */
+                    fs_kHz = orig_kHz == 8 ? 12 : 16;
+
+                    /* New transition */
+                    psEncC->sLP.transition_frame_no = 0;
+
+                    /* Reset transition filter state */
+                    silk_memset( psEncC->sLP.In_LP_State, 0, sizeof( psEncC->sLP.In_LP_State ) );
+
+                    /* Direction: up */
+                    psEncC->sLP.mode = 1;
+                } else {
+                   if( psEncC->sLP.mode == 0 ) {
+                       encControl->switchReady = 1;
+                       /* Make room for redundancy */
+                       encControl->maxBits -= encControl->maxBits * 5 / ( encControl->payloadSize_ms + 5 );
+                   } else {
+                       /* Direction: up */
+                       psEncC->sLP.mode = 1;
+                   }
+                }
+            } else {
+               if (psEncC->sLP.mode<0)
+                  psEncC->sLP.mode = 1;
+            }
+        }
+    }
+
+    return fs_kHz;
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/control_codec.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/control_codec.c
new file mode 100644 (file)
index 0000000..784ffe6
--- /dev/null
@@ -0,0 +1,423 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#ifdef FIXED_POINT
+#include "main_FIX.h"
+#define silk_encoder_state_Fxx      silk_encoder_state_FIX
+#else
+#include "main_FLP.h"
+#define silk_encoder_state_Fxx      silk_encoder_state_FLP
+#endif
+#include "stack_alloc.h"
+#include "tuning_parameters.h"
+#include "pitch_est_defines.h"
+
+static opus_int silk_setup_resamplers(
+    silk_encoder_state_Fxx          *psEnc,             /* I/O                      */
+    opus_int                        fs_kHz              /* I                        */
+);
+
+static opus_int silk_setup_fs(
+    silk_encoder_state_Fxx          *psEnc,             /* I/O                      */
+    opus_int                        fs_kHz,             /* I                        */
+    opus_int                        PacketSize_ms       /* I                        */
+);
+
+static opus_int silk_setup_complexity(
+    silk_encoder_state              *psEncC,            /* I/O                      */
+    opus_int                        Complexity          /* I                        */
+);
+
+static OPUS_INLINE opus_int silk_setup_LBRR(
+    silk_encoder_state              *psEncC,            /* I/O                      */
+    const silk_EncControlStruct     *encControl         /* I                        */
+);
+
+
+/* Control encoder */
+opus_int silk_control_encoder(
+    silk_encoder_state_Fxx          *psEnc,                                 /* I/O  Pointer to Silk encoder state                                               */
+    silk_EncControlStruct           *encControl,                            /* I    Control structure                                                           */
+    const opus_int                  allow_bw_switch,                        /* I    Flag to allow switching audio bandwidth                                     */
+    const opus_int                  channelNb,                              /* I    Channel number                                                              */
+    const opus_int                  force_fs_kHz
+)
+{
+    opus_int   fs_kHz, ret = 0;
+
+    psEnc->sCmn.useDTX                 = encControl->useDTX;
+    psEnc->sCmn.useCBR                 = encControl->useCBR;
+    psEnc->sCmn.API_fs_Hz              = encControl->API_sampleRate;
+    psEnc->sCmn.maxInternal_fs_Hz      = encControl->maxInternalSampleRate;
+    psEnc->sCmn.minInternal_fs_Hz      = encControl->minInternalSampleRate;
+    psEnc->sCmn.desiredInternal_fs_Hz  = encControl->desiredInternalSampleRate;
+    psEnc->sCmn.useInBandFEC           = encControl->useInBandFEC;
+    psEnc->sCmn.nChannelsAPI           = encControl->nChannelsAPI;
+    psEnc->sCmn.nChannelsInternal      = encControl->nChannelsInternal;
+    psEnc->sCmn.allow_bandwidth_switch = allow_bw_switch;
+    psEnc->sCmn.channelNb              = channelNb;
+
+    if( psEnc->sCmn.controlled_since_last_payload != 0 && psEnc->sCmn.prefillFlag == 0 ) {
+        if( psEnc->sCmn.API_fs_Hz != psEnc->sCmn.prev_API_fs_Hz && psEnc->sCmn.fs_kHz > 0 ) {
+            /* Change in API sampling rate in the middle of encoding a packet */
+            ret += silk_setup_resamplers( psEnc, psEnc->sCmn.fs_kHz );
+        }
+        return ret;
+    }
+
+    /* Beyond this point we know that there are no previously coded frames in the payload buffer */
+
+    /********************************************/
+    /* Determine internal sampling rate         */
+    /********************************************/
+    fs_kHz = silk_control_audio_bandwidth( &psEnc->sCmn, encControl );
+    if( force_fs_kHz ) {
+       fs_kHz = force_fs_kHz;
+    }
+    /********************************************/
+    /* Prepare resampler and buffered data      */
+    /********************************************/
+    ret += silk_setup_resamplers( psEnc, fs_kHz );
+
+    /********************************************/
+    /* Set internal sampling frequency          */
+    /********************************************/
+    ret += silk_setup_fs( psEnc, fs_kHz, encControl->payloadSize_ms );
+
+    /********************************************/
+    /* Set encoding complexity                  */
+    /********************************************/
+    ret += silk_setup_complexity( &psEnc->sCmn, encControl->complexity  );
+
+    /********************************************/
+    /* Set packet loss rate measured by farend  */
+    /********************************************/
+    psEnc->sCmn.PacketLoss_perc = encControl->packetLossPercentage;
+
+    /********************************************/
+    /* Set LBRR usage                           */
+    /********************************************/
+    ret += silk_setup_LBRR( &psEnc->sCmn, encControl );
+
+    psEnc->sCmn.controlled_since_last_payload = 1;
+
+    return ret;
+}
+
+static opus_int silk_setup_resamplers(
+    silk_encoder_state_Fxx          *psEnc,             /* I/O                      */
+    opus_int                         fs_kHz              /* I                        */
+)
+{
+    opus_int   ret = SILK_NO_ERROR;
+    SAVE_STACK;
+
+    if( psEnc->sCmn.fs_kHz != fs_kHz || psEnc->sCmn.prev_API_fs_Hz != psEnc->sCmn.API_fs_Hz )
+    {
+        if( psEnc->sCmn.fs_kHz == 0 ) {
+            /* Initialize the resampler for enc_API.c preparing resampling from API_fs_Hz to fs_kHz */
+            ret += silk_resampler_init( &psEnc->sCmn.resampler_state, psEnc->sCmn.API_fs_Hz, fs_kHz * 1000, 1 );
+        } else {
+            VARDECL( opus_int16, x_buf_API_fs_Hz );
+            VARDECL( silk_resampler_state_struct, temp_resampler_state );
+#ifdef FIXED_POINT
+            opus_int16 *x_bufFIX = psEnc->x_buf;
+#else
+            VARDECL( opus_int16, x_bufFIX );
+            opus_int32 new_buf_samples;
+#endif
+            opus_int32 api_buf_samples;
+            opus_int32 old_buf_samples;
+            opus_int32 buf_length_ms;
+
+            buf_length_ms = silk_LSHIFT( psEnc->sCmn.nb_subfr * 5, 1 ) + LA_SHAPE_MS;
+            old_buf_samples = buf_length_ms * psEnc->sCmn.fs_kHz;
+
+#ifndef FIXED_POINT
+            new_buf_samples = buf_length_ms * fs_kHz;
+            ALLOC( x_bufFIX, silk_max( old_buf_samples, new_buf_samples ),
+                   opus_int16 );
+            silk_float2short_array( x_bufFIX, psEnc->x_buf, old_buf_samples );
+#endif
+
+            /* Initialize resampler for temporary resampling of x_buf data to API_fs_Hz */
+            ALLOC( temp_resampler_state, 1, silk_resampler_state_struct );
+            ret += silk_resampler_init( temp_resampler_state, silk_SMULBB( psEnc->sCmn.fs_kHz, 1000 ), psEnc->sCmn.API_fs_Hz, 0 );
+
+            /* Calculate number of samples to temporarily upsample */
+            api_buf_samples = buf_length_ms * silk_DIV32_16( psEnc->sCmn.API_fs_Hz, 1000 );
+
+            /* Temporary resampling of x_buf data to API_fs_Hz */
+            ALLOC( x_buf_API_fs_Hz, api_buf_samples, opus_int16 );
+            ret += silk_resampler( temp_resampler_state, x_buf_API_fs_Hz, x_bufFIX, old_buf_samples );
+
+            /* Initialize the resampler for enc_API.c preparing resampling from API_fs_Hz to fs_kHz */
+            ret += silk_resampler_init( &psEnc->sCmn.resampler_state, psEnc->sCmn.API_fs_Hz, silk_SMULBB( fs_kHz, 1000 ), 1 );
+
+            /* Correct resampler state by resampling buffered data from API_fs_Hz to fs_kHz */
+            ret += silk_resampler( &psEnc->sCmn.resampler_state, x_bufFIX, x_buf_API_fs_Hz, api_buf_samples );
+
+#ifndef FIXED_POINT
+            silk_short2float_array( psEnc->x_buf, x_bufFIX, new_buf_samples);
+#endif
+        }
+    }
+
+    psEnc->sCmn.prev_API_fs_Hz = psEnc->sCmn.API_fs_Hz;
+
+    RESTORE_STACK;
+    return ret;
+}
+
+static opus_int silk_setup_fs(
+    silk_encoder_state_Fxx          *psEnc,             /* I/O                      */
+    opus_int                        fs_kHz,             /* I                        */
+    opus_int                        PacketSize_ms       /* I                        */
+)
+{
+    opus_int ret = SILK_NO_ERROR;
+
+    /* Set packet size */
+    if( PacketSize_ms != psEnc->sCmn.PacketSize_ms ) {
+        if( ( PacketSize_ms !=  10 ) &&
+            ( PacketSize_ms !=  20 ) &&
+            ( PacketSize_ms !=  40 ) &&
+            ( PacketSize_ms !=  60 ) ) {
+            ret = SILK_ENC_PACKET_SIZE_NOT_SUPPORTED;
+        }
+        if( PacketSize_ms <= 10 ) {
+            psEnc->sCmn.nFramesPerPacket = 1;
+            psEnc->sCmn.nb_subfr = PacketSize_ms == 10 ? 2 : 1;
+            psEnc->sCmn.frame_length = silk_SMULBB( PacketSize_ms, fs_kHz );
+            psEnc->sCmn.pitch_LPC_win_length = silk_SMULBB( FIND_PITCH_LPC_WIN_MS_2_SF, fs_kHz );
+            if( psEnc->sCmn.fs_kHz == 8 ) {
+                psEnc->sCmn.pitch_contour_iCDF = silk_pitch_contour_10_ms_NB_iCDF;
+            } else {
+                psEnc->sCmn.pitch_contour_iCDF = silk_pitch_contour_10_ms_iCDF;
+            }
+        } else {
+            psEnc->sCmn.nFramesPerPacket = silk_DIV32_16( PacketSize_ms, MAX_FRAME_LENGTH_MS );
+            psEnc->sCmn.nb_subfr = MAX_NB_SUBFR;
+            psEnc->sCmn.frame_length = silk_SMULBB( 20, fs_kHz );
+            psEnc->sCmn.pitch_LPC_win_length = silk_SMULBB( FIND_PITCH_LPC_WIN_MS, fs_kHz );
+            if( psEnc->sCmn.fs_kHz == 8 ) {
+                psEnc->sCmn.pitch_contour_iCDF = silk_pitch_contour_NB_iCDF;
+            } else {
+                psEnc->sCmn.pitch_contour_iCDF = silk_pitch_contour_iCDF;
+            }
+        }
+        psEnc->sCmn.PacketSize_ms  = PacketSize_ms;
+        psEnc->sCmn.TargetRate_bps = 0;         /* trigger new SNR computation */
+    }
+
+    /* Set internal sampling frequency */
+    celt_assert( fs_kHz == 8 || fs_kHz == 12 || fs_kHz == 16 );
+    celt_assert( psEnc->sCmn.nb_subfr == 2 || psEnc->sCmn.nb_subfr == 4 );
+    if( psEnc->sCmn.fs_kHz != fs_kHz ) {
+        /* reset part of the state */
+        silk_memset( &psEnc->sShape,               0, sizeof( psEnc->sShape ) );
+        silk_memset( &psEnc->sCmn.sNSQ,            0, sizeof( psEnc->sCmn.sNSQ ) );
+        silk_memset( psEnc->sCmn.prev_NLSFq_Q15,   0, sizeof( psEnc->sCmn.prev_NLSFq_Q15 ) );
+        silk_memset( &psEnc->sCmn.sLP.In_LP_State, 0, sizeof( psEnc->sCmn.sLP.In_LP_State ) );
+        psEnc->sCmn.inputBufIx                  = 0;
+        psEnc->sCmn.nFramesEncoded              = 0;
+        psEnc->sCmn.TargetRate_bps              = 0;     /* trigger new SNR computation */
+
+        /* Initialize non-zero parameters */
+        psEnc->sCmn.prevLag                     = 100;
+        psEnc->sCmn.first_frame_after_reset     = 1;
+        psEnc->sShape.LastGainIndex             = 10;
+        psEnc->sCmn.sNSQ.lagPrev                = 100;
+        psEnc->sCmn.sNSQ.prev_gain_Q16          = 65536;
+        psEnc->sCmn.prevSignalType              = TYPE_NO_VOICE_ACTIVITY;
+
+        psEnc->sCmn.fs_kHz = fs_kHz;
+        if( psEnc->sCmn.fs_kHz == 8 ) {
+            if( psEnc->sCmn.nb_subfr == MAX_NB_SUBFR ) {
+                psEnc->sCmn.pitch_contour_iCDF = silk_pitch_contour_NB_iCDF;
+            } else {
+                psEnc->sCmn.pitch_contour_iCDF = silk_pitch_contour_10_ms_NB_iCDF;
+            }
+        } else {
+            if( psEnc->sCmn.nb_subfr == MAX_NB_SUBFR ) {
+                psEnc->sCmn.pitch_contour_iCDF = silk_pitch_contour_iCDF;
+            } else {
+                psEnc->sCmn.pitch_contour_iCDF = silk_pitch_contour_10_ms_iCDF;
+            }
+        }
+        if( psEnc->sCmn.fs_kHz == 8 || psEnc->sCmn.fs_kHz == 12 ) {
+            psEnc->sCmn.predictLPCOrder = MIN_LPC_ORDER;
+            psEnc->sCmn.psNLSF_CB  = &silk_NLSF_CB_NB_MB;
+        } else {
+            psEnc->sCmn.predictLPCOrder = MAX_LPC_ORDER;
+            psEnc->sCmn.psNLSF_CB  = &silk_NLSF_CB_WB;
+        }
+        psEnc->sCmn.subfr_length   = SUB_FRAME_LENGTH_MS * fs_kHz;
+        psEnc->sCmn.frame_length   = silk_SMULBB( psEnc->sCmn.subfr_length, psEnc->sCmn.nb_subfr );
+        psEnc->sCmn.ltp_mem_length = silk_SMULBB( LTP_MEM_LENGTH_MS, fs_kHz );
+        psEnc->sCmn.la_pitch       = silk_SMULBB( LA_PITCH_MS, fs_kHz );
+        psEnc->sCmn.max_pitch_lag  = silk_SMULBB( 18, fs_kHz );
+        if( psEnc->sCmn.nb_subfr == MAX_NB_SUBFR ) {
+            psEnc->sCmn.pitch_LPC_win_length = silk_SMULBB( FIND_PITCH_LPC_WIN_MS, fs_kHz );
+        } else {
+            psEnc->sCmn.pitch_LPC_win_length = silk_SMULBB( FIND_PITCH_LPC_WIN_MS_2_SF, fs_kHz );
+        }
+        if( psEnc->sCmn.fs_kHz == 16 ) {
+            psEnc->sCmn.pitch_lag_low_bits_iCDF = silk_uniform8_iCDF;
+        } else if( psEnc->sCmn.fs_kHz == 12 ) {
+            psEnc->sCmn.pitch_lag_low_bits_iCDF = silk_uniform6_iCDF;
+        } else {
+            psEnc->sCmn.pitch_lag_low_bits_iCDF = silk_uniform4_iCDF;
+        }
+    }
+
+    /* Check that settings are valid */
+    celt_assert( ( psEnc->sCmn.subfr_length * psEnc->sCmn.nb_subfr ) == psEnc->sCmn.frame_length );
+
+    return ret;
+}
+
+static opus_int silk_setup_complexity(
+    silk_encoder_state              *psEncC,            /* I/O                      */
+    opus_int                        Complexity          /* I                        */
+)
+{
+    opus_int ret = 0;
+
+    /* Set encoding complexity */
+    celt_assert( Complexity >= 0 && Complexity <= 10 );
+    if( Complexity < 1 ) {
+        psEncC->pitchEstimationComplexity       = SILK_PE_MIN_COMPLEX;
+        psEncC->pitchEstimationThreshold_Q16    = SILK_FIX_CONST( 0.8, 16 );
+        psEncC->pitchEstimationLPCOrder         = 6;
+        psEncC->shapingLPCOrder                 = 12;
+        psEncC->la_shape                        = 3 * psEncC->fs_kHz;
+        psEncC->nStatesDelayedDecision          = 1;
+        psEncC->useInterpolatedNLSFs            = 0;
+        psEncC->NLSF_MSVQ_Survivors             = 2;
+        psEncC->warping_Q16                     = 0;
+    } else if( Complexity < 2 ) {
+        psEncC->pitchEstimationComplexity       = SILK_PE_MID_COMPLEX;
+        psEncC->pitchEstimationThreshold_Q16    = SILK_FIX_CONST( 0.76, 16 );
+        psEncC->pitchEstimationLPCOrder         = 8;
+        psEncC->shapingLPCOrder                 = 14;
+        psEncC->la_shape                        = 5 * psEncC->fs_kHz;
+        psEncC->nStatesDelayedDecision          = 1;
+        psEncC->useInterpolatedNLSFs            = 0;
+        psEncC->NLSF_MSVQ_Survivors             = 3;
+        psEncC->warping_Q16                     = 0;
+    } else if( Complexity < 3 ) {
+        psEncC->pitchEstimationComplexity       = SILK_PE_MIN_COMPLEX;
+        psEncC->pitchEstimationThreshold_Q16    = SILK_FIX_CONST( 0.8, 16 );
+        psEncC->pitchEstimationLPCOrder         = 6;
+        psEncC->shapingLPCOrder                 = 12;
+        psEncC->la_shape                        = 3 * psEncC->fs_kHz;
+        psEncC->nStatesDelayedDecision          = 2;
+        psEncC->useInterpolatedNLSFs            = 0;
+        psEncC->NLSF_MSVQ_Survivors             = 2;
+        psEncC->warping_Q16                     = 0;
+    } else if( Complexity < 4 ) {
+        psEncC->pitchEstimationComplexity       = SILK_PE_MID_COMPLEX;
+        psEncC->pitchEstimationThreshold_Q16    = SILK_FIX_CONST( 0.76, 16 );
+        psEncC->pitchEstimationLPCOrder         = 8;
+        psEncC->shapingLPCOrder                 = 14;
+        psEncC->la_shape                        = 5 * psEncC->fs_kHz;
+        psEncC->nStatesDelayedDecision          = 2;
+        psEncC->useInterpolatedNLSFs            = 0;
+        psEncC->NLSF_MSVQ_Survivors             = 4;
+        psEncC->warping_Q16                     = 0;
+    } else if( Complexity < 6 ) {
+        psEncC->pitchEstimationComplexity       = SILK_PE_MID_COMPLEX;
+        psEncC->pitchEstimationThreshold_Q16    = SILK_FIX_CONST( 0.74, 16 );
+        psEncC->pitchEstimationLPCOrder         = 10;
+        psEncC->shapingLPCOrder                 = 16;
+        psEncC->la_shape                        = 5 * psEncC->fs_kHz;
+        psEncC->nStatesDelayedDecision          = 2;
+        psEncC->useInterpolatedNLSFs            = 1;
+        psEncC->NLSF_MSVQ_Survivors             = 6;
+        psEncC->warping_Q16                     = psEncC->fs_kHz * SILK_FIX_CONST( WARPING_MULTIPLIER, 16 );
+    } else if( Complexity < 8 ) {
+        psEncC->pitchEstimationComplexity       = SILK_PE_MID_COMPLEX;
+        psEncC->pitchEstimationThreshold_Q16    = SILK_FIX_CONST( 0.72, 16 );
+        psEncC->pitchEstimationLPCOrder         = 12;
+        psEncC->shapingLPCOrder                 = 20;
+        psEncC->la_shape                        = 5 * psEncC->fs_kHz;
+        psEncC->nStatesDelayedDecision          = 3;
+        psEncC->useInterpolatedNLSFs            = 1;
+        psEncC->NLSF_MSVQ_Survivors             = 8;
+        psEncC->warping_Q16                     = psEncC->fs_kHz * SILK_FIX_CONST( WARPING_MULTIPLIER, 16 );
+    } else {
+        psEncC->pitchEstimationComplexity       = SILK_PE_MAX_COMPLEX;
+        psEncC->pitchEstimationThreshold_Q16    = SILK_FIX_CONST( 0.7, 16 );
+        psEncC->pitchEstimationLPCOrder         = 16;
+        psEncC->shapingLPCOrder                 = 24;
+        psEncC->la_shape                        = 5 * psEncC->fs_kHz;
+        psEncC->nStatesDelayedDecision          = MAX_DEL_DEC_STATES;
+        psEncC->useInterpolatedNLSFs            = 1;
+        psEncC->NLSF_MSVQ_Survivors             = 16;
+        psEncC->warping_Q16                     = psEncC->fs_kHz * SILK_FIX_CONST( WARPING_MULTIPLIER, 16 );
+    }
+
+    /* Do not allow higher pitch estimation LPC order than predict LPC order */
+    psEncC->pitchEstimationLPCOrder = silk_min_int( psEncC->pitchEstimationLPCOrder, psEncC->predictLPCOrder );
+    psEncC->shapeWinLength          = SUB_FRAME_LENGTH_MS * psEncC->fs_kHz + 2 * psEncC->la_shape;
+    psEncC->Complexity              = Complexity;
+
+    celt_assert( psEncC->pitchEstimationLPCOrder <= MAX_FIND_PITCH_LPC_ORDER );
+    celt_assert( psEncC->shapingLPCOrder         <= MAX_SHAPE_LPC_ORDER      );
+    celt_assert( psEncC->nStatesDelayedDecision  <= MAX_DEL_DEC_STATES       );
+    celt_assert( psEncC->warping_Q16             <= 32767                    );
+    celt_assert( psEncC->la_shape                <= LA_SHAPE_MAX             );
+    celt_assert( psEncC->shapeWinLength          <= SHAPE_LPC_WIN_MAX        );
+
+    return ret;
+}
+
+static OPUS_INLINE opus_int silk_setup_LBRR(
+    silk_encoder_state          *psEncC,            /* I/O                      */
+    const silk_EncControlStruct *encControl         /* I                        */
+)
+{
+    opus_int   LBRR_in_previous_packet, ret = SILK_NO_ERROR;
+
+    LBRR_in_previous_packet = psEncC->LBRR_enabled;
+    psEncC->LBRR_enabled = encControl->LBRR_coded;
+    if( psEncC->LBRR_enabled ) {
+        /* Set gain increase for coding LBRR excitation */
+        if( LBRR_in_previous_packet == 0 ) {
+            /* Previous packet did not have LBRR, and was therefore coded at a higher bitrate */
+            psEncC->LBRR_GainIncreases = 7;
+        } else {
+            psEncC->LBRR_GainIncreases = silk_max_int( 7 - silk_SMULWB( (opus_int32)psEncC->PacketLoss_perc, SILK_FIX_CONST( 0.2, 16 ) ), 3 );
+        }
+    }
+
+    return ret;
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/enc_API.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/enc_API.c
new file mode 100644 (file)
index 0000000..d0fee4d
--- /dev/null
@@ -0,0 +1,601 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "define.h"
+#include "API.h"
+#include "control.h"
+#include "typedef.h"
+#include "stack_alloc.h"
+#include "structs.h"
+#include "tuning_parameters.h"
+#ifdef FIXED_POINT
+#include "main_FIX.h"
+#else
+#include "main_FLP.h"
+#endif
+
+#ifdef ENABLE_DRED
+#include "dred_encoder.h"
+#endif
+
+/***************************************/
+/* Read control structure from encoder */
+/***************************************/
+static opus_int silk_QueryEncoder(                      /* O    Returns error code                              */
+    const void                      *encState,          /* I    State                                           */
+    silk_EncControlStruct           *encStatus          /* O    Encoder Status                                  */
+);
+
+/****************************************/
+/* Encoder functions                    */
+/****************************************/
+
+opus_int silk_Get_Encoder_Size(                         /* O    Returns error code                              */
+    opus_int                        *encSizeBytes,      /* O    Number of bytes in SILK encoder state           */
+    opus_int                         channels           /* I    Number of channels                              */
+)
+{
+    opus_int ret = SILK_NO_ERROR;
+
+    *encSizeBytes = sizeof( silk_encoder );
+    /* Skip second encoder state for mono. */
+    if ( channels == 1 ) {
+        *encSizeBytes -= sizeof( silk_encoder_state_Fxx );
+    }
+
+    return ret;
+}
+
+/*************************/
+/* Init or Reset encoder */
+/*************************/
+opus_int silk_InitEncoder(                              /* O    Returns error code                              */
+    void                            *encState,          /* I/O  State                                           */
+    int                              channels,          /* I    Number of channels                              */
+    int                              arch,              /* I    Run-time architecture                           */
+    silk_EncControlStruct           *encStatus          /* O    Encoder Status                                  */
+)
+{
+    silk_encoder *psEnc;
+    opus_int n, ret = SILK_NO_ERROR;
+
+    psEnc = (silk_encoder *)encState;
+
+    /* Reset encoder. Skip second encoder state for mono. */
+    silk_memset( psEnc, 0, sizeof( silk_encoder ) - (channels==1)*sizeof( silk_encoder_state_Fxx ) );
+    for( n = 0; n < channels; n++ ) {
+        if( ret += silk_init_encoder( &psEnc->state_Fxx[ n ], arch ) ) {
+            celt_assert( 0 );
+        }
+    }
+
+    psEnc->nChannelsAPI = 1;
+    psEnc->nChannelsInternal = 1;
+
+    /* Read control structure */
+    if( ret += silk_QueryEncoder( encState, encStatus ) ) {
+        celt_assert( 0 );
+    }
+
+    return ret;
+}
+
+/***************************************/
+/* Read control structure from encoder */
+/***************************************/
+static opus_int silk_QueryEncoder(                      /* O    Returns error code                              */
+    const void                      *encState,          /* I    State                                           */
+    silk_EncControlStruct           *encStatus          /* O    Encoder Status                                  */
+)
+{
+    opus_int ret = SILK_NO_ERROR;
+    silk_encoder_state_Fxx *state_Fxx;
+    silk_encoder *psEnc = (silk_encoder *)encState;
+
+    state_Fxx = psEnc->state_Fxx;
+
+    encStatus->nChannelsAPI              = psEnc->nChannelsAPI;
+    encStatus->nChannelsInternal         = psEnc->nChannelsInternal;
+    encStatus->API_sampleRate            = state_Fxx[ 0 ].sCmn.API_fs_Hz;
+    encStatus->maxInternalSampleRate     = state_Fxx[ 0 ].sCmn.maxInternal_fs_Hz;
+    encStatus->minInternalSampleRate     = state_Fxx[ 0 ].sCmn.minInternal_fs_Hz;
+    encStatus->desiredInternalSampleRate = state_Fxx[ 0 ].sCmn.desiredInternal_fs_Hz;
+    encStatus->payloadSize_ms            = state_Fxx[ 0 ].sCmn.PacketSize_ms;
+    encStatus->bitRate                   = state_Fxx[ 0 ].sCmn.TargetRate_bps;
+    encStatus->packetLossPercentage      = state_Fxx[ 0 ].sCmn.PacketLoss_perc;
+    encStatus->complexity                = state_Fxx[ 0 ].sCmn.Complexity;
+    encStatus->useInBandFEC              = state_Fxx[ 0 ].sCmn.useInBandFEC;
+    encStatus->useDTX                    = state_Fxx[ 0 ].sCmn.useDTX;
+    encStatus->useCBR                    = state_Fxx[ 0 ].sCmn.useCBR;
+    encStatus->internalSampleRate        = silk_SMULBB( state_Fxx[ 0 ].sCmn.fs_kHz, 1000 );
+    encStatus->allowBandwidthSwitch      = state_Fxx[ 0 ].sCmn.allow_bandwidth_switch;
+    encStatus->inWBmodeWithoutVariableLP = state_Fxx[ 0 ].sCmn.fs_kHz == 16 && state_Fxx[ 0 ].sCmn.sLP.mode == 0;
+
+    return ret;
+}
+
+
+/**************************/
+/* Encode frame with Silk */
+/**************************/
+/* Note: if prefillFlag is set, the input must contain 10 ms of audio, irrespective of what                     */
+/* encControl->payloadSize_ms is set to                                                                         */
+opus_int silk_Encode(                                   /* O    Returns error code                              */
+    void                            *encState,          /* I/O  State                                           */
+    silk_EncControlStruct           *encControl,        /* I    Control status                                  */
+    const opus_res                  *samplesIn,         /* I    Speech sample input vector                      */
+    opus_int                        nSamplesIn,         /* I    Number of samples in input vector               */
+    ec_enc                          *psRangeEnc,        /* I/O  Compressor data structure                       */
+    opus_int32                      *nBytesOut,         /* I/O  Number of bytes in payload (input: Max bytes)   */
+    const opus_int                  prefillFlag,        /* I    Flag to indicate prefilling buffers no coding   */
+    opus_int                        activity            /* I    Decision of Opus voice activity detector        */
+)
+{
+    opus_int   n, i, nBits, flags, tmp_payloadSize_ms = 0, tmp_complexity = 0, ret = 0;
+    opus_int   nSamplesToBuffer, nSamplesToBufferMax, nBlocksOf10ms;
+    opus_int   nSamplesFromInput = 0, nSamplesFromInputMax;
+    opus_int   speech_act_thr_for_switch_Q8;
+    opus_int32 TargetRate_bps, MStargetRates_bps[ 2 ], channelRate_bps, LBRR_symbol, sum;
+    silk_encoder *psEnc = ( silk_encoder * )encState;
+    VARDECL( opus_int16, buf );
+    opus_int transition, curr_block, tot_blocks;
+    SAVE_STACK;
+
+    celt_assert( encControl->nChannelsAPI >= encControl->nChannelsInternal && encControl->nChannelsAPI >= psEnc->nChannelsInternal );
+    if (encControl->reducedDependency)
+    {
+       for( n = 0; n < encControl->nChannelsAPI; n++ ) {
+           psEnc->state_Fxx[ n ].sCmn.first_frame_after_reset = 1;
+       }
+    }
+    for( n = 0; n < encControl->nChannelsAPI; n++ ) {
+        psEnc->state_Fxx[ n ].sCmn.nFramesEncoded = 0;
+    }
+    /* Check values in encoder control structure */
+    if( ( ret = check_control_input( encControl ) ) != 0 ) {
+        celt_assert( 0 );
+        RESTORE_STACK;
+        return ret;
+    }
+
+    encControl->switchReady = 0;
+
+    if( encControl->nChannelsInternal > psEnc->nChannelsInternal ) {
+        /* Mono -> Stereo transition: init state of second channel and stereo state */
+        ret += silk_init_encoder( &psEnc->state_Fxx[ 1 ], psEnc->state_Fxx[ 0 ].sCmn.arch );
+        silk_memset( psEnc->sStereo.pred_prev_Q13, 0, sizeof( psEnc->sStereo.pred_prev_Q13 ) );
+        silk_memset( psEnc->sStereo.sSide, 0, sizeof( psEnc->sStereo.sSide ) );
+        psEnc->sStereo.mid_side_amp_Q0[ 0 ] = 0;
+        psEnc->sStereo.mid_side_amp_Q0[ 1 ] = 1;
+        psEnc->sStereo.mid_side_amp_Q0[ 2 ] = 0;
+        psEnc->sStereo.mid_side_amp_Q0[ 3 ] = 1;
+        psEnc->sStereo.width_prev_Q14 = 0;
+        psEnc->sStereo.smth_width_Q14 = SILK_FIX_CONST( 1, 14 );
+        if( psEnc->nChannelsAPI == 2 ) {
+            silk_memcpy( &psEnc->state_Fxx[ 1 ].sCmn.resampler_state, &psEnc->state_Fxx[ 0 ].sCmn.resampler_state, sizeof( silk_resampler_state_struct ) );
+            silk_memcpy( &psEnc->state_Fxx[ 1 ].sCmn.In_HP_State,     &psEnc->state_Fxx[ 0 ].sCmn.In_HP_State,     sizeof( psEnc->state_Fxx[ 1 ].sCmn.In_HP_State ) );
+        }
+    }
+
+    transition = (encControl->payloadSize_ms != psEnc->state_Fxx[ 0 ].sCmn.PacketSize_ms) || (psEnc->nChannelsInternal != encControl->nChannelsInternal);
+
+    psEnc->nChannelsAPI = encControl->nChannelsAPI;
+    psEnc->nChannelsInternal = encControl->nChannelsInternal;
+
+    nBlocksOf10ms = silk_DIV32( 100 * nSamplesIn, encControl->API_sampleRate );
+    tot_blocks = ( nBlocksOf10ms > 1 ) ? nBlocksOf10ms >> 1 : 1;
+    curr_block = 0;
+    if( prefillFlag ) {
+        silk_LP_state save_LP;
+        /* Only accept input length of 10 ms */
+        if( nBlocksOf10ms != 1 ) {
+            celt_assert( 0 );
+            RESTORE_STACK;
+            return SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES;
+        }
+        if ( prefillFlag == 2 ) {
+            save_LP = psEnc->state_Fxx[ 0 ].sCmn.sLP;
+            /* Save the sampling rate so the bandwidth switching code can keep handling transitions. */
+            save_LP.saved_fs_kHz = psEnc->state_Fxx[ 0 ].sCmn.fs_kHz;
+        }
+        /* Reset Encoder */
+        for( n = 0; n < encControl->nChannelsInternal; n++ ) {
+            ret = silk_init_encoder( &psEnc->state_Fxx[ n ], psEnc->state_Fxx[ n ].sCmn.arch );
+            /* Restore the variable LP state. */
+            if ( prefillFlag == 2 ) {
+                psEnc->state_Fxx[ n ].sCmn.sLP = save_LP;
+            }
+            celt_assert( !ret );
+        }
+        tmp_payloadSize_ms = encControl->payloadSize_ms;
+        encControl->payloadSize_ms = 10;
+        tmp_complexity = encControl->complexity;
+        encControl->complexity = 0;
+        for( n = 0; n < encControl->nChannelsInternal; n++ ) {
+            psEnc->state_Fxx[ n ].sCmn.controlled_since_last_payload = 0;
+            psEnc->state_Fxx[ n ].sCmn.prefillFlag = 1;
+        }
+    } else {
+        /* Only accept input lengths that are a multiple of 10 ms */
+        if( nBlocksOf10ms * encControl->API_sampleRate != 100 * nSamplesIn || nSamplesIn < 0 ) {
+            celt_assert( 0 );
+            RESTORE_STACK;
+            return SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES;
+        }
+        /* Make sure no more than one packet can be produced */
+        if( 1000 * (opus_int32)nSamplesIn > encControl->payloadSize_ms * encControl->API_sampleRate ) {
+            celt_assert( 0 );
+            RESTORE_STACK;
+            return SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES;
+        }
+    }
+
+    for( n = 0; n < encControl->nChannelsInternal; n++ ) {
+        /* Force the side channel to the same rate as the mid */
+        opus_int force_fs_kHz = (n==1) ? psEnc->state_Fxx[0].sCmn.fs_kHz : 0;
+        if( ( ret = silk_control_encoder( &psEnc->state_Fxx[ n ], encControl, psEnc->allowBandwidthSwitch, n, force_fs_kHz ) ) != 0 ) {
+            silk_assert( 0 );
+            RESTORE_STACK;
+            return ret;
+        }
+        if( psEnc->state_Fxx[n].sCmn.first_frame_after_reset || transition ) {
+            for( i = 0; i < psEnc->state_Fxx[ 0 ].sCmn.nFramesPerPacket; i++ ) {
+                psEnc->state_Fxx[ n ].sCmn.LBRR_flags[ i ] = 0;
+            }
+        }
+        psEnc->state_Fxx[ n ].sCmn.inDTX = psEnc->state_Fxx[ n ].sCmn.useDTX;
+    }
+    celt_assert( encControl->nChannelsInternal == 1 || psEnc->state_Fxx[ 0 ].sCmn.fs_kHz == psEnc->state_Fxx[ 1 ].sCmn.fs_kHz );
+
+    /* Input buffering/resampling and encoding */
+    nSamplesToBufferMax =
+        10 * nBlocksOf10ms * psEnc->state_Fxx[ 0 ].sCmn.fs_kHz;
+    nSamplesFromInputMax =
+        silk_DIV32_16( nSamplesToBufferMax *
+                           psEnc->state_Fxx[ 0 ].sCmn.API_fs_Hz,
+                       psEnc->state_Fxx[ 0 ].sCmn.fs_kHz * 1000 );
+    ALLOC( buf, nSamplesFromInputMax, opus_int16 );
+    while( 1 ) {
+        int curr_nBitsUsedLBRR = 0;
+        nSamplesToBuffer  = psEnc->state_Fxx[ 0 ].sCmn.frame_length - psEnc->state_Fxx[ 0 ].sCmn.inputBufIx;
+        nSamplesToBuffer  = silk_min( nSamplesToBuffer, nSamplesToBufferMax );
+        nSamplesFromInput = silk_DIV32_16( nSamplesToBuffer * psEnc->state_Fxx[ 0 ].sCmn.API_fs_Hz, psEnc->state_Fxx[ 0 ].sCmn.fs_kHz * 1000 );
+        /* Resample and write to buffer */
+        if( encControl->nChannelsAPI == 2 && encControl->nChannelsInternal == 2 ) {
+            opus_int id = psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded;
+            for( n = 0; n < nSamplesFromInput; n++ ) {
+                buf[ n ] = RES2INT16(samplesIn[ 2 * n ]);
+            }
+            /* Making sure to start both resamplers from the same state when switching from mono to stereo */
+            if( psEnc->nPrevChannelsInternal == 1 && id==0 ) {
+               silk_memcpy( &psEnc->state_Fxx[ 1 ].sCmn.resampler_state, &psEnc->state_Fxx[ 0 ].sCmn.resampler_state, sizeof(psEnc->state_Fxx[ 1 ].sCmn.resampler_state));
+            }
+
+            ret += silk_resampler( &psEnc->state_Fxx[ 0 ].sCmn.resampler_state,
+                &psEnc->state_Fxx[ 0 ].sCmn.inputBuf[ psEnc->state_Fxx[ 0 ].sCmn.inputBufIx + 2 ], buf, nSamplesFromInput );
+            psEnc->state_Fxx[ 0 ].sCmn.inputBufIx += nSamplesToBuffer;
+
+            nSamplesToBuffer  = psEnc->state_Fxx[ 1 ].sCmn.frame_length - psEnc->state_Fxx[ 1 ].sCmn.inputBufIx;
+            nSamplesToBuffer  = silk_min( nSamplesToBuffer, 10 * nBlocksOf10ms * psEnc->state_Fxx[ 1 ].sCmn.fs_kHz );
+            for( n = 0; n < nSamplesFromInput; n++ ) {
+                buf[ n ] = RES2INT16(samplesIn[ 2 * n + 1 ]);
+            }
+            ret += silk_resampler( &psEnc->state_Fxx[ 1 ].sCmn.resampler_state,
+                &psEnc->state_Fxx[ 1 ].sCmn.inputBuf[ psEnc->state_Fxx[ 1 ].sCmn.inputBufIx + 2 ], buf, nSamplesFromInput );
+
+            psEnc->state_Fxx[ 1 ].sCmn.inputBufIx += nSamplesToBuffer;
+        } else if( encControl->nChannelsAPI == 2 && encControl->nChannelsInternal == 1 ) {
+            /* Combine left and right channels before resampling */
+            for( n = 0; n < nSamplesFromInput; n++ ) {
+                sum = RES2INT16(samplesIn[ 2 * n ] + samplesIn[ 2 * n + 1 ]);
+                buf[ n ] = (opus_int16)silk_RSHIFT_ROUND( sum,  1 );
+            }
+            ret += silk_resampler( &psEnc->state_Fxx[ 0 ].sCmn.resampler_state,
+                &psEnc->state_Fxx[ 0 ].sCmn.inputBuf[ psEnc->state_Fxx[ 0 ].sCmn.inputBufIx + 2 ], buf, nSamplesFromInput );
+            /* On the first mono frame, average the results for the two resampler states  */
+            if( psEnc->nPrevChannelsInternal == 2 && psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded == 0 ) {
+               ret += silk_resampler( &psEnc->state_Fxx[ 1 ].sCmn.resampler_state,
+                   &psEnc->state_Fxx[ 1 ].sCmn.inputBuf[ psEnc->state_Fxx[ 1 ].sCmn.inputBufIx + 2 ], buf, nSamplesFromInput );
+               for( n = 0; n < psEnc->state_Fxx[ 0 ].sCmn.frame_length; n++ ) {
+                  psEnc->state_Fxx[ 0 ].sCmn.inputBuf[ psEnc->state_Fxx[ 0 ].sCmn.inputBufIx+n+2 ] =
+                        silk_RSHIFT(psEnc->state_Fxx[ 0 ].sCmn.inputBuf[ psEnc->state_Fxx[ 0 ].sCmn.inputBufIx+n+2 ]
+                                  + psEnc->state_Fxx[ 1 ].sCmn.inputBuf[ psEnc->state_Fxx[ 1 ].sCmn.inputBufIx+n+2 ], 1);
+               }
+            }
+            psEnc->state_Fxx[ 0 ].sCmn.inputBufIx += nSamplesToBuffer;
+        } else {
+            celt_assert( encControl->nChannelsAPI == 1 && encControl->nChannelsInternal == 1 );
+            for( n = 0; n < nSamplesFromInput; n++ ) {
+                buf[n] = RES2INT16(samplesIn[n]);
+            }
+            ret += silk_resampler( &psEnc->state_Fxx[ 0 ].sCmn.resampler_state,
+                &psEnc->state_Fxx[ 0 ].sCmn.inputBuf[ psEnc->state_Fxx[ 0 ].sCmn.inputBufIx + 2 ], buf, nSamplesFromInput );
+            psEnc->state_Fxx[ 0 ].sCmn.inputBufIx += nSamplesToBuffer;
+        }
+
+        samplesIn  += nSamplesFromInput * encControl->nChannelsAPI;
+        nSamplesIn -= nSamplesFromInput;
+
+        /* Default */
+        psEnc->allowBandwidthSwitch = 0;
+
+        /* Silk encoder */
+        if( psEnc->state_Fxx[ 0 ].sCmn.inputBufIx >= psEnc->state_Fxx[ 0 ].sCmn.frame_length ) {
+            /* Enough data in input buffer, so encode */
+            celt_assert( psEnc->state_Fxx[ 0 ].sCmn.inputBufIx == psEnc->state_Fxx[ 0 ].sCmn.frame_length );
+            celt_assert( encControl->nChannelsInternal == 1 || psEnc->state_Fxx[ 1 ].sCmn.inputBufIx == psEnc->state_Fxx[ 1 ].sCmn.frame_length );
+
+            /* Deal with LBRR data */
+            if( psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded == 0 && !prefillFlag ) {
+                /* Create space at start of payload for VAD and FEC flags */
+                opus_uint8 iCDF[ 2 ] = { 0, 0 };
+                iCDF[ 0 ] = 256 - silk_RSHIFT( 256, ( psEnc->state_Fxx[ 0 ].sCmn.nFramesPerPacket + 1 ) * encControl->nChannelsInternal );
+                ec_enc_icdf( psRangeEnc, 0, iCDF, 8 );
+                curr_nBitsUsedLBRR = ec_tell( psRangeEnc );
+
+                /* Encode any LBRR data from previous packet */
+                /* Encode LBRR flags */
+                for( n = 0; n < encControl->nChannelsInternal; n++ ) {
+                    LBRR_symbol = 0;
+                    for( i = 0; i < psEnc->state_Fxx[ n ].sCmn.nFramesPerPacket; i++ ) {
+                        LBRR_symbol |= silk_LSHIFT( psEnc->state_Fxx[ n ].sCmn.LBRR_flags[ i ], i );
+                    }
+                    psEnc->state_Fxx[ n ].sCmn.LBRR_flag = LBRR_symbol > 0 ? 1 : 0;
+                    if( LBRR_symbol && psEnc->state_Fxx[ n ].sCmn.nFramesPerPacket > 1 ) {
+                        ec_enc_icdf( psRangeEnc, LBRR_symbol - 1, silk_LBRR_flags_iCDF_ptr[ psEnc->state_Fxx[ n ].sCmn.nFramesPerPacket - 2 ], 8 );
+                    }
+                }
+
+                /* Code LBRR indices and excitation signals */
+                for( i = 0; i < psEnc->state_Fxx[ 0 ].sCmn.nFramesPerPacket; i++ ) {
+                    for( n = 0; n < encControl->nChannelsInternal; n++ ) {
+                        if( psEnc->state_Fxx[ n ].sCmn.LBRR_flags[ i ] ) {
+                            opus_int condCoding;
+
+                            if( encControl->nChannelsInternal == 2 && n == 0 ) {
+                                silk_stereo_encode_pred( psRangeEnc, psEnc->sStereo.predIx[ i ] );
+                                /* For LBRR data there's no need to code the mid-only flag if the side-channel LBRR flag is set */
+                                if( psEnc->state_Fxx[ 1 ].sCmn.LBRR_flags[ i ] == 0 ) {
+                                    silk_stereo_encode_mid_only( psRangeEnc, psEnc->sStereo.mid_only_flags[ i ] );
+                                }
+                            }
+                            /* Use conditional coding if previous frame available */
+                            if( i > 0 && psEnc->state_Fxx[ n ].sCmn.LBRR_flags[ i - 1 ] ) {
+                                condCoding = CODE_CONDITIONALLY;
+                            } else {
+                                condCoding = CODE_INDEPENDENTLY;
+                            }
+                            silk_encode_indices( &psEnc->state_Fxx[ n ].sCmn, psRangeEnc, i, 1, condCoding );
+                            silk_encode_pulses( psRangeEnc, psEnc->state_Fxx[ n ].sCmn.indices_LBRR[i].signalType, psEnc->state_Fxx[ n ].sCmn.indices_LBRR[i].quantOffsetType,
+                                psEnc->state_Fxx[ n ].sCmn.pulses_LBRR[ i ], psEnc->state_Fxx[ n ].sCmn.frame_length );
+                        }
+                    }
+                }
+
+                /* Reset LBRR flags */
+                for( n = 0; n < encControl->nChannelsInternal; n++ ) {
+                    silk_memset( psEnc->state_Fxx[ n ].sCmn.LBRR_flags, 0, sizeof( psEnc->state_Fxx[ n ].sCmn.LBRR_flags ) );
+                }
+                curr_nBitsUsedLBRR = ec_tell( psRangeEnc ) - curr_nBitsUsedLBRR;
+            }
+
+            silk_HP_variable_cutoff( psEnc->state_Fxx );
+
+            /* Total target bits for packet */
+            nBits = silk_DIV32_16( silk_MUL( encControl->bitRate, encControl->payloadSize_ms ), 1000 );
+            /* Subtract bits used for LBRR */
+            if( !prefillFlag ) {
+                /* psEnc->nBitsUsedLBRR is an exponential moving average of the LBRR usage,
+                   except that for the first LBRR frame it does no averaging and for the first
+                   frame after after LBRR, it goes back to zero immediately. */
+                if ( curr_nBitsUsedLBRR < 10 ) {
+                    psEnc->nBitsUsedLBRR = 0;
+                } else if ( psEnc->nBitsUsedLBRR < 10) {
+                    psEnc->nBitsUsedLBRR = curr_nBitsUsedLBRR;
+                } else {
+                    psEnc->nBitsUsedLBRR = ( psEnc->nBitsUsedLBRR + curr_nBitsUsedLBRR ) / 2;
+                }
+                nBits -= psEnc->nBitsUsedLBRR;
+            }
+            /* Divide by number of uncoded frames left in packet */
+            nBits = silk_DIV32_16( nBits, psEnc->state_Fxx[ 0 ].sCmn.nFramesPerPacket );
+            /* Convert to bits/second */
+            if( encControl->payloadSize_ms == 10 ) {
+                TargetRate_bps = silk_SMULBB( nBits, 100 );
+            } else {
+                TargetRate_bps = silk_SMULBB( nBits, 50 );
+            }
+            /* Subtract fraction of bits in excess of target in previous frames and packets */
+            TargetRate_bps -= silk_DIV32_16( silk_MUL( psEnc->nBitsExceeded, 1000 ), BITRESERVOIR_DECAY_TIME_MS );
+            if( !prefillFlag && psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded > 0 ) {
+                /* Compare actual vs target bits so far in this packet */
+                opus_int32 bitsBalance = ec_tell( psRangeEnc ) - psEnc->nBitsUsedLBRR - nBits * psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded;
+                TargetRate_bps -= silk_DIV32_16( silk_MUL( bitsBalance, 1000 ), BITRESERVOIR_DECAY_TIME_MS );
+            }
+            /* Never exceed input bitrate */
+            TargetRate_bps = silk_LIMIT( TargetRate_bps, encControl->bitRate, 5000 );
+
+            /* Convert Left/Right to Mid/Side */
+            if( encControl->nChannelsInternal == 2 ) {
+                silk_stereo_LR_to_MS( &psEnc->sStereo, &psEnc->state_Fxx[ 0 ].sCmn.inputBuf[ 2 ], &psEnc->state_Fxx[ 1 ].sCmn.inputBuf[ 2 ],
+                    psEnc->sStereo.predIx[ psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded ], &psEnc->sStereo.mid_only_flags[ psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded ],
+                    MStargetRates_bps, TargetRate_bps, psEnc->state_Fxx[ 0 ].sCmn.speech_activity_Q8, encControl->toMono,
+                    psEnc->state_Fxx[ 0 ].sCmn.fs_kHz, psEnc->state_Fxx[ 0 ].sCmn.frame_length );
+                if( psEnc->sStereo.mid_only_flags[ psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded ] == 0 ) {
+                    /* Reset side channel encoder memory for first frame with side coding */
+                    if( psEnc->prev_decode_only_middle == 1 ) {
+                        silk_memset( &psEnc->state_Fxx[ 1 ].sShape,               0, sizeof( psEnc->state_Fxx[ 1 ].sShape ) );
+                        silk_memset( &psEnc->state_Fxx[ 1 ].sCmn.sNSQ,            0, sizeof( psEnc->state_Fxx[ 1 ].sCmn.sNSQ ) );
+                        silk_memset( psEnc->state_Fxx[ 1 ].sCmn.prev_NLSFq_Q15,   0, sizeof( psEnc->state_Fxx[ 1 ].sCmn.prev_NLSFq_Q15 ) );
+                        silk_memset( &psEnc->state_Fxx[ 1 ].sCmn.sLP.In_LP_State, 0, sizeof( psEnc->state_Fxx[ 1 ].sCmn.sLP.In_LP_State ) );
+                        psEnc->state_Fxx[ 1 ].sCmn.prevLag                 = 100;
+                        psEnc->state_Fxx[ 1 ].sCmn.sNSQ.lagPrev            = 100;
+                        psEnc->state_Fxx[ 1 ].sShape.LastGainIndex         = 10;
+                        psEnc->state_Fxx[ 1 ].sCmn.prevSignalType          = TYPE_NO_VOICE_ACTIVITY;
+                        psEnc->state_Fxx[ 1 ].sCmn.sNSQ.prev_gain_Q16      = 65536;
+                        psEnc->state_Fxx[ 1 ].sCmn.first_frame_after_reset = 1;
+                    }
+                    silk_encode_do_VAD_Fxx( &psEnc->state_Fxx[ 1 ], activity );
+                } else {
+                    psEnc->state_Fxx[ 1 ].sCmn.VAD_flags[ psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded ] = 0;
+                }
+                if( !prefillFlag ) {
+                    silk_stereo_encode_pred( psRangeEnc, psEnc->sStereo.predIx[ psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded ] );
+                    if( psEnc->state_Fxx[ 1 ].sCmn.VAD_flags[ psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded ] == 0 ) {
+                        silk_stereo_encode_mid_only( psRangeEnc, psEnc->sStereo.mid_only_flags[ psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded ] );
+                    }
+                }
+            } else {
+                /* Buffering */
+                silk_memcpy( psEnc->state_Fxx[ 0 ].sCmn.inputBuf, psEnc->sStereo.sMid, 2 * sizeof( opus_int16 ) );
+                silk_memcpy( psEnc->sStereo.sMid, &psEnc->state_Fxx[ 0 ].sCmn.inputBuf[ psEnc->state_Fxx[ 0 ].sCmn.frame_length ], 2 * sizeof( opus_int16 ) );
+            }
+            silk_encode_do_VAD_Fxx( &psEnc->state_Fxx[ 0 ], activity );
+
+            /* Encode */
+            for( n = 0; n < encControl->nChannelsInternal; n++ ) {
+                opus_int maxBits, useCBR;
+
+                /* Handling rate constraints */
+                maxBits = encControl->maxBits;
+                if( tot_blocks == 2 && curr_block == 0 ) {
+                    maxBits = maxBits * 3 / 5;
+                } else if( tot_blocks == 3 ) {
+                    if( curr_block == 0 ) {
+                        maxBits = maxBits * 2 / 5;
+                    } else if( curr_block == 1 ) {
+                        maxBits = maxBits * 3 / 4;
+                    }
+                }
+                useCBR = encControl->useCBR && curr_block == tot_blocks - 1;
+
+                if( encControl->nChannelsInternal == 1 ) {
+                    channelRate_bps = TargetRate_bps;
+                } else {
+                    channelRate_bps = MStargetRates_bps[ n ];
+                    if( n == 0 && MStargetRates_bps[ 1 ] > 0 ) {
+                        useCBR = 0;
+                        /* Give mid up to 1/2 of the max bits for that frame */
+                        maxBits -= encControl->maxBits / ( tot_blocks * 2 );
+                    }
+                }
+
+                if( channelRate_bps > 0 ) {
+                    opus_int condCoding;
+
+                    silk_control_SNR( &psEnc->state_Fxx[ n ].sCmn, channelRate_bps );
+
+                    /* Use independent coding if no previous frame available */
+                    if( psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded - n <= 0 ) {
+                        condCoding = CODE_INDEPENDENTLY;
+                    } else if( n > 0 && psEnc->prev_decode_only_middle ) {
+                        /* If we skipped a side frame in this packet, we don't
+                           need LTP scaling; the LTP state is well-defined. */
+                        condCoding = CODE_INDEPENDENTLY_NO_LTP_SCALING;
+                    } else {
+                        condCoding = CODE_CONDITIONALLY;
+                    }
+                    if( ( ret = silk_encode_frame_Fxx( &psEnc->state_Fxx[ n ], nBytesOut, psRangeEnc, condCoding, maxBits, useCBR ) ) != 0 ) {
+                        silk_assert( 0 );
+                    }
+                }
+                psEnc->state_Fxx[ n ].sCmn.controlled_since_last_payload = 0;
+                psEnc->state_Fxx[ n ].sCmn.inputBufIx = 0;
+                psEnc->state_Fxx[ n ].sCmn.nFramesEncoded++;
+            }
+            psEnc->prev_decode_only_middle = psEnc->sStereo.mid_only_flags[ psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded - 1 ];
+
+            /* Insert VAD and FEC flags at beginning of bitstream */
+            if( *nBytesOut > 0 && psEnc->state_Fxx[ 0 ].sCmn.nFramesEncoded == psEnc->state_Fxx[ 0 ].sCmn.nFramesPerPacket) {
+                flags = 0;
+                for( n = 0; n < encControl->nChannelsInternal; n++ ) {
+                    for( i = 0; i < psEnc->state_Fxx[ n ].sCmn.nFramesPerPacket; i++ ) {
+                        flags  = silk_LSHIFT( flags, 1 );
+                        flags |= psEnc->state_Fxx[ n ].sCmn.VAD_flags[ i ];
+                    }
+                    flags  = silk_LSHIFT( flags, 1 );
+                    flags |= psEnc->state_Fxx[ n ].sCmn.LBRR_flag;
+                }
+                if( !prefillFlag ) {
+                    ec_enc_patch_initial_bits( psRangeEnc, flags, ( psEnc->state_Fxx[ 0 ].sCmn.nFramesPerPacket + 1 ) * encControl->nChannelsInternal );
+                }
+
+                /* Return zero bytes if all channels DTXed */
+                if( psEnc->state_Fxx[ 0 ].sCmn.inDTX && ( encControl->nChannelsInternal == 1 || psEnc->state_Fxx[ 1 ].sCmn.inDTX ) ) {
+                    *nBytesOut = 0;
+                }
+
+                psEnc->nBitsExceeded += *nBytesOut * 8;
+                psEnc->nBitsExceeded -= silk_DIV32_16( silk_MUL( encControl->bitRate, encControl->payloadSize_ms ), 1000 );
+                psEnc->nBitsExceeded  = silk_LIMIT( psEnc->nBitsExceeded, 0, 10000 );
+
+                /* Update flag indicating if bandwidth switching is allowed */
+                speech_act_thr_for_switch_Q8 = silk_SMLAWB( SILK_FIX_CONST( SPEECH_ACTIVITY_DTX_THRES, 8 ),
+                    SILK_FIX_CONST( ( 1 - SPEECH_ACTIVITY_DTX_THRES ) / MAX_BANDWIDTH_SWITCH_DELAY_MS, 16 + 8 ), psEnc->timeSinceSwitchAllowed_ms );
+                if( psEnc->state_Fxx[ 0 ].sCmn.speech_activity_Q8 < speech_act_thr_for_switch_Q8 ) {
+                    psEnc->allowBandwidthSwitch = 1;
+                    psEnc->timeSinceSwitchAllowed_ms = 0;
+                } else {
+                    psEnc->allowBandwidthSwitch = 0;
+                    psEnc->timeSinceSwitchAllowed_ms += encControl->payloadSize_ms;
+                }
+            }
+
+            if( nSamplesIn == 0 ) {
+                break;
+            }
+        } else {
+            break;
+        }
+        curr_block++;
+    }
+
+    psEnc->nPrevChannelsInternal = encControl->nChannelsInternal;
+
+    encControl->allowBandwidthSwitch = psEnc->allowBandwidthSwitch;
+    encControl->inWBmodeWithoutVariableLP = psEnc->state_Fxx[ 0 ].sCmn.fs_kHz == 16 && psEnc->state_Fxx[ 0 ].sCmn.sLP.mode == 0;
+    encControl->internalSampleRate = silk_SMULBB( psEnc->state_Fxx[ 0 ].sCmn.fs_kHz, 1000 );
+    encControl->stereoWidth_Q14 = encControl->toMono ? 0 : psEnc->sStereo.smth_width_Q14;
+    if( prefillFlag ) {
+        encControl->payloadSize_ms = tmp_payloadSize_ms;
+        encControl->complexity = tmp_complexity;
+        for( n = 0; n < encControl->nChannelsInternal; n++ ) {
+            psEnc->state_Fxx[ n ].sCmn.controlled_since_last_payload = 0;
+            psEnc->state_Fxx[ n ].sCmn.prefillFlag = 0;
+        }
+    }
+
+    encControl->signalType = psEnc->state_Fxx[0].sCmn.indices.signalType;
+    encControl->offset = silk_Quantization_Offsets_Q10
+                         [ psEnc->state_Fxx[0].sCmn.indices.signalType >> 1 ]
+                         [ psEnc->state_Fxx[0].sCmn.indices.quantOffsetType ];
+    RESTORE_STACK;
+    return ret;
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/encode_indices.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/encode_indices.c
new file mode 100644 (file)
index 0000000..8a6db2c
--- /dev/null
@@ -0,0 +1,181 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main.h"
+
+/* Encode side-information parameters to payload */
+void silk_encode_indices(
+    silk_encoder_state          *psEncC,                        /* I/O  Encoder state                               */
+    ec_enc                      *psRangeEnc,                    /* I/O  Compressor data structure                   */
+    opus_int                    FrameIndex,                     /* I    Frame number                                */
+    opus_int                    encode_LBRR,                    /* I    Flag indicating LBRR data is being encoded  */
+    opus_int                    condCoding                      /* I    The type of conditional coding to use       */
+)
+{
+    opus_int   i, k, typeOffset;
+    opus_int   encode_absolute_lagIndex, delta_lagIndex;
+    opus_int16 ec_ix[ MAX_LPC_ORDER ];
+    opus_uint8 pred_Q8[ MAX_LPC_ORDER ];
+    const SideInfoIndices *psIndices;
+
+    if( encode_LBRR ) {
+         psIndices = &psEncC->indices_LBRR[ FrameIndex ];
+    } else {
+         psIndices = &psEncC->indices;
+    }
+
+    /*******************************************/
+    /* Encode signal type and quantizer offset */
+    /*******************************************/
+    typeOffset = 2 * psIndices->signalType + psIndices->quantOffsetType;
+    celt_assert( typeOffset >= 0 && typeOffset < 6 );
+    celt_assert( encode_LBRR == 0 || typeOffset >= 2 );
+    if( encode_LBRR || typeOffset >= 2 ) {
+        ec_enc_icdf( psRangeEnc, typeOffset - 2, silk_type_offset_VAD_iCDF, 8 );
+    } else {
+        ec_enc_icdf( psRangeEnc, typeOffset, silk_type_offset_no_VAD_iCDF, 8 );
+    }
+
+    /****************/
+    /* Encode gains */
+    /****************/
+    /* first subframe */
+    if( condCoding == CODE_CONDITIONALLY ) {
+        /* conditional coding */
+        silk_assert( psIndices->GainsIndices[ 0 ] >= 0 && psIndices->GainsIndices[ 0 ] < MAX_DELTA_GAIN_QUANT - MIN_DELTA_GAIN_QUANT + 1 );
+        ec_enc_icdf( psRangeEnc, psIndices->GainsIndices[ 0 ], silk_delta_gain_iCDF, 8 );
+    } else {
+        /* independent coding, in two stages: MSB bits followed by 3 LSBs */
+        silk_assert( psIndices->GainsIndices[ 0 ] >= 0 && psIndices->GainsIndices[ 0 ] < N_LEVELS_QGAIN );
+        ec_enc_icdf( psRangeEnc, silk_RSHIFT( psIndices->GainsIndices[ 0 ], 3 ), silk_gain_iCDF[ psIndices->signalType ], 8 );
+        ec_enc_icdf( psRangeEnc, psIndices->GainsIndices[ 0 ] & 7, silk_uniform8_iCDF, 8 );
+    }
+
+    /* remaining subframes */
+    for( i = 1; i < psEncC->nb_subfr; i++ ) {
+        silk_assert( psIndices->GainsIndices[ i ] >= 0 && psIndices->GainsIndices[ i ] < MAX_DELTA_GAIN_QUANT - MIN_DELTA_GAIN_QUANT + 1 );
+        ec_enc_icdf( psRangeEnc, psIndices->GainsIndices[ i ], silk_delta_gain_iCDF, 8 );
+    }
+
+    /****************/
+    /* Encode NLSFs */
+    /****************/
+    ec_enc_icdf( psRangeEnc, psIndices->NLSFIndices[ 0 ], &psEncC->psNLSF_CB->CB1_iCDF[ ( psIndices->signalType >> 1 ) * psEncC->psNLSF_CB->nVectors ], 8 );
+    silk_NLSF_unpack( ec_ix, pred_Q8, psEncC->psNLSF_CB, psIndices->NLSFIndices[ 0 ] );
+    celt_assert( psEncC->psNLSF_CB->order == psEncC->predictLPCOrder );
+    for( i = 0; i < psEncC->psNLSF_CB->order; i++ ) {
+        if( psIndices->NLSFIndices[ i+1 ] >= NLSF_QUANT_MAX_AMPLITUDE ) {
+            ec_enc_icdf( psRangeEnc, 2 * NLSF_QUANT_MAX_AMPLITUDE, &psEncC->psNLSF_CB->ec_iCDF[ ec_ix[ i ] ], 8 );
+            ec_enc_icdf( psRangeEnc, psIndices->NLSFIndices[ i+1 ] - NLSF_QUANT_MAX_AMPLITUDE, silk_NLSF_EXT_iCDF, 8 );
+        } else if( psIndices->NLSFIndices[ i+1 ] <= -NLSF_QUANT_MAX_AMPLITUDE ) {
+            ec_enc_icdf( psRangeEnc, 0, &psEncC->psNLSF_CB->ec_iCDF[ ec_ix[ i ] ], 8 );
+            ec_enc_icdf( psRangeEnc, -psIndices->NLSFIndices[ i+1 ] - NLSF_QUANT_MAX_AMPLITUDE, silk_NLSF_EXT_iCDF, 8 );
+        } else {
+            ec_enc_icdf( psRangeEnc, psIndices->NLSFIndices[ i+1 ] + NLSF_QUANT_MAX_AMPLITUDE, &psEncC->psNLSF_CB->ec_iCDF[ ec_ix[ i ] ], 8 );
+        }
+    }
+
+    /* Encode NLSF interpolation factor */
+    if( psEncC->nb_subfr == MAX_NB_SUBFR ) {
+        silk_assert( psIndices->NLSFInterpCoef_Q2 >= 0 && psIndices->NLSFInterpCoef_Q2 < 5 );
+        ec_enc_icdf( psRangeEnc, psIndices->NLSFInterpCoef_Q2, silk_NLSF_interpolation_factor_iCDF, 8 );
+    }
+
+    if( psIndices->signalType == TYPE_VOICED )
+    {
+        /*********************/
+        /* Encode pitch lags */
+        /*********************/
+        /* lag index */
+        encode_absolute_lagIndex = 1;
+        if( condCoding == CODE_CONDITIONALLY && psEncC->ec_prevSignalType == TYPE_VOICED ) {
+            /* Delta Encoding */
+            delta_lagIndex = psIndices->lagIndex - psEncC->ec_prevLagIndex;
+            if( delta_lagIndex < -8 || delta_lagIndex > 11 ) {
+                delta_lagIndex = 0;
+            } else {
+                delta_lagIndex = delta_lagIndex + 9;
+                encode_absolute_lagIndex = 0; /* Only use delta */
+            }
+            silk_assert( delta_lagIndex >= 0 && delta_lagIndex < 21 );
+            ec_enc_icdf( psRangeEnc, delta_lagIndex, silk_pitch_delta_iCDF, 8 );
+        }
+        if( encode_absolute_lagIndex ) {
+            /* Absolute encoding */
+            opus_int32 pitch_high_bits, pitch_low_bits;
+            pitch_high_bits = silk_DIV32_16( psIndices->lagIndex, silk_RSHIFT( psEncC->fs_kHz, 1 ) );
+            pitch_low_bits = psIndices->lagIndex - silk_SMULBB( pitch_high_bits, silk_RSHIFT( psEncC->fs_kHz, 1 ) );
+            silk_assert( pitch_low_bits < psEncC->fs_kHz / 2 );
+            silk_assert( pitch_high_bits < 32 );
+            ec_enc_icdf( psRangeEnc, pitch_high_bits, silk_pitch_lag_iCDF, 8 );
+            ec_enc_icdf( psRangeEnc, pitch_low_bits, psEncC->pitch_lag_low_bits_iCDF, 8 );
+        }
+        psEncC->ec_prevLagIndex = psIndices->lagIndex;
+
+        /* Contour index */
+        silk_assert(   psIndices->contourIndex  >= 0 );
+        silk_assert( ( psIndices->contourIndex < 34 && psEncC->fs_kHz  > 8 && psEncC->nb_subfr == 4 ) ||
+                    ( psIndices->contourIndex < 11 && psEncC->fs_kHz == 8 && psEncC->nb_subfr == 4 ) ||
+                    ( psIndices->contourIndex < 12 && psEncC->fs_kHz  > 8 && psEncC->nb_subfr == 2 ) ||
+                    ( psIndices->contourIndex <  3 && psEncC->fs_kHz == 8 && psEncC->nb_subfr == 2 ) );
+        ec_enc_icdf( psRangeEnc, psIndices->contourIndex, psEncC->pitch_contour_iCDF, 8 );
+
+        /********************/
+        /* Encode LTP gains */
+        /********************/
+        /* PERIndex value */
+        silk_assert( psIndices->PERIndex >= 0 && psIndices->PERIndex < 3 );
+        ec_enc_icdf( psRangeEnc, psIndices->PERIndex, silk_LTP_per_index_iCDF, 8 );
+
+        /* Codebook Indices */
+        for( k = 0; k < psEncC->nb_subfr; k++ ) {
+            silk_assert( psIndices->LTPIndex[ k ] >= 0 && psIndices->LTPIndex[ k ] < ( 8 << psIndices->PERIndex ) );
+            ec_enc_icdf( psRangeEnc, psIndices->LTPIndex[ k ], silk_LTP_gain_iCDF_ptrs[ psIndices->PERIndex ], 8 );
+        }
+
+        /**********************/
+        /* Encode LTP scaling */
+        /**********************/
+        if( condCoding == CODE_INDEPENDENTLY ) {
+            silk_assert( psIndices->LTP_scaleIndex >= 0 && psIndices->LTP_scaleIndex < 3 );
+            ec_enc_icdf( psRangeEnc, psIndices->LTP_scaleIndex, silk_LTPscale_iCDF, 8 );
+        }
+        silk_assert( !condCoding || psIndices->LTP_scaleIndex == 0 );
+    }
+
+    psEncC->ec_prevSignalType = psIndices->signalType;
+
+    /***************/
+    /* Encode seed */
+    /***************/
+    silk_assert( psIndices->Seed >= 0 && psIndices->Seed < 4 );
+    ec_enc_icdf( psRangeEnc, psIndices->Seed, silk_uniform4_iCDF, 8 );
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/encode_pulses.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/encode_pulses.c
new file mode 100644 (file)
index 0000000..8a19991
--- /dev/null
@@ -0,0 +1,206 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main.h"
+#include "stack_alloc.h"
+
+/*********************************************/
+/* Encode quantization indices of excitation */
+/*********************************************/
+
+static OPUS_INLINE opus_int combine_and_check(    /* return ok                           */
+    opus_int         *pulses_comb,           /* O                                   */
+    const opus_int   *pulses_in,             /* I                                   */
+    opus_int         max_pulses,             /* I    max value for sum of pulses    */
+    opus_int         len                     /* I    number of output values        */
+)
+{
+    opus_int k, sum;
+
+    for( k = 0; k < len; k++ ) {
+        sum = pulses_in[ 2 * k ] + pulses_in[ 2 * k + 1 ];
+        if( sum > max_pulses ) {
+            return 1;
+        }
+        pulses_comb[ k ] = sum;
+    }
+
+    return 0;
+}
+
+/* Encode quantization indices of excitation */
+void silk_encode_pulses(
+    ec_enc                      *psRangeEnc,                    /* I/O  compressor data structure                   */
+    const opus_int              signalType,                     /* I    Signal type                                 */
+    const opus_int              quantOffsetType,                /* I    quantOffsetType                             */
+    opus_int8                   pulses[],                       /* I    quantization indices                        */
+    const opus_int              frame_length                    /* I    Frame length                                */
+)
+{
+    opus_int   i, k, j, iter, bit, nLS, scale_down, RateLevelIndex = 0;
+    opus_int32 abs_q, minSumBits_Q5, sumBits_Q5;
+    VARDECL( opus_int, abs_pulses );
+    VARDECL( opus_int, sum_pulses );
+    VARDECL( opus_int, nRshifts );
+    opus_int   pulses_comb[ 8 ];
+    opus_int   *abs_pulses_ptr;
+    const opus_int8 *pulses_ptr;
+    const opus_uint8 *cdf_ptr;
+    const opus_uint8 *nBits_ptr;
+    SAVE_STACK;
+
+    silk_memset( pulses_comb, 0, 8 * sizeof( opus_int ) ); /* Fixing Valgrind reported problem*/
+
+    /****************************/
+    /* Prepare for shell coding */
+    /****************************/
+    /* Calculate number of shell blocks */
+    silk_assert( 1 << LOG2_SHELL_CODEC_FRAME_LENGTH == SHELL_CODEC_FRAME_LENGTH );
+    iter = silk_RSHIFT( frame_length, LOG2_SHELL_CODEC_FRAME_LENGTH );
+    if( iter * SHELL_CODEC_FRAME_LENGTH < frame_length ) {
+        celt_assert( frame_length == 12 * 10 ); /* Make sure only happens for 10 ms @ 12 kHz */
+        iter++;
+        silk_memset( &pulses[ frame_length ], 0, SHELL_CODEC_FRAME_LENGTH * sizeof(opus_int8));
+    }
+
+    /* Take the absolute value of the pulses */
+    ALLOC( abs_pulses, iter * SHELL_CODEC_FRAME_LENGTH, opus_int );
+    silk_assert( !( SHELL_CODEC_FRAME_LENGTH & 3 ) );
+    for( i = 0; i < iter * SHELL_CODEC_FRAME_LENGTH; i+=4 ) {
+        abs_pulses[i+0] = ( opus_int )silk_abs( pulses[ i + 0 ] );
+        abs_pulses[i+1] = ( opus_int )silk_abs( pulses[ i + 1 ] );
+        abs_pulses[i+2] = ( opus_int )silk_abs( pulses[ i + 2 ] );
+        abs_pulses[i+3] = ( opus_int )silk_abs( pulses[ i + 3 ] );
+    }
+
+    /* Calc sum pulses per shell code frame */
+    ALLOC( sum_pulses, iter, opus_int );
+    ALLOC( nRshifts, iter, opus_int );
+    abs_pulses_ptr = abs_pulses;
+    for( i = 0; i < iter; i++ ) {
+        nRshifts[ i ] = 0;
+
+        while( 1 ) {
+            /* 1+1 -> 2 */
+            scale_down = combine_and_check( pulses_comb, abs_pulses_ptr, silk_max_pulses_table[ 0 ], 8 );
+            /* 2+2 -> 4 */
+            scale_down += combine_and_check( pulses_comb, pulses_comb, silk_max_pulses_table[ 1 ], 4 );
+            /* 4+4 -> 8 */
+            scale_down += combine_and_check( pulses_comb, pulses_comb, silk_max_pulses_table[ 2 ], 2 );
+            /* 8+8 -> 16 */
+            scale_down += combine_and_check( &sum_pulses[ i ], pulses_comb, silk_max_pulses_table[ 3 ], 1 );
+
+            if( scale_down ) {
+                /* We need to downscale the quantization signal */
+                nRshifts[ i ]++;
+                for( k = 0; k < SHELL_CODEC_FRAME_LENGTH; k++ ) {
+                    abs_pulses_ptr[ k ] = silk_RSHIFT( abs_pulses_ptr[ k ], 1 );
+                }
+            } else {
+                /* Jump out of while(1) loop and go to next shell coding frame */
+                break;
+            }
+        }
+        abs_pulses_ptr += SHELL_CODEC_FRAME_LENGTH;
+    }
+
+    /**************/
+    /* Rate level */
+    /**************/
+    /* find rate level that leads to fewest bits for coding of pulses per block info */
+    minSumBits_Q5 = silk_int32_MAX;
+    for( k = 0; k < N_RATE_LEVELS - 1; k++ ) {
+        nBits_ptr  = silk_pulses_per_block_BITS_Q5[ k ];
+        sumBits_Q5 = silk_rate_levels_BITS_Q5[ signalType >> 1 ][ k ];
+        for( i = 0; i < iter; i++ ) {
+            if( nRshifts[ i ] > 0 ) {
+                sumBits_Q5 += nBits_ptr[ SILK_MAX_PULSES + 1 ];
+            } else {
+                sumBits_Q5 += nBits_ptr[ sum_pulses[ i ] ];
+            }
+        }
+        if( sumBits_Q5 < minSumBits_Q5 ) {
+            minSumBits_Q5 = sumBits_Q5;
+            RateLevelIndex = k;
+        }
+    }
+    ec_enc_icdf( psRangeEnc, RateLevelIndex, silk_rate_levels_iCDF[ signalType >> 1 ], 8 );
+
+    /***************************************************/
+    /* Sum-Weighted-Pulses Encoding                    */
+    /***************************************************/
+    cdf_ptr = silk_pulses_per_block_iCDF[ RateLevelIndex ];
+    for( i = 0; i < iter; i++ ) {
+        if( nRshifts[ i ] == 0 ) {
+            ec_enc_icdf( psRangeEnc, sum_pulses[ i ], cdf_ptr, 8 );
+        } else {
+            ec_enc_icdf( psRangeEnc, SILK_MAX_PULSES + 1, cdf_ptr, 8 );
+            for( k = 0; k < nRshifts[ i ] - 1; k++ ) {
+                ec_enc_icdf( psRangeEnc, SILK_MAX_PULSES + 1, silk_pulses_per_block_iCDF[ N_RATE_LEVELS - 1 ], 8 );
+            }
+            ec_enc_icdf( psRangeEnc, sum_pulses[ i ], silk_pulses_per_block_iCDF[ N_RATE_LEVELS - 1 ], 8 );
+        }
+    }
+
+    /******************/
+    /* Shell Encoding */
+    /******************/
+    for( i = 0; i < iter; i++ ) {
+        if( sum_pulses[ i ] > 0 ) {
+            silk_shell_encoder( psRangeEnc, &abs_pulses[ i * SHELL_CODEC_FRAME_LENGTH ] );
+        }
+    }
+
+    /****************/
+    /* LSB Encoding */
+    /****************/
+    for( i = 0; i < iter; i++ ) {
+        if( nRshifts[ i ] > 0 ) {
+            pulses_ptr = &pulses[ i * SHELL_CODEC_FRAME_LENGTH ];
+            nLS = nRshifts[ i ] - 1;
+            for( k = 0; k < SHELL_CODEC_FRAME_LENGTH; k++ ) {
+                abs_q = (opus_int8)silk_abs( pulses_ptr[ k ] );
+                for( j = nLS; j > 0; j-- ) {
+                    bit = silk_RSHIFT( abs_q, j ) & 1;
+                    ec_enc_icdf( psRangeEnc, bit, silk_lsb_iCDF, 8 );
+                }
+                bit = abs_q & 1;
+                ec_enc_icdf( psRangeEnc, bit, silk_lsb_iCDF, 8 );
+            }
+        }
+    }
+
+    /****************/
+    /* Encode signs */
+    /****************/
+    silk_encode_signs( psRangeEnc, pulses, frame_length, signalType, quantOffsetType, sum_pulses );
+    RESTORE_STACK;
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/LTP_analysis_filter_FIX.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/LTP_analysis_filter_FIX.c
new file mode 100644 (file)
index 0000000..c1a4f0d
--- /dev/null
@@ -0,0 +1,89 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main_FIX.h"
+
+void silk_LTP_analysis_filter_FIX(
+    opus_int16                      *LTP_res,                               /* O    LTP residual signal of length MAX_NB_SUBFR * ( pre_length + subfr_length )  */
+    const opus_int16                *x,                                     /* I    Pointer to input signal with at least max( pitchL ) preceding samples       */
+    const opus_int16                LTPCoef_Q14[ LTP_ORDER * MAX_NB_SUBFR ],/* I    LTP_ORDER LTP coefficients for each MAX_NB_SUBFR subframe                   */
+    const opus_int                  pitchL[ MAX_NB_SUBFR ],                 /* I    Pitch lag, one for each subframe                                            */
+    const opus_int32                invGains_Q16[ MAX_NB_SUBFR ],           /* I    Inverse quantization gains, one for each subframe                           */
+    const opus_int                  subfr_length,                           /* I    Length of each subframe                                                     */
+    const opus_int                  nb_subfr,                               /* I    Number of subframes                                                         */
+    const opus_int                  pre_length                              /* I    Length of the preceding samples starting at &x[0] for each subframe         */
+)
+{
+    const opus_int16 *x_ptr, *x_lag_ptr;
+    opus_int16   Btmp_Q14[ LTP_ORDER ];
+    opus_int16   *LTP_res_ptr;
+    opus_int     k, i;
+    opus_int32   LTP_est;
+
+    x_ptr = x;
+    LTP_res_ptr = LTP_res;
+    for( k = 0; k < nb_subfr; k++ ) {
+
+        x_lag_ptr = x_ptr - pitchL[ k ];
+
+        Btmp_Q14[ 0 ] = LTPCoef_Q14[ k * LTP_ORDER ];
+        Btmp_Q14[ 1 ] = LTPCoef_Q14[ k * LTP_ORDER + 1 ];
+        Btmp_Q14[ 2 ] = LTPCoef_Q14[ k * LTP_ORDER + 2 ];
+        Btmp_Q14[ 3 ] = LTPCoef_Q14[ k * LTP_ORDER + 3 ];
+        Btmp_Q14[ 4 ] = LTPCoef_Q14[ k * LTP_ORDER + 4 ];
+
+        /* LTP analysis FIR filter */
+        for( i = 0; i < subfr_length + pre_length; i++ ) {
+            LTP_res_ptr[ i ] = x_ptr[ i ];
+
+            /* Long-term prediction */
+            LTP_est = silk_SMULBB( x_lag_ptr[ LTP_ORDER / 2 ], Btmp_Q14[ 0 ] );
+            LTP_est = silk_SMLABB_ovflw( LTP_est, x_lag_ptr[ 1 ], Btmp_Q14[ 1 ] );
+            LTP_est = silk_SMLABB_ovflw( LTP_est, x_lag_ptr[ 0 ], Btmp_Q14[ 2 ] );
+            LTP_est = silk_SMLABB_ovflw( LTP_est, x_lag_ptr[ -1 ], Btmp_Q14[ 3 ] );
+            LTP_est = silk_SMLABB_ovflw( LTP_est, x_lag_ptr[ -2 ], Btmp_Q14[ 4 ] );
+
+            LTP_est = silk_RSHIFT_ROUND( LTP_est, 14 ); /* round and -> Q0*/
+
+            /* Subtract long-term prediction */
+            LTP_res_ptr[ i ] = (opus_int16)silk_SAT16( (opus_int32)x_ptr[ i ] - LTP_est );
+
+            /* Scale residual */
+            LTP_res_ptr[ i ] = silk_SMULWB( invGains_Q16[ k ], LTP_res_ptr[ i ] );
+
+            x_lag_ptr++;
+        }
+
+        /* Update pointers */
+        LTP_res_ptr += subfr_length + pre_length;
+        x_ptr       += subfr_length;
+    }
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/LTP_scale_ctrl_FIX.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/LTP_scale_ctrl_FIX.c
new file mode 100644 (file)
index 0000000..db1016e
--- /dev/null
@@ -0,0 +1,58 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main_FIX.h"
+
+/* Calculation of LTP state scaling */
+void silk_LTP_scale_ctrl_FIX(
+    silk_encoder_state_FIX          *psEnc,                                 /* I/O  encoder state                                                               */
+    silk_encoder_control_FIX        *psEncCtrl,                             /* I/O  encoder control                                                             */
+    opus_int                        condCoding                              /* I    The type of conditional coding to use                                       */
+)
+{
+    opus_int round_loss;
+
+    if( condCoding == CODE_INDEPENDENTLY ) {
+        /* Only scale if first frame in packet */
+        round_loss = psEnc->sCmn.PacketLoss_perc * psEnc->sCmn.nFramesPerPacket;
+        if ( psEnc->sCmn.LBRR_flag ) {
+            /* LBRR reduces the effective loss. In practice, it does not square the loss because
+               losses aren't independent, but that still seems to work best. We also never go below 2%. */
+            round_loss = 2 + silk_SMULBB( round_loss, round_loss ) / 100;
+        }
+        psEnc->sCmn.indices.LTP_scaleIndex = silk_SMULBB( psEncCtrl->LTPredCodGain_Q7, round_loss ) > silk_log2lin( 128*7 + 2900-psEnc->sCmn.SNR_dB_Q7 );
+        psEnc->sCmn.indices.LTP_scaleIndex += silk_SMULBB( psEncCtrl->LTPredCodGain_Q7, round_loss ) > silk_log2lin( 128*7 + 3900-psEnc->sCmn.SNR_dB_Q7 );
+    } else {
+        /* Default is minimum scaling */
+        psEnc->sCmn.indices.LTP_scaleIndex = 0;
+    }
+    psEncCtrl->LTP_scale_Q14 = silk_LTPScales_table_Q14[ psEnc->sCmn.indices.LTP_scaleIndex ];
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/apply_sine_window_FIX.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/apply_sine_window_FIX.c
new file mode 100644 (file)
index 0000000..03e088a
--- /dev/null
@@ -0,0 +1,101 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "SigProc_FIX.h"
+
+/* Apply sine window to signal vector.                                      */
+/* Window types:                                                            */
+/*    1 -> sine window from 0 to pi/2                                       */
+/*    2 -> sine window from pi/2 to pi                                      */
+/* Every other sample is linearly interpolated, for speed.                  */
+/* Window length must be between 16 and 120 (incl) and a multiple of 4.     */
+
+/* Matlab code for table:
+   for k=16:9*4:16+2*9*4, fprintf(' %7.d,', -round(65536*pi ./ (k:4:k+8*4))); fprintf('\n'); end
+*/
+static const opus_int16 freq_table_Q16[ 27 ] = {
+   12111,    9804,    8235,    7100,    6239,    5565,    5022,    4575,    4202,
+    3885,    3612,    3375,    3167,    2984,    2820,    2674,    2542,    2422,
+    2313,    2214,    2123,    2038,    1961,    1889,    1822,    1760,    1702,
+};
+
+void silk_apply_sine_window(
+    opus_int16                  px_win[],           /* O    Pointer to windowed signal                                  */
+    const opus_int16            px[],               /* I    Pointer to input signal                                     */
+    const opus_int              win_type,           /* I    Selects a window type                                       */
+    const opus_int              length              /* I    Window length, multiple of 4                                */
+)
+{
+    opus_int   k, f_Q16, c_Q16;
+    opus_int32 S0_Q16, S1_Q16;
+
+    celt_assert( win_type == 1 || win_type == 2 );
+
+    /* Length must be in a range from 16 to 120 and a multiple of 4 */
+    celt_assert( length >= 16 && length <= 120 );
+    celt_assert( ( length & 3 ) == 0 );
+
+    /* Frequency */
+    k = ( length >> 2 ) - 4;
+    celt_assert( k >= 0 && k <= 26 );
+    f_Q16 = (opus_int)freq_table_Q16[ k ];
+
+    /* Factor used for cosine approximation */
+    c_Q16 = silk_SMULWB( (opus_int32)f_Q16, -f_Q16 );
+    silk_assert( c_Q16 >= -32768 );
+
+    /* initialize state */
+    if( win_type == 1 ) {
+        /* start from 0 */
+        S0_Q16 = 0;
+        /* approximation of sin(f) */
+        S1_Q16 = f_Q16 + silk_RSHIFT( length, 3 );
+    } else {
+        /* start from 1 */
+        S0_Q16 = ( (opus_int32)1 << 16 );
+        /* approximation of cos(f) */
+        S1_Q16 = ( (opus_int32)1 << 16 ) + silk_RSHIFT( c_Q16, 1 ) + silk_RSHIFT( length, 4 );
+    }
+
+    /* Uses the recursive equation:   sin(n*f) = 2 * cos(f) * sin((n-1)*f) - sin((n-2)*f)    */
+    /* 4 samples at a time */
+    for( k = 0; k < length; k += 4 ) {
+        px_win[ k ]     = (opus_int16)silk_SMULWB( silk_RSHIFT( S0_Q16 + S1_Q16, 1 ), px[ k ] );
+        px_win[ k + 1 ] = (opus_int16)silk_SMULWB( S1_Q16, px[ k + 1] );
+        S0_Q16 = silk_SMULWB( S1_Q16, c_Q16 ) + silk_LSHIFT( S1_Q16, 1 ) - S0_Q16 + 1;
+        S0_Q16 = silk_min( S0_Q16, ( (opus_int32)1 << 16 ) );
+
+        px_win[ k + 2 ] = (opus_int16)silk_SMULWB( silk_RSHIFT( S0_Q16 + S1_Q16, 1 ), px[ k + 2] );
+        px_win[ k + 3 ] = (opus_int16)silk_SMULWB( S0_Q16, px[ k + 3 ] );
+        S1_Q16 = silk_SMULWB( S0_Q16, c_Q16 ) + silk_LSHIFT( S0_Q16, 1 ) - S1_Q16;
+        S1_Q16 = silk_min( S1_Q16, ( (opus_int32)1 << 16 ) );
+    }
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/autocorr_FIX.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/autocorr_FIX.c
new file mode 100644 (file)
index 0000000..de95c98
--- /dev/null
@@ -0,0 +1,48 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "SigProc_FIX.h"
+#include "celt_lpc.h"
+
+/* Compute autocorrelation */
+void silk_autocorr(
+    opus_int32                  *results,           /* O    Result (length correlationCount)                            */
+    opus_int                    *scale,             /* O    Scaling of the correlation vector                           */
+    const opus_int16            *inputData,         /* I    Input data to correlate                                     */
+    const opus_int              inputDataSize,      /* I    Length of input                                             */
+    const opus_int              correlationCount,   /* I    Number of correlation taps to compute                       */
+    int                         arch                /* I    Run-time architecture                                       */
+)
+{
+    opus_int   corrCount;
+    corrCount = silk_min_int( inputDataSize, correlationCount );
+    *scale = _celt_autocorr(inputData, results, NULL, 0, corrCount-1, inputDataSize, arch);
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/burg_modified_FIX.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/burg_modified_FIX.c
new file mode 100644 (file)
index 0000000..185a12b
--- /dev/null
@@ -0,0 +1,280 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "SigProc_FIX.h"
+#include "define.h"
+#include "tuning_parameters.h"
+#include "pitch.h"
+
+#define MAX_FRAME_SIZE              384             /* subfr_length * nb_subfr = ( 0.005 * 16000 + 16 ) * 4 = 384 */
+
+#define QA                          25
+#define N_BITS_HEAD_ROOM            3
+#define MIN_RSHIFTS                 -16
+#define MAX_RSHIFTS                 (32 - QA)
+
+/* Compute reflection coefficients from input signal */
+void silk_burg_modified_c(
+    opus_int32                  *res_nrg,           /* O    Residual energy                                             */
+    opus_int                    *res_nrg_Q,         /* O    Residual energy Q value                                     */
+    opus_int32                  A_Q16[],            /* O    Prediction coefficients (length order)                      */
+    const opus_int16            x[],                /* I    Input signal, length: nb_subfr * ( D + subfr_length )       */
+    const opus_int32            minInvGain_Q30,     /* I    Inverse of max prediction gain                              */
+    const opus_int              subfr_length,       /* I    Input signal subframe length (incl. D preceding samples)    */
+    const opus_int              nb_subfr,           /* I    Number of subframes stacked in x                            */
+    const opus_int              D,                  /* I    Order                                                       */
+    int                         arch                /* I    Run-time architecture                                       */
+)
+{
+    opus_int         k, n, s, lz, rshifts, reached_max_gain;
+    opus_int32       C0, num, nrg, rc_Q31, invGain_Q30, Atmp_QA, Atmp1, tmp1, tmp2, x1, x2;
+    const opus_int16 *x_ptr;
+    opus_int32       C_first_row[ SILK_MAX_ORDER_LPC ];
+    opus_int32       C_last_row[  SILK_MAX_ORDER_LPC ];
+    opus_int32       Af_QA[       SILK_MAX_ORDER_LPC ];
+    opus_int32       CAf[ SILK_MAX_ORDER_LPC + 1 ];
+    opus_int32       CAb[ SILK_MAX_ORDER_LPC + 1 ];
+    opus_int32       xcorr[ SILK_MAX_ORDER_LPC ];
+    opus_int64       C0_64;
+
+    celt_assert( subfr_length * nb_subfr <= MAX_FRAME_SIZE );
+
+    /* Compute autocorrelations, added over subframes */
+    C0_64 = silk_inner_prod16( x, x, subfr_length*nb_subfr, arch );
+    lz = silk_CLZ64(C0_64);
+    rshifts = 32 + 1 + N_BITS_HEAD_ROOM - lz;
+    if (rshifts > MAX_RSHIFTS) rshifts = MAX_RSHIFTS;
+    if (rshifts < MIN_RSHIFTS) rshifts = MIN_RSHIFTS;
+
+    if (rshifts > 0) {
+        C0 = (opus_int32)silk_RSHIFT64(C0_64, rshifts );
+    } else {
+        C0 = silk_LSHIFT32((opus_int32)C0_64, -rshifts );
+    }
+
+    CAb[ 0 ] = CAf[ 0 ] = C0 + silk_SMMUL( SILK_FIX_CONST( FIND_LPC_COND_FAC, 32 ), C0 ) + 1;                                /* Q(-rshifts) */
+    silk_memset( C_first_row, 0, SILK_MAX_ORDER_LPC * sizeof( opus_int32 ) );
+    if( rshifts > 0 ) {
+        for( s = 0; s < nb_subfr; s++ ) {
+            x_ptr = x + s * subfr_length;
+            for( n = 1; n < D + 1; n++ ) {
+                C_first_row[ n - 1 ] += (opus_int32)silk_RSHIFT64(
+                    silk_inner_prod16( x_ptr, x_ptr + n, subfr_length - n, arch ), rshifts );
+            }
+        }
+    } else {
+        for( s = 0; s < nb_subfr; s++ ) {
+            int i;
+            opus_int32 d;
+            x_ptr = x + s * subfr_length;
+            celt_pitch_xcorr(x_ptr, x_ptr + 1, xcorr, subfr_length - D, D, arch );
+            for( n = 1; n < D + 1; n++ ) {
+               for ( i = n + subfr_length - D, d = 0; i < subfr_length; i++ )
+                  d = MAC16_16( d, x_ptr[ i ], x_ptr[ i - n ] );
+               xcorr[ n - 1 ] += d;
+            }
+            for( n = 1; n < D + 1; n++ ) {
+                C_first_row[ n - 1 ] += silk_LSHIFT32( xcorr[ n - 1 ], -rshifts );
+            }
+        }
+    }
+    silk_memcpy( C_last_row, C_first_row, SILK_MAX_ORDER_LPC * sizeof( opus_int32 ) );
+
+    /* Initialize */
+    CAb[ 0 ] = CAf[ 0 ] = C0 + silk_SMMUL( SILK_FIX_CONST( FIND_LPC_COND_FAC, 32 ), C0 ) + 1;                                /* Q(-rshifts) */
+
+    invGain_Q30 = (opus_int32)1 << 30;
+    reached_max_gain = 0;
+    for( n = 0; n < D; n++ ) {
+        /* Update first row of correlation matrix (without first element) */
+        /* Update last row of correlation matrix (without last element, stored in reversed order) */
+        /* Update C * Af */
+        /* Update C * flipud(Af) (stored in reversed order) */
+        if( rshifts > -2 ) {
+            for( s = 0; s < nb_subfr; s++ ) {
+                x_ptr = x + s * subfr_length;
+                x1  = -silk_LSHIFT32( (opus_int32)x_ptr[ n ],                    16 - rshifts );        /* Q(16-rshifts) */
+                x2  = -silk_LSHIFT32( (opus_int32)x_ptr[ subfr_length - n - 1 ], 16 - rshifts );        /* Q(16-rshifts) */
+                tmp1 = silk_LSHIFT32( (opus_int32)x_ptr[ n ],                    QA - 16 );             /* Q(QA-16) */
+                tmp2 = silk_LSHIFT32( (opus_int32)x_ptr[ subfr_length - n - 1 ], QA - 16 );             /* Q(QA-16) */
+                for( k = 0; k < n; k++ ) {
+                    C_first_row[ k ] = silk_SMLAWB( C_first_row[ k ], x1, x_ptr[ n - k - 1 ]            ); /* Q( -rshifts ) */
+                    C_last_row[ k ]  = silk_SMLAWB( C_last_row[ k ],  x2, x_ptr[ subfr_length - n + k ] ); /* Q( -rshifts ) */
+                    Atmp_QA = Af_QA[ k ];
+                    tmp1 = silk_SMLAWB( tmp1, Atmp_QA, x_ptr[ n - k - 1 ]            );                 /* Q(QA-16) */
+                    tmp2 = silk_SMLAWB( tmp2, Atmp_QA, x_ptr[ subfr_length - n + k ] );                 /* Q(QA-16) */
+                }
+                tmp1 = silk_LSHIFT32( -tmp1, 32 - QA - rshifts );                                       /* Q(16-rshifts) */
+                tmp2 = silk_LSHIFT32( -tmp2, 32 - QA - rshifts );                                       /* Q(16-rshifts) */
+                for( k = 0; k <= n; k++ ) {
+                    CAf[ k ] = silk_SMLAWB( CAf[ k ], tmp1, x_ptr[ n - k ]                    );        /* Q( -rshift ) */
+                    CAb[ k ] = silk_SMLAWB( CAb[ k ], tmp2, x_ptr[ subfr_length - n + k - 1 ] );        /* Q( -rshift ) */
+                }
+            }
+        } else {
+            for( s = 0; s < nb_subfr; s++ ) {
+                x_ptr = x + s * subfr_length;
+                x1  = -silk_LSHIFT32( (opus_int32)x_ptr[ n ],                    -rshifts );            /* Q( -rshifts ) */
+                x2  = -silk_LSHIFT32( (opus_int32)x_ptr[ subfr_length - n - 1 ], -rshifts );            /* Q( -rshifts ) */
+                tmp1 = silk_LSHIFT32( (opus_int32)x_ptr[ n ],                    17 );                  /* Q17 */
+                tmp2 = silk_LSHIFT32( (opus_int32)x_ptr[ subfr_length - n - 1 ], 17 );                  /* Q17 */
+                for( k = 0; k < n; k++ ) {
+                    C_first_row[ k ] = silk_MLA( C_first_row[ k ], x1, x_ptr[ n - k - 1 ]            ); /* Q( -rshifts ) */
+                    C_last_row[ k ]  = silk_MLA( C_last_row[ k ],  x2, x_ptr[ subfr_length - n + k ] ); /* Q( -rshifts ) */
+                    Atmp1 = silk_RSHIFT_ROUND( Af_QA[ k ], QA - 17 );                                   /* Q17 */
+                    /* We sometimes get overflows in the multiplications (even beyond +/- 2^32),
+                       but they cancel each other and the real result seems to always fit in a 32-bit
+                       signed integer. This was determined experimentally, not theoretically (unfortunately). */
+                    tmp1 = silk_MLA_ovflw( tmp1, x_ptr[ n - k - 1 ],            Atmp1 );                      /* Q17 */
+                    tmp2 = silk_MLA_ovflw( tmp2, x_ptr[ subfr_length - n + k ], Atmp1 );                      /* Q17 */
+                }
+                tmp1 = -tmp1;                                                                           /* Q17 */
+                tmp2 = -tmp2;                                                                           /* Q17 */
+                for( k = 0; k <= n; k++ ) {
+                    CAf[ k ] = silk_SMLAWW( CAf[ k ], tmp1,
+                        silk_LSHIFT32( (opus_int32)x_ptr[ n - k ], -rshifts - 1 ) );                    /* Q( -rshift ) */
+                    CAb[ k ] = silk_SMLAWW( CAb[ k ], tmp2,
+                        silk_LSHIFT32( (opus_int32)x_ptr[ subfr_length - n + k - 1 ], -rshifts - 1 ) ); /* Q( -rshift ) */
+                }
+            }
+        }
+
+        /* Calculate nominator and denominator for the next order reflection (parcor) coefficient */
+        tmp1 = C_first_row[ n ];                                                                        /* Q( -rshifts ) */
+        tmp2 = C_last_row[ n ];                                                                         /* Q( -rshifts ) */
+        num  = 0;                                                                                       /* Q( -rshifts ) */
+        nrg  = silk_ADD32( CAb[ 0 ], CAf[ 0 ] );                                                        /* Q( 1-rshifts ) */
+        for( k = 0; k < n; k++ ) {
+            Atmp_QA = Af_QA[ k ];
+            lz = silk_CLZ32( silk_abs( Atmp_QA ) ) - 1;
+            lz = silk_min( 32 - QA, lz );
+            Atmp1 = silk_LSHIFT32( Atmp_QA, lz );                                                       /* Q( QA + lz ) */
+
+            tmp1 = silk_ADD_LSHIFT32( tmp1, silk_SMMUL( C_last_row[  n - k - 1 ], Atmp1 ), 32 - QA - lz );  /* Q( -rshifts ) */
+            tmp2 = silk_ADD_LSHIFT32( tmp2, silk_SMMUL( C_first_row[ n - k - 1 ], Atmp1 ), 32 - QA - lz );  /* Q( -rshifts ) */
+            num  = silk_ADD_LSHIFT32( num,  silk_SMMUL( CAb[ n - k ],             Atmp1 ), 32 - QA - lz );  /* Q( -rshifts ) */
+            nrg  = silk_ADD_LSHIFT32( nrg,  silk_SMMUL( silk_ADD32( CAb[ k + 1 ], CAf[ k + 1 ] ),
+                                                                                Atmp1 ), 32 - QA - lz );    /* Q( 1-rshifts ) */
+        }
+        CAf[ n + 1 ] = tmp1;                                                                            /* Q( -rshifts ) */
+        CAb[ n + 1 ] = tmp2;                                                                            /* Q( -rshifts ) */
+        num = silk_ADD32( num, tmp2 );                                                                  /* Q( -rshifts ) */
+        num = silk_LSHIFT32( -num, 1 );                                                                 /* Q( 1-rshifts ) */
+
+        /* Calculate the next order reflection (parcor) coefficient */
+        if( silk_abs( num ) < nrg ) {
+            rc_Q31 = silk_DIV32_varQ( num, nrg, 31 );
+        } else {
+            rc_Q31 = ( num > 0 ) ? silk_int32_MAX : silk_int32_MIN;
+        }
+
+        /* Update inverse prediction gain */
+        tmp1 = ( (opus_int32)1 << 30 ) - silk_SMMUL( rc_Q31, rc_Q31 );
+        tmp1 = silk_LSHIFT( silk_SMMUL( invGain_Q30, tmp1 ), 2 );
+        if( tmp1 <= minInvGain_Q30 ) {
+            /* Max prediction gain exceeded; set reflection coefficient such that max prediction gain is exactly hit */
+            tmp2 = ( (opus_int32)1 << 30 ) - silk_DIV32_varQ( minInvGain_Q30, invGain_Q30, 30 );            /* Q30 */
+            rc_Q31 = silk_SQRT_APPROX( tmp2 );                                                  /* Q15 */
+            if( rc_Q31 > 0 ) {
+                /* Newton-Raphson iteration */
+                rc_Q31 = silk_RSHIFT32( rc_Q31 + silk_DIV32( tmp2, rc_Q31 ), 1 );                       /* Q15 */
+                rc_Q31 = silk_LSHIFT32( rc_Q31, 16 );                                                   /* Q31 */
+                if( num < 0 ) {
+                    /* Ensure adjusted reflection coefficients has the original sign */
+                    rc_Q31 = -rc_Q31;
+                }
+            }
+            invGain_Q30 = minInvGain_Q30;
+            reached_max_gain = 1;
+        } else {
+            invGain_Q30 = tmp1;
+        }
+
+        /* Update the AR coefficients */
+        for( k = 0; k < (n + 1) >> 1; k++ ) {
+            tmp1 = Af_QA[ k ];                                                                  /* QA */
+            tmp2 = Af_QA[ n - k - 1 ];                                                          /* QA */
+            Af_QA[ k ]         = silk_ADD_LSHIFT32( tmp1, silk_SMMUL( tmp2, rc_Q31 ), 1 );      /* QA */
+            Af_QA[ n - k - 1 ] = silk_ADD_LSHIFT32( tmp2, silk_SMMUL( tmp1, rc_Q31 ), 1 );      /* QA */
+        }
+        Af_QA[ n ] = silk_RSHIFT32( rc_Q31, 31 - QA );                                          /* QA */
+
+        if( reached_max_gain ) {
+            /* Reached max prediction gain; set remaining coefficients to zero and exit loop */
+            for( k = n + 1; k < D; k++ ) {
+                Af_QA[ k ] = 0;
+            }
+            break;
+        }
+
+        /* Update C * Af and C * Ab */
+        for( k = 0; k <= n + 1; k++ ) {
+            tmp1 = CAf[ k ];                                                                    /* Q( -rshifts ) */
+            tmp2 = CAb[ n - k + 1 ];                                                            /* Q( -rshifts ) */
+            CAf[ k ]         = silk_ADD_LSHIFT32( tmp1, silk_SMMUL( tmp2, rc_Q31 ), 1 );        /* Q( -rshifts ) */
+            CAb[ n - k + 1 ] = silk_ADD_LSHIFT32( tmp2, silk_SMMUL( tmp1, rc_Q31 ), 1 );        /* Q( -rshifts ) */
+        }
+    }
+
+    if( reached_max_gain ) {
+        for( k = 0; k < D; k++ ) {
+            /* Scale coefficients */
+            A_Q16[ k ] = -silk_RSHIFT_ROUND( Af_QA[ k ], QA - 16 );
+        }
+        /* Subtract energy of preceding samples from C0 */
+        if( rshifts > 0 ) {
+            for( s = 0; s < nb_subfr; s++ ) {
+                x_ptr = x + s * subfr_length;
+                C0 -= (opus_int32)silk_RSHIFT64( silk_inner_prod16( x_ptr, x_ptr, D, arch ), rshifts );
+            }
+        } else {
+            for( s = 0; s < nb_subfr; s++ ) {
+                x_ptr = x + s * subfr_length;
+                C0 -= silk_LSHIFT32( silk_inner_prod_aligned( x_ptr, x_ptr, D, arch), -rshifts);
+            }
+        }
+        /* Approximate residual energy */
+        *res_nrg = silk_LSHIFT( silk_SMMUL( invGain_Q30, C0 ), 2 );
+        *res_nrg_Q = -rshifts;
+    } else {
+        /* Return residual energy */
+        nrg  = CAf[ 0 ];                                                                            /* Q( -rshifts ) */
+        tmp1 = (opus_int32)1 << 16;                                                                             /* Q16 */
+        for( k = 0; k < D; k++ ) {
+            Atmp1 = silk_RSHIFT_ROUND( Af_QA[ k ], QA - 16 );                                       /* Q16 */
+            nrg  = silk_SMLAWW( nrg, CAf[ k + 1 ], Atmp1 );                                         /* Q( -rshifts ) */
+            tmp1 = silk_SMLAWW( tmp1, Atmp1, Atmp1 );                                               /* Q16 */
+            A_Q16[ k ] = -Atmp1;
+        }
+        *res_nrg = silk_SMLAWW( nrg, silk_SMMUL( SILK_FIX_CONST( FIND_LPC_COND_FAC, 32 ), C0 ), -tmp1 );/* Q( -rshifts ) */
+        *res_nrg_Q = -rshifts;
+    }
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/burg_modified_FIX_sse4_1.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/burg_modified_FIX_sse4_1.c
new file mode 100644 (file)
index 0000000..c13695e
--- /dev/null
@@ -0,0 +1,400 @@
+/* Copyright (c) 2014-2020, Cisco Systems, INC
+   Written by XiangMingZhu WeiZhou MinPeng YanWang FrancisQuiers
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+   - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+   - Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+   OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <xmmintrin.h>
+#include <emmintrin.h>
+#include <smmintrin.h>
+
+#include "SigProc_FIX.h"
+#include "define.h"
+#include "tuning_parameters.h"
+#include "pitch.h"
+#include "celt/x86/x86cpu.h"
+
+#define MAX_FRAME_SIZE              384             /* subfr_length * nb_subfr = ( 0.005 * 16000 + 16 ) * 4 = 384 */
+
+#define QA                          25
+#define N_BITS_HEAD_ROOM            3
+#define MIN_RSHIFTS                 -16
+#define MAX_RSHIFTS                 (32 - QA)
+
+/* Compute reflection coefficients from input signal */
+void silk_burg_modified_sse4_1(
+    opus_int32                  *res_nrg,           /* O    Residual energy                                             */
+    opus_int                    *res_nrg_Q,         /* O    Residual energy Q value                                     */
+    opus_int32                  A_Q16[],            /* O    Prediction coefficients (length order)                      */
+    const opus_int16            x[],                /* I    Input signal, length: nb_subfr * ( D + subfr_length )       */
+    const opus_int32            minInvGain_Q30,     /* I    Inverse of max prediction gain                              */
+    const opus_int              subfr_length,       /* I    Input signal subframe length (incl. D preceding samples)    */
+    const opus_int              nb_subfr,           /* I    Number of subframes stacked in x                            */
+    const opus_int              D,                  /* I    Order                                                       */
+    int                         arch                /* I    Run-time architecture                                       */
+)
+{
+    opus_int         k, n, s, lz, rshifts, reached_max_gain;
+    opus_int32       C0, num, nrg, rc_Q31, invGain_Q30, Atmp_QA, Atmp1, tmp1, tmp2, x1, x2;
+    const opus_int16 *x_ptr;
+    opus_int32       C_first_row[ SILK_MAX_ORDER_LPC ];
+    opus_int32       C_last_row[  SILK_MAX_ORDER_LPC ];
+    opus_int32       Af_QA[       SILK_MAX_ORDER_LPC ];
+    opus_int32       CAf[ SILK_MAX_ORDER_LPC + 1 ];
+    opus_int32       CAb[ SILK_MAX_ORDER_LPC + 1 ];
+    opus_int32       xcorr[ SILK_MAX_ORDER_LPC ];
+    opus_int64       C0_64;
+
+    __m128i FIRST_3210, LAST_3210, ATMP_3210, TMP1_3210, TMP2_3210, T1_3210, T2_3210, PTR_3210, SUBFR_3210, X1_3210, X2_3210;
+    __m128i CONST1 = _mm_set1_epi32(1);
+
+    celt_assert( subfr_length * nb_subfr <= MAX_FRAME_SIZE );
+
+    /* Compute autocorrelations, added over subframes */
+    C0_64 = silk_inner_prod16( x, x, subfr_length*nb_subfr, arch );
+    lz = silk_CLZ64(C0_64);
+    rshifts = 32 + 1 + N_BITS_HEAD_ROOM - lz;
+    if (rshifts > MAX_RSHIFTS) rshifts = MAX_RSHIFTS;
+    if (rshifts < MIN_RSHIFTS) rshifts = MIN_RSHIFTS;
+
+    if (rshifts > 0) {
+        C0 = (opus_int32)silk_RSHIFT64(C0_64, rshifts );
+    } else {
+        C0 = silk_LSHIFT32((opus_int32)C0_64, -rshifts );
+    }
+
+    CAb[ 0 ] = CAf[ 0 ] = C0 + silk_SMMUL( SILK_FIX_CONST( FIND_LPC_COND_FAC, 32 ), C0 ) + 1;                                /* Q(-rshifts) */
+    silk_memset( C_first_row, 0, SILK_MAX_ORDER_LPC * sizeof( opus_int32 ) );
+    if( rshifts > 0 ) {
+        for( s = 0; s < nb_subfr; s++ ) {
+            x_ptr = x + s * subfr_length;
+            for( n = 1; n < D + 1; n++ ) {
+                C_first_row[ n - 1 ] += (opus_int32)silk_RSHIFT64(
+                    silk_inner_prod16( x_ptr, x_ptr + n, subfr_length - n, arch ), rshifts );
+            }
+        }
+    } else {
+        for( s = 0; s < nb_subfr; s++ ) {
+            int i;
+            opus_int32 d;
+            x_ptr = x + s * subfr_length;
+            celt_pitch_xcorr(x_ptr, x_ptr + 1, xcorr, subfr_length - D, D, arch );
+            for( n = 1; n < D + 1; n++ ) {
+               for ( i = n + subfr_length - D, d = 0; i < subfr_length; i++ )
+                  d = MAC16_16( d, x_ptr[ i ], x_ptr[ i - n ] );
+               xcorr[ n - 1 ] += d;
+            }
+            for( n = 1; n < D + 1; n++ ) {
+                C_first_row[ n - 1 ] += silk_LSHIFT32( xcorr[ n - 1 ], -rshifts );
+            }
+        }
+    }
+    silk_memcpy( C_last_row, C_first_row, SILK_MAX_ORDER_LPC * sizeof( opus_int32 ) );
+
+    /* Initialize */
+    CAb[ 0 ] = CAf[ 0 ] = C0 + silk_SMMUL( SILK_FIX_CONST( FIND_LPC_COND_FAC, 32 ), C0 ) + 1;                                /* Q(-rshifts) */
+
+    invGain_Q30 = (opus_int32)1 << 30;
+    reached_max_gain = 0;
+    for( n = 0; n < D; n++ ) {
+        /* Update first row of correlation matrix (without first element) */
+        /* Update last row of correlation matrix (without last element, stored in reversed order) */
+        /* Update C * Af */
+        /* Update C * flipud(Af) (stored in reversed order) */
+        if( rshifts > -2 ) {
+            for( s = 0; s < nb_subfr; s++ ) {
+                x_ptr = x + s * subfr_length;
+                x1  = -silk_LSHIFT32( (opus_int32)x_ptr[ n ],                    16 - rshifts );        /* Q(16-rshifts) */
+                x2  = -silk_LSHIFT32( (opus_int32)x_ptr[ subfr_length - n - 1 ], 16 - rshifts );        /* Q(16-rshifts) */
+                tmp1 = silk_LSHIFT32( (opus_int32)x_ptr[ n ],                    QA - 16 );             /* Q(QA-16) */
+                tmp2 = silk_LSHIFT32( (opus_int32)x_ptr[ subfr_length - n - 1 ], QA - 16 );             /* Q(QA-16) */
+                for( k = 0; k < n; k++ ) {
+                    C_first_row[ k ] = silk_SMLAWB( C_first_row[ k ], x1, x_ptr[ n - k - 1 ]            ); /* Q( -rshifts ) */
+                    C_last_row[ k ]  = silk_SMLAWB( C_last_row[ k ],  x2, x_ptr[ subfr_length - n + k ] ); /* Q( -rshifts ) */
+                    Atmp_QA = Af_QA[ k ];
+                    tmp1 = silk_SMLAWB( tmp1, Atmp_QA, x_ptr[ n - k - 1 ]            );                 /* Q(QA-16) */
+                    tmp2 = silk_SMLAWB( tmp2, Atmp_QA, x_ptr[ subfr_length - n + k ] );                 /* Q(QA-16) */
+                }
+                tmp1 = silk_LSHIFT32( -tmp1, 32 - QA - rshifts );                                       /* Q(16-rshifts) */
+                tmp2 = silk_LSHIFT32( -tmp2, 32 - QA - rshifts );                                       /* Q(16-rshifts) */
+                for( k = 0; k <= n; k++ ) {
+                    CAf[ k ] = silk_SMLAWB( CAf[ k ], tmp1, x_ptr[ n - k ]                    );        /* Q( -rshift ) */
+                    CAb[ k ] = silk_SMLAWB( CAb[ k ], tmp2, x_ptr[ subfr_length - n + k - 1 ] );        /* Q( -rshift ) */
+                }
+            }
+        } else {
+            for( s = 0; s < nb_subfr; s++ ) {
+                x_ptr = x + s * subfr_length;
+                x1  = -silk_LSHIFT32( (opus_int32)x_ptr[ n ],                    -rshifts );            /* Q( -rshifts ) */
+                x2  = -silk_LSHIFT32( (opus_int32)x_ptr[ subfr_length - n - 1 ], -rshifts );            /* Q( -rshifts ) */
+                tmp1 = silk_LSHIFT32( (opus_int32)x_ptr[ n ],                    17 );                  /* Q17 */
+                tmp2 = silk_LSHIFT32( (opus_int32)x_ptr[ subfr_length - n - 1 ], 17 );                  /* Q17 */
+
+                X1_3210 = _mm_set1_epi32( x1 );
+                X2_3210 = _mm_set1_epi32( x2 );
+                TMP1_3210 = _mm_setzero_si128();
+                TMP2_3210 = _mm_setzero_si128();
+                for( k = 0; k < n - 3; k += 4 ) {
+                    PTR_3210   = OP_CVTEPI16_EPI32_M64( &x_ptr[ n - k - 1 - 3 ] );
+                    SUBFR_3210 = OP_CVTEPI16_EPI32_M64( &x_ptr[ subfr_length - n + k ] );
+                    FIRST_3210 = _mm_loadu_si128( (__m128i *)(void*)&C_first_row[ k ] );
+                    PTR_3210   = _mm_shuffle_epi32( PTR_3210,  _MM_SHUFFLE( 0, 1, 2, 3 ) );
+                    LAST_3210  = _mm_loadu_si128( (__m128i *)(void*)&C_last_row[ k ] );
+                    ATMP_3210  = _mm_loadu_si128( (__m128i *)(void*)&Af_QA[ k ] );
+
+                    T1_3210 = _mm_mullo_epi32( PTR_3210, X1_3210 );
+                    T2_3210 = _mm_mullo_epi32( SUBFR_3210, X2_3210 );
+
+                    ATMP_3210 = _mm_srai_epi32( ATMP_3210, 7 );
+                    ATMP_3210 = _mm_add_epi32( ATMP_3210, CONST1 );
+                    ATMP_3210 = _mm_srai_epi32( ATMP_3210, 1 );
+
+                    FIRST_3210 = _mm_add_epi32( FIRST_3210, T1_3210 );
+                    LAST_3210 = _mm_add_epi32( LAST_3210, T2_3210 );
+
+                    PTR_3210   = _mm_mullo_epi32( ATMP_3210, PTR_3210 );
+                    SUBFR_3210   = _mm_mullo_epi32( ATMP_3210, SUBFR_3210 );
+
+                    _mm_storeu_si128( (__m128i *)(void*)&C_first_row[ k ], FIRST_3210 );
+                    _mm_storeu_si128( (__m128i *)(void*)&C_last_row[ k ], LAST_3210 );
+
+                    TMP1_3210 = _mm_add_epi32( TMP1_3210, PTR_3210 );
+                    TMP2_3210 = _mm_add_epi32( TMP2_3210, SUBFR_3210 );
+                }
+
+                TMP1_3210 = _mm_add_epi32( TMP1_3210, _mm_unpackhi_epi64(TMP1_3210, TMP1_3210 ) );
+                TMP2_3210 = _mm_add_epi32( TMP2_3210, _mm_unpackhi_epi64(TMP2_3210, TMP2_3210 ) );
+                TMP1_3210 = _mm_add_epi32( TMP1_3210, _mm_shufflelo_epi16(TMP1_3210, 0x0E ) );
+                TMP2_3210 = _mm_add_epi32( TMP2_3210, _mm_shufflelo_epi16(TMP2_3210, 0x0E ) );
+
+                tmp1 += _mm_cvtsi128_si32( TMP1_3210 );
+                tmp2 += _mm_cvtsi128_si32( TMP2_3210 );
+
+                for( ; k < n; k++ ) {
+                    C_first_row[ k ] = silk_MLA( C_first_row[ k ], x1, x_ptr[ n - k - 1 ]            ); /* Q( -rshifts ) */
+                    C_last_row[ k ]  = silk_MLA( C_last_row[ k ],  x2, x_ptr[ subfr_length - n + k ] ); /* Q( -rshifts ) */
+                    Atmp1 = silk_RSHIFT_ROUND( Af_QA[ k ], QA - 17 );                                   /* Q17 */
+                    /* We sometimes get overflows in the multiplications (even beyond +/- 2^32),
+                       but they cancel each other and the real result seems to always fit in a 32-bit
+                       signed integer. This was determined experimentally, not theoretically (unfortunately). */
+                    tmp1 = silk_MLA_ovflw( tmp1, x_ptr[ n - k - 1 ],            Atmp1 );                      /* Q17 */
+                    tmp2 = silk_MLA_ovflw( tmp2, x_ptr[ subfr_length - n + k ], Atmp1 );                      /* Q17 */
+                }
+
+                tmp1 = -tmp1;                /* Q17 */
+                tmp2 = -tmp2;                /* Q17 */
+
+                {
+                    __m128i xmm_tmp1, xmm_tmp2;
+                    __m128i xmm_x_ptr_n_k_x2x0, xmm_x_ptr_n_k_x3x1;
+                    __m128i xmm_x_ptr_sub_x2x0, xmm_x_ptr_sub_x3x1;
+
+                    xmm_tmp1 = _mm_set1_epi32( tmp1 );
+                    xmm_tmp2 = _mm_set1_epi32( tmp2 );
+
+                    for( k = 0; k <= n - 3; k += 4 ) {
+                        xmm_x_ptr_n_k_x2x0 = OP_CVTEPI16_EPI32_M64( &x_ptr[ n - k - 3 ] );
+                        xmm_x_ptr_sub_x2x0 = OP_CVTEPI16_EPI32_M64( &x_ptr[ subfr_length - n + k - 1 ] );
+
+                        xmm_x_ptr_n_k_x2x0 = _mm_shuffle_epi32( xmm_x_ptr_n_k_x2x0, _MM_SHUFFLE( 0, 1, 2, 3 ) );
+
+                        xmm_x_ptr_n_k_x2x0 = _mm_slli_epi32( xmm_x_ptr_n_k_x2x0, -rshifts - 1 );
+                        xmm_x_ptr_sub_x2x0 = _mm_slli_epi32( xmm_x_ptr_sub_x2x0, -rshifts - 1 );
+
+                        /* equal shift right 4 bytes, xmm_x_ptr_n_k_x3x1 = _mm_srli_si128(xmm_x_ptr_n_k_x2x0, 4)*/
+                        xmm_x_ptr_n_k_x3x1 = _mm_shuffle_epi32( xmm_x_ptr_n_k_x2x0, _MM_SHUFFLE( 0, 3, 2, 1 ) );
+                        xmm_x_ptr_sub_x3x1 = _mm_shuffle_epi32( xmm_x_ptr_sub_x2x0, _MM_SHUFFLE( 0, 3, 2, 1 ) );
+
+                        xmm_x_ptr_n_k_x2x0 = _mm_mul_epi32( xmm_x_ptr_n_k_x2x0, xmm_tmp1 );
+                        xmm_x_ptr_n_k_x3x1 = _mm_mul_epi32( xmm_x_ptr_n_k_x3x1, xmm_tmp1 );
+                        xmm_x_ptr_sub_x2x0 = _mm_mul_epi32( xmm_x_ptr_sub_x2x0, xmm_tmp2 );
+                        xmm_x_ptr_sub_x3x1 = _mm_mul_epi32( xmm_x_ptr_sub_x3x1, xmm_tmp2 );
+
+                        xmm_x_ptr_n_k_x2x0 = _mm_srli_epi64( xmm_x_ptr_n_k_x2x0, 16 );
+                        xmm_x_ptr_n_k_x3x1 = _mm_slli_epi64( xmm_x_ptr_n_k_x3x1, 16 );
+                        xmm_x_ptr_sub_x2x0 = _mm_srli_epi64( xmm_x_ptr_sub_x2x0, 16 );
+                        xmm_x_ptr_sub_x3x1 = _mm_slli_epi64( xmm_x_ptr_sub_x3x1, 16 );
+
+                        xmm_x_ptr_n_k_x2x0 = _mm_blend_epi16( xmm_x_ptr_n_k_x2x0, xmm_x_ptr_n_k_x3x1, 0xCC );
+                        xmm_x_ptr_sub_x2x0 = _mm_blend_epi16( xmm_x_ptr_sub_x2x0, xmm_x_ptr_sub_x3x1, 0xCC );
+
+                        X1_3210  = _mm_loadu_si128( (__m128i *)(void*)&CAf[ k ] );
+                        PTR_3210 = _mm_loadu_si128( (__m128i *)(void*)&CAb[ k ] );
+
+                        X1_3210  = _mm_add_epi32( X1_3210, xmm_x_ptr_n_k_x2x0 );
+                        PTR_3210 = _mm_add_epi32( PTR_3210, xmm_x_ptr_sub_x2x0 );
+
+                        _mm_storeu_si128( (__m128i *)(void*)&CAf[ k ], X1_3210 );
+                        _mm_storeu_si128( (__m128i *)(void*)&CAb[ k ], PTR_3210 );
+                    }
+
+                    for( ; k <= n; k++ ) {
+                        CAf[ k ] = silk_SMLAWW( CAf[ k ], tmp1,
+                            silk_LSHIFT32( (opus_int32)x_ptr[ n - k ], -rshifts - 1 ) );                    /* Q( -rshift ) */
+                        CAb[ k ] = silk_SMLAWW( CAb[ k ], tmp2,
+                            silk_LSHIFT32( (opus_int32)x_ptr[ subfr_length - n + k - 1 ], -rshifts - 1 ) ); /* Q( -rshift ) */
+                    }
+                }
+            }
+        }
+
+        /* Calculate nominator and denominator for the next order reflection (parcor) coefficient */
+        tmp1 = C_first_row[ n ];                                                                        /* Q( -rshifts ) */
+        tmp2 = C_last_row[ n ];                                                                         /* Q( -rshifts ) */
+        num  = 0;                                                                                       /* Q( -rshifts ) */
+        nrg  = silk_ADD32( CAb[ 0 ], CAf[ 0 ] );                                                        /* Q( 1-rshifts ) */
+        for( k = 0; k < n; k++ ) {
+            Atmp_QA = Af_QA[ k ];
+            lz = silk_CLZ32( silk_abs( Atmp_QA ) ) - 1;
+            lz = silk_min( 32 - QA, lz );
+            Atmp1 = silk_LSHIFT32( Atmp_QA, lz );                                                       /* Q( QA + lz ) */
+
+            tmp1 = silk_ADD_LSHIFT32( tmp1, silk_SMMUL( C_last_row[  n - k - 1 ], Atmp1 ), 32 - QA - lz );  /* Q( -rshifts ) */
+            tmp2 = silk_ADD_LSHIFT32( tmp2, silk_SMMUL( C_first_row[ n - k - 1 ], Atmp1 ), 32 - QA - lz );  /* Q( -rshifts ) */
+            num  = silk_ADD_LSHIFT32( num,  silk_SMMUL( CAb[ n - k ],             Atmp1 ), 32 - QA - lz );  /* Q( -rshifts ) */
+            nrg  = silk_ADD_LSHIFT32( nrg,  silk_SMMUL( silk_ADD32( CAb[ k + 1 ], CAf[ k + 1 ] ),
+                                                                                Atmp1 ), 32 - QA - lz );    /* Q( 1-rshifts ) */
+        }
+        CAf[ n + 1 ] = tmp1;                                                                            /* Q( -rshifts ) */
+        CAb[ n + 1 ] = tmp2;                                                                            /* Q( -rshifts ) */
+        num = silk_ADD32( num, tmp2 );                                                                  /* Q( -rshifts ) */
+        num = silk_LSHIFT32( -num, 1 );                                                                 /* Q( 1-rshifts ) */
+
+        /* Calculate the next order reflection (parcor) coefficient */
+        if( silk_abs( num ) < nrg ) {
+            rc_Q31 = silk_DIV32_varQ( num, nrg, 31 );
+        } else {
+            rc_Q31 = ( num > 0 ) ? silk_int32_MAX : silk_int32_MIN;
+        }
+
+        /* Update inverse prediction gain */
+        tmp1 = ( (opus_int32)1 << 30 ) - silk_SMMUL( rc_Q31, rc_Q31 );
+        tmp1 = silk_LSHIFT( silk_SMMUL( invGain_Q30, tmp1 ), 2 );
+        if( tmp1 <= minInvGain_Q30 ) {
+            /* Max prediction gain exceeded; set reflection coefficient such that max prediction gain is exactly hit */
+            tmp2 = ( (opus_int32)1 << 30 ) - silk_DIV32_varQ( minInvGain_Q30, invGain_Q30, 30 );            /* Q30 */
+            rc_Q31 = silk_SQRT_APPROX( tmp2 );                                                  /* Q15 */
+            if( rc_Q31 > 0 ) {
+                 /* Newton-Raphson iteration */
+                rc_Q31 = silk_RSHIFT32( rc_Q31 + silk_DIV32( tmp2, rc_Q31 ), 1 );                   /* Q15 */
+                rc_Q31 = silk_LSHIFT32( rc_Q31, 16 );                                               /* Q31 */
+                if( num < 0 ) {
+                    /* Ensure adjusted reflection coefficients has the original sign */
+                    rc_Q31 = -rc_Q31;
+                }
+            }
+            invGain_Q30 = minInvGain_Q30;
+            reached_max_gain = 1;
+        } else {
+            invGain_Q30 = tmp1;
+        }
+
+        /* Update the AR coefficients */
+        for( k = 0; k < (n + 1) >> 1; k++ ) {
+            tmp1 = Af_QA[ k ];                                                                  /* QA */
+            tmp2 = Af_QA[ n - k - 1 ];                                                          /* QA */
+            Af_QA[ k ]         = silk_ADD_LSHIFT32( tmp1, silk_SMMUL( tmp2, rc_Q31 ), 1 );      /* QA */
+            Af_QA[ n - k - 1 ] = silk_ADD_LSHIFT32( tmp2, silk_SMMUL( tmp1, rc_Q31 ), 1 );      /* QA */
+        }
+        Af_QA[ n ] = silk_RSHIFT32( rc_Q31, 31 - QA );                                          /* QA */
+
+        if( reached_max_gain ) {
+            /* Reached max prediction gain; set remaining coefficients to zero and exit loop */
+            for( k = n + 1; k < D; k++ ) {
+                Af_QA[ k ] = 0;
+            }
+            break;
+        }
+
+        /* Update C * Af and C * Ab */
+        for( k = 0; k <= n + 1; k++ ) {
+            tmp1 = CAf[ k ];                                                                    /* Q( -rshifts ) */
+            tmp2 = CAb[ n - k + 1 ];                                                            /* Q( -rshifts ) */
+            CAf[ k ]         = silk_ADD_LSHIFT32( tmp1, silk_SMMUL( tmp2, rc_Q31 ), 1 );        /* Q( -rshifts ) */
+            CAb[ n - k + 1 ] = silk_ADD_LSHIFT32( tmp2, silk_SMMUL( tmp1, rc_Q31 ), 1 );        /* Q( -rshifts ) */
+        }
+    }
+
+    if( reached_max_gain ) {
+        for( k = 0; k < D; k++ ) {
+            /* Scale coefficients */
+            A_Q16[ k ] = -silk_RSHIFT_ROUND( Af_QA[ k ], QA - 16 );
+        }
+        /* Subtract energy of preceding samples from C0 */
+        if( rshifts > 0 ) {
+            for( s = 0; s < nb_subfr; s++ ) {
+                x_ptr = x + s * subfr_length;
+                C0 -= (opus_int32)silk_RSHIFT64( silk_inner_prod16( x_ptr, x_ptr, D, arch ), rshifts );
+            }
+        } else {
+            for( s = 0; s < nb_subfr; s++ ) {
+                x_ptr = x + s * subfr_length;
+                C0 -= silk_LSHIFT32( silk_inner_prod_aligned( x_ptr, x_ptr, D, arch ), -rshifts );
+            }
+        }
+        /* Approximate residual energy */
+        *res_nrg = silk_LSHIFT( silk_SMMUL( invGain_Q30, C0 ), 2 );
+        *res_nrg_Q = -rshifts;
+    } else {
+        /* Return residual energy */
+        nrg  = CAf[ 0 ];                                                                            /* Q( -rshifts ) */
+        tmp1 = (opus_int32)1 << 16;                                                                             /* Q16 */
+        for( k = 0; k < D; k++ ) {
+            Atmp1 = silk_RSHIFT_ROUND( Af_QA[ k ], QA - 16 );                                       /* Q16 */
+            nrg  = silk_SMLAWW( nrg, CAf[ k + 1 ], Atmp1 );                                         /* Q( -rshifts ) */
+            tmp1 = silk_SMLAWW( tmp1, Atmp1, Atmp1 );                                               /* Q16 */
+            A_Q16[ k ] = -Atmp1;
+        }
+        *res_nrg = silk_SMLAWW( nrg, silk_SMMUL( SILK_FIX_CONST( FIND_LPC_COND_FAC, 32 ), C0 ), -tmp1 );/* Q( -rshifts ) */
+        *res_nrg_Q = -rshifts;
+    }
+
+#ifdef OPUS_CHECK_ASM
+    {
+        opus_int32 res_nrg_c = 0;
+        opus_int res_nrg_Q_c = 0;
+        opus_int32 A_Q16_c[ MAX_LPC_ORDER ] = {0};
+
+        silk_burg_modified_c(
+            &res_nrg_c,
+            &res_nrg_Q_c,
+            A_Q16_c,
+            x,
+            minInvGain_Q30,
+            subfr_length,
+            nb_subfr,
+            D,
+            0
+        );
+
+        silk_assert( *res_nrg == res_nrg_c );
+        silk_assert( *res_nrg_Q == res_nrg_Q_c );
+        silk_assert( !memcmp( A_Q16, A_Q16_c, D * sizeof( *A_Q16 ) ) );
+    }
+#endif
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/corrMatrix_FIX.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/corrMatrix_FIX.c
new file mode 100644 (file)
index 0000000..3b194b1
--- /dev/null
@@ -0,0 +1,149 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/**********************************************************************
+ * Correlation Matrix Computations for LS estimate.
+ **********************************************************************/
+
+#include "main_FIX.h"
+
+/* Calculates correlation vector X'*t */
+void silk_corrVector_FIX(
+    const opus_int16                *x,                                     /* I    x vector [L + order - 1] used to form data matrix X                         */
+    const opus_int16                *t,                                     /* I    Target vector [L]                                                           */
+    const opus_int                  L,                                      /* I    Length of vectors                                                           */
+    const opus_int                  order,                                  /* I    Max lag for correlation                                                     */
+    opus_int32                      *Xt,                                    /* O    Pointer to X'*t correlation vector [order]                                  */
+    const opus_int                  rshifts,                                /* I    Right shifts of correlations                                                */
+    int                             arch                                    /* I    Run-time architecture                                                       */
+)
+{
+    opus_int         lag, i;
+    const opus_int16 *ptr1, *ptr2;
+    opus_int32       inner_prod;
+
+    ptr1 = &x[ order - 1 ]; /* Points to first sample of column 0 of X: X[:,0] */
+    ptr2 = t;
+    /* Calculate X'*t */
+    if( rshifts > 0 ) {
+        /* Right shifting used */
+        for( lag = 0; lag < order; lag++ ) {
+            inner_prod = 0;
+            for( i = 0; i < L; i++ ) {
+                inner_prod = silk_ADD_RSHIFT32( inner_prod, silk_SMULBB( ptr1[ i ], ptr2[i] ), rshifts );
+            }
+            Xt[ lag ] = inner_prod; /* X[:,lag]'*t */
+            ptr1--; /* Go to next column of X */
+        }
+    } else {
+        silk_assert( rshifts == 0 );
+        for( lag = 0; lag < order; lag++ ) {
+            Xt[ lag ] = silk_inner_prod_aligned( ptr1, ptr2, L, arch ); /* X[:,lag]'*t */
+            ptr1--; /* Go to next column of X */
+        }
+    }
+}
+
+/* Calculates correlation matrix X'*X */
+void silk_corrMatrix_FIX(
+    const opus_int16                *x,                                     /* I    x vector [L + order - 1] used to form data matrix X                         */
+    const opus_int                  L,                                      /* I    Length of vectors                                                           */
+    const opus_int                  order,                                  /* I    Max lag for correlation                                                     */
+    opus_int32                      *XX,                                    /* O    Pointer to X'*X correlation matrix [ order x order ]                        */
+    opus_int32                      *nrg,                                    /* O    Energy of x vector                                                            */
+    opus_int                        *rshifts,                               /* O    Right shifts of correlations and energy                                     */
+    int                             arch                                    /* I    Run-time architecture                                                       */
+)
+{
+    opus_int         i, j, lag;
+    opus_int32       energy;
+    const opus_int16 *ptr1, *ptr2;
+
+    /* Calculate energy to find shift used to fit in 32 bits */
+    silk_sum_sqr_shift( nrg, rshifts, x, L + order - 1 );
+    energy = *nrg;
+
+    /* Calculate energy of first column (0) of X: X[:,0]'*X[:,0] */
+    /* Remove contribution of first order - 1 samples */
+    for( i = 0; i < order - 1; i++ ) {
+        energy -= silk_RSHIFT32( silk_SMULBB( x[ i ], x[ i ] ), *rshifts );
+    }
+
+    /* Calculate energy of remaining columns of X: X[:,j]'*X[:,j] */
+    /* Fill out the diagonal of the correlation matrix */
+    matrix_ptr( XX, 0, 0, order ) = energy;
+    silk_assert( energy >= 0 );
+    ptr1 = &x[ order - 1 ]; /* First sample of column 0 of X */
+    for( j = 1; j < order; j++ ) {
+        energy = silk_SUB32( energy, silk_RSHIFT32( silk_SMULBB( ptr1[ L - j ], ptr1[ L - j ] ), *rshifts ) );
+        energy = silk_ADD32( energy, silk_RSHIFT32( silk_SMULBB( ptr1[ -j ], ptr1[ -j ] ), *rshifts ) );
+        matrix_ptr( XX, j, j, order ) = energy;
+        silk_assert( energy >= 0 );
+    }
+
+    ptr2 = &x[ order - 2 ]; /* First sample of column 1 of X */
+    /* Calculate the remaining elements of the correlation matrix */
+    if( *rshifts > 0 ) {
+        /* Right shifting used */
+        for( lag = 1; lag < order; lag++ ) {
+            /* Inner product of column 0 and column lag: X[:,0]'*X[:,lag] */
+            energy = 0;
+            for( i = 0; i < L; i++ ) {
+                energy += silk_RSHIFT32( silk_SMULBB( ptr1[ i ], ptr2[i] ), *rshifts );
+            }
+            /* Calculate remaining off diagonal: X[:,j]'*X[:,j + lag] */
+            matrix_ptr( XX, lag, 0, order ) = energy;
+            matrix_ptr( XX, 0, lag, order ) = energy;
+            for( j = 1; j < ( order - lag ); j++ ) {
+                energy = silk_SUB32( energy, silk_RSHIFT32( silk_SMULBB( ptr1[ L - j ], ptr2[ L - j ] ), *rshifts ) );
+                energy = silk_ADD32( energy, silk_RSHIFT32( silk_SMULBB( ptr1[ -j ], ptr2[ -j ] ), *rshifts ) );
+                matrix_ptr( XX, lag + j, j, order ) = energy;
+                matrix_ptr( XX, j, lag + j, order ) = energy;
+            }
+            ptr2--; /* Update pointer to first sample of next column (lag) in X */
+        }
+    } else {
+        for( lag = 1; lag < order; lag++ ) {
+            /* Inner product of column 0 and column lag: X[:,0]'*X[:,lag] */
+            energy = silk_inner_prod_aligned( ptr1, ptr2, L, arch );
+            matrix_ptr( XX, lag, 0, order ) = energy;
+            matrix_ptr( XX, 0, lag, order ) = energy;
+            /* Calculate remaining off diagonal: X[:,j]'*X[:,j + lag] */
+            for( j = 1; j < ( order - lag ); j++ ) {
+                energy = silk_SUB32( energy, silk_SMULBB( ptr1[ L - j ], ptr2[ L - j ] ) );
+                energy = silk_SMLABB( energy, ptr1[ -j ], ptr2[ -j ] );
+                matrix_ptr( XX, lag + j, j, order ) = energy;
+                matrix_ptr( XX, j, lag + j, order ) = energy;
+            }
+            ptr2--;/* Update pointer to first sample of next column (lag) in X */
+        }
+    }
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/encode_frame_FIX.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/encode_frame_FIX.c
new file mode 100644 (file)
index 0000000..c2f5281
--- /dev/null
@@ -0,0 +1,454 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include "main_FIX.h"
+#include "stack_alloc.h"
+#include "tuning_parameters.h"
+
+/* Low Bitrate Redundancy (LBRR) encoding. Reuse all parameters but encode with lower bitrate           */
+static OPUS_INLINE void silk_LBRR_encode_FIX(
+    silk_encoder_state_FIX          *psEnc,                                 /* I/O  Pointer to Silk FIX encoder state                                           */
+    silk_encoder_control_FIX        *psEncCtrl,                             /* I/O  Pointer to Silk FIX encoder control struct                                  */
+    const opus_int16                x16[],                                  /* I    Input signal                                                                */
+    opus_int                        condCoding                              /* I    The type of conditional coding used so far for this frame                   */
+);
+
+void silk_encode_do_VAD_FIX(
+    silk_encoder_state_FIX          *psEnc,                                 /* I/O  Pointer to Silk FIX encoder state                                           */
+    opus_int                        activity                                /* I    Decision of Opus voice activity detector                                    */
+)
+{
+    const opus_int activity_threshold = SILK_FIX_CONST( SPEECH_ACTIVITY_DTX_THRES, 8 );
+
+    /****************************/
+    /* Voice Activity Detection */
+    /****************************/
+    silk_VAD_GetSA_Q8( &psEnc->sCmn, psEnc->sCmn.inputBuf + 1, psEnc->sCmn.arch );
+    /* If Opus VAD is inactive and Silk VAD is active: lower Silk VAD to just under the threshold */
+    if( activity == VAD_NO_ACTIVITY && psEnc->sCmn.speech_activity_Q8 >= activity_threshold ) {
+        psEnc->sCmn.speech_activity_Q8 = activity_threshold - 1;
+    }
+
+    /**************************************************/
+    /* Convert speech activity into VAD and DTX flags */
+    /**************************************************/
+    if( psEnc->sCmn.speech_activity_Q8 < activity_threshold ) {
+        psEnc->sCmn.indices.signalType = TYPE_NO_VOICE_ACTIVITY;
+        psEnc->sCmn.noSpeechCounter++;
+        if( psEnc->sCmn.noSpeechCounter <= NB_SPEECH_FRAMES_BEFORE_DTX ) {
+            psEnc->sCmn.inDTX = 0;
+        } else if( psEnc->sCmn.noSpeechCounter > MAX_CONSECUTIVE_DTX + NB_SPEECH_FRAMES_BEFORE_DTX ) {
+            psEnc->sCmn.noSpeechCounter = NB_SPEECH_FRAMES_BEFORE_DTX;
+            psEnc->sCmn.inDTX           = 0;
+        }
+        psEnc->sCmn.VAD_flags[ psEnc->sCmn.nFramesEncoded ] = 0;
+    } else {
+        psEnc->sCmn.noSpeechCounter    = 0;
+        psEnc->sCmn.inDTX              = 0;
+        psEnc->sCmn.indices.signalType = TYPE_UNVOICED;
+        psEnc->sCmn.VAD_flags[ psEnc->sCmn.nFramesEncoded ] = 1;
+    }
+}
+
+/****************/
+/* Encode frame */
+/****************/
+opus_int silk_encode_frame_FIX(
+    silk_encoder_state_FIX          *psEnc,                                 /* I/O  Pointer to Silk FIX encoder state                                           */
+    opus_int32                      *pnBytesOut,                            /* O    Pointer to number of payload bytes;                                         */
+    ec_enc                          *psRangeEnc,                            /* I/O  compressor data structure                                                   */
+    opus_int                        condCoding,                             /* I    The type of conditional coding to use                                       */
+    opus_int                        maxBits,                                /* I    If > 0: maximum number of output bits                                       */
+    opus_int                        useCBR                                  /* I    Flag to force constant-bitrate operation                                    */
+)
+{
+    silk_encoder_control_FIX sEncCtrl;
+    opus_int     i, iter, maxIter, found_upper, found_lower, ret = 0;
+    opus_int16   *x_frame;
+    ec_enc       sRangeEnc_copy, sRangeEnc_copy2;
+    VARDECL(silk_nsq_state, sNSQ_copy);
+    opus_int32   seed_copy, nBits, nBits_lower, nBits_upper, gainMult_lower, gainMult_upper;
+    opus_int32   gainsID, gainsID_lower, gainsID_upper;
+    opus_int16   gainMult_Q8;
+    opus_int16   ec_prevLagIndex_copy;
+    opus_int     ec_prevSignalType_copy;
+    opus_int8    LastGainIndex_copy2;
+    opus_int     gain_lock[ MAX_NB_SUBFR ] = {0};
+    opus_int16   best_gain_mult[ MAX_NB_SUBFR ];
+    opus_int     best_sum[ MAX_NB_SUBFR ];
+    opus_int     bits_margin;
+    SAVE_STACK;
+
+    /* Using ALLOC() instead of a regular stack allocation to minimize real stack use when using the pseudostack.
+       This is useful on some embedded systems. */
+    ALLOC(sNSQ_copy, 2, silk_nsq_state);
+
+    /* For CBR, 5 bits below budget is close enough. For VBR, allow up to 25% below the cap if we initially busted the budget. */
+    bits_margin = useCBR ? 5 : maxBits/4;
+    /* This is totally unnecessary but many compilers (including gcc) are too dumb to realise it */
+    LastGainIndex_copy2 = nBits_lower = nBits_upper = gainMult_lower = gainMult_upper = 0;
+
+    psEnc->sCmn.indices.Seed = psEnc->sCmn.frameCounter++ & 3;
+
+    /**************************************************************/
+    /* Set up Input Pointers, and insert frame in input buffer   */
+    /*************************************************************/
+    /* start of frame to encode */
+    x_frame = psEnc->x_buf + psEnc->sCmn.ltp_mem_length;
+
+    /***************************************/
+    /* Ensure smooth bandwidth transitions */
+    /***************************************/
+    silk_LP_variable_cutoff( &psEnc->sCmn.sLP, psEnc->sCmn.inputBuf + 1, psEnc->sCmn.frame_length );
+
+    /*******************************************/
+    /* Copy new frame to front of input buffer */
+    /*******************************************/
+    silk_memcpy( x_frame + LA_SHAPE_MS * psEnc->sCmn.fs_kHz, psEnc->sCmn.inputBuf + 1, psEnc->sCmn.frame_length * sizeof( opus_int16 ) );
+
+    if( !psEnc->sCmn.prefillFlag ) {
+        VARDECL( opus_int16, res_pitch );
+        VARDECL( opus_uint8, ec_buf_copy );
+        opus_int16 *res_pitch_frame;
+
+        ALLOC( res_pitch,
+               psEnc->sCmn.la_pitch + psEnc->sCmn.frame_length
+                   + psEnc->sCmn.ltp_mem_length, opus_int16 );
+        /* start of pitch LPC residual frame */
+        res_pitch_frame = res_pitch + psEnc->sCmn.ltp_mem_length;
+
+        /*****************************************/
+        /* Find pitch lags, initial LPC analysis */
+        /*****************************************/
+        silk_find_pitch_lags_FIX( psEnc, &sEncCtrl, res_pitch, x_frame - psEnc->sCmn.ltp_mem_length, psEnc->sCmn.arch );
+
+        /************************/
+        /* Noise shape analysis */
+        /************************/
+        silk_noise_shape_analysis_FIX( psEnc, &sEncCtrl, res_pitch_frame, x_frame, psEnc->sCmn.arch );
+
+        /***************************************************/
+        /* Find linear prediction coefficients (LPC + LTP) */
+        /***************************************************/
+        silk_find_pred_coefs_FIX( psEnc, &sEncCtrl, res_pitch_frame, x_frame, condCoding );
+
+        /****************************************/
+        /* Process gains                        */
+        /****************************************/
+        silk_process_gains_FIX( psEnc, &sEncCtrl, condCoding );
+
+        /****************************************/
+        /* Low Bitrate Redundant Encoding       */
+        /****************************************/
+        silk_LBRR_encode_FIX( psEnc, &sEncCtrl, x_frame, condCoding );
+
+        /* Loop over quantizer and entropy coding to control bitrate */
+        maxIter = 6;
+        gainMult_Q8 = SILK_FIX_CONST( 1, 8 );
+        found_lower = 0;
+        found_upper = 0;
+        gainsID = silk_gains_ID( psEnc->sCmn.indices.GainsIndices, psEnc->sCmn.nb_subfr );
+        gainsID_lower = -1;
+        gainsID_upper = -1;
+        /* Copy part of the input state */
+        silk_memcpy( &sRangeEnc_copy, psRangeEnc, sizeof( ec_enc ) );
+        silk_memcpy( &sNSQ_copy[0], &psEnc->sCmn.sNSQ, sizeof( silk_nsq_state ) );
+        seed_copy = psEnc->sCmn.indices.Seed;
+        ec_prevLagIndex_copy = psEnc->sCmn.ec_prevLagIndex;
+        ec_prevSignalType_copy = psEnc->sCmn.ec_prevSignalType;
+        ALLOC( ec_buf_copy, 1275, opus_uint8 );
+        for( iter = 0; ; iter++ ) {
+            if( gainsID == gainsID_lower ) {
+                nBits = nBits_lower;
+            } else if( gainsID == gainsID_upper ) {
+                nBits = nBits_upper;
+            } else {
+                /* Restore part of the input state */
+                if( iter > 0 ) {
+                    silk_memcpy( psRangeEnc, &sRangeEnc_copy, sizeof( ec_enc ) );
+                    silk_memcpy( &psEnc->sCmn.sNSQ, &sNSQ_copy[0], sizeof( silk_nsq_state ) );
+                    psEnc->sCmn.indices.Seed = seed_copy;
+                    psEnc->sCmn.ec_prevLagIndex = ec_prevLagIndex_copy;
+                    psEnc->sCmn.ec_prevSignalType = ec_prevSignalType_copy;
+                }
+
+                /*****************************************/
+                /* Noise shaping quantization            */
+                /*****************************************/
+                if( psEnc->sCmn.nStatesDelayedDecision > 1 || psEnc->sCmn.warping_Q16 > 0 ) {
+                    silk_NSQ_del_dec( &psEnc->sCmn, &psEnc->sCmn.sNSQ, &psEnc->sCmn.indices, x_frame, psEnc->sCmn.pulses,
+                           sEncCtrl.PredCoef_Q12[ 0 ], sEncCtrl.LTPCoef_Q14, sEncCtrl.AR_Q13, sEncCtrl.HarmShapeGain_Q14,
+                           sEncCtrl.Tilt_Q14, sEncCtrl.LF_shp_Q14, sEncCtrl.Gains_Q16, sEncCtrl.pitchL, sEncCtrl.Lambda_Q10, sEncCtrl.LTP_scale_Q14,
+                           psEnc->sCmn.arch );
+                } else {
+                    silk_NSQ( &psEnc->sCmn, &psEnc->sCmn.sNSQ, &psEnc->sCmn.indices, x_frame, psEnc->sCmn.pulses,
+                            sEncCtrl.PredCoef_Q12[ 0 ], sEncCtrl.LTPCoef_Q14, sEncCtrl.AR_Q13, sEncCtrl.HarmShapeGain_Q14,
+                            sEncCtrl.Tilt_Q14, sEncCtrl.LF_shp_Q14, sEncCtrl.Gains_Q16, sEncCtrl.pitchL, sEncCtrl.Lambda_Q10, sEncCtrl.LTP_scale_Q14,
+                            psEnc->sCmn.arch);
+                }
+
+                if ( iter == maxIter && !found_lower ) {
+                    silk_memcpy( &sRangeEnc_copy2, psRangeEnc, sizeof( ec_enc ) );
+                }
+
+                /****************************************/
+                /* Encode Parameters                    */
+                /****************************************/
+                silk_encode_indices( &psEnc->sCmn, psRangeEnc, psEnc->sCmn.nFramesEncoded, 0, condCoding );
+
+                /****************************************/
+                /* Encode Excitation Signal             */
+                /****************************************/
+                silk_encode_pulses( psRangeEnc, psEnc->sCmn.indices.signalType, psEnc->sCmn.indices.quantOffsetType,
+                    psEnc->sCmn.pulses, psEnc->sCmn.frame_length );
+
+                nBits = ec_tell( psRangeEnc );
+
+                /* If we still bust after the last iteration, do some damage control. */
+                if ( iter == maxIter && !found_lower && nBits > maxBits ) {
+                    silk_memcpy( psRangeEnc, &sRangeEnc_copy2, sizeof( ec_enc ) );
+
+                    /* Keep gains the same as the last frame. */
+                    psEnc->sShape.LastGainIndex = sEncCtrl.lastGainIndexPrev;
+                    for ( i = 0; i < psEnc->sCmn.nb_subfr; i++ ) {
+                        psEnc->sCmn.indices.GainsIndices[ i ] = 4;
+                    }
+                    if (condCoding != CODE_CONDITIONALLY) {
+                       psEnc->sCmn.indices.GainsIndices[ 0 ] = sEncCtrl.lastGainIndexPrev;
+                    }
+                    psEnc->sCmn.ec_prevLagIndex = ec_prevLagIndex_copy;
+                    psEnc->sCmn.ec_prevSignalType = ec_prevSignalType_copy;
+                    /* Clear all pulses. */
+                    for ( i = 0; i < psEnc->sCmn.frame_length; i++ ) {
+                        psEnc->sCmn.pulses[ i ] = 0;
+                    }
+
+                    silk_encode_indices( &psEnc->sCmn, psRangeEnc, psEnc->sCmn.nFramesEncoded, 0, condCoding );
+
+                    silk_encode_pulses( psRangeEnc, psEnc->sCmn.indices.signalType, psEnc->sCmn.indices.quantOffsetType,
+                        psEnc->sCmn.pulses, psEnc->sCmn.frame_length );
+
+                    nBits = ec_tell( psRangeEnc );
+                }
+
+                if( useCBR == 0 && iter == 0 && nBits <= maxBits ) {
+                    break;
+                }
+            }
+
+            if( iter == maxIter ) {
+                if( found_lower && ( gainsID == gainsID_lower || nBits > maxBits ) ) {
+                    /* Restore output state from earlier iteration that did meet the bitrate budget */
+                    silk_memcpy( psRangeEnc, &sRangeEnc_copy2, sizeof( ec_enc ) );
+                    celt_assert( sRangeEnc_copy2.offs <= 1275 );
+                    silk_memcpy( psRangeEnc->buf, ec_buf_copy, sRangeEnc_copy2.offs );
+                    silk_memcpy( &psEnc->sCmn.sNSQ, &sNSQ_copy[1], sizeof( silk_nsq_state ) );
+                    psEnc->sShape.LastGainIndex = LastGainIndex_copy2;
+                }
+                break;
+            }
+
+            if( nBits > maxBits ) {
+                if( found_lower == 0 && iter >= 2 ) {
+                    /* Adjust the quantizer's rate/distortion tradeoff and discard previous "upper" results */
+                    sEncCtrl.Lambda_Q10 = silk_ADD_RSHIFT32( sEncCtrl.Lambda_Q10, sEncCtrl.Lambda_Q10, 1 );
+                    found_upper = 0;
+                    gainsID_upper = -1;
+                } else {
+                    found_upper = 1;
+                    nBits_upper = nBits;
+                    gainMult_upper = gainMult_Q8;
+                    gainsID_upper = gainsID;
+                }
+            } else if( nBits < maxBits - bits_margin ) {
+                found_lower = 1;
+                nBits_lower = nBits;
+                gainMult_lower = gainMult_Q8;
+                if( gainsID != gainsID_lower ) {
+                    gainsID_lower = gainsID;
+                    /* Copy part of the output state */
+                    silk_memcpy( &sRangeEnc_copy2, psRangeEnc, sizeof( ec_enc ) );
+                    celt_assert( psRangeEnc->offs <= 1275 );
+                    silk_memcpy( ec_buf_copy, psRangeEnc->buf, psRangeEnc->offs );
+                    silk_memcpy( &sNSQ_copy[1], &psEnc->sCmn.sNSQ, sizeof( silk_nsq_state ) );
+                    LastGainIndex_copy2 = psEnc->sShape.LastGainIndex;
+                }
+            } else {
+                /* Close enough */
+                break;
+            }
+
+            if ( !found_lower && nBits > maxBits ) {
+                int j;
+                for ( i = 0; i < psEnc->sCmn.nb_subfr; i++ ) {
+                    int sum=0;
+                    for ( j = i*psEnc->sCmn.subfr_length; j < (i+1)*psEnc->sCmn.subfr_length; j++ ) {
+                        sum += abs( psEnc->sCmn.pulses[j] );
+                    }
+                    if ( iter == 0 || (sum < best_sum[i] && !gain_lock[i]) ) {
+                        best_sum[i] = sum;
+                        best_gain_mult[i] = gainMult_Q8;
+                    } else {
+                        gain_lock[i] = 1;
+                    }
+                }
+            }
+            if( ( found_lower & found_upper ) == 0 ) {
+                /* Adjust gain according to high-rate rate/distortion curve */
+                if( nBits > maxBits ) {
+                    gainMult_Q8 = silk_min_32( 1024, gainMult_Q8*3/2 );
+                } else {
+                    gainMult_Q8 = silk_max_32( 64, gainMult_Q8*4/5 );
+                }
+            } else {
+                /* Adjust gain by interpolating */
+                gainMult_Q8 = gainMult_lower + silk_DIV32_16( silk_MUL( gainMult_upper - gainMult_lower, maxBits - nBits_lower ), nBits_upper - nBits_lower );
+                /* New gain multiplier must be between 25% and 75% of old range (note that gainMult_upper < gainMult_lower) */
+                if( gainMult_Q8 > silk_ADD_RSHIFT32( gainMult_lower, gainMult_upper - gainMult_lower, 2 ) ) {
+                    gainMult_Q8 = silk_ADD_RSHIFT32( gainMult_lower, gainMult_upper - gainMult_lower, 2 );
+                } else
+                if( gainMult_Q8 < silk_SUB_RSHIFT32( gainMult_upper, gainMult_upper - gainMult_lower, 2 ) ) {
+                    gainMult_Q8 = silk_SUB_RSHIFT32( gainMult_upper, gainMult_upper - gainMult_lower, 2 );
+                }
+            }
+
+            for( i = 0; i < psEnc->sCmn.nb_subfr; i++ ) {
+                opus_int16 tmp;
+                if ( gain_lock[i] ) {
+                    tmp = best_gain_mult[i];
+                } else {
+                    tmp = gainMult_Q8;
+                }
+                sEncCtrl.Gains_Q16[ i ] = silk_LSHIFT_SAT32( silk_SMULWB( sEncCtrl.GainsUnq_Q16[ i ], tmp ), 8 );
+            }
+
+            /* Quantize gains */
+            psEnc->sShape.LastGainIndex = sEncCtrl.lastGainIndexPrev;
+            silk_gains_quant( psEnc->sCmn.indices.GainsIndices, sEncCtrl.Gains_Q16,
+                  &psEnc->sShape.LastGainIndex, condCoding == CODE_CONDITIONALLY, psEnc->sCmn.nb_subfr );
+
+            /* Unique identifier of gains vector */
+            gainsID = silk_gains_ID( psEnc->sCmn.indices.GainsIndices, psEnc->sCmn.nb_subfr );
+        }
+    }
+
+    /* Update input buffer */
+    silk_memmove( psEnc->x_buf, &psEnc->x_buf[ psEnc->sCmn.frame_length ],
+        ( psEnc->sCmn.ltp_mem_length + LA_SHAPE_MS * psEnc->sCmn.fs_kHz ) * sizeof( opus_int16 ) );
+
+    /* Exit without entropy coding */
+    if( psEnc->sCmn.prefillFlag ) {
+        /* No payload */
+        *pnBytesOut = 0;
+        RESTORE_STACK;
+        return ret;
+    }
+
+    /* Parameters needed for next frame */
+    psEnc->sCmn.prevLag        = sEncCtrl.pitchL[ psEnc->sCmn.nb_subfr - 1 ];
+    psEnc->sCmn.prevSignalType = psEnc->sCmn.indices.signalType;
+
+    /****************************************/
+    /* Finalize payload                     */
+    /****************************************/
+    psEnc->sCmn.first_frame_after_reset = 0;
+    /* Payload size */
+    *pnBytesOut = silk_RSHIFT( ec_tell( psRangeEnc ) + 7, 3 );
+
+    RESTORE_STACK;
+    return ret;
+}
+
+/* Low-Bitrate Redundancy (LBRR) encoding. Reuse all parameters but encode excitation at lower bitrate  */
+static OPUS_INLINE void silk_LBRR_encode_FIX(
+    silk_encoder_state_FIX          *psEnc,                                 /* I/O  Pointer to Silk FIX encoder state                                           */
+    silk_encoder_control_FIX        *psEncCtrl,                             /* I/O  Pointer to Silk FIX encoder control struct                                  */
+    const opus_int16                x16[],                                  /* I    Input signal                                                                */
+    opus_int                        condCoding                              /* I    The type of conditional coding used so far for this frame                   */
+)
+{
+    opus_int32   TempGains_Q16[ MAX_NB_SUBFR ];
+    SideInfoIndices *psIndices_LBRR = &psEnc->sCmn.indices_LBRR[ psEnc->sCmn.nFramesEncoded ];
+    VARDECL(silk_nsq_state, sNSQ_LBRR);
+    SAVE_STACK;
+
+    /*******************************************/
+    /* Control use of inband LBRR              */
+    /*******************************************/
+    if( psEnc->sCmn.LBRR_enabled && psEnc->sCmn.speech_activity_Q8 > SILK_FIX_CONST( LBRR_SPEECH_ACTIVITY_THRES, 8 ) ) {
+        /* Using ALLOC() instead of a regular stack allocation to minimize real stack use when using the pseudostack.
+           This is useful on some embedded systems. */
+        ALLOC(sNSQ_LBRR, 1, silk_nsq_state);
+
+        psEnc->sCmn.LBRR_flags[ psEnc->sCmn.nFramesEncoded ] = 1;
+
+        /* Copy noise shaping quantizer state and quantization indices from regular encoding */
+        silk_memcpy( &sNSQ_LBRR[0], &psEnc->sCmn.sNSQ, sizeof( silk_nsq_state ) );
+        silk_memcpy( psIndices_LBRR, &psEnc->sCmn.indices, sizeof( SideInfoIndices ) );
+
+        /* Save original gains */
+        silk_memcpy( TempGains_Q16, psEncCtrl->Gains_Q16, psEnc->sCmn.nb_subfr * sizeof( opus_int32 ) );
+
+        if( psEnc->sCmn.nFramesEncoded == 0 || psEnc->sCmn.LBRR_flags[ psEnc->sCmn.nFramesEncoded - 1 ] == 0 ) {
+            /* First frame in packet or previous frame not LBRR coded */
+            psEnc->sCmn.LBRRprevLastGainIndex = psEnc->sShape.LastGainIndex;
+
+            /* Increase Gains to get target LBRR rate */
+            psIndices_LBRR->GainsIndices[ 0 ] = psIndices_LBRR->GainsIndices[ 0 ] + psEnc->sCmn.LBRR_GainIncreases;
+            psIndices_LBRR->GainsIndices[ 0 ] = silk_min_int( psIndices_LBRR->GainsIndices[ 0 ], N_LEVELS_QGAIN - 1 );
+        }
+
+        /* Decode to get gains in sync with decoder         */
+        /* Overwrite unquantized gains with quantized gains */
+        silk_gains_dequant( psEncCtrl->Gains_Q16, psIndices_LBRR->GainsIndices,
+            &psEnc->sCmn.LBRRprevLastGainIndex, condCoding == CODE_CONDITIONALLY, psEnc->sCmn.nb_subfr );
+
+        /*****************************************/
+        /* Noise shaping quantization            */
+        /*****************************************/
+        if( psEnc->sCmn.nStatesDelayedDecision > 1 || psEnc->sCmn.warping_Q16 > 0 ) {
+            silk_NSQ_del_dec( &psEnc->sCmn, &sNSQ_LBRR[0], psIndices_LBRR, x16,
+                psEnc->sCmn.pulses_LBRR[ psEnc->sCmn.nFramesEncoded ], psEncCtrl->PredCoef_Q12[ 0 ], psEncCtrl->LTPCoef_Q14,
+                psEncCtrl->AR_Q13, psEncCtrl->HarmShapeGain_Q14, psEncCtrl->Tilt_Q14, psEncCtrl->LF_shp_Q14,
+                psEncCtrl->Gains_Q16, psEncCtrl->pitchL, psEncCtrl->Lambda_Q10, psEncCtrl->LTP_scale_Q14, psEnc->sCmn.arch );
+        } else {
+            silk_NSQ( &psEnc->sCmn, &sNSQ_LBRR[0], psIndices_LBRR, x16,
+                psEnc->sCmn.pulses_LBRR[ psEnc->sCmn.nFramesEncoded ], psEncCtrl->PredCoef_Q12[ 0 ], psEncCtrl->LTPCoef_Q14,
+                psEncCtrl->AR_Q13, psEncCtrl->HarmShapeGain_Q14, psEncCtrl->Tilt_Q14, psEncCtrl->LF_shp_Q14,
+                psEncCtrl->Gains_Q16, psEncCtrl->pitchL, psEncCtrl->Lambda_Q10, psEncCtrl->LTP_scale_Q14, psEnc->sCmn.arch );
+        }
+
+        /* Restore original gains */
+        silk_memcpy( psEncCtrl->Gains_Q16, TempGains_Q16, psEnc->sCmn.nb_subfr * sizeof( opus_int32 ) );
+    }
+    RESTORE_STACK;
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/find_LPC_FIX.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/find_LPC_FIX.c
new file mode 100644 (file)
index 0000000..c762a0f
--- /dev/null
@@ -0,0 +1,151 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main_FIX.h"
+#include "stack_alloc.h"
+#include "tuning_parameters.h"
+
+/* Finds LPC vector from correlations, and converts to NLSF */
+void silk_find_LPC_FIX(
+    silk_encoder_state              *psEncC,                                /* I/O  Encoder state                                                               */
+    opus_int16                      NLSF_Q15[],                             /* O    NLSFs                                                                       */
+    const opus_int16                x[],                                    /* I    Input signal                                                                */
+    const opus_int32                minInvGain_Q30                          /* I    Inverse of max prediction gain                                              */
+)
+{
+    opus_int     k, subfr_length;
+    opus_int32   a_Q16[ MAX_LPC_ORDER ];
+    opus_int     isInterpLower, shift;
+    opus_int32   res_nrg0, res_nrg1;
+    opus_int     rshift0, rshift1;
+
+    /* Used only for LSF interpolation */
+    opus_int32   a_tmp_Q16[ MAX_LPC_ORDER ], res_nrg_interp, res_nrg, res_tmp_nrg;
+    opus_int     res_nrg_interp_Q, res_nrg_Q, res_tmp_nrg_Q;
+    opus_int16   a_tmp_Q12[ MAX_LPC_ORDER ];
+    opus_int16   NLSF0_Q15[ MAX_LPC_ORDER ];
+    SAVE_STACK;
+
+    subfr_length = psEncC->subfr_length + psEncC->predictLPCOrder;
+
+    /* Default: no interpolation */
+    psEncC->indices.NLSFInterpCoef_Q2 = 4;
+
+    /* Burg AR analysis for the full frame */
+    silk_burg_modified( &res_nrg, &res_nrg_Q, a_Q16, x, minInvGain_Q30, subfr_length, psEncC->nb_subfr, psEncC->predictLPCOrder, psEncC->arch );
+
+    if( psEncC->useInterpolatedNLSFs && !psEncC->first_frame_after_reset && psEncC->nb_subfr == MAX_NB_SUBFR ) {
+        VARDECL( opus_int16, LPC_res );
+
+        /* Optimal solution for last 10 ms */
+        silk_burg_modified( &res_tmp_nrg, &res_tmp_nrg_Q, a_tmp_Q16, x + 2 * subfr_length, minInvGain_Q30, subfr_length, 2, psEncC->predictLPCOrder, psEncC->arch );
+
+        /* subtract residual energy here, as that's easier than adding it to the    */
+        /* residual energy of the first 10 ms in each iteration of the search below */
+        shift = res_tmp_nrg_Q - res_nrg_Q;
+        if( shift >= 0 ) {
+            if( shift < 32 ) {
+                res_nrg = res_nrg - silk_RSHIFT( res_tmp_nrg, shift );
+            }
+        } else {
+            silk_assert( shift > -32 );
+            res_nrg   = silk_RSHIFT( res_nrg, -shift ) - res_tmp_nrg;
+            res_nrg_Q = res_tmp_nrg_Q;
+        }
+
+        /* Convert to NLSFs */
+        silk_A2NLSF( NLSF_Q15, a_tmp_Q16, psEncC->predictLPCOrder );
+
+        ALLOC( LPC_res, 2 * subfr_length, opus_int16 );
+
+        /* Search over interpolation indices to find the one with lowest residual energy */
+        for( k = 3; k >= 0; k-- ) {
+            /* Interpolate NLSFs for first half */
+            silk_interpolate( NLSF0_Q15, psEncC->prev_NLSFq_Q15, NLSF_Q15, k, psEncC->predictLPCOrder );
+
+            /* Convert to LPC for residual energy evaluation */
+            silk_NLSF2A( a_tmp_Q12, NLSF0_Q15, psEncC->predictLPCOrder, psEncC->arch );
+
+            /* Calculate residual energy with NLSF interpolation */
+            silk_LPC_analysis_filter( LPC_res, x, a_tmp_Q12, 2 * subfr_length, psEncC->predictLPCOrder, psEncC->arch );
+
+            silk_sum_sqr_shift( &res_nrg0, &rshift0, LPC_res + psEncC->predictLPCOrder,                subfr_length - psEncC->predictLPCOrder );
+            silk_sum_sqr_shift( &res_nrg1, &rshift1, LPC_res + psEncC->predictLPCOrder + subfr_length, subfr_length - psEncC->predictLPCOrder );
+
+            /* Add subframe energies from first half frame */
+            shift = rshift0 - rshift1;
+            if( shift >= 0 ) {
+                res_nrg1         = silk_RSHIFT( res_nrg1, shift );
+                res_nrg_interp_Q = -rshift0;
+            } else {
+                res_nrg0         = silk_RSHIFT( res_nrg0, -shift );
+                res_nrg_interp_Q = -rshift1;
+            }
+            res_nrg_interp = silk_ADD32( res_nrg0, res_nrg1 );
+
+            /* Compare with first half energy without NLSF interpolation, or best interpolated value so far */
+            shift = res_nrg_interp_Q - res_nrg_Q;
+            if( shift >= 0 ) {
+                if( silk_RSHIFT( res_nrg_interp, shift ) < res_nrg ) {
+                    isInterpLower = silk_TRUE;
+                } else {
+                    isInterpLower = silk_FALSE;
+                }
+            } else {
+                if( -shift < 32 ) {
+                    if( res_nrg_interp < silk_RSHIFT( res_nrg, -shift ) ) {
+                        isInterpLower = silk_TRUE;
+                    } else {
+                        isInterpLower = silk_FALSE;
+                    }
+                } else {
+                    isInterpLower = silk_FALSE;
+                }
+            }
+
+            /* Determine whether current interpolated NLSFs are best so far */
+            if( isInterpLower == silk_TRUE ) {
+                /* Interpolation has lower residual energy */
+                res_nrg   = res_nrg_interp;
+                res_nrg_Q = res_nrg_interp_Q;
+                psEncC->indices.NLSFInterpCoef_Q2 = (opus_int8)k;
+            }
+        }
+    }
+
+    if( psEncC->indices.NLSFInterpCoef_Q2 == 4 ) {
+        /* NLSF interpolation is currently inactive, calculate NLSFs from full frame AR coefficients */
+        silk_A2NLSF( NLSF_Q15, a_Q16, psEncC->predictLPCOrder );
+    }
+
+    celt_assert( psEncC->indices.NLSFInterpCoef_Q2 == 4 || ( psEncC->useInterpolatedNLSFs && !psEncC->first_frame_after_reset && psEncC->nb_subfr == MAX_NB_SUBFR ) );
+    RESTORE_STACK;
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/find_LTP_FIX.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/find_LTP_FIX.c
new file mode 100644 (file)
index 0000000..62d4afb
--- /dev/null
@@ -0,0 +1,99 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main_FIX.h"
+#include "tuning_parameters.h"
+
+void silk_find_LTP_FIX(
+    opus_int32                      XXLTP_Q17[ MAX_NB_SUBFR * LTP_ORDER * LTP_ORDER ], /* O    Correlation matrix                                               */
+    opus_int32                      xXLTP_Q17[ MAX_NB_SUBFR * LTP_ORDER ],  /* O    Correlation vector                                                          */
+    const opus_int16                r_ptr[],                                /* I    Residual signal after LPC                                                   */
+    const opus_int                  lag[ MAX_NB_SUBFR ],                    /* I    LTP lags                                                                    */
+    const opus_int                  subfr_length,                           /* I    Subframe length                                                             */
+    const opus_int                  nb_subfr,                               /* I    Number of subframes                                                         */
+    int                             arch                                    /* I    Run-time architecture                                                       */
+)
+{
+    opus_int   i, k, extra_shifts;
+    opus_int   xx_shifts, xX_shifts, XX_shifts;
+    const opus_int16 *lag_ptr;
+    opus_int32 *XXLTP_Q17_ptr, *xXLTP_Q17_ptr;
+    opus_int32 xx, nrg, temp;
+
+    xXLTP_Q17_ptr = xXLTP_Q17;
+    XXLTP_Q17_ptr = XXLTP_Q17;
+    for( k = 0; k < nb_subfr; k++ ) {
+        lag_ptr = r_ptr - ( lag[ k ] + LTP_ORDER / 2 );
+
+        silk_sum_sqr_shift( &xx, &xx_shifts, r_ptr, subfr_length + LTP_ORDER );                            /* xx in Q( -xx_shifts ) */
+        silk_corrMatrix_FIX( lag_ptr, subfr_length, LTP_ORDER, XXLTP_Q17_ptr, &nrg, &XX_shifts, arch );    /* XXLTP_Q17_ptr and nrg in Q( -XX_shifts ) */
+        extra_shifts = xx_shifts - XX_shifts;
+        if( extra_shifts > 0 ) {
+            /* Shift XX */
+            xX_shifts = xx_shifts;
+            for( i = 0; i < LTP_ORDER * LTP_ORDER; i++ ) {
+                XXLTP_Q17_ptr[ i ] = silk_RSHIFT32( XXLTP_Q17_ptr[ i ], extra_shifts );              /* Q( -xX_shifts ) */
+            }
+            nrg = silk_RSHIFT32( nrg, extra_shifts );                                                /* Q( -xX_shifts ) */
+        } else if( extra_shifts < 0 ) {
+            /* Shift xx */
+            xX_shifts = XX_shifts;
+            xx = silk_RSHIFT32( xx, -extra_shifts );                                                 /* Q( -xX_shifts ) */
+        } else {
+            xX_shifts = xx_shifts;
+        }
+        silk_corrVector_FIX( lag_ptr, r_ptr, subfr_length, LTP_ORDER, xXLTP_Q17_ptr, xX_shifts, arch );    /* xXLTP_Q17_ptr in Q( -xX_shifts ) */
+
+        /* At this point all correlations are in Q(-xX_shifts) */
+        temp = silk_SMLAWB( 1, nrg, SILK_FIX_CONST( LTP_CORR_INV_MAX, 16 ) );
+        temp = silk_max( temp, xx );
+TIC(div)
+#if 0
+        for( i = 0; i < LTP_ORDER * LTP_ORDER; i++ ) {
+            XXLTP_Q17_ptr[ i ] = silk_DIV32_varQ( XXLTP_Q17_ptr[ i ], temp, 17 );
+        }
+        for( i = 0; i < LTP_ORDER; i++ ) {
+            xXLTP_Q17_ptr[ i ] = silk_DIV32_varQ( xXLTP_Q17_ptr[ i ], temp, 17 );
+        }
+#else
+        for( i = 0; i < LTP_ORDER * LTP_ORDER; i++ ) {
+            XXLTP_Q17_ptr[ i ] = (opus_int32)( silk_LSHIFT64( (opus_int64)XXLTP_Q17_ptr[ i ], 17 ) / temp );
+        }
+        for( i = 0; i < LTP_ORDER; i++ ) {
+            xXLTP_Q17_ptr[ i ] = (opus_int32)( silk_LSHIFT64( (opus_int64)xXLTP_Q17_ptr[ i ], 17 ) / temp );
+        }
+#endif
+TOC(div)
+        r_ptr         += subfr_length;
+        XXLTP_Q17_ptr += LTP_ORDER * LTP_ORDER;
+        xXLTP_Q17_ptr += LTP_ORDER;
+    }
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/find_pitch_lags_FIX.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/find_pitch_lags_FIX.c
new file mode 100644 (file)
index 0000000..6c3379f
--- /dev/null
@@ -0,0 +1,143 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main_FIX.h"
+#include "stack_alloc.h"
+#include "tuning_parameters.h"
+
+/* Find pitch lags */
+void silk_find_pitch_lags_FIX(
+    silk_encoder_state_FIX          *psEnc,                                 /* I/O  encoder state                                                               */
+    silk_encoder_control_FIX        *psEncCtrl,                             /* I/O  encoder control                                                             */
+    opus_int16                      res[],                                  /* O    residual                                                                    */
+    const opus_int16                x[],                                    /* I    Speech signal                                                               */
+    int                             arch                                    /* I    Run-time architecture                                                       */
+)
+{
+    opus_int   buf_len, i, scale;
+    opus_int32 thrhld_Q13, res_nrg;
+    const opus_int16 *x_ptr;
+    VARDECL( opus_int16, Wsig );
+    opus_int16 *Wsig_ptr;
+    opus_int32 auto_corr[ MAX_FIND_PITCH_LPC_ORDER + 1 ];
+    opus_int16 rc_Q15[    MAX_FIND_PITCH_LPC_ORDER ];
+    opus_int32 A_Q24[     MAX_FIND_PITCH_LPC_ORDER ];
+    opus_int16 A_Q12[     MAX_FIND_PITCH_LPC_ORDER ];
+    SAVE_STACK;
+
+    /******************************************/
+    /* Set up buffer lengths etc based on Fs  */
+    /******************************************/
+    buf_len = psEnc->sCmn.la_pitch + psEnc->sCmn.frame_length + psEnc->sCmn.ltp_mem_length;
+
+    /* Safety check */
+    celt_assert( buf_len >= psEnc->sCmn.pitch_LPC_win_length );
+
+    /*************************************/
+    /* Estimate LPC AR coefficients      */
+    /*************************************/
+
+    /* Calculate windowed signal */
+
+    ALLOC( Wsig, psEnc->sCmn.pitch_LPC_win_length, opus_int16 );
+
+    /* First LA_LTP samples */
+    x_ptr = x + buf_len - psEnc->sCmn.pitch_LPC_win_length;
+    Wsig_ptr  = Wsig;
+    silk_apply_sine_window( Wsig_ptr, x_ptr, 1, psEnc->sCmn.la_pitch );
+
+    /* Middle un - windowed samples */
+    Wsig_ptr  += psEnc->sCmn.la_pitch;
+    x_ptr += psEnc->sCmn.la_pitch;
+    silk_memcpy( Wsig_ptr, x_ptr, ( psEnc->sCmn.pitch_LPC_win_length - silk_LSHIFT( psEnc->sCmn.la_pitch, 1 ) ) * sizeof( opus_int16 ) );
+
+    /* Last LA_LTP samples */
+    Wsig_ptr  += psEnc->sCmn.pitch_LPC_win_length - silk_LSHIFT( psEnc->sCmn.la_pitch, 1 );
+    x_ptr += psEnc->sCmn.pitch_LPC_win_length - silk_LSHIFT( psEnc->sCmn.la_pitch, 1 );
+    silk_apply_sine_window( Wsig_ptr, x_ptr, 2, psEnc->sCmn.la_pitch );
+
+    /* Calculate autocorrelation sequence */
+    silk_autocorr( auto_corr, &scale, Wsig, psEnc->sCmn.pitch_LPC_win_length, psEnc->sCmn.pitchEstimationLPCOrder + 1, arch );
+
+    /* Add white noise, as fraction of energy */
+    auto_corr[ 0 ] = silk_SMLAWB( auto_corr[ 0 ], auto_corr[ 0 ], SILK_FIX_CONST( FIND_PITCH_WHITE_NOISE_FRACTION, 16 ) ) + 1;
+
+    /* Calculate the reflection coefficients using schur */
+    res_nrg = silk_schur( rc_Q15, auto_corr, psEnc->sCmn.pitchEstimationLPCOrder );
+
+    /* Prediction gain */
+    psEncCtrl->predGain_Q16 = silk_DIV32_varQ( auto_corr[ 0 ], silk_max_int( res_nrg, 1 ), 16 );
+
+    /* Convert reflection coefficients to prediction coefficients */
+    silk_k2a( A_Q24, rc_Q15, psEnc->sCmn.pitchEstimationLPCOrder );
+
+    /* Convert From 32 bit Q24 to 16 bit Q12 coefs */
+    for( i = 0; i < psEnc->sCmn.pitchEstimationLPCOrder; i++ ) {
+        A_Q12[ i ] = (opus_int16)silk_SAT16( silk_RSHIFT( A_Q24[ i ], 12 ) );
+    }
+
+    /* Do BWE */
+    silk_bwexpander( A_Q12, psEnc->sCmn.pitchEstimationLPCOrder, SILK_FIX_CONST( FIND_PITCH_BANDWIDTH_EXPANSION, 16 ) );
+
+    /*****************************************/
+    /* LPC analysis filtering                */
+    /*****************************************/
+    silk_LPC_analysis_filter( res, x, A_Q12, buf_len, psEnc->sCmn.pitchEstimationLPCOrder, psEnc->sCmn.arch );
+
+    if( psEnc->sCmn.indices.signalType != TYPE_NO_VOICE_ACTIVITY && psEnc->sCmn.first_frame_after_reset == 0 ) {
+        /* Threshold for pitch estimator */
+        thrhld_Q13 = SILK_FIX_CONST( 0.6, 13 );
+        thrhld_Q13 = silk_SMLABB( thrhld_Q13, SILK_FIX_CONST( -0.004, 13 ), psEnc->sCmn.pitchEstimationLPCOrder );
+        thrhld_Q13 = silk_SMLAWB( thrhld_Q13, SILK_FIX_CONST( -0.1,   21  ), psEnc->sCmn.speech_activity_Q8 );
+        thrhld_Q13 = silk_SMLABB( thrhld_Q13, SILK_FIX_CONST( -0.15,  13 ), silk_RSHIFT( psEnc->sCmn.prevSignalType, 1 ) );
+        thrhld_Q13 = silk_SMLAWB( thrhld_Q13, SILK_FIX_CONST( -0.1,   14 ), psEnc->sCmn.input_tilt_Q15 );
+        thrhld_Q13 = silk_SAT16(  thrhld_Q13 );
+
+        /*****************************************/
+        /* Call pitch estimator                  */
+        /*****************************************/
+        if( silk_pitch_analysis_core( res, psEncCtrl->pitchL, &psEnc->sCmn.indices.lagIndex, &psEnc->sCmn.indices.contourIndex,
+                &psEnc->LTPCorr_Q15, psEnc->sCmn.prevLag, psEnc->sCmn.pitchEstimationThreshold_Q16,
+                (opus_int)thrhld_Q13, psEnc->sCmn.fs_kHz, psEnc->sCmn.pitchEstimationComplexity, psEnc->sCmn.nb_subfr,
+                psEnc->sCmn.arch) == 0 )
+        {
+            psEnc->sCmn.indices.signalType = TYPE_VOICED;
+        } else {
+            psEnc->sCmn.indices.signalType = TYPE_UNVOICED;
+        }
+    } else {
+        silk_memset( psEncCtrl->pitchL, 0, sizeof( psEncCtrl->pitchL ) );
+        psEnc->sCmn.indices.lagIndex = 0;
+        psEnc->sCmn.indices.contourIndex = 0;
+        psEnc->LTPCorr_Q15 = 0;
+    }
+    RESTORE_STACK;
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/find_pred_coefs_FIX.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/find_pred_coefs_FIX.c
new file mode 100644 (file)
index 0000000..c49e44f
--- /dev/null
@@ -0,0 +1,147 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main_FIX.h"
+#include "stack_alloc.h"
+
+void silk_find_pred_coefs_FIX(
+    silk_encoder_state_FIX          *psEnc,                                 /* I/O  encoder state                                                               */
+    silk_encoder_control_FIX        *psEncCtrl,                             /* I/O  encoder control                                                             */
+    const opus_int16                res_pitch[],                            /* I    Residual from pitch analysis                                                */
+    const opus_int16                x[],                                    /* I    Speech signal                                                               */
+    opus_int                        condCoding                              /* I    The type of conditional coding to use                                       */
+)
+{
+    opus_int         i;
+    opus_int32       invGains_Q16[ MAX_NB_SUBFR ], local_gains[ MAX_NB_SUBFR ];
+    /* Set to NLSF_Q15 to zero so we don't copy junk to the state. */
+    opus_int16       NLSF_Q15[ MAX_LPC_ORDER ]={0};
+    const opus_int16 *x_ptr;
+    opus_int16       *x_pre_ptr;
+    VARDECL( opus_int16, LPC_in_pre );
+    opus_int32       min_gain_Q16, minInvGain_Q30;
+    SAVE_STACK;
+
+    /* weighting for weighted least squares */
+    min_gain_Q16 = silk_int32_MAX >> 6;
+    for( i = 0; i < psEnc->sCmn.nb_subfr; i++ ) {
+        min_gain_Q16 = silk_min( min_gain_Q16, psEncCtrl->Gains_Q16[ i ] );
+    }
+    for( i = 0; i < psEnc->sCmn.nb_subfr; i++ ) {
+        /* Divide to Q16 */
+        silk_assert( psEncCtrl->Gains_Q16[ i ] > 0 );
+        /* Invert and normalize gains, and ensure that maximum invGains_Q16 is within range of a 16 bit int */
+        invGains_Q16[ i ] = silk_DIV32_varQ( min_gain_Q16, psEncCtrl->Gains_Q16[ i ], 16 - 2 );
+
+        /* Limit inverse */
+        invGains_Q16[ i ] = silk_max( invGains_Q16[ i ], 100 );
+
+        /* Square the inverted gains */
+        silk_assert( invGains_Q16[ i ] == silk_SAT16( invGains_Q16[ i ] ) );
+
+        /* Invert the inverted and normalized gains */
+        local_gains[ i ] = silk_DIV32( ( (opus_int32)1 << 16 ), invGains_Q16[ i ] );
+    }
+
+    ALLOC( LPC_in_pre,
+           psEnc->sCmn.nb_subfr * psEnc->sCmn.predictLPCOrder
+               + psEnc->sCmn.frame_length, opus_int16 );
+    if( psEnc->sCmn.indices.signalType == TYPE_VOICED ) {
+        VARDECL( opus_int32, xXLTP_Q17 );
+        VARDECL( opus_int32, XXLTP_Q17 );
+
+        /**********/
+        /* VOICED */
+        /**********/
+        celt_assert( psEnc->sCmn.ltp_mem_length - psEnc->sCmn.predictLPCOrder >= psEncCtrl->pitchL[ 0 ] + LTP_ORDER / 2 );
+
+        ALLOC( xXLTP_Q17, psEnc->sCmn.nb_subfr * LTP_ORDER, opus_int32 );
+        ALLOC( XXLTP_Q17, psEnc->sCmn.nb_subfr * LTP_ORDER * LTP_ORDER, opus_int32 );
+
+        /* LTP analysis */
+        silk_find_LTP_FIX( XXLTP_Q17, xXLTP_Q17, res_pitch,
+            psEncCtrl->pitchL, psEnc->sCmn.subfr_length, psEnc->sCmn.nb_subfr, psEnc->sCmn.arch );
+
+        /* Quantize LTP gain parameters */
+        silk_quant_LTP_gains( psEncCtrl->LTPCoef_Q14, psEnc->sCmn.indices.LTPIndex, &psEnc->sCmn.indices.PERIndex,
+            &psEnc->sCmn.sum_log_gain_Q7, &psEncCtrl->LTPredCodGain_Q7, XXLTP_Q17, xXLTP_Q17, psEnc->sCmn.subfr_length, psEnc->sCmn.nb_subfr, psEnc->sCmn.arch );
+
+        /* Control LTP scaling */
+        silk_LTP_scale_ctrl_FIX( psEnc, psEncCtrl, condCoding );
+
+        /* Create LTP residual */
+        silk_LTP_analysis_filter_FIX( LPC_in_pre, x - psEnc->sCmn.predictLPCOrder, psEncCtrl->LTPCoef_Q14,
+            psEncCtrl->pitchL, invGains_Q16, psEnc->sCmn.subfr_length, psEnc->sCmn.nb_subfr, psEnc->sCmn.predictLPCOrder );
+
+    } else {
+        /************/
+        /* UNVOICED */
+        /************/
+        /* Create signal with prepended subframes, scaled by inverse gains */
+        x_ptr     = x - psEnc->sCmn.predictLPCOrder;
+        x_pre_ptr = LPC_in_pre;
+        for( i = 0; i < psEnc->sCmn.nb_subfr; i++ ) {
+            silk_scale_copy_vector16( x_pre_ptr, x_ptr, invGains_Q16[ i ],
+                psEnc->sCmn.subfr_length + psEnc->sCmn.predictLPCOrder );
+            x_pre_ptr += psEnc->sCmn.subfr_length + psEnc->sCmn.predictLPCOrder;
+            x_ptr     += psEnc->sCmn.subfr_length;
+        }
+
+        silk_memset( psEncCtrl->LTPCoef_Q14, 0, psEnc->sCmn.nb_subfr * LTP_ORDER * sizeof( opus_int16 ) );
+        psEncCtrl->LTPredCodGain_Q7 = 0;
+        psEnc->sCmn.sum_log_gain_Q7 = 0;
+        psEncCtrl->LTP_scale_Q14 = 0;
+    }
+
+    /* Limit on total predictive coding gain */
+    if( psEnc->sCmn.first_frame_after_reset ) {
+        minInvGain_Q30 = SILK_FIX_CONST( 1.0f / MAX_PREDICTION_POWER_GAIN_AFTER_RESET, 30 );
+    } else {
+        minInvGain_Q30 = silk_log2lin( silk_SMLAWB( 16 << 7, (opus_int32)psEncCtrl->LTPredCodGain_Q7, SILK_FIX_CONST( 1.0 / 3, 16 ) ) );      /* Q16 */
+        minInvGain_Q30 = silk_DIV32_varQ( minInvGain_Q30,
+            silk_SMULWW( SILK_FIX_CONST( MAX_PREDICTION_POWER_GAIN, 0 ),
+                silk_SMLAWB( SILK_FIX_CONST( 0.25, 18 ), SILK_FIX_CONST( 0.75, 18 ), psEncCtrl->coding_quality_Q14 ) ), 14 );
+    }
+
+    /* LPC_in_pre contains the LTP-filtered input for voiced, and the unfiltered input for unvoiced */
+    silk_find_LPC_FIX( &psEnc->sCmn, NLSF_Q15, LPC_in_pre, minInvGain_Q30 );
+
+    /* Quantize LSFs */
+    silk_process_NLSFs( &psEnc->sCmn, psEncCtrl->PredCoef_Q12, NLSF_Q15, psEnc->sCmn.prev_NLSFq_Q15 );
+
+    /* Calculate residual energy using quantized LPC coefficients */
+    silk_residual_energy_FIX( psEncCtrl->ResNrg, psEncCtrl->ResNrgQ, LPC_in_pre, psEncCtrl->PredCoef_Q12, local_gains,
+        psEnc->sCmn.subfr_length, psEnc->sCmn.nb_subfr, psEnc->sCmn.predictLPCOrder, psEnc->sCmn.arch );
+
+    /* Copy to prediction struct for use in next frame for interpolation */
+    silk_memcpy( psEnc->sCmn.prev_NLSFq_Q15, NLSF_Q15, sizeof( psEnc->sCmn.prev_NLSFq_Q15 ) );
+    RESTORE_STACK;
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/k2a_FIX.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/k2a_FIX.c
new file mode 100644 (file)
index 0000000..549f6ea
--- /dev/null
@@ -0,0 +1,54 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "SigProc_FIX.h"
+
+/* Step up function, converts reflection coefficients to prediction coefficients */
+void silk_k2a(
+    opus_int32                  *A_Q24,             /* O    Prediction coefficients [order] Q24                         */
+    const opus_int16            *rc_Q15,            /* I    Reflection coefficients [order] Q15                         */
+    const opus_int32            order               /* I    Prediction order                                            */
+)
+{
+    opus_int   k, n;
+    opus_int32 rc, tmp1, tmp2;
+
+    for( k = 0; k < order; k++ ) {
+        rc = rc_Q15[ k ];
+        for( n = 0; n < (k + 1) >> 1; n++ ) {
+            tmp1 = A_Q24[ n ];
+            tmp2 = A_Q24[ k - n - 1 ];
+            A_Q24[ n ]         = silk_SMLAWB( tmp1, silk_LSHIFT( tmp2, 1 ), rc );
+            A_Q24[ k - n - 1 ] = silk_SMLAWB( tmp2, silk_LSHIFT( tmp1, 1 ), rc );
+        }
+        A_Q24[ k ] = -silk_LSHIFT( (opus_int32)rc_Q15[ k ], 9 );
+    }
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/k2a_Q16_FIX.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/k2a_Q16_FIX.c
new file mode 100644 (file)
index 0000000..1595aa6
--- /dev/null
@@ -0,0 +1,54 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "SigProc_FIX.h"
+
+/* Step up function, converts reflection coefficients to prediction coefficients */
+void silk_k2a_Q16(
+    opus_int32                  *A_Q24,             /* O    Prediction coefficients [order] Q24                         */
+    const opus_int32            *rc_Q16,            /* I    Reflection coefficients [order] Q16                         */
+    const opus_int32            order               /* I    Prediction order                                            */
+)
+{
+    opus_int   k, n;
+    opus_int32 rc, tmp1, tmp2;
+
+    for( k = 0; k < order; k++ ) {
+        rc = rc_Q16[ k ];
+        for( n = 0; n < (k + 1) >> 1; n++ ) {
+            tmp1 = A_Q24[ n ];
+            tmp2 = A_Q24[ k - n - 1 ];
+            A_Q24[ n ]         = silk_SMLAWW( tmp1, tmp2, rc );
+            A_Q24[ k - n - 1 ] = silk_SMLAWW( tmp2, tmp1, rc );
+        }
+        A_Q24[ k ] = -silk_LSHIFT( rc, 8 );
+    }
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/main_FIX.h b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/main_FIX.h
new file mode 100644 (file)
index 0000000..53a97c5
--- /dev/null
@@ -0,0 +1,233 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifndef SILK_MAIN_FIX_H
+#define SILK_MAIN_FIX_H
+
+#include "SigProc_FIX.h"
+#include "structs_FIX.h"
+#include "control.h"
+#include "main.h"
+#include "PLC.h"
+#include "debug.h"
+#include "entenc.h"
+
+#if ((defined(OPUS_ARM_ASM) && defined(FIXED_POINT)) \
+   || defined(OPUS_ARM_MAY_HAVE_NEON_INTR))
+#include "fixed/arm/warped_autocorrelation_FIX_arm.h"
+#endif
+
+
+#define silk_encoder_state_Fxx      silk_encoder_state_FIX
+#define silk_encode_do_VAD_Fxx      silk_encode_do_VAD_FIX
+#define silk_encode_frame_Fxx       silk_encode_frame_FIX
+
+#define QC  10
+#define QS  13
+
+/*********************/
+/* Encoder Functions */
+/*********************/
+
+/* High-pass filter with cutoff frequency adaptation based on pitch lag statistics */
+void silk_HP_variable_cutoff(
+    silk_encoder_state_Fxx          state_Fxx[]                             /* I/O  Encoder states                                                              */
+);
+
+/* Encoder main function */
+void silk_encode_do_VAD_FIX(
+    silk_encoder_state_FIX          *psEnc,                                 /* I/O  Pointer to Silk FIX encoder state                                           */
+    opus_int                        activity                                /* I    Decision of Opus voice activity detector                                    */
+);
+
+/* Encoder main function */
+opus_int silk_encode_frame_FIX(
+    silk_encoder_state_FIX          *psEnc,                                 /* I/O  Pointer to Silk FIX encoder state                                           */
+    opus_int32                      *pnBytesOut,                            /* O    Pointer to number of payload bytes;                                         */
+    ec_enc                          *psRangeEnc,                            /* I/O  compressor data structure                                                   */
+    opus_int                        condCoding,                             /* I    The type of conditional coding to use                                       */
+    opus_int                        maxBits,                                /* I    If > 0: maximum number of output bits                                       */
+    opus_int                        useCBR                                  /* I    Flag to force constant-bitrate operation                                    */
+);
+
+/* Initializes the Silk encoder state */
+opus_int silk_init_encoder(
+    silk_encoder_state_Fxx          *psEnc,                                 /* I/O  Pointer to Silk FIX encoder state                                           */
+    int                              arch                                   /* I    Run-time architecture                                                       */
+);
+
+/* Control the Silk encoder */
+opus_int silk_control_encoder(
+    silk_encoder_state_Fxx          *psEnc,                                 /* I/O  Pointer to Silk encoder state                                               */
+    silk_EncControlStruct           *encControl,                            /* I    Control structure                                                           */
+    const opus_int                  allow_bw_switch,                        /* I    Flag to allow switching audio bandwidth                                     */
+    const opus_int                  channelNb,                              /* I    Channel number                                                              */
+    const opus_int                  force_fs_kHz
+);
+
+/**************************/
+/* Noise shaping analysis */
+/**************************/
+/* Compute noise shaping coefficients and initial gain values */
+void silk_noise_shape_analysis_FIX(
+    silk_encoder_state_FIX          *psEnc,                                 /* I/O  Encoder state FIX                                                           */
+    silk_encoder_control_FIX        *psEncCtrl,                             /* I/O  Encoder control FIX                                                         */
+    const opus_int16                *pitch_res,                             /* I    LPC residual from pitch analysis                                            */
+    const opus_int16                *x,                                     /* I    Input signal [ frame_length + la_shape ]                                    */
+    int                              arch                                   /* I    Run-time architecture                                                       */
+);
+
+/* Autocorrelations for a warped frequency axis */
+void silk_warped_autocorrelation_FIX_c(
+          opus_int32                *corr,                                  /* O    Result [order + 1]                                                          */
+          opus_int                  *scale,                                 /* O    Scaling of the correlation vector                                           */
+    const opus_int16                *input,                                 /* I    Input data to correlate                                                     */
+    const opus_int                  warping_Q16,                            /* I    Warping coefficient                                                         */
+    const opus_int                  length,                                 /* I    Length of input                                                             */
+    const opus_int                  order                                   /* I    Correlation order (even)                                                    */
+);
+
+#if !defined(OVERRIDE_silk_warped_autocorrelation_FIX)
+#define silk_warped_autocorrelation_FIX(corr, scale, input, warping_Q16, length, order, arch) \
+        ((void)(arch), silk_warped_autocorrelation_FIX_c(corr, scale, input, warping_Q16, length, order))
+#endif
+
+/* Calculation of LTP state scaling */
+void silk_LTP_scale_ctrl_FIX(
+    silk_encoder_state_FIX          *psEnc,                                 /* I/O  encoder state                                                               */
+    silk_encoder_control_FIX        *psEncCtrl,                             /* I/O  encoder control                                                             */
+    opus_int                        condCoding                              /* I    The type of conditional coding to use                                       */
+);
+
+/**********************************************/
+/* Prediction Analysis                        */
+/**********************************************/
+/* Find pitch lags */
+void silk_find_pitch_lags_FIX(
+    silk_encoder_state_FIX          *psEnc,                                 /* I/O  encoder state                                                               */
+    silk_encoder_control_FIX        *psEncCtrl,                             /* I/O  encoder control                                                             */
+    opus_int16                      res[],                                  /* O    residual                                                                    */
+    const opus_int16                x[],                                    /* I    Speech signal                                                               */
+    int                             arch                                    /* I    Run-time architecture                                                       */
+);
+
+/* Find LPC and LTP coefficients */
+void silk_find_pred_coefs_FIX(
+    silk_encoder_state_FIX          *psEnc,                                 /* I/O  encoder state                                                               */
+    silk_encoder_control_FIX        *psEncCtrl,                             /* I/O  encoder control                                                             */
+    const opus_int16                res_pitch[],                            /* I    Residual from pitch analysis                                                */
+    const opus_int16                x[],                                    /* I    Speech signal                                                               */
+    opus_int                        condCoding                              /* I    The type of conditional coding to use                                       */
+);
+
+/* LPC analysis */
+void silk_find_LPC_FIX(
+    silk_encoder_state              *psEncC,                                /* I/O  Encoder state                                                               */
+    opus_int16                      NLSF_Q15[],                             /* O    NLSFs                                                                       */
+    const opus_int16                x[],                                    /* I    Input signal                                                                */
+    const opus_int32                minInvGain_Q30                          /* I    Inverse of max prediction gain                                              */
+);
+
+/* LTP analysis */
+void silk_find_LTP_FIX(
+    opus_int32                      XXLTP_Q17[ MAX_NB_SUBFR * LTP_ORDER * LTP_ORDER ], /* O    Correlation matrix                                               */
+    opus_int32                      xXLTP_Q17[ MAX_NB_SUBFR * LTP_ORDER ],  /* O    Correlation vector                                                          */
+    const opus_int16                r_lpc[],                                /* I    Residual signal after LPC                                                   */
+    const opus_int                  lag[ MAX_NB_SUBFR ],                    /* I    LTP lags                                                                    */
+    const opus_int                  subfr_length,                           /* I    Subframe length                                                             */
+    const opus_int                  nb_subfr,                               /* I    Number of subframes                                                         */
+    int                             arch                                    /* I    Run-time architecture                                                       */
+);
+
+void silk_LTP_analysis_filter_FIX(
+    opus_int16                      *LTP_res,                               /* O    LTP residual signal of length MAX_NB_SUBFR * ( pre_length + subfr_length )  */
+    const opus_int16                *x,                                     /* I    Pointer to input signal with at least max( pitchL ) preceding samples       */
+    const opus_int16                LTPCoef_Q14[ LTP_ORDER * MAX_NB_SUBFR ],/* I    LTP_ORDER LTP coefficients for each MAX_NB_SUBFR subframe                   */
+    const opus_int                  pitchL[ MAX_NB_SUBFR ],                 /* I    Pitch lag, one for each subframe                                            */
+    const opus_int32                invGains_Q16[ MAX_NB_SUBFR ],           /* I    Inverse quantization gains, one for each subframe                           */
+    const opus_int                  subfr_length,                           /* I    Length of each subframe                                                     */
+    const opus_int                  nb_subfr,                               /* I    Number of subframes                                                         */
+    const opus_int                  pre_length                              /* I    Length of the preceding samples starting at &x[0] for each subframe         */
+);
+
+/* Calculates residual energies of input subframes where all subframes have LPC_order   */
+/* of preceding samples                                                                 */
+void silk_residual_energy_FIX(
+          opus_int32                nrgs[ MAX_NB_SUBFR ],                   /* O    Residual energy per subframe                                                */
+          opus_int                  nrgsQ[ MAX_NB_SUBFR ],                  /* O    Q value per subframe                                                        */
+    const opus_int16                x[],                                    /* I    Input signal                                                                */
+          opus_int16                a_Q12[ 2 ][ MAX_LPC_ORDER ],            /* I    AR coefs for each frame half                                                */
+    const opus_int32                gains[ MAX_NB_SUBFR ],                  /* I    Quantization gains                                                          */
+    const opus_int                  subfr_length,                           /* I    Subframe length                                                             */
+    const opus_int                  nb_subfr,                               /* I    Number of subframes                                                         */
+    const opus_int                  LPC_order,                              /* I    LPC order                                                                   */
+    int                             arch                                    /* I    Run-time architecture                                                       */
+);
+
+/* Residual energy: nrg = wxx - 2 * wXx * c + c' * wXX * c */
+opus_int32 silk_residual_energy16_covar_FIX(
+    const opus_int16                *c,                                     /* I    Prediction vector                                                           */
+    const opus_int32                *wXX,                                   /* I    Correlation matrix                                                          */
+    const opus_int32                *wXx,                                   /* I    Correlation vector                                                          */
+    opus_int32                      wxx,                                    /* I    Signal energy                                                               */
+    opus_int                        D,                                      /* I    Dimension                                                                   */
+    opus_int                        cQ                                      /* I    Q value for c vector 0 - 15                                                 */
+);
+
+/* Processing of gains */
+void silk_process_gains_FIX(
+    silk_encoder_state_FIX          *psEnc,                                 /* I/O  Encoder state                                                               */
+    silk_encoder_control_FIX        *psEncCtrl,                             /* I/O  Encoder control                                                             */
+    opus_int                        condCoding                              /* I    The type of conditional coding to use                                       */
+);
+
+/******************/
+/* Linear Algebra */
+/******************/
+/* Calculates correlation matrix X'*X */
+void silk_corrMatrix_FIX(
+    const opus_int16                *x,                                     /* I    x vector [L + order - 1] used to form data matrix X                         */
+    const opus_int                  L,                                      /* I    Length of vectors                                                           */
+    const opus_int                  order,                                  /* I    Max lag for correlation                                                     */
+    opus_int32                      *XX,                                    /* O    Pointer to X'*X correlation matrix [ order x order ]                        */
+    opus_int32                      *nrg,                                   /* O    Energy of x vector                                                          */
+    opus_int                        *rshifts,                               /* O    Right shifts of correlations                                                */
+    int                              arch                                   /* I    Run-time architecture                                                       */
+);
+
+/* Calculates correlation vector X'*t */
+void silk_corrVector_FIX(
+    const opus_int16                *x,                                     /* I    x vector [L + order - 1] used to form data matrix X                         */
+    const opus_int16                *t,                                     /* I    Target vector [L]                                                           */
+    const opus_int                  L,                                      /* I    Length of vectors                                                           */
+    const opus_int                  order,                                  /* I    Max lag for correlation                                                     */
+    opus_int32                      *Xt,                                    /* O    Pointer to X'*t correlation vector [order]                                  */
+    const opus_int                  rshifts,                                /* I    Right shifts of correlations                                                */
+    int                             arch                                    /* I    Run-time architecture                                                       */
+);
+
+#endif /* SILK_MAIN_FIX_H */
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/noise_shape_analysis_FIX.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/noise_shape_analysis_FIX.c
new file mode 100644 (file)
index 0000000..49ea59e
--- /dev/null
@@ -0,0 +1,400 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main_FIX.h"
+#include "stack_alloc.h"
+#include "tuning_parameters.h"
+
+/* Compute gain to make warped filter coefficients have a zero mean log frequency response on a   */
+/* non-warped frequency scale. (So that it can be implemented with a minimum-phase monic filter.) */
+/* Note: A monic filter is one with the first coefficient equal to 1.0. In Silk we omit the first */
+/* coefficient in an array of coefficients, for monic filters.                                    */
+static OPUS_INLINE opus_int32 warped_gain( /* gain in Q16*/
+    const opus_int32     *coefs_Q24,
+    opus_int             lambda_Q16,
+    opus_int             order
+) {
+    opus_int   i;
+    opus_int32 gain_Q24;
+
+    lambda_Q16 = -lambda_Q16;
+    gain_Q24 = coefs_Q24[ order - 1 ];
+    for( i = order - 2; i >= 0; i-- ) {
+        gain_Q24 = silk_SMLAWB( coefs_Q24[ i ], gain_Q24, lambda_Q16 );
+    }
+    gain_Q24  = silk_SMLAWB( SILK_FIX_CONST( 1.0, 24 ), gain_Q24, -lambda_Q16 );
+    return silk_INVERSE32_varQ( gain_Q24, 40 );
+}
+
+/* Convert warped filter coefficients to monic pseudo-warped coefficients and limit maximum     */
+/* amplitude of monic warped coefficients by using bandwidth expansion on the true coefficients */
+static OPUS_INLINE void limit_warped_coefs(
+    opus_int32           *coefs_Q24,
+    opus_int             lambda_Q16,
+    opus_int32           limit_Q24,
+    opus_int             order
+) {
+    opus_int   i, iter, ind = 0;
+    opus_int32 tmp, maxabs_Q24, chirp_Q16, gain_Q16;
+    opus_int32 nom_Q16, den_Q24;
+    opus_int32 limit_Q20, maxabs_Q20;
+
+    /* Convert to monic coefficients */
+    lambda_Q16 = -lambda_Q16;
+    for( i = order - 1; i > 0; i-- ) {
+        coefs_Q24[ i - 1 ] = silk_SMLAWB( coefs_Q24[ i - 1 ], coefs_Q24[ i ], lambda_Q16 );
+    }
+    lambda_Q16 = -lambda_Q16;
+    nom_Q16  = silk_SMLAWB( SILK_FIX_CONST( 1.0, 16 ), -(opus_int32)lambda_Q16, lambda_Q16 );
+    den_Q24  = silk_SMLAWB( SILK_FIX_CONST( 1.0, 24 ), coefs_Q24[ 0 ], lambda_Q16 );
+    gain_Q16 = silk_DIV32_varQ( nom_Q16, den_Q24, 24 );
+    for( i = 0; i < order; i++ ) {
+        coefs_Q24[ i ] = silk_SMULWW( gain_Q16, coefs_Q24[ i ] );
+    }
+    limit_Q20 = silk_RSHIFT(limit_Q24, 4);
+    for( iter = 0; iter < 10; iter++ ) {
+        /* Find maximum absolute value */
+        maxabs_Q24 = -1;
+        for( i = 0; i < order; i++ ) {
+            tmp = silk_abs_int32( coefs_Q24[ i ] );
+            if( tmp > maxabs_Q24 ) {
+                maxabs_Q24 = tmp;
+                ind = i;
+            }
+        }
+        /* Use Q20 to avoid any overflow when multiplying by (ind + 1) later. */
+        maxabs_Q20 = silk_RSHIFT(maxabs_Q24, 4);
+        if( maxabs_Q20 <= limit_Q20 ) {
+            /* Coefficients are within range - done */
+            return;
+        }
+
+        /* Convert back to true warped coefficients */
+        for( i = 1; i < order; i++ ) {
+            coefs_Q24[ i - 1 ] = silk_SMLAWB( coefs_Q24[ i - 1 ], coefs_Q24[ i ], lambda_Q16 );
+        }
+        gain_Q16 = silk_INVERSE32_varQ( gain_Q16, 32 );
+        for( i = 0; i < order; i++ ) {
+            coefs_Q24[ i ] = silk_SMULWW( gain_Q16, coefs_Q24[ i ] );
+        }
+
+        /* Apply bandwidth expansion */
+        chirp_Q16 = SILK_FIX_CONST( 0.99, 16 ) - silk_DIV32_varQ(
+            silk_SMULWB( maxabs_Q20 - limit_Q20, silk_SMLABB( SILK_FIX_CONST( 0.8, 10 ), SILK_FIX_CONST( 0.1, 10 ), iter ) ),
+            silk_MUL( maxabs_Q20, ind + 1 ), 22 );
+        silk_bwexpander_32( coefs_Q24, order, chirp_Q16 );
+
+        /* Convert to monic warped coefficients */
+        lambda_Q16 = -lambda_Q16;
+        for( i = order - 1; i > 0; i-- ) {
+            coefs_Q24[ i - 1 ] = silk_SMLAWB( coefs_Q24[ i - 1 ], coefs_Q24[ i ], lambda_Q16 );
+        }
+        lambda_Q16 = -lambda_Q16;
+        nom_Q16  = silk_SMLAWB( SILK_FIX_CONST( 1.0, 16 ), -(opus_int32)lambda_Q16,        lambda_Q16 );
+        den_Q24  = silk_SMLAWB( SILK_FIX_CONST( 1.0, 24 ), coefs_Q24[ 0 ], lambda_Q16 );
+        gain_Q16 = silk_DIV32_varQ( nom_Q16, den_Q24, 24 );
+        for( i = 0; i < order; i++ ) {
+            coefs_Q24[ i ] = silk_SMULWW( gain_Q16, coefs_Q24[ i ] );
+        }
+    }
+    silk_assert( 0 );
+}
+
+/**************************************************************/
+/* Compute noise shaping coefficients and initial gain values */
+/**************************************************************/
+void silk_noise_shape_analysis_FIX(
+    silk_encoder_state_FIX          *psEnc,                                 /* I/O  Encoder state FIX                                                           */
+    silk_encoder_control_FIX        *psEncCtrl,                             /* I/O  Encoder control FIX                                                         */
+    const opus_int16                *pitch_res,                             /* I    LPC residual from pitch analysis                                            */
+    const opus_int16                *x,                                     /* I    Input signal [ frame_length + la_shape ]                                    */
+    int                              arch                                   /* I    Run-time architecture                                                       */
+)
+{
+    silk_shape_state_FIX *psShapeSt = &psEnc->sShape;
+    opus_int     k, i, nSamples, nSegs, Qnrg, b_Q14, warping_Q16, scale = 0;
+    opus_int32   SNR_adj_dB_Q7, HarmShapeGain_Q16, Tilt_Q16, tmp32;
+    opus_int32   nrg, log_energy_Q7, log_energy_prev_Q7, energy_variation_Q7;
+    opus_int32   BWExp_Q16, gain_mult_Q16, gain_add_Q16, strength_Q16, b_Q8;
+    opus_int32   auto_corr[     MAX_SHAPE_LPC_ORDER + 1 ];
+    opus_int32   refl_coef_Q16[ MAX_SHAPE_LPC_ORDER ];
+    opus_int32   AR_Q24[       MAX_SHAPE_LPC_ORDER ];
+    VARDECL( opus_int16, x_windowed );
+    const opus_int16 *x_ptr, *pitch_res_ptr;
+    SAVE_STACK;
+
+    /* Point to start of first LPC analysis block */
+    x_ptr = x - psEnc->sCmn.la_shape;
+
+    /****************/
+    /* GAIN CONTROL */
+    /****************/
+    SNR_adj_dB_Q7 = psEnc->sCmn.SNR_dB_Q7;
+
+    /* Input quality is the average of the quality in the lowest two VAD bands */
+    psEncCtrl->input_quality_Q14 = ( opus_int )silk_RSHIFT( (opus_int32)psEnc->sCmn.input_quality_bands_Q15[ 0 ]
+        + psEnc->sCmn.input_quality_bands_Q15[ 1 ], 2 );
+
+    /* Coding quality level, between 0.0_Q0 and 1.0_Q0, but in Q14 */
+    psEncCtrl->coding_quality_Q14 = silk_RSHIFT( silk_sigm_Q15( silk_RSHIFT_ROUND( SNR_adj_dB_Q7 -
+        SILK_FIX_CONST( 20.0, 7 ), 4 ) ), 1 );
+
+    /* Reduce coding SNR during low speech activity */
+    if( psEnc->sCmn.useCBR == 0 ) {
+        b_Q8 = SILK_FIX_CONST( 1.0, 8 ) - psEnc->sCmn.speech_activity_Q8;
+        b_Q8 = silk_SMULWB( silk_LSHIFT( b_Q8, 8 ), b_Q8 );
+        SNR_adj_dB_Q7 = silk_SMLAWB( SNR_adj_dB_Q7,
+            silk_SMULBB( SILK_FIX_CONST( -BG_SNR_DECR_dB, 7 ) >> ( 4 + 1 ), b_Q8 ),                                       /* Q11*/
+            silk_SMULWB( SILK_FIX_CONST( 1.0, 14 ) + psEncCtrl->input_quality_Q14, psEncCtrl->coding_quality_Q14 ) );     /* Q12*/
+    }
+
+    if( psEnc->sCmn.indices.signalType == TYPE_VOICED ) {
+        /* Reduce gains for periodic signals */
+        SNR_adj_dB_Q7 = silk_SMLAWB( SNR_adj_dB_Q7, SILK_FIX_CONST( HARM_SNR_INCR_dB, 8 ), psEnc->LTPCorr_Q15 );
+    } else {
+        /* For unvoiced signals and low-quality input, adjust the quality slower than SNR_dB setting */
+        SNR_adj_dB_Q7 = silk_SMLAWB( SNR_adj_dB_Q7,
+            silk_SMLAWB( SILK_FIX_CONST( 6.0, 9 ), -SILK_FIX_CONST( 0.4, 18 ), psEnc->sCmn.SNR_dB_Q7 ),
+            SILK_FIX_CONST( 1.0, 14 ) - psEncCtrl->input_quality_Q14 );
+    }
+
+    /*************************/
+    /* SPARSENESS PROCESSING */
+    /*************************/
+    /* Set quantizer offset */
+    if( psEnc->sCmn.indices.signalType == TYPE_VOICED ) {
+        /* Initially set to 0; may be overruled in process_gains(..) */
+        psEnc->sCmn.indices.quantOffsetType = 0;
+    } else {
+        /* Sparseness measure, based on relative fluctuations of energy per 2 milliseconds */
+        nSamples = silk_LSHIFT( psEnc->sCmn.fs_kHz, 1 );
+        energy_variation_Q7 = 0;
+        log_energy_prev_Q7  = 0;
+        pitch_res_ptr = pitch_res;
+        nSegs = silk_SMULBB( SUB_FRAME_LENGTH_MS, psEnc->sCmn.nb_subfr ) / 2;
+        for( k = 0; k < nSegs; k++ ) {
+            silk_sum_sqr_shift( &nrg, &scale, pitch_res_ptr, nSamples );
+            nrg += silk_RSHIFT( nSamples, scale );           /* Q(-scale)*/
+
+            log_energy_Q7 = silk_lin2log( nrg );
+            if( k > 0 ) {
+                energy_variation_Q7 += silk_abs( log_energy_Q7 - log_energy_prev_Q7 );
+            }
+            log_energy_prev_Q7 = log_energy_Q7;
+            pitch_res_ptr += nSamples;
+        }
+
+        /* Set quantization offset depending on sparseness measure */
+        if( energy_variation_Q7 > SILK_FIX_CONST( ENERGY_VARIATION_THRESHOLD_QNT_OFFSET, 7 ) * (nSegs-1) ) {
+            psEnc->sCmn.indices.quantOffsetType = 0;
+        } else {
+            psEnc->sCmn.indices.quantOffsetType = 1;
+        }
+    }
+
+    /*******************************/
+    /* Control bandwidth expansion */
+    /*******************************/
+    /* More BWE for signals with high prediction gain */
+    strength_Q16 = silk_SMULWB( psEncCtrl->predGain_Q16, SILK_FIX_CONST( FIND_PITCH_WHITE_NOISE_FRACTION, 16 ) );
+    BWExp_Q16 = silk_DIV32_varQ( SILK_FIX_CONST( BANDWIDTH_EXPANSION, 16 ),
+        silk_SMLAWW( SILK_FIX_CONST( 1.0, 16 ), strength_Q16, strength_Q16 ), 16 );
+
+    if( psEnc->sCmn.warping_Q16 > 0 ) {
+        /* Slightly more warping in analysis will move quantization noise up in frequency, where it's better masked */
+        warping_Q16 = silk_SMLAWB( psEnc->sCmn.warping_Q16, (opus_int32)psEncCtrl->coding_quality_Q14, SILK_FIX_CONST( 0.01, 18 ) );
+    } else {
+        warping_Q16 = 0;
+    }
+
+    /********************************************/
+    /* Compute noise shaping AR coefs and gains */
+    /********************************************/
+    ALLOC( x_windowed, psEnc->sCmn.shapeWinLength, opus_int16 );
+    for( k = 0; k < psEnc->sCmn.nb_subfr; k++ ) {
+        /* Apply window: sine slope followed by flat part followed by cosine slope */
+        opus_int shift, slope_part, flat_part;
+        flat_part = psEnc->sCmn.fs_kHz * 3;
+        slope_part = silk_RSHIFT( psEnc->sCmn.shapeWinLength - flat_part, 1 );
+
+        silk_apply_sine_window( x_windowed, x_ptr, 1, slope_part );
+        shift = slope_part;
+        silk_memcpy( x_windowed + shift, x_ptr + shift, flat_part * sizeof(opus_int16) );
+        shift += flat_part;
+        silk_apply_sine_window( x_windowed + shift, x_ptr + shift, 2, slope_part );
+
+        /* Update pointer: next LPC analysis block */
+        x_ptr += psEnc->sCmn.subfr_length;
+
+        if( psEnc->sCmn.warping_Q16 > 0 ) {
+            /* Calculate warped auto correlation */
+            silk_warped_autocorrelation_FIX( auto_corr, &scale, x_windowed, warping_Q16, psEnc->sCmn.shapeWinLength, psEnc->sCmn.shapingLPCOrder, arch );
+        } else {
+            /* Calculate regular auto correlation */
+            silk_autocorr( auto_corr, &scale, x_windowed, psEnc->sCmn.shapeWinLength, psEnc->sCmn.shapingLPCOrder + 1, arch );
+        }
+
+        /* Add white noise, as a fraction of energy */
+        auto_corr[0] = silk_ADD32( auto_corr[0], silk_max_32( silk_SMULWB( silk_RSHIFT( auto_corr[ 0 ], 4 ),
+            SILK_FIX_CONST( SHAPE_WHITE_NOISE_FRACTION, 20 ) ), 1 ) );
+
+        /* Calculate the reflection coefficients using schur */
+        nrg = silk_schur64( refl_coef_Q16, auto_corr, psEnc->sCmn.shapingLPCOrder );
+        silk_assert( nrg >= 0 );
+
+        /* Convert reflection coefficients to prediction coefficients */
+        silk_k2a_Q16( AR_Q24, refl_coef_Q16, psEnc->sCmn.shapingLPCOrder );
+
+        Qnrg = -scale;          /* range: -12...30*/
+        silk_assert( Qnrg >= -12 );
+        silk_assert( Qnrg <=  30 );
+
+        /* Make sure that Qnrg is an even number */
+        if( Qnrg & 1 ) {
+            Qnrg -= 1;
+            nrg >>= 1;
+        }
+
+        tmp32 = silk_SQRT_APPROX( nrg );
+        Qnrg >>= 1;             /* range: -6...15*/
+
+        psEncCtrl->Gains_Q16[ k ] = silk_LSHIFT_SAT32( tmp32, 16 - Qnrg );
+
+        if( psEnc->sCmn.warping_Q16 > 0 ) {
+            /* Adjust gain for warping */
+            gain_mult_Q16 = warped_gain( AR_Q24, warping_Q16, psEnc->sCmn.shapingLPCOrder );
+            silk_assert( psEncCtrl->Gains_Q16[ k ] > 0 );
+            if( psEncCtrl->Gains_Q16[ k ] < SILK_FIX_CONST( 0.25, 16 ) ) {
+                psEncCtrl->Gains_Q16[ k ] = silk_SMULWW( psEncCtrl->Gains_Q16[ k ], gain_mult_Q16 );
+            } else {
+                psEncCtrl->Gains_Q16[ k ] = silk_SMULWW( silk_RSHIFT_ROUND( psEncCtrl->Gains_Q16[ k ], 1 ), gain_mult_Q16 );
+                if ( psEncCtrl->Gains_Q16[ k ] >= ( silk_int32_MAX >> 1 ) ) {
+                    psEncCtrl->Gains_Q16[ k ] = silk_int32_MAX;
+                } else {
+                    psEncCtrl->Gains_Q16[ k ] = silk_LSHIFT32( psEncCtrl->Gains_Q16[ k ], 1 );
+                }
+            }
+            silk_assert( psEncCtrl->Gains_Q16[ k ] > 0 );
+        }
+
+        /* Bandwidth expansion */
+        silk_bwexpander_32( AR_Q24, psEnc->sCmn.shapingLPCOrder, BWExp_Q16 );
+
+        if( psEnc->sCmn.warping_Q16 > 0 ) {
+            /* Convert to monic warped prediction coefficients and limit absolute values */
+            limit_warped_coefs( AR_Q24, warping_Q16, SILK_FIX_CONST( 3.999, 24 ), psEnc->sCmn.shapingLPCOrder );
+
+            /* Convert from Q24 to Q13 and store in int16 */
+            for( i = 0; i < psEnc->sCmn.shapingLPCOrder; i++ ) {
+                psEncCtrl->AR_Q13[ k * MAX_SHAPE_LPC_ORDER + i ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( AR_Q24[ i ], 11 ) );
+            }
+        } else {
+            silk_LPC_fit( &psEncCtrl->AR_Q13[ k * MAX_SHAPE_LPC_ORDER ], AR_Q24, 13, 24, psEnc->sCmn.shapingLPCOrder );
+        }
+    }
+
+    /*****************/
+    /* Gain tweaking */
+    /*****************/
+    /* Increase gains during low speech activity and put lower limit on gains */
+    gain_mult_Q16 = silk_log2lin( -silk_SMLAWB( -SILK_FIX_CONST( 16.0, 7 ), SNR_adj_dB_Q7, SILK_FIX_CONST( 0.16, 16 ) ) );
+    gain_add_Q16  = silk_log2lin(  silk_SMLAWB(  SILK_FIX_CONST( 16.0, 7 ), SILK_FIX_CONST( MIN_QGAIN_DB, 7 ), SILK_FIX_CONST( 0.16, 16 ) ) );
+    silk_assert( gain_mult_Q16 > 0 );
+    for( k = 0; k < psEnc->sCmn.nb_subfr; k++ ) {
+        psEncCtrl->Gains_Q16[ k ] = silk_SMULWW( psEncCtrl->Gains_Q16[ k ], gain_mult_Q16 );
+        silk_assert( psEncCtrl->Gains_Q16[ k ] >= 0 );
+        psEncCtrl->Gains_Q16[ k ] = silk_ADD_POS_SAT32( psEncCtrl->Gains_Q16[ k ], gain_add_Q16 );
+    }
+
+
+    /************************************************/
+    /* Control low-frequency shaping and noise tilt */
+    /************************************************/
+    /* Less low frequency shaping for noisy inputs */
+    strength_Q16 = silk_MUL( SILK_FIX_CONST( LOW_FREQ_SHAPING, 4 ), silk_SMLAWB( SILK_FIX_CONST( 1.0, 12 ),
+        SILK_FIX_CONST( LOW_QUALITY_LOW_FREQ_SHAPING_DECR, 13 ), psEnc->sCmn.input_quality_bands_Q15[ 0 ] - SILK_FIX_CONST( 1.0, 15 ) ) );
+    strength_Q16 = silk_RSHIFT( silk_MUL( strength_Q16, psEnc->sCmn.speech_activity_Q8 ), 8 );
+    if( psEnc->sCmn.indices.signalType == TYPE_VOICED ) {
+        /* Reduce low frequencies quantization noise for periodic signals, depending on pitch lag */
+        /*f = 400; freqz([1, -0.98 + 2e-4 * f], [1, -0.97 + 7e-4 * f], 2^12, Fs); axis([0, 1000, -10, 1])*/
+        opus_int fs_kHz_inv = silk_DIV32_16( SILK_FIX_CONST( 0.2, 14 ), psEnc->sCmn.fs_kHz );
+        for( k = 0; k < psEnc->sCmn.nb_subfr; k++ ) {
+            b_Q14 = fs_kHz_inv + silk_DIV32_16( SILK_FIX_CONST( 3.0, 14 ), psEncCtrl->pitchL[ k ] );
+            /* Pack two coefficients in one int32 */
+            psEncCtrl->LF_shp_Q14[ k ]  = silk_LSHIFT( SILK_FIX_CONST( 1.0, 14 ) - b_Q14 - silk_SMULWB( strength_Q16, b_Q14 ), 16 );
+            psEncCtrl->LF_shp_Q14[ k ] |= (opus_uint16)( b_Q14 - SILK_FIX_CONST( 1.0, 14 ) );
+        }
+        silk_assert( SILK_FIX_CONST( HARM_HP_NOISE_COEF, 24 ) < SILK_FIX_CONST( 0.5, 24 ) ); /* Guarantees that second argument to SMULWB() is within range of an opus_int16*/
+        Tilt_Q16 = - SILK_FIX_CONST( HP_NOISE_COEF, 16 ) -
+            silk_SMULWB( SILK_FIX_CONST( 1.0, 16 ) - SILK_FIX_CONST( HP_NOISE_COEF, 16 ),
+                silk_SMULWB( SILK_FIX_CONST( HARM_HP_NOISE_COEF, 24 ), psEnc->sCmn.speech_activity_Q8 ) );
+    } else {
+        b_Q14 = silk_DIV32_16( 21299, psEnc->sCmn.fs_kHz ); /* 1.3_Q0 = 21299_Q14*/
+        /* Pack two coefficients in one int32 */
+        psEncCtrl->LF_shp_Q14[ 0 ]  = silk_LSHIFT( SILK_FIX_CONST( 1.0, 14 ) - b_Q14 -
+            silk_SMULWB( strength_Q16, silk_SMULWB( SILK_FIX_CONST( 0.6, 16 ), b_Q14 ) ), 16 );
+        psEncCtrl->LF_shp_Q14[ 0 ] |= (opus_uint16)( b_Q14 - SILK_FIX_CONST( 1.0, 14 ) );
+        for( k = 1; k < psEnc->sCmn.nb_subfr; k++ ) {
+            psEncCtrl->LF_shp_Q14[ k ] = psEncCtrl->LF_shp_Q14[ 0 ];
+        }
+        Tilt_Q16 = -SILK_FIX_CONST( HP_NOISE_COEF, 16 );
+    }
+
+    /****************************/
+    /* HARMONIC SHAPING CONTROL */
+    /****************************/
+    if( USE_HARM_SHAPING && psEnc->sCmn.indices.signalType == TYPE_VOICED ) {
+        /* More harmonic noise shaping for high bitrates or noisy input */
+        HarmShapeGain_Q16 = silk_SMLAWB( SILK_FIX_CONST( HARMONIC_SHAPING, 16 ),
+                SILK_FIX_CONST( 1.0, 16 ) - silk_SMULWB( SILK_FIX_CONST( 1.0, 18 ) - silk_LSHIFT( psEncCtrl->coding_quality_Q14, 4 ),
+                psEncCtrl->input_quality_Q14 ), SILK_FIX_CONST( HIGH_RATE_OR_LOW_QUALITY_HARMONIC_SHAPING, 16 ) );
+
+        /* Less harmonic noise shaping for less periodic signals */
+        HarmShapeGain_Q16 = silk_SMULWB( silk_LSHIFT( HarmShapeGain_Q16, 1 ),
+            silk_SQRT_APPROX( silk_LSHIFT( psEnc->LTPCorr_Q15, 15 ) ) );
+    } else {
+        HarmShapeGain_Q16 = 0;
+    }
+
+    /*************************/
+    /* Smooth over subframes */
+    /*************************/
+    for( k = 0; k < MAX_NB_SUBFR; k++ ) {
+        psShapeSt->HarmShapeGain_smth_Q16 =
+            silk_SMLAWB( psShapeSt->HarmShapeGain_smth_Q16, HarmShapeGain_Q16 - psShapeSt->HarmShapeGain_smth_Q16, SILK_FIX_CONST( SUBFR_SMTH_COEF, 16 ) );
+        psShapeSt->Tilt_smth_Q16 =
+            silk_SMLAWB( psShapeSt->Tilt_smth_Q16,          Tilt_Q16          - psShapeSt->Tilt_smth_Q16,          SILK_FIX_CONST( SUBFR_SMTH_COEF, 16 ) );
+
+        psEncCtrl->HarmShapeGain_Q14[ k ] = ( opus_int )silk_RSHIFT_ROUND( psShapeSt->HarmShapeGain_smth_Q16, 2 );
+        psEncCtrl->Tilt_Q14[ k ]          = ( opus_int )silk_RSHIFT_ROUND( psShapeSt->Tilt_smth_Q16,          2 );
+    }
+    RESTORE_STACK;
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/pitch_analysis_core_FIX.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/pitch_analysis_core_FIX.c
new file mode 100644 (file)
index 0000000..1472904
--- /dev/null
@@ -0,0 +1,721 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/***********************************************************
+* Pitch analyser function
+********************************************************** */
+#include "SigProc_FIX.h"
+#include "pitch_est_defines.h"
+#include "stack_alloc.h"
+#include "debug.h"
+#include "pitch.h"
+
+#define SCRATCH_SIZE    22
+#define SF_LENGTH_4KHZ  ( PE_SUBFR_LENGTH_MS * 4 )
+#define SF_LENGTH_8KHZ  ( PE_SUBFR_LENGTH_MS * 8 )
+#define MIN_LAG_4KHZ    ( PE_MIN_LAG_MS * 4 )
+#define MIN_LAG_8KHZ    ( PE_MIN_LAG_MS * 8 )
+#define MAX_LAG_4KHZ    ( PE_MAX_LAG_MS * 4 )
+#define MAX_LAG_8KHZ    ( PE_MAX_LAG_MS * 8 - 1 )
+#define CSTRIDE_4KHZ    ( MAX_LAG_4KHZ + 1 - MIN_LAG_4KHZ )
+#define CSTRIDE_8KHZ    ( MAX_LAG_8KHZ + 3 - ( MIN_LAG_8KHZ - 2 ) )
+#define D_COMP_MIN      ( MIN_LAG_8KHZ - 3 )
+#define D_COMP_MAX      ( MAX_LAG_8KHZ + 4 )
+#define D_COMP_STRIDE   ( D_COMP_MAX - D_COMP_MIN )
+
+typedef opus_int32 silk_pe_stage3_vals[ PE_NB_STAGE3_LAGS ];
+
+/************************************************************/
+/* Internally used functions                                */
+/************************************************************/
+static void silk_P_Ana_calc_corr_st3(
+    silk_pe_stage3_vals cross_corr_st3[],              /* O 3 DIM correlation array */
+    const opus_int16  frame[],                         /* I vector to correlate         */
+    opus_int          start_lag,                       /* I lag offset to search around */
+    opus_int          sf_length,                       /* I length of a 5 ms subframe   */
+    opus_int          nb_subfr,                        /* I number of subframes         */
+    opus_int          complexity,                      /* I Complexity setting          */
+    int               arch                             /* I Run-time architecture       */
+);
+
+static void silk_P_Ana_calc_energy_st3(
+    silk_pe_stage3_vals energies_st3[],                /* O 3 DIM energy array */
+    const opus_int16  frame[],                         /* I vector to calc energy in    */
+    opus_int          start_lag,                       /* I lag offset to search around */
+    opus_int          sf_length,                       /* I length of one 5 ms subframe */
+    opus_int          nb_subfr,                        /* I number of subframes         */
+    opus_int          complexity,                      /* I Complexity setting          */
+    int               arch                             /* I Run-time architecture       */
+);
+
+/*************************************************************/
+/*      FIXED POINT CORE PITCH ANALYSIS FUNCTION             */
+/*************************************************************/
+opus_int silk_pitch_analysis_core(                  /* O    Voicing estimate: 0 voiced, 1 unvoiced                      */
+    const opus_int16            *frame_unscaled,    /* I    Signal of length PE_FRAME_LENGTH_MS*Fs_kHz                  */
+    opus_int                    *pitch_out,         /* O    4 pitch lag values                                          */
+    opus_int16                  *lagIndex,          /* O    Lag Index                                                   */
+    opus_int8                   *contourIndex,      /* O    Pitch contour Index                                         */
+    opus_int                    *LTPCorr_Q15,       /* I/O  Normalized correlation; input: value from previous frame    */
+    opus_int                    prevLag,            /* I    Last lag of previous frame; set to zero is unvoiced         */
+    const opus_int32            search_thres1_Q16,  /* I    First stage threshold for lag candidates 0 - 1              */
+    const opus_int              search_thres2_Q13,  /* I    Final threshold for lag candidates 0 - 1                    */
+    const opus_int              Fs_kHz,             /* I    Sample frequency (kHz)                                      */
+    const opus_int              complexity,         /* I    Complexity setting, 0-2, where 2 is highest                 */
+    const opus_int              nb_subfr,           /* I    number of 5 ms subframes                                    */
+    int                         arch                /* I    Run-time architecture                                       */
+)
+{
+    VARDECL( opus_int16, frame_8kHz_buf );
+    VARDECL( opus_int16, frame_4kHz );
+    VARDECL( opus_int16, frame_scaled );
+    opus_int32 filt_state[ 6 ];
+    const opus_int16 *frame, *frame_8kHz;
+    opus_int   i, k, d, j;
+    VARDECL( opus_int16, C );
+    VARDECL( opus_int32, xcorr32 );
+    const opus_int16 *target_ptr, *basis_ptr;
+    opus_int32 cross_corr, normalizer, energy, energy_basis, energy_target;
+    opus_int   d_srch[ PE_D_SRCH_LENGTH ], Cmax, length_d_srch, length_d_comp, shift;
+    VARDECL( opus_int16, d_comp );
+    opus_int32 sum, threshold, lag_counter;
+    opus_int   CBimax, CBimax_new, CBimax_old, lag, start_lag, end_lag, lag_new;
+    opus_int32 CC[ PE_NB_CBKS_STAGE2_EXT ], CCmax, CCmax_b, CCmax_new_b, CCmax_new;
+    VARDECL( silk_pe_stage3_vals, energies_st3 );
+    VARDECL( silk_pe_stage3_vals, cross_corr_st3 );
+    opus_int   frame_length, frame_length_8kHz, frame_length_4kHz;
+    opus_int   sf_length;
+    opus_int   min_lag;
+    opus_int   max_lag;
+    opus_int32 contour_bias_Q15, diff;
+    opus_int   nb_cbk_search, cbk_size;
+    opus_int32 delta_lag_log2_sqr_Q7, lag_log2_Q7, prevLag_log2_Q7, prev_lag_bias_Q13;
+    const opus_int8 *Lag_CB_ptr;
+    SAVE_STACK;
+
+    /* Check for valid sampling frequency */
+    celt_assert( Fs_kHz == 8 || Fs_kHz == 12 || Fs_kHz == 16 );
+
+    /* Check for valid complexity setting */
+    celt_assert( complexity >= SILK_PE_MIN_COMPLEX );
+    celt_assert( complexity <= SILK_PE_MAX_COMPLEX );
+
+    silk_assert( search_thres1_Q16 >= 0 && search_thres1_Q16 <= (1<<16) );
+    silk_assert( search_thres2_Q13 >= 0 && search_thres2_Q13 <= (1<<13) );
+
+    /* Set up frame lengths max / min lag for the sampling frequency */
+    frame_length      = ( PE_LTP_MEM_LENGTH_MS + nb_subfr * PE_SUBFR_LENGTH_MS ) * Fs_kHz;
+    frame_length_4kHz = ( PE_LTP_MEM_LENGTH_MS + nb_subfr * PE_SUBFR_LENGTH_MS ) * 4;
+    frame_length_8kHz = ( PE_LTP_MEM_LENGTH_MS + nb_subfr * PE_SUBFR_LENGTH_MS ) * 8;
+    sf_length         = PE_SUBFR_LENGTH_MS * Fs_kHz;
+    min_lag           = PE_MIN_LAG_MS * Fs_kHz;
+    max_lag           = PE_MAX_LAG_MS * Fs_kHz - 1;
+
+    /* Downscale input if necessary */
+    silk_sum_sqr_shift( &energy, &shift, frame_unscaled, frame_length );
+    shift += 3 - silk_CLZ32( energy );        /* at least two bits headroom */
+    ALLOC( frame_scaled, frame_length, opus_int16 );
+    if( shift > 0 ) {
+        shift = silk_RSHIFT( shift + 1, 1 );
+        for( i = 0; i < frame_length; i++ ) {
+            frame_scaled[ i ] = silk_RSHIFT( frame_unscaled[ i ], shift );
+        }
+        frame = frame_scaled;
+    } else {
+        frame = frame_unscaled;
+    }
+
+    ALLOC( frame_8kHz_buf, ( Fs_kHz == 8 ) ? 1 : frame_length_8kHz, opus_int16 );
+    /* Resample from input sampled at Fs_kHz to 8 kHz */
+    if( Fs_kHz == 16 ) {
+        silk_memset( filt_state, 0, 2 * sizeof( opus_int32 ) );
+        silk_resampler_down2( filt_state, frame_8kHz_buf, frame, frame_length );
+        frame_8kHz = frame_8kHz_buf;
+    } else if( Fs_kHz == 12 ) {
+        silk_memset( filt_state, 0, 6 * sizeof( opus_int32 ) );
+        silk_resampler_down2_3( filt_state, frame_8kHz_buf, frame, frame_length );
+        frame_8kHz = frame_8kHz_buf;
+    } else {
+        celt_assert( Fs_kHz == 8 );
+        frame_8kHz = frame;
+    }
+
+    /* Decimate again to 4 kHz */
+    silk_memset( filt_state, 0, 2 * sizeof( opus_int32 ) );/* Set state to zero */
+    ALLOC( frame_4kHz, frame_length_4kHz, opus_int16 );
+    silk_resampler_down2( filt_state, frame_4kHz, frame_8kHz, frame_length_8kHz );
+
+    /* Low-pass filter */
+    for( i = frame_length_4kHz - 1; i > 0; i-- ) {
+        frame_4kHz[ i ] = silk_ADD_SAT16( frame_4kHz[ i ], frame_4kHz[ i - 1 ] );
+    }
+
+
+    /******************************************************************************
+    * FIRST STAGE, operating in 4 khz
+    ******************************************************************************/
+    ALLOC( C, nb_subfr * CSTRIDE_8KHZ, opus_int16 );
+    ALLOC( xcorr32, MAX_LAG_4KHZ-MIN_LAG_4KHZ+1, opus_int32 );
+    silk_memset( C, 0, (nb_subfr >> 1) * CSTRIDE_4KHZ * sizeof( opus_int16 ) );
+    target_ptr = &frame_4kHz[ silk_LSHIFT( SF_LENGTH_4KHZ, 2 ) ];
+    for( k = 0; k < nb_subfr >> 1; k++ ) {
+        /* Check that we are within range of the array */
+        celt_assert( target_ptr >= frame_4kHz );
+        celt_assert( target_ptr + SF_LENGTH_8KHZ <= frame_4kHz + frame_length_4kHz );
+
+        basis_ptr = target_ptr - MIN_LAG_4KHZ;
+
+        /* Check that we are within range of the array */
+        celt_assert( basis_ptr >= frame_4kHz );
+        celt_assert( basis_ptr + SF_LENGTH_8KHZ <= frame_4kHz + frame_length_4kHz );
+
+        celt_pitch_xcorr( target_ptr, target_ptr - MAX_LAG_4KHZ, xcorr32, SF_LENGTH_8KHZ, MAX_LAG_4KHZ - MIN_LAG_4KHZ + 1, arch );
+
+        /* Calculate first vector products before loop */
+        cross_corr = xcorr32[ MAX_LAG_4KHZ - MIN_LAG_4KHZ ];
+        normalizer = silk_inner_prod_aligned( target_ptr, target_ptr, SF_LENGTH_8KHZ, arch );
+        normalizer = silk_ADD32( normalizer, silk_inner_prod_aligned( basis_ptr,  basis_ptr, SF_LENGTH_8KHZ, arch ) );
+        normalizer = silk_ADD32( normalizer, silk_SMULBB( SF_LENGTH_8KHZ, 4000 ) );
+
+        matrix_ptr( C, k, 0, CSTRIDE_4KHZ ) =
+            (opus_int16)silk_DIV32_varQ( cross_corr, normalizer, 13 + 1 );                      /* Q13 */
+
+        /* From now on normalizer is computed recursively */
+        for( d = MIN_LAG_4KHZ + 1; d <= MAX_LAG_4KHZ; d++ ) {
+            basis_ptr--;
+
+            /* Check that we are within range of the array */
+            silk_assert( basis_ptr >= frame_4kHz );
+            silk_assert( basis_ptr + SF_LENGTH_8KHZ <= frame_4kHz + frame_length_4kHz );
+
+            cross_corr = xcorr32[ MAX_LAG_4KHZ - d ];
+
+            /* Add contribution of new sample and remove contribution from oldest sample */
+            normalizer = silk_ADD32( normalizer,
+                silk_SMULBB( basis_ptr[ 0 ], basis_ptr[ 0 ] ) -
+                silk_SMULBB( basis_ptr[ SF_LENGTH_8KHZ ], basis_ptr[ SF_LENGTH_8KHZ ] ) );
+
+            matrix_ptr( C, k, d - MIN_LAG_4KHZ, CSTRIDE_4KHZ) =
+                (opus_int16)silk_DIV32_varQ( cross_corr, normalizer, 13 + 1 );                  /* Q13 */
+        }
+        /* Update target pointer */
+        target_ptr += SF_LENGTH_8KHZ;
+    }
+
+    /* Combine two subframes into single correlation measure and apply short-lag bias */
+    if( nb_subfr == PE_MAX_NB_SUBFR ) {
+        for( i = MAX_LAG_4KHZ; i >= MIN_LAG_4KHZ; i-- ) {
+            sum = (opus_int32)matrix_ptr( C, 0, i - MIN_LAG_4KHZ, CSTRIDE_4KHZ )
+                + (opus_int32)matrix_ptr( C, 1, i - MIN_LAG_4KHZ, CSTRIDE_4KHZ );               /* Q14 */
+            sum = silk_SMLAWB( sum, sum, silk_LSHIFT( -i, 4 ) );                                /* Q14 */
+            C[ i - MIN_LAG_4KHZ ] = (opus_int16)sum;                                            /* Q14 */
+        }
+    } else {
+        /* Only short-lag bias */
+        for( i = MAX_LAG_4KHZ; i >= MIN_LAG_4KHZ; i-- ) {
+            sum = silk_LSHIFT( (opus_int32)C[ i - MIN_LAG_4KHZ ], 1 );                          /* Q14 */
+            sum = silk_SMLAWB( sum, sum, silk_LSHIFT( -i, 4 ) );                                /* Q14 */
+            C[ i - MIN_LAG_4KHZ ] = (opus_int16)sum;                                            /* Q14 */
+        }
+    }
+
+    /* Sort */
+    length_d_srch = silk_ADD_LSHIFT32( 4, complexity, 1 );
+    celt_assert( 3 * length_d_srch <= PE_D_SRCH_LENGTH );
+    silk_insertion_sort_decreasing_int16( C, d_srch, CSTRIDE_4KHZ,
+                                          length_d_srch );
+
+    /* Escape if correlation is very low already here */
+    Cmax = (opus_int)C[ 0 ];                                                    /* Q14 */
+    if( Cmax < SILK_FIX_CONST( 0.2, 14 ) ) {
+        silk_memset( pitch_out, 0, nb_subfr * sizeof( opus_int ) );
+        *LTPCorr_Q15  = 0;
+        *lagIndex     = 0;
+        *contourIndex = 0;
+        RESTORE_STACK;
+        return 1;
+    }
+
+    threshold = silk_SMULWB( search_thres1_Q16, Cmax );
+    for( i = 0; i < length_d_srch; i++ ) {
+        /* Convert to 8 kHz indices for the sorted correlation that exceeds the threshold */
+        if( C[ i ] > threshold ) {
+            d_srch[ i ] = silk_LSHIFT( d_srch[ i ] + MIN_LAG_4KHZ, 1 );
+        } else {
+            length_d_srch = i;
+            break;
+        }
+    }
+    celt_assert( length_d_srch > 0 );
+
+    ALLOC( d_comp, D_COMP_STRIDE, opus_int16 );
+    for( i = D_COMP_MIN; i < D_COMP_MAX; i++ ) {
+        d_comp[ i - D_COMP_MIN ] = 0;
+    }
+    for( i = 0; i < length_d_srch; i++ ) {
+        d_comp[ d_srch[ i ] - D_COMP_MIN ] = 1;
+    }
+
+    /* Convolution */
+    for( i = D_COMP_MAX - 1; i >= MIN_LAG_8KHZ; i-- ) {
+        d_comp[ i - D_COMP_MIN ] +=
+            d_comp[ i - 1 - D_COMP_MIN ] + d_comp[ i - 2 - D_COMP_MIN ];
+    }
+
+    length_d_srch = 0;
+    for( i = MIN_LAG_8KHZ; i < MAX_LAG_8KHZ + 1; i++ ) {
+        if( d_comp[ i + 1 - D_COMP_MIN ] > 0 ) {
+            d_srch[ length_d_srch ] = i;
+            length_d_srch++;
+        }
+    }
+
+    /* Convolution */
+    for( i = D_COMP_MAX - 1; i >= MIN_LAG_8KHZ; i-- ) {
+        d_comp[ i - D_COMP_MIN ] += d_comp[ i - 1 - D_COMP_MIN ]
+            + d_comp[ i - 2 - D_COMP_MIN ] + d_comp[ i - 3 - D_COMP_MIN ];
+    }
+
+    length_d_comp = 0;
+    for( i = MIN_LAG_8KHZ; i < D_COMP_MAX; i++ ) {
+        if( d_comp[ i - D_COMP_MIN ] > 0 ) {
+            d_comp[ length_d_comp ] = i - 2;
+            length_d_comp++;
+        }
+    }
+
+    /**********************************************************************************
+    ** SECOND STAGE, operating at 8 kHz, on lag sections with high correlation
+    *************************************************************************************/
+
+    /*********************************************************************************
+    * Find energy of each subframe projected onto its history, for a range of delays
+    *********************************************************************************/
+    silk_memset( C, 0, nb_subfr * CSTRIDE_8KHZ * sizeof( opus_int16 ) );
+
+    target_ptr = &frame_8kHz[ PE_LTP_MEM_LENGTH_MS * 8 ];
+    for( k = 0; k < nb_subfr; k++ ) {
+
+        /* Check that we are within range of the array */
+        celt_assert( target_ptr >= frame_8kHz );
+        celt_assert( target_ptr + SF_LENGTH_8KHZ <= frame_8kHz + frame_length_8kHz );
+
+        energy_target = silk_ADD32( silk_inner_prod_aligned( target_ptr, target_ptr, SF_LENGTH_8KHZ, arch ), 1 );
+        for( j = 0; j < length_d_comp; j++ ) {
+            d = d_comp[ j ];
+            basis_ptr = target_ptr - d;
+
+            /* Check that we are within range of the array */
+            silk_assert( basis_ptr >= frame_8kHz );
+            silk_assert( basis_ptr + SF_LENGTH_8KHZ <= frame_8kHz + frame_length_8kHz );
+
+            cross_corr = silk_inner_prod_aligned( target_ptr, basis_ptr, SF_LENGTH_8KHZ, arch );
+            if( cross_corr > 0 ) {
+                energy_basis = silk_inner_prod_aligned( basis_ptr, basis_ptr, SF_LENGTH_8KHZ, arch );
+                matrix_ptr( C, k, d - ( MIN_LAG_8KHZ - 2 ), CSTRIDE_8KHZ ) =
+                    (opus_int16)silk_DIV32_varQ( cross_corr,
+                                                 silk_ADD32( energy_target,
+                                                             energy_basis ),
+                                                 13 + 1 );                                      /* Q13 */
+            } else {
+                matrix_ptr( C, k, d - ( MIN_LAG_8KHZ - 2 ), CSTRIDE_8KHZ ) = 0;
+            }
+        }
+        target_ptr += SF_LENGTH_8KHZ;
+    }
+
+    /* search over lag range and lags codebook */
+    /* scale factor for lag codebook, as a function of center lag */
+
+    CCmax   = silk_int32_MIN;
+    CCmax_b = silk_int32_MIN;
+
+    CBimax = 0; /* To avoid returning undefined lag values */
+    lag = -1;   /* To check if lag with strong enough correlation has been found */
+
+    if( prevLag > 0 ) {
+        if( Fs_kHz == 12 ) {
+            prevLag = silk_DIV32_16( silk_LSHIFT( prevLag, 1 ), 3 );
+        } else if( Fs_kHz == 16 ) {
+            prevLag = silk_RSHIFT( prevLag, 1 );
+        }
+        prevLag_log2_Q7 = silk_lin2log( (opus_int32)prevLag );
+    } else {
+        prevLag_log2_Q7 = 0;
+    }
+    silk_assert( search_thres2_Q13 == silk_SAT16( search_thres2_Q13 ) );
+    /* Set up stage 2 codebook based on number of subframes */
+    if( nb_subfr == PE_MAX_NB_SUBFR ) {
+        cbk_size   = PE_NB_CBKS_STAGE2_EXT;
+        Lag_CB_ptr = &silk_CB_lags_stage2[ 0 ][ 0 ];
+        if( Fs_kHz == 8 && complexity > SILK_PE_MIN_COMPLEX ) {
+            /* If input is 8 khz use a larger codebook here because it is last stage */
+            nb_cbk_search = PE_NB_CBKS_STAGE2_EXT;
+        } else {
+            nb_cbk_search = PE_NB_CBKS_STAGE2;
+        }
+    } else {
+        cbk_size       = PE_NB_CBKS_STAGE2_10MS;
+        Lag_CB_ptr     = &silk_CB_lags_stage2_10_ms[ 0 ][ 0 ];
+        nb_cbk_search  = PE_NB_CBKS_STAGE2_10MS;
+    }
+
+    for( k = 0; k < length_d_srch; k++ ) {
+        d = d_srch[ k ];
+        for( j = 0; j < nb_cbk_search; j++ ) {
+            CC[ j ] = 0;
+            for( i = 0; i < nb_subfr; i++ ) {
+                opus_int d_subfr;
+                /* Try all codebooks */
+                d_subfr = d + matrix_ptr( Lag_CB_ptr, i, j, cbk_size );
+                CC[ j ] = CC[ j ]
+                    + (opus_int32)matrix_ptr( C, i,
+                                              d_subfr - ( MIN_LAG_8KHZ - 2 ),
+                                              CSTRIDE_8KHZ );
+            }
+        }
+        /* Find best codebook */
+        CCmax_new = silk_int32_MIN;
+        CBimax_new = 0;
+        for( i = 0; i < nb_cbk_search; i++ ) {
+            if( CC[ i ] > CCmax_new ) {
+                CCmax_new = CC[ i ];
+                CBimax_new = i;
+            }
+        }
+
+        /* Bias towards shorter lags */
+        lag_log2_Q7 = silk_lin2log( d ); /* Q7 */
+        silk_assert( lag_log2_Q7 == silk_SAT16( lag_log2_Q7 ) );
+        silk_assert( nb_subfr * SILK_FIX_CONST( PE_SHORTLAG_BIAS, 13 ) == silk_SAT16( nb_subfr * SILK_FIX_CONST( PE_SHORTLAG_BIAS, 13 ) ) );
+        CCmax_new_b = CCmax_new - silk_RSHIFT( silk_SMULBB( nb_subfr * SILK_FIX_CONST( PE_SHORTLAG_BIAS, 13 ), lag_log2_Q7 ), 7 ); /* Q13 */
+
+        /* Bias towards previous lag */
+        silk_assert( nb_subfr * SILK_FIX_CONST( PE_PREVLAG_BIAS, 13 ) == silk_SAT16( nb_subfr * SILK_FIX_CONST( PE_PREVLAG_BIAS, 13 ) ) );
+        if( prevLag > 0 ) {
+            delta_lag_log2_sqr_Q7 = lag_log2_Q7 - prevLag_log2_Q7;
+            silk_assert( delta_lag_log2_sqr_Q7 == silk_SAT16( delta_lag_log2_sqr_Q7 ) );
+            delta_lag_log2_sqr_Q7 = silk_RSHIFT( silk_SMULBB( delta_lag_log2_sqr_Q7, delta_lag_log2_sqr_Q7 ), 7 );
+            prev_lag_bias_Q13 = silk_RSHIFT( silk_SMULBB( nb_subfr * SILK_FIX_CONST( PE_PREVLAG_BIAS, 13 ), *LTPCorr_Q15 ), 15 ); /* Q13 */
+            prev_lag_bias_Q13 = silk_DIV32( silk_MUL( prev_lag_bias_Q13, delta_lag_log2_sqr_Q7 ), delta_lag_log2_sqr_Q7 + SILK_FIX_CONST( 0.5, 7 ) );
+            CCmax_new_b -= prev_lag_bias_Q13; /* Q13 */
+        }
+
+        if( CCmax_new_b > CCmax_b                                   &&  /* Find maximum biased correlation                  */
+            CCmax_new > silk_SMULBB( nb_subfr, search_thres2_Q13 )  &&  /* Correlation needs to be high enough to be voiced */
+            silk_CB_lags_stage2[ 0 ][ CBimax_new ] <= MIN_LAG_8KHZ      /* Lag must be in range                             */
+         ) {
+            CCmax_b = CCmax_new_b;
+            CCmax   = CCmax_new;
+            lag     = d;
+            CBimax  = CBimax_new;
+        }
+    }
+
+    if( lag == -1 ) {
+        /* No suitable candidate found */
+        silk_memset( pitch_out, 0, nb_subfr * sizeof( opus_int ) );
+        *LTPCorr_Q15  = 0;
+        *lagIndex     = 0;
+        *contourIndex = 0;
+        RESTORE_STACK;
+        return 1;
+    }
+
+    /* Output normalized correlation */
+    *LTPCorr_Q15 = (opus_int)silk_LSHIFT( silk_DIV32_16( CCmax, nb_subfr ), 2 );
+    silk_assert( *LTPCorr_Q15 >= 0 );
+
+    if( Fs_kHz > 8 ) {
+        /* Search in original signal */
+
+        CBimax_old = CBimax;
+        /* Compensate for decimation */
+        silk_assert( lag == silk_SAT16( lag ) );
+        if( Fs_kHz == 12 ) {
+            lag = silk_RSHIFT( silk_SMULBB( lag, 3 ), 1 );
+        } else if( Fs_kHz == 16 ) {
+            lag = silk_LSHIFT( lag, 1 );
+        } else {
+            lag = silk_SMULBB( lag, 3 );
+        }
+
+        lag = silk_LIMIT_int( lag, min_lag, max_lag );
+        start_lag = silk_max_int( lag - 2, min_lag );
+        end_lag   = silk_min_int( lag + 2, max_lag );
+        lag_new   = lag;                                    /* to avoid undefined lag */
+        CBimax    = 0;                                      /* to avoid undefined lag */
+
+        CCmax = silk_int32_MIN;
+        /* pitch lags according to second stage */
+        for( k = 0; k < nb_subfr; k++ ) {
+            pitch_out[ k ] = lag + 2 * silk_CB_lags_stage2[ k ][ CBimax_old ];
+        }
+
+        /* Set up codebook parameters according to complexity setting and frame length */
+        if( nb_subfr == PE_MAX_NB_SUBFR ) {
+            nb_cbk_search   = (opus_int)silk_nb_cbk_searchs_stage3[ complexity ];
+            cbk_size        = PE_NB_CBKS_STAGE3_MAX;
+            Lag_CB_ptr      = &silk_CB_lags_stage3[ 0 ][ 0 ];
+        } else {
+            nb_cbk_search   = PE_NB_CBKS_STAGE3_10MS;
+            cbk_size        = PE_NB_CBKS_STAGE3_10MS;
+            Lag_CB_ptr      = &silk_CB_lags_stage3_10_ms[ 0 ][ 0 ];
+        }
+
+        /* Calculate the correlations and energies needed in stage 3 */
+        ALLOC( energies_st3, nb_subfr * nb_cbk_search, silk_pe_stage3_vals );
+        ALLOC( cross_corr_st3, nb_subfr * nb_cbk_search, silk_pe_stage3_vals );
+        silk_P_Ana_calc_corr_st3(  cross_corr_st3, frame, start_lag, sf_length, nb_subfr, complexity, arch );
+        silk_P_Ana_calc_energy_st3( energies_st3, frame, start_lag, sf_length, nb_subfr, complexity, arch );
+
+        lag_counter = 0;
+        silk_assert( lag == silk_SAT16( lag ) );
+        contour_bias_Q15 = silk_DIV32_16( SILK_FIX_CONST( PE_FLATCONTOUR_BIAS, 15 ), lag );
+
+        target_ptr = &frame[ PE_LTP_MEM_LENGTH_MS * Fs_kHz ];
+        energy_target = silk_ADD32( silk_inner_prod_aligned( target_ptr, target_ptr, nb_subfr * sf_length, arch ), 1 );
+        for( d = start_lag; d <= end_lag; d++ ) {
+            for( j = 0; j < nb_cbk_search; j++ ) {
+                cross_corr = 0;
+                energy     = energy_target;
+                for( k = 0; k < nb_subfr; k++ ) {
+                    cross_corr = silk_ADD32( cross_corr,
+                        matrix_ptr( cross_corr_st3, k, j,
+                                    nb_cbk_search )[ lag_counter ] );
+                    energy     = silk_ADD32( energy,
+                        matrix_ptr( energies_st3, k, j,
+                                    nb_cbk_search )[ lag_counter ] );
+                    silk_assert( energy >= 0 );
+                }
+                if( cross_corr > 0 ) {
+                    CCmax_new = silk_DIV32_varQ( cross_corr, energy, 13 + 1 );          /* Q13 */
+                    /* Reduce depending on flatness of contour */
+                    diff = silk_int16_MAX - silk_MUL( contour_bias_Q15, j );            /* Q15 */
+                    silk_assert( diff == silk_SAT16( diff ) );
+                    CCmax_new = silk_SMULWB( CCmax_new, diff );                         /* Q14 */
+                } else {
+                    CCmax_new = 0;
+                }
+
+                if( CCmax_new > CCmax && ( d + silk_CB_lags_stage3[ 0 ][ j ] ) <= max_lag ) {
+                    CCmax   = CCmax_new;
+                    lag_new = d;
+                    CBimax  = j;
+                }
+            }
+            lag_counter++;
+        }
+
+        for( k = 0; k < nb_subfr; k++ ) {
+            pitch_out[ k ] = lag_new + matrix_ptr( Lag_CB_ptr, k, CBimax, cbk_size );
+            pitch_out[ k ] = silk_LIMIT( pitch_out[ k ], min_lag, PE_MAX_LAG_MS * Fs_kHz );
+        }
+        *lagIndex = (opus_int16)( lag_new - min_lag);
+        *contourIndex = (opus_int8)CBimax;
+    } else {        /* Fs_kHz == 8 */
+        /* Save Lags */
+        for( k = 0; k < nb_subfr; k++ ) {
+            pitch_out[ k ] = lag + matrix_ptr( Lag_CB_ptr, k, CBimax, cbk_size );
+            pitch_out[ k ] = silk_LIMIT( pitch_out[ k ], MIN_LAG_8KHZ, PE_MAX_LAG_MS * 8 );
+        }
+        *lagIndex = (opus_int16)( lag - MIN_LAG_8KHZ );
+        *contourIndex = (opus_int8)CBimax;
+    }
+    celt_assert( *lagIndex >= 0 );
+    /* return as voiced */
+    RESTORE_STACK;
+    return 0;
+}
+
+/***********************************************************************
+ * Calculates the correlations used in stage 3 search. In order to cover
+ * the whole lag codebook for all the searched offset lags (lag +- 2),
+ * the following correlations are needed in each sub frame:
+ *
+ * sf1: lag range [-8,...,7] total 16 correlations
+ * sf2: lag range [-4,...,4] total 9 correlations
+ * sf3: lag range [-3,....4] total 8 correltions
+ * sf4: lag range [-6,....8] total 15 correlations
+ *
+ * In total 48 correlations. The direct implementation computed in worst
+ * case 4*12*5 = 240 correlations, but more likely around 120.
+ ***********************************************************************/
+static void silk_P_Ana_calc_corr_st3(
+    silk_pe_stage3_vals cross_corr_st3[],              /* O 3 DIM correlation array */
+    const opus_int16  frame[],                         /* I vector to correlate         */
+    opus_int          start_lag,                       /* I lag offset to search around */
+    opus_int          sf_length,                       /* I length of a 5 ms subframe   */
+    opus_int          nb_subfr,                        /* I number of subframes         */
+    opus_int          complexity,                      /* I Complexity setting          */
+    int               arch                             /* I Run-time architecture       */
+)
+{
+    const opus_int16 *target_ptr;
+    opus_int   i, j, k, lag_counter, lag_low, lag_high;
+    opus_int   nb_cbk_search, delta, idx, cbk_size;
+    VARDECL( opus_int32, scratch_mem );
+    VARDECL( opus_int32, xcorr32 );
+    const opus_int8 *Lag_range_ptr, *Lag_CB_ptr;
+    SAVE_STACK;
+
+    celt_assert( complexity >= SILK_PE_MIN_COMPLEX );
+    celt_assert( complexity <= SILK_PE_MAX_COMPLEX );
+
+    if( nb_subfr == PE_MAX_NB_SUBFR ) {
+        Lag_range_ptr = &silk_Lag_range_stage3[ complexity ][ 0 ][ 0 ];
+        Lag_CB_ptr    = &silk_CB_lags_stage3[ 0 ][ 0 ];
+        nb_cbk_search = silk_nb_cbk_searchs_stage3[ complexity ];
+        cbk_size      = PE_NB_CBKS_STAGE3_MAX;
+    } else {
+        celt_assert( nb_subfr == PE_MAX_NB_SUBFR >> 1);
+        Lag_range_ptr = &silk_Lag_range_stage3_10_ms[ 0 ][ 0 ];
+        Lag_CB_ptr    = &silk_CB_lags_stage3_10_ms[ 0 ][ 0 ];
+        nb_cbk_search = PE_NB_CBKS_STAGE3_10MS;
+        cbk_size      = PE_NB_CBKS_STAGE3_10MS;
+    }
+    ALLOC( scratch_mem, SCRATCH_SIZE, opus_int32 );
+    ALLOC( xcorr32, SCRATCH_SIZE, opus_int32 );
+
+    target_ptr = &frame[ silk_LSHIFT( sf_length, 2 ) ]; /* Pointer to middle of frame */
+    for( k = 0; k < nb_subfr; k++ ) {
+        lag_counter = 0;
+
+        /* Calculate the correlations for each subframe */
+        lag_low  = matrix_ptr( Lag_range_ptr, k, 0, 2 );
+        lag_high = matrix_ptr( Lag_range_ptr, k, 1, 2 );
+        celt_assert(lag_high-lag_low+1 <= SCRATCH_SIZE);
+        celt_pitch_xcorr( target_ptr, target_ptr - start_lag - lag_high, xcorr32, sf_length, lag_high - lag_low + 1, arch );
+        for( j = lag_low; j <= lag_high; j++ ) {
+            silk_assert( lag_counter < SCRATCH_SIZE );
+            scratch_mem[ lag_counter ] = xcorr32[ lag_high - j ];
+            lag_counter++;
+        }
+
+        delta = matrix_ptr( Lag_range_ptr, k, 0, 2 );
+        for( i = 0; i < nb_cbk_search; i++ ) {
+            /* Fill out the 3 dim array that stores the correlations for */
+            /* each code_book vector for each start lag */
+            idx = matrix_ptr( Lag_CB_ptr, k, i, cbk_size ) - delta;
+            for( j = 0; j < PE_NB_STAGE3_LAGS; j++ ) {
+                silk_assert( idx + j < SCRATCH_SIZE );
+                silk_assert( idx + j < lag_counter );
+                matrix_ptr( cross_corr_st3, k, i, nb_cbk_search )[ j ] =
+                    scratch_mem[ idx + j ];
+            }
+        }
+        target_ptr += sf_length;
+    }
+    RESTORE_STACK;
+}
+
+/********************************************************************/
+/* Calculate the energies for first two subframes. The energies are */
+/* calculated recursively.                                          */
+/********************************************************************/
+static void silk_P_Ana_calc_energy_st3(
+    silk_pe_stage3_vals energies_st3[],                 /* O 3 DIM energy array */
+    const opus_int16  frame[],                          /* I vector to calc energy in    */
+    opus_int          start_lag,                        /* I lag offset to search around */
+    opus_int          sf_length,                        /* I length of one 5 ms subframe */
+    opus_int          nb_subfr,                         /* I number of subframes         */
+    opus_int          complexity,                       /* I Complexity setting          */
+    int               arch                              /* I Run-time architecture       */
+)
+{
+    const opus_int16 *target_ptr, *basis_ptr;
+    opus_int32 energy;
+    opus_int   k, i, j, lag_counter;
+    opus_int   nb_cbk_search, delta, idx, cbk_size, lag_diff;
+    VARDECL( opus_int32, scratch_mem );
+    const opus_int8 *Lag_range_ptr, *Lag_CB_ptr;
+    SAVE_STACK;
+
+    celt_assert( complexity >= SILK_PE_MIN_COMPLEX );
+    celt_assert( complexity <= SILK_PE_MAX_COMPLEX );
+
+    if( nb_subfr == PE_MAX_NB_SUBFR ) {
+        Lag_range_ptr = &silk_Lag_range_stage3[ complexity ][ 0 ][ 0 ];
+        Lag_CB_ptr    = &silk_CB_lags_stage3[ 0 ][ 0 ];
+        nb_cbk_search = silk_nb_cbk_searchs_stage3[ complexity ];
+        cbk_size      = PE_NB_CBKS_STAGE3_MAX;
+    } else {
+        celt_assert( nb_subfr == PE_MAX_NB_SUBFR >> 1);
+        Lag_range_ptr = &silk_Lag_range_stage3_10_ms[ 0 ][ 0 ];
+        Lag_CB_ptr    = &silk_CB_lags_stage3_10_ms[ 0 ][ 0 ];
+        nb_cbk_search = PE_NB_CBKS_STAGE3_10MS;
+        cbk_size      = PE_NB_CBKS_STAGE3_10MS;
+    }
+    ALLOC( scratch_mem, SCRATCH_SIZE, opus_int32 );
+
+    target_ptr = &frame[ silk_LSHIFT( sf_length, 2 ) ];
+    for( k = 0; k < nb_subfr; k++ ) {
+        lag_counter = 0;
+
+        /* Calculate the energy for first lag */
+        basis_ptr = target_ptr - ( start_lag + matrix_ptr( Lag_range_ptr, k, 0, 2 ) );
+        energy = silk_inner_prod_aligned( basis_ptr, basis_ptr, sf_length, arch );
+        silk_assert( energy >= 0 );
+        scratch_mem[ lag_counter ] = energy;
+        lag_counter++;
+
+        lag_diff = ( matrix_ptr( Lag_range_ptr, k, 1, 2 ) -  matrix_ptr( Lag_range_ptr, k, 0, 2 ) + 1 );
+        for( i = 1; i < lag_diff; i++ ) {
+            /* remove part outside new window */
+            energy -= silk_SMULBB( basis_ptr[ sf_length - i ], basis_ptr[ sf_length - i ] );
+            silk_assert( energy >= 0 );
+
+            /* add part that comes into window */
+            energy = silk_ADD_SAT32( energy, silk_SMULBB( basis_ptr[ -i ], basis_ptr[ -i ] ) );
+            silk_assert( energy >= 0 );
+            silk_assert( lag_counter < SCRATCH_SIZE );
+            scratch_mem[ lag_counter ] = energy;
+            lag_counter++;
+        }
+
+        delta = matrix_ptr( Lag_range_ptr, k, 0, 2 );
+        for( i = 0; i < nb_cbk_search; i++ ) {
+            /* Fill out the 3 dim array that stores the correlations for    */
+            /* each code_book vector for each start lag                     */
+            idx = matrix_ptr( Lag_CB_ptr, k, i, cbk_size ) - delta;
+            for( j = 0; j < PE_NB_STAGE3_LAGS; j++ ) {
+                silk_assert( idx + j < SCRATCH_SIZE );
+                silk_assert( idx + j < lag_counter );
+                matrix_ptr( energies_st3, k, i, nb_cbk_search )[ j ] =
+                    scratch_mem[ idx + j ];
+                silk_assert(
+                    matrix_ptr( energies_st3, k, i, nb_cbk_search )[ j ] >= 0 );
+            }
+        }
+        target_ptr += sf_length;
+    }
+    RESTORE_STACK;
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/process_gains_FIX.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/process_gains_FIX.c
new file mode 100644 (file)
index 0000000..05aba31
--- /dev/null
@@ -0,0 +1,117 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main_FIX.h"
+#include "tuning_parameters.h"
+
+/* Processing of gains */
+void silk_process_gains_FIX(
+    silk_encoder_state_FIX          *psEnc,                                 /* I/O  Encoder state                                                               */
+    silk_encoder_control_FIX        *psEncCtrl,                             /* I/O  Encoder control                                                             */
+    opus_int                        condCoding                              /* I    The type of conditional coding to use                                       */
+)
+{
+    silk_shape_state_FIX *psShapeSt = &psEnc->sShape;
+    opus_int     k;
+    opus_int32   s_Q16, InvMaxSqrVal_Q16, gain, gain_squared, ResNrg, ResNrgPart, quant_offset_Q10;
+
+    /* Gain reduction when LTP coding gain is high */
+    if( psEnc->sCmn.indices.signalType == TYPE_VOICED ) {
+        /*s = -0.5f * silk_sigmoid( 0.25f * ( psEncCtrl->LTPredCodGain - 12.0f ) ); */
+        s_Q16 = -silk_sigm_Q15( silk_RSHIFT_ROUND( psEncCtrl->LTPredCodGain_Q7 - SILK_FIX_CONST( 12.0, 7 ), 4 ) );
+        for( k = 0; k < psEnc->sCmn.nb_subfr; k++ ) {
+            psEncCtrl->Gains_Q16[ k ] = silk_SMLAWB( psEncCtrl->Gains_Q16[ k ], psEncCtrl->Gains_Q16[ k ], s_Q16 );
+        }
+    }
+
+    /* Limit the quantized signal */
+    /* InvMaxSqrVal = pow( 2.0f, 0.33f * ( 21.0f - SNR_dB ) ) / subfr_length; */
+    InvMaxSqrVal_Q16 = silk_DIV32_16( silk_log2lin(
+        silk_SMULWB( SILK_FIX_CONST( 21 + 16 / 0.33, 7 ) - psEnc->sCmn.SNR_dB_Q7, SILK_FIX_CONST( 0.33, 16 ) ) ), psEnc->sCmn.subfr_length );
+
+    for( k = 0; k < psEnc->sCmn.nb_subfr; k++ ) {
+        /* Soft limit on ratio residual energy and squared gains */
+        ResNrg     = psEncCtrl->ResNrg[ k ];
+        ResNrgPart = silk_SMULWW( ResNrg, InvMaxSqrVal_Q16 );
+        if( psEncCtrl->ResNrgQ[ k ] > 0 ) {
+            ResNrgPart = silk_RSHIFT_ROUND( ResNrgPart, psEncCtrl->ResNrgQ[ k ] );
+        } else {
+            if( ResNrgPart >= silk_RSHIFT( silk_int32_MAX, -psEncCtrl->ResNrgQ[ k ] ) ) {
+                ResNrgPart = silk_int32_MAX;
+            } else {
+                ResNrgPart = silk_LSHIFT( ResNrgPart, -psEncCtrl->ResNrgQ[ k ] );
+            }
+        }
+        gain = psEncCtrl->Gains_Q16[ k ];
+        gain_squared = silk_ADD_SAT32( ResNrgPart, silk_SMMUL( gain, gain ) );
+        if( gain_squared < silk_int16_MAX ) {
+            /* recalculate with higher precision */
+            gain_squared = silk_SMLAWW( silk_LSHIFT( ResNrgPart, 16 ), gain, gain );
+            silk_assert( gain_squared > 0 );
+            gain = silk_SQRT_APPROX( gain_squared );                    /* Q8   */
+            gain = silk_min( gain, silk_int32_MAX >> 8 );
+            psEncCtrl->Gains_Q16[ k ] = silk_LSHIFT_SAT32( gain, 8 );   /* Q16  */
+        } else {
+            gain = silk_SQRT_APPROX( gain_squared );                    /* Q0   */
+            gain = silk_min( gain, silk_int32_MAX >> 16 );
+            psEncCtrl->Gains_Q16[ k ] = silk_LSHIFT_SAT32( gain, 16 );  /* Q16  */
+        }
+    }
+
+    /* Save unquantized gains and gain Index */
+    silk_memcpy( psEncCtrl->GainsUnq_Q16, psEncCtrl->Gains_Q16, psEnc->sCmn.nb_subfr * sizeof( opus_int32 ) );
+    psEncCtrl->lastGainIndexPrev = psShapeSt->LastGainIndex;
+
+    /* Quantize gains */
+    silk_gains_quant( psEnc->sCmn.indices.GainsIndices, psEncCtrl->Gains_Q16,
+        &psShapeSt->LastGainIndex, condCoding == CODE_CONDITIONALLY, psEnc->sCmn.nb_subfr );
+
+    /* Set quantizer offset for voiced signals. Larger offset when LTP coding gain is low or tilt is high (ie low-pass) */
+    if( psEnc->sCmn.indices.signalType == TYPE_VOICED ) {
+        if( psEncCtrl->LTPredCodGain_Q7 + silk_RSHIFT( psEnc->sCmn.input_tilt_Q15, 8 ) > SILK_FIX_CONST( 1.0, 7 ) ) {
+            psEnc->sCmn.indices.quantOffsetType = 0;
+        } else {
+            psEnc->sCmn.indices.quantOffsetType = 1;
+        }
+    }
+
+    /* Quantizer boundary adjustment */
+    quant_offset_Q10 = silk_Quantization_Offsets_Q10[ psEnc->sCmn.indices.signalType >> 1 ][ psEnc->sCmn.indices.quantOffsetType ];
+    psEncCtrl->Lambda_Q10 = SILK_FIX_CONST( LAMBDA_OFFSET, 10 )
+                          + silk_SMULBB( SILK_FIX_CONST( LAMBDA_DELAYED_DECISIONS, 10 ), psEnc->sCmn.nStatesDelayedDecision )
+                          + silk_SMULWB( SILK_FIX_CONST( LAMBDA_SPEECH_ACT,        18 ), psEnc->sCmn.speech_activity_Q8     )
+                          + silk_SMULWB( SILK_FIX_CONST( LAMBDA_INPUT_QUALITY,     12 ), psEncCtrl->input_quality_Q14       )
+                          + silk_SMULWB( SILK_FIX_CONST( LAMBDA_CODING_QUALITY,    12 ), psEncCtrl->coding_quality_Q14      )
+                          + silk_SMULWB( SILK_FIX_CONST( LAMBDA_QUANT_OFFSET,      16 ), quant_offset_Q10                   );
+
+    silk_assert( psEncCtrl->Lambda_Q10 > 0 );
+    silk_assert( psEncCtrl->Lambda_Q10 < SILK_FIX_CONST( 2, 10 ) );
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/regularize_correlations_FIX.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/regularize_correlations_FIX.c
new file mode 100644 (file)
index 0000000..a2836b0
--- /dev/null
@@ -0,0 +1,47 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main_FIX.h"
+
+/* Add noise to matrix diagonal */
+void silk_regularize_correlations_FIX(
+    opus_int32                      *XX,                                    /* I/O  Correlation matrices                                                        */
+    opus_int32                      *xx,                                    /* I/O  Correlation values                                                          */
+    opus_int32                      noise,                                  /* I    Noise to add                                                                */
+    opus_int                        D                                       /* I    Dimension of XX                                                             */
+)
+{
+    opus_int i;
+    for( i = 0; i < D; i++ ) {
+        matrix_ptr( &XX[ 0 ], i, i, D ) = silk_ADD32( matrix_ptr( &XX[ 0 ], i, i, D ), noise );
+    }
+    xx[ 0 ] += noise;
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/residual_energy16_FIX.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/residual_energy16_FIX.c
new file mode 100644 (file)
index 0000000..7f130f3
--- /dev/null
@@ -0,0 +1,103 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main_FIX.h"
+
+/* Residual energy: nrg = wxx - 2 * wXx * c + c' * wXX * c */
+opus_int32 silk_residual_energy16_covar_FIX(
+    const opus_int16                *c,                                     /* I    Prediction vector                                                           */
+    const opus_int32                *wXX,                                   /* I    Correlation matrix                                                          */
+    const opus_int32                *wXx,                                   /* I    Correlation vector                                                          */
+    opus_int32                      wxx,                                    /* I    Signal energy                                                               */
+    opus_int                        D,                                      /* I    Dimension                                                                   */
+    opus_int                        cQ                                      /* I    Q value for c vector 0 - 15                                                 */
+)
+{
+    opus_int   i, j, lshifts, Qxtra;
+    opus_int32 c_max, w_max, tmp, tmp2, nrg;
+    opus_int   cn[ MAX_MATRIX_SIZE ];
+    const opus_int32 *pRow;
+
+    /* Safety checks */
+    celt_assert( D >=  0 );
+    celt_assert( D <= 16 );
+    celt_assert( cQ >  0 );
+    celt_assert( cQ < 16 );
+
+    lshifts = 16 - cQ;
+    Qxtra = lshifts;
+
+    c_max = 0;
+    for( i = 0; i < D; i++ ) {
+        c_max = silk_max_32( c_max, silk_abs( (opus_int32)c[ i ] ) );
+    }
+    Qxtra = silk_min_int( Qxtra, silk_CLZ32( c_max ) - 17 );
+
+    w_max = silk_max_32( wXX[ 0 ], wXX[ D * D - 1 ] );
+    Qxtra = silk_min_int( Qxtra, silk_CLZ32( silk_MUL( D, silk_RSHIFT( silk_SMULWB( w_max, c_max ), 4 ) ) ) - 5 );
+    Qxtra = silk_max_int( Qxtra, 0 );
+    for( i = 0; i < D; i++ ) {
+        cn[ i ] = silk_LSHIFT( ( opus_int )c[ i ], Qxtra );
+        silk_assert( silk_abs(cn[i]) <= ( silk_int16_MAX + 1 ) ); /* Check that silk_SMLAWB can be used */
+    }
+    lshifts -= Qxtra;
+
+    /* Compute wxx - 2 * wXx * c */
+    tmp = 0;
+    for( i = 0; i < D; i++ ) {
+        tmp = silk_SMLAWB( tmp, wXx[ i ], cn[ i ] );
+    }
+    nrg = silk_RSHIFT( wxx, 1 + lshifts ) - tmp;                         /* Q: -lshifts - 1 */
+
+    /* Add c' * wXX * c, assuming wXX is symmetric */
+    tmp2 = 0;
+    for( i = 0; i < D; i++ ) {
+        tmp = 0;
+        pRow = &wXX[ i * D ];
+        for( j = i + 1; j < D; j++ ) {
+            tmp = silk_SMLAWB( tmp, pRow[ j ], cn[ j ] );
+        }
+        tmp  = silk_SMLAWB( tmp,  silk_RSHIFT( pRow[ i ], 1 ), cn[ i ] );
+        tmp2 = silk_SMLAWB( tmp2, tmp,                        cn[ i ] );
+    }
+    nrg = silk_ADD_LSHIFT32( nrg, tmp2, lshifts );                       /* Q: -lshifts - 1 */
+
+    /* Keep one bit free always, because we add them for LSF interpolation */
+    if( nrg < 1 ) {
+        nrg = 1;
+    } else if( nrg > silk_RSHIFT( silk_int32_MAX, lshifts + 2 ) ) {
+        nrg = silk_int32_MAX >> 1;
+    } else {
+        nrg = silk_LSHIFT( nrg, lshifts + 1 );                           /* Q0 */
+    }
+    return nrg;
+
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/residual_energy_FIX.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/residual_energy_FIX.c
new file mode 100644 (file)
index 0000000..6c7cade
--- /dev/null
@@ -0,0 +1,98 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main_FIX.h"
+#include "stack_alloc.h"
+
+/* Calculates residual energies of input subframes where all subframes have LPC_order   */
+/* of preceding samples                                                                 */
+void silk_residual_energy_FIX(
+          opus_int32                nrgs[ MAX_NB_SUBFR ],                   /* O    Residual energy per subframe                                                */
+          opus_int                  nrgsQ[ MAX_NB_SUBFR ],                  /* O    Q value per subframe                                                        */
+    const opus_int16                x[],                                    /* I    Input signal                                                                */
+          opus_int16                a_Q12[ 2 ][ MAX_LPC_ORDER ],            /* I    AR coefs for each frame half                                                */
+    const opus_int32                gains[ MAX_NB_SUBFR ],                  /* I    Quantization gains                                                          */
+    const opus_int                  subfr_length,                           /* I    Subframe length                                                             */
+    const opus_int                  nb_subfr,                               /* I    Number of subframes                                                         */
+    const opus_int                  LPC_order,                              /* I    LPC order                                                                   */
+          int                       arch                                    /* I    Run-time architecture                                                       */
+)
+{
+    opus_int         offset, i, j, rshift, lz1, lz2;
+    opus_int16       *LPC_res_ptr;
+    VARDECL( opus_int16, LPC_res );
+    const opus_int16 *x_ptr;
+    opus_int32       tmp32;
+    SAVE_STACK;
+
+    x_ptr  = x;
+    offset = LPC_order + subfr_length;
+
+    /* Filter input to create the LPC residual for each frame half, and measure subframe energies */
+    ALLOC( LPC_res, ( MAX_NB_SUBFR >> 1 ) * offset, opus_int16 );
+    celt_assert( ( nb_subfr >> 1 ) * ( MAX_NB_SUBFR >> 1 ) == nb_subfr );
+    for( i = 0; i < nb_subfr >> 1; i++ ) {
+        /* Calculate half frame LPC residual signal including preceding samples */
+        silk_LPC_analysis_filter( LPC_res, x_ptr, a_Q12[ i ], ( MAX_NB_SUBFR >> 1 ) * offset, LPC_order, arch );
+
+        /* Point to first subframe of the just calculated LPC residual signal */
+        LPC_res_ptr = LPC_res + LPC_order;
+        for( j = 0; j < ( MAX_NB_SUBFR >> 1 ); j++ ) {
+            /* Measure subframe energy */
+            silk_sum_sqr_shift( &nrgs[ i * ( MAX_NB_SUBFR >> 1 ) + j ], &rshift, LPC_res_ptr, subfr_length );
+
+            /* Set Q values for the measured energy */
+            nrgsQ[ i * ( MAX_NB_SUBFR >> 1 ) + j ] = -rshift;
+
+            /* Move to next subframe */
+            LPC_res_ptr += offset;
+        }
+        /* Move to next frame half */
+        x_ptr += ( MAX_NB_SUBFR >> 1 ) * offset;
+    }
+
+    /* Apply the squared subframe gains */
+    for( i = 0; i < nb_subfr; i++ ) {
+        /* Fully upscale gains and energies */
+        lz1 = silk_CLZ32( nrgs[  i ] ) - 1;
+        lz2 = silk_CLZ32( gains[ i ] ) - 1;
+
+        tmp32 = silk_LSHIFT32( gains[ i ], lz2 );
+
+        /* Find squared gains */
+        tmp32 = silk_SMMUL( tmp32, tmp32 ); /* Q( 2 * lz2 - 32 )*/
+
+        /* Scale energies */
+        nrgs[ i ] = silk_SMMUL( tmp32, silk_LSHIFT32( nrgs[ i ], lz1 ) ); /* Q( nrgsQ[ i ] + lz1 + 2 * lz2 - 32 - 32 )*/
+        nrgsQ[ i ] += lz1 + 2 * lz2 - 32 - 32;
+    }
+    RESTORE_STACK;
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/schur64_FIX.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/schur64_FIX.c
new file mode 100644 (file)
index 0000000..4b7e19e
--- /dev/null
@@ -0,0 +1,93 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "SigProc_FIX.h"
+
+/* Slower than schur(), but more accurate.                              */
+/* Uses SMULL(), available on armv4                                     */
+opus_int32 silk_schur64(                            /* O    returns residual energy                                     */
+    opus_int32                  rc_Q16[],           /* O    Reflection coefficients [order] Q16                         */
+    const opus_int32            c[],                /* I    Correlations [order+1]                                      */
+    opus_int32                  order               /* I    Prediction order                                            */
+)
+{
+    opus_int   k, n;
+    opus_int32 C[ SILK_MAX_ORDER_LPC + 1 ][ 2 ];
+    opus_int32 Ctmp1_Q30, Ctmp2_Q30, rc_tmp_Q31;
+
+    celt_assert( order >= 0 && order <= SILK_MAX_ORDER_LPC );
+
+    /* Check for invalid input */
+    if( c[ 0 ] <= 0 ) {
+        silk_memset( rc_Q16, 0, order * sizeof( opus_int32 ) );
+        return 0;
+    }
+
+    k = 0;
+    do {
+        C[ k ][ 0 ] = C[ k ][ 1 ] = c[ k ];
+    } while( ++k <= order );
+
+    for( k = 0; k < order; k++ ) {
+        /* Check that we won't be getting an unstable rc, otherwise stop here. */
+        if (silk_abs_int32(C[ k + 1 ][ 0 ]) >= C[ 0 ][ 1 ]) {
+           if ( C[ k + 1 ][ 0 ] > 0 ) {
+              rc_Q16[ k ] = -SILK_FIX_CONST( .99f, 16 );
+           } else {
+              rc_Q16[ k ] = SILK_FIX_CONST( .99f, 16 );
+           }
+           k++;
+           break;
+        }
+
+        /* Get reflection coefficient: divide two Q30 values and get result in Q31 */
+        rc_tmp_Q31 = silk_DIV32_varQ( -C[ k + 1 ][ 0 ], C[ 0 ][ 1 ], 31 );
+
+        /* Save the output */
+        rc_Q16[ k ] = silk_RSHIFT_ROUND( rc_tmp_Q31, 15 );
+
+        /* Update correlations */
+        for( n = 0; n < order - k; n++ ) {
+            Ctmp1_Q30 = C[ n + k + 1 ][ 0 ];
+            Ctmp2_Q30 = C[ n ][ 1 ];
+
+            /* Multiply and add the highest int32 */
+            C[ n + k + 1 ][ 0 ] = Ctmp1_Q30 + silk_SMMUL( silk_LSHIFT( Ctmp2_Q30, 1 ), rc_tmp_Q31 );
+            C[ n ][ 1 ]         = Ctmp2_Q30 + silk_SMMUL( silk_LSHIFT( Ctmp1_Q30, 1 ), rc_tmp_Q31 );
+        }
+    }
+
+    for(; k < order; k++ ) {
+       rc_Q16[ k ] = 0;
+    }
+
+    return silk_max_32( 1, C[ 0 ][ 1 ] );
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/schur_FIX.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/schur_FIX.c
new file mode 100644 (file)
index 0000000..2840f6b
--- /dev/null
@@ -0,0 +1,107 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "SigProc_FIX.h"
+
+/* Faster than schur64(), but much less accurate.                       */
+/* uses SMLAWB(), requiring armv5E and higher.                          */
+opus_int32 silk_schur(                              /* O    Returns residual energy                                     */
+    opus_int16                  *rc_Q15,            /* O    reflection coefficients [order] Q15                         */
+    const opus_int32            *c,                 /* I    correlations [order+1]                                      */
+    const opus_int32            order               /* I    prediction order                                            */
+)
+{
+    opus_int        k, n, lz;
+    opus_int32    C[ SILK_MAX_ORDER_LPC + 1 ][ 2 ];
+    opus_int32    Ctmp1, Ctmp2, rc_tmp_Q15;
+
+    celt_assert( order >= 0 && order <= SILK_MAX_ORDER_LPC );
+
+    /* Get number of leading zeros */
+    lz = silk_CLZ32( c[ 0 ] );
+
+    /* Copy correlations and adjust level to Q30 */
+    k = 0;
+    if( lz < 2 ) {
+        /* lz must be 1, so shift one to the right */
+        do {
+            C[ k ][ 0 ] = C[ k ][ 1 ] = silk_RSHIFT( c[ k ], 1 );
+        } while( ++k <= order );
+    } else if( lz > 2 ) {
+        /* Shift to the left */
+        lz -= 2;
+        do {
+            C[ k ][ 0 ] = C[ k ][ 1 ] = silk_LSHIFT( c[ k ], lz );
+        } while( ++k <= order );
+    } else {
+        /* No need to shift */
+        do {
+            C[ k ][ 0 ] = C[ k ][ 1 ] = c[ k ];
+        } while( ++k <= order );
+    }
+
+    for( k = 0; k < order; k++ ) {
+        /* Check that we won't be getting an unstable rc, otherwise stop here. */
+        if (silk_abs_int32(C[ k + 1 ][ 0 ]) >= C[ 0 ][ 1 ]) {
+           if ( C[ k + 1 ][ 0 ] > 0 ) {
+              rc_Q15[ k ] = -SILK_FIX_CONST( .99f, 15 );
+           } else {
+              rc_Q15[ k ] = SILK_FIX_CONST( .99f, 15 );
+           }
+           k++;
+           break;
+        }
+
+        /* Get reflection coefficient */
+        rc_tmp_Q15 = -silk_DIV32_16( C[ k + 1 ][ 0 ], silk_max_32( silk_RSHIFT( C[ 0 ][ 1 ], 15 ), 1 ) );
+
+        /* Clip (shouldn't happen for properly conditioned inputs) */
+        rc_tmp_Q15 = silk_SAT16( rc_tmp_Q15 );
+
+        /* Store */
+        rc_Q15[ k ] = (opus_int16)rc_tmp_Q15;
+
+        /* Update correlations */
+        for( n = 0; n < order - k; n++ ) {
+            Ctmp1 = C[ n + k + 1 ][ 0 ];
+            Ctmp2 = C[ n ][ 1 ];
+            C[ n + k + 1 ][ 0 ] = silk_SMLAWB( Ctmp1, silk_LSHIFT( Ctmp2, 1 ), rc_tmp_Q15 );
+            C[ n ][ 1 ]         = silk_SMLAWB( Ctmp2, silk_LSHIFT( Ctmp1, 1 ), rc_tmp_Q15 );
+        }
+    }
+
+    for(; k < order; k++ ) {
+       rc_Q15[ k ] = 0;
+    }
+
+    /* return residual energy */
+    return silk_max_32( 1, C[ 0 ][ 1 ] );
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/structs_FIX.h b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/structs_FIX.h
new file mode 100644 (file)
index 0000000..42920d6
--- /dev/null
@@ -0,0 +1,109 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifndef SILK_STRUCTS_FIX_H
+#define SILK_STRUCTS_FIX_H
+
+#include "typedef.h"
+#include "main.h"
+#include "structs.h"
+
+
+/********************************/
+/* Noise shaping analysis state */
+/********************************/
+typedef struct {
+    opus_int8                   LastGainIndex;
+    opus_int32                  HarmBoost_smth_Q16;
+    opus_int32                  HarmShapeGain_smth_Q16;
+    opus_int32                  Tilt_smth_Q16;
+} silk_shape_state_FIX;
+
+/********************************/
+/* Encoder state FIX            */
+/********************************/
+typedef struct {
+    silk_encoder_state          sCmn;                                   /* Common struct, shared with floating-point code       */
+    silk_shape_state_FIX        sShape;                                 /* Shape state                                          */
+
+    /* Buffer for find pitch and noise shape analysis */
+    silk_DWORD_ALIGN opus_int16 x_buf[ 2 * MAX_FRAME_LENGTH + LA_SHAPE_MAX ];/* Buffer for find pitch and noise shape analysis  */
+    opus_int                    LTPCorr_Q15;                            /* Normalized correlation from pitch lag estimator      */
+    opus_int32                    resNrgSmth;
+} silk_encoder_state_FIX;
+
+/************************/
+/* Encoder control FIX  */
+/************************/
+typedef struct {
+    /* Prediction and coding parameters */
+    opus_int32                  Gains_Q16[ MAX_NB_SUBFR ];
+    silk_DWORD_ALIGN opus_int16 PredCoef_Q12[ 2 ][ MAX_LPC_ORDER ];
+    opus_int16                  LTPCoef_Q14[ LTP_ORDER * MAX_NB_SUBFR ];
+    opus_int                    LTP_scale_Q14;
+    opus_int                    pitchL[ MAX_NB_SUBFR ];
+
+    /* Noise shaping parameters */
+    /* Testing */
+    silk_DWORD_ALIGN opus_int16 AR_Q13[ MAX_NB_SUBFR * MAX_SHAPE_LPC_ORDER ];
+    opus_int32                  LF_shp_Q14[        MAX_NB_SUBFR ];      /* Packs two int16 coefficients per int32 value         */
+    opus_int                    Tilt_Q14[          MAX_NB_SUBFR ];
+    opus_int                    HarmShapeGain_Q14[ MAX_NB_SUBFR ];
+    opus_int                    Lambda_Q10;
+    opus_int                    input_quality_Q14;
+    opus_int                    coding_quality_Q14;
+
+    /* measures */
+    opus_int32                  predGain_Q16;
+    opus_int                    LTPredCodGain_Q7;
+    opus_int32                  ResNrg[ MAX_NB_SUBFR ];                 /* Residual energy per subframe                         */
+    opus_int                    ResNrgQ[ MAX_NB_SUBFR ];                /* Q domain for the residual energy > 0                 */
+
+    /* Parameters for CBR mode */
+    opus_int32                  GainsUnq_Q16[ MAX_NB_SUBFR ];
+    opus_int8                   lastGainIndexPrev;
+} silk_encoder_control_FIX;
+
+/************************/
+/* Encoder Super Struct */
+/************************/
+typedef struct {
+    stereo_enc_state            sStereo;
+    opus_int32                  nBitsUsedLBRR;
+    opus_int32                  nBitsExceeded;
+    opus_int                    nChannelsAPI;
+    opus_int                    nChannelsInternal;
+    opus_int                    nPrevChannelsInternal;
+    opus_int                    timeSinceSwitchAllowed_ms;
+    opus_int                    allowBandwidthSwitch;
+    opus_int                    prev_decode_only_middle;
+    /* This needs to be last so we can skip the second state for mono. */
+    silk_encoder_state_FIX      state_Fxx[ ENCODER_NUM_CHANNELS ];
+} silk_encoder;
+
+
+#endif
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/vector_ops_FIX.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/vector_ops_FIX.c
new file mode 100644 (file)
index 0000000..dcf8407
--- /dev/null
@@ -0,0 +1,102 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "SigProc_FIX.h"
+#include "pitch.h"
+
+/* Copy and multiply a vector by a constant */
+void silk_scale_copy_vector16(
+    opus_int16                  *data_out,
+    const opus_int16            *data_in,
+    opus_int32                  gain_Q16,           /* I    Gain in Q16                                                 */
+    const opus_int              dataSize            /* I    Length                                                      */
+)
+{
+    opus_int  i;
+    opus_int32 tmp32;
+
+    for( i = 0; i < dataSize; i++ ) {
+        tmp32 = silk_SMULWB( gain_Q16, data_in[ i ] );
+        data_out[ i ] = (opus_int16)silk_CHECK_FIT16( tmp32 );
+    }
+}
+
+/* Multiply a vector by a constant */
+void silk_scale_vector32_Q26_lshift_18(
+    opus_int32                  *data1,             /* I/O  Q0/Q18                                                      */
+    opus_int32                  gain_Q26,           /* I    Q26                                                         */
+    opus_int                    dataSize            /* I    length                                                      */
+)
+{
+    opus_int  i;
+
+    for( i = 0; i < dataSize; i++ ) {
+        data1[ i ] = (opus_int32)silk_CHECK_FIT32( silk_RSHIFT64( silk_SMULL( data1[ i ], gain_Q26 ), 8 ) );    /* OUTPUT: Q18 */
+    }
+}
+
+/* sum = for(i=0;i<len;i++)inVec1[i]*inVec2[i];      ---        inner product   */
+/* Note for ARM asm:                                                            */
+/*        * inVec1 and inVec2 should be at least 2 byte aligned.                */
+/*        * len should be positive 16bit integer.                               */
+/*        * only when len>6, memory access can be reduced by half.              */
+opus_int32 silk_inner_prod_aligned(
+    const opus_int16 *const     inVec1,             /*    I input vector 1                                              */
+    const opus_int16 *const     inVec2,             /*    I input vector 2                                              */
+    const opus_int              len,                /*    I vector lengths                                              */
+    int                         arch                /*    I Run-time architecture                                       */
+)
+{
+#ifdef FIXED_POINT
+   return celt_inner_prod(inVec1, inVec2, len, arch);
+#else
+    opus_int   i;
+    opus_int32 sum = 0;
+    for( i = 0; i < len; i++ ) {
+        sum = silk_SMLABB( sum, inVec1[ i ], inVec2[ i ] );
+    }
+    return sum;
+#endif
+}
+
+opus_int64 silk_inner_prod16_c(
+    const opus_int16            *inVec1,            /*    I input vector 1                                              */
+    const opus_int16            *inVec2,            /*    I input vector 2                                              */
+    const opus_int              len                 /*    I vector lengths                                              */
+)
+{
+    opus_int   i;
+    opus_int64 sum = 0;
+    for( i = 0; i < len; i++ ) {
+        sum = silk_SMLALBB( sum, inVec1[ i ], inVec2[ i ] );
+    }
+    return sum;
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/vector_ops_FIX_sse4_1.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/vector_ops_FIX_sse4_1.c
new file mode 100644 (file)
index 0000000..deea857
--- /dev/null
@@ -0,0 +1,93 @@
+/* Copyright (c) 2014, Cisco Systems, INC
+   Written by XiangMingZhu WeiZhou MinPeng YanWang
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+   - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+   - Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+   OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <xmmintrin.h>
+#include <emmintrin.h>
+#include <smmintrin.h>
+#include "main.h"
+
+#include "SigProc_FIX.h"
+#include "pitch.h"
+#include "celt/x86/x86cpu.h"
+
+opus_int64 silk_inner_prod16_sse4_1(
+    const opus_int16            *inVec1,            /*    I input vector 1                                              */
+    const opus_int16            *inVec2,            /*    I input vector 2                                              */
+    const opus_int              len                 /*    I vector lengths                                              */
+)
+{
+    opus_int  i, dataSize4;
+    opus_int64 sum;
+
+    __m128i xmm_prod_20, xmm_prod_31;
+    __m128i inVec1_3210, acc1;
+    __m128i inVec2_3210, acc2;
+
+    sum = 0;
+    dataSize4 = len & ~3;
+
+    acc1 = _mm_setzero_si128();
+    acc2 = _mm_setzero_si128();
+
+    for( i = 0; i < dataSize4; i += 4 ) {
+        inVec1_3210 = OP_CVTEPI16_EPI32_M64( &inVec1[i + 0] );
+        inVec2_3210 = OP_CVTEPI16_EPI32_M64( &inVec2[i + 0] );
+        xmm_prod_20 = _mm_mul_epi32( inVec1_3210, inVec2_3210 );
+
+        inVec1_3210 = _mm_shuffle_epi32( inVec1_3210, _MM_SHUFFLE( 0, 3, 2, 1 ) );
+        inVec2_3210 = _mm_shuffle_epi32( inVec2_3210, _MM_SHUFFLE( 0, 3, 2, 1 ) );
+        xmm_prod_31 = _mm_mul_epi32( inVec1_3210, inVec2_3210 );
+
+        acc1 = _mm_add_epi64( acc1, xmm_prod_20 );
+        acc2 = _mm_add_epi64( acc2, xmm_prod_31 );
+    }
+
+    acc1 = _mm_add_epi64( acc1, acc2 );
+
+    /* equal shift right 8 bytes */
+    acc2 = _mm_shuffle_epi32( acc1, _MM_SHUFFLE( 0, 0, 3, 2 ) );
+    acc1 = _mm_add_epi64( acc1, acc2 );
+
+    _mm_storel_epi64( (__m128i *)(void *)&sum, acc1 );
+
+    for( ; i < len; i++ ) {
+        sum = silk_SMLALBB( sum, inVec1[ i ], inVec2[ i ] );
+    }
+
+#ifdef OPUS_CHECK_ASM
+    {
+        opus_int64 sum_c = silk_inner_prod16_c( inVec1, inVec2, len );
+        silk_assert( sum == sum_c );
+    }
+#endif
+
+    return sum;
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/warped_autocorrelation_FIX.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/warped_autocorrelation_FIX.c
new file mode 100644 (file)
index 0000000..00bd9ff
--- /dev/null
@@ -0,0 +1,92 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main_FIX.h"
+
+#if defined(__mips)
+#include "mips/warped_autocorrelation_FIX_mipsr1.h"
+#endif
+
+
+/* Autocorrelations for a warped frequency axis */
+#ifndef OVERRIDE_silk_warped_autocorrelation_FIX_c
+void silk_warped_autocorrelation_FIX_c(
+          opus_int32                *corr,                                  /* O    Result [order + 1]                                                          */
+          opus_int                  *scale,                                 /* O    Scaling of the correlation vector                                           */
+    const opus_int16                *input,                                 /* I    Input data to correlate                                                     */
+    const opus_int                  warping_Q16,                            /* I    Warping coefficient                                                         */
+    const opus_int                  length,                                 /* I    Length of input                                                             */
+    const opus_int                  order                                   /* I    Correlation order (even)                                                    */
+)
+{
+    opus_int   n, i, lsh;
+    opus_int32 tmp1_QS, tmp2_QS;
+    opus_int32 state_QS[ MAX_SHAPE_LPC_ORDER + 1 ] = { 0 };
+    opus_int64 corr_QC[  MAX_SHAPE_LPC_ORDER + 1 ] = { 0 };
+
+    /* Order must be even */
+    celt_assert( ( order & 1 ) == 0 );
+    silk_assert( 2 * QS - QC >= 0 );
+
+    /* Loop over samples */
+    for( n = 0; n < length; n++ ) {
+        tmp1_QS = silk_LSHIFT32( (opus_int32)input[ n ], QS );
+        /* Loop over allpass sections */
+        for( i = 0; i < order; i += 2 ) {
+            /* Output of allpass section */
+            tmp2_QS = silk_SMLAWB( state_QS[ i ], state_QS[ i + 1 ] - tmp1_QS, warping_Q16 );
+            state_QS[ i ]  = tmp1_QS;
+            corr_QC[  i ] += silk_RSHIFT64( silk_SMULL( tmp1_QS, state_QS[ 0 ] ), 2 * QS - QC );
+            /* Output of allpass section */
+            tmp1_QS = silk_SMLAWB( state_QS[ i + 1 ], state_QS[ i + 2 ] - tmp2_QS, warping_Q16 );
+            state_QS[ i + 1 ]  = tmp2_QS;
+            corr_QC[  i + 1 ] += silk_RSHIFT64( silk_SMULL( tmp2_QS, state_QS[ 0 ] ), 2 * QS - QC );
+        }
+        state_QS[ order ] = tmp1_QS;
+        corr_QC[  order ] += silk_RSHIFT64( silk_SMULL( tmp1_QS, state_QS[ 0 ] ), 2 * QS - QC );
+    }
+
+    lsh = silk_CLZ64( corr_QC[ 0 ] ) - 35;
+    lsh = silk_LIMIT( lsh, -12 - QC, 30 - QC );
+    *scale = -( QC + lsh );
+    silk_assert( *scale >= -30 && *scale <= 12 );
+    if( lsh >= 0 ) {
+        for( i = 0; i < order + 1; i++ ) {
+            corr[ i ] = (opus_int32)silk_CHECK_FIT32( silk_LSHIFT64( corr_QC[ i ], lsh ) );
+        }
+    } else {
+        for( i = 0; i < order + 1; i++ ) {
+            corr[ i ] = (opus_int32)silk_CHECK_FIT32( silk_RSHIFT64( corr_QC[ i ], -lsh ) );
+        }
+    }
+    silk_assert( corr_QC[ 0 ] >= 0 ); /* If breaking, decrease QC*/
+}
+#endif /* OVERRIDE_silk_warped_autocorrelation_FIX_c */
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/warped_autocorrelation_FIX_arm.h b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/warped_autocorrelation_FIX_arm.h
new file mode 100644 (file)
index 0000000..1992e43
--- /dev/null
@@ -0,0 +1,68 @@
+/***********************************************************************
+Copyright (c) 2017 Google Inc.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifndef SILK_WARPED_AUTOCORRELATION_FIX_ARM_H
+# define SILK_WARPED_AUTOCORRELATION_FIX_ARM_H
+
+# include "celt/arm/armcpu.h"
+
+# if defined(FIXED_POINT)
+
+#  if defined(OPUS_ARM_MAY_HAVE_NEON_INTR)
+void silk_warped_autocorrelation_FIX_neon(
+          opus_int32                *corr,                                  /* O    Result [order + 1]                                                          */
+          opus_int                  *scale,                                 /* O    Scaling of the correlation vector                                           */
+    const opus_int16                *input,                                 /* I    Input data to correlate                                                     */
+    const opus_int                  warping_Q16,                            /* I    Warping coefficient                                                         */
+    const opus_int                  length,                                 /* I    Length of input                                                             */
+    const opus_int                  order                                   /* I    Correlation order (even)                                                    */
+);
+
+#  if !defined(OPUS_HAVE_RTCD) && defined(OPUS_ARM_PRESUME_NEON)
+#   define OVERRIDE_silk_warped_autocorrelation_FIX (1)
+#   define silk_warped_autocorrelation_FIX(corr, scale, input, warping_Q16, length, order, arch) \
+    ((void)(arch), PRESUME_NEON(silk_warped_autocorrelation_FIX)(corr, scale, input, warping_Q16, length, order))
+#  endif
+#  endif
+
+#  if !defined(OVERRIDE_silk_warped_autocorrelation_FIX)
+/*Is run-time CPU detection enabled on this platform?*/
+#   if defined(OPUS_HAVE_RTCD) && (defined(OPUS_ARM_MAY_HAVE_NEON_INTR) && !defined(OPUS_ARM_PRESUME_NEON_INTR))
+extern void (*const SILK_WARPED_AUTOCORRELATION_FIX_IMPL[OPUS_ARCHMASK+1])(opus_int32*, opus_int*, const opus_int16*, const opus_int, const opus_int, const opus_int);
+#    define OVERRIDE_silk_warped_autocorrelation_FIX (1)
+#    define silk_warped_autocorrelation_FIX(corr, scale, input, warping_Q16, length, order, arch) \
+    ((*SILK_WARPED_AUTOCORRELATION_FIX_IMPL[(arch)&OPUS_ARCHMASK])(corr, scale, input, warping_Q16, length, order))
+#   elif defined(OPUS_ARM_PRESUME_NEON_INTR)
+#    define OVERRIDE_silk_warped_autocorrelation_FIX (1)
+#    define silk_warped_autocorrelation_FIX(corr, scale, input, warping_Q16, length, order, arch) \
+    ((void)(arch), silk_warped_autocorrelation_FIX_neon(corr, scale, input, warping_Q16, length, order))
+#   endif
+#  endif
+
+# endif /* end FIXED_POINT */
+
+#endif /* end SILK_WARPED_AUTOCORRELATION_FIX_ARM_H */
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/warped_autocorrelation_FIX_mipsr1.h b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/warped_autocorrelation_FIX_mipsr1.h
new file mode 100644 (file)
index 0000000..9d9cb05
--- /dev/null
@@ -0,0 +1,189 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifndef __WARPED_AUTOCORRELATION_FIX_MIPSR1_H__
+#define __WARPED_AUTOCORRELATION_FIX_MIPSR1_H__
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main_FIX.h"
+
+#if defined (__mips_dsp) && __mips == 32
+
+#define MIPS_MADD __builtin_mips_madd
+#define MIPS_SHILO __builtin_mips_shilo
+
+#define OVERRIDE_silk_warped_autocorrelation_FIX_c
+
+#elif defined (__mips_isa_rev) && __mips == 32
+
+static inline long long MIPS_MADD(long long acc, int a, int b) {
+    return acc + (long long)a * b;
+}
+
+static inline long long MIPS_SHILO(long long acc, int sh) {
+    return (sh >= 0) ? (acc >> sh) : (acc << -sh);
+}
+
+#define OVERRIDE_silk_warped_autocorrelation_FIX_c
+
+#endif
+
+/* Autocorrelations for a warped frequency axis */
+#if defined (OVERRIDE_silk_warped_autocorrelation_FIX_c)
+
+#undef QC
+#define QC  10
+
+#undef QS
+#define QS  14
+
+void silk_warped_autocorrelation_FIX_c(
+          opus_int32                *corr,                                  /* O    Result [order + 1]                                                          */
+          opus_int                  *scale,                                 /* O    Scaling of the correlation vector                                           */
+    const opus_int16                *input,                                 /* I    Input data to correlate                                                     */
+    const opus_int                  warping_Q16,                            /* I    Warping coefficient                                                         */
+    const opus_int                  length,                                 /* I    Length of input                                                             */
+    const opus_int                  order                                   /* I    Correlation order (even)                                                    */
+)
+{
+    opus_int   n, i, lsh;
+    opus_int32 tmp1_QS=0, tmp2_QS=0, tmp3_QS=0, tmp4_QS=0, tmp5_QS=0, tmp6_QS=0, tmp7_QS=0, tmp8_QS=0, start_1=0, start_2=0, start_3=0;
+    opus_int32 state_QS[ MAX_SHAPE_LPC_ORDER + 1 ] = { 0 };
+    opus_int64 corr_QC[  MAX_SHAPE_LPC_ORDER + 1 ] = { 0 };
+    opus_int64 temp64;
+
+    opus_int32 val;
+    val = 2 * QS - QC;
+
+    /* Order must be even */
+    silk_assert( ( order & 1 ) == 0 );
+    silk_assert( 2 * QS - QC >= 0 );
+
+    /* Loop over samples */
+    for( n = 0; n < length; n=n+4 ) {
+
+        tmp1_QS = silk_LSHIFT32( (opus_int32)input[ n ], QS );
+        start_1 = tmp1_QS;
+        tmp3_QS = silk_LSHIFT32( (opus_int32)input[ n+1], QS );
+        start_2 = tmp3_QS;
+        tmp5_QS = silk_LSHIFT32( (opus_int32)input[ n+2], QS );
+        start_3 = tmp5_QS;
+        tmp7_QS = silk_LSHIFT32( (opus_int32)input[ n+3], QS );
+
+        /* Loop over allpass sections */
+        for( i = 0; i < order; i += 2 ) {
+            /* Output of allpass section */
+            tmp2_QS = silk_SMLAWB( state_QS[ i ], state_QS[ i + 1 ] - tmp1_QS, warping_Q16 );
+            corr_QC[  i ] = MIPS_MADD( corr_QC[  i ], tmp1_QS,  start_1);
+
+            tmp4_QS = silk_SMLAWB( tmp1_QS, tmp2_QS - tmp3_QS, warping_Q16 );
+            corr_QC[  i ] = MIPS_MADD( corr_QC[  i ], tmp3_QS,  start_2);
+
+            tmp6_QS = silk_SMLAWB( tmp3_QS, tmp4_QS - tmp5_QS, warping_Q16 );
+            corr_QC[  i ] = MIPS_MADD( corr_QC[  i ], tmp5_QS,  start_3);
+
+            tmp8_QS = silk_SMLAWB( tmp5_QS, tmp6_QS - tmp7_QS, warping_Q16 );
+            state_QS[ i ]  = tmp7_QS;
+            corr_QC[  i ] = MIPS_MADD( corr_QC[  i ], tmp7_QS, state_QS[0]);
+
+            /* Output of allpass section */
+            tmp1_QS = silk_SMLAWB( state_QS[ i + 1 ], state_QS[ i + 2 ] - tmp2_QS, warping_Q16 );
+            corr_QC[  i+1 ] = MIPS_MADD( corr_QC[  i+1 ], tmp2_QS,  start_1);
+
+            tmp3_QS = silk_SMLAWB( tmp2_QS, tmp1_QS - tmp4_QS, warping_Q16 );
+            corr_QC[  i+1 ] = MIPS_MADD( corr_QC[  i+1 ], tmp4_QS,  start_2);
+
+            tmp5_QS = silk_SMLAWB( tmp4_QS, tmp3_QS - tmp6_QS, warping_Q16 );
+            corr_QC[  i+1 ] = MIPS_MADD( corr_QC[  i+1 ], tmp6_QS,  start_3);
+
+            tmp7_QS = silk_SMLAWB( tmp6_QS, tmp5_QS - tmp8_QS, warping_Q16 );
+            state_QS[ i + 1 ]  = tmp8_QS;
+            corr_QC[  i+1 ] = MIPS_MADD( corr_QC[  i+1 ], tmp8_QS,  state_QS[ 0 ]);
+
+        }
+        state_QS[ order ] = tmp7_QS;
+
+        corr_QC[  order ] = MIPS_MADD( corr_QC[  order ], tmp1_QS,  start_1);
+        corr_QC[  order ] = MIPS_MADD( corr_QC[  order ], tmp3_QS,  start_2);
+        corr_QC[  order ] = MIPS_MADD( corr_QC[  order ], tmp5_QS,  start_3);
+        corr_QC[  order ] = MIPS_MADD( corr_QC[  order ], tmp7_QS,  state_QS[ 0 ]);
+    }
+
+    for(;n< length; n++ ) {
+
+        tmp1_QS = silk_LSHIFT32( (opus_int32)input[ n ], QS );
+
+        /* Loop over allpass sections */
+        for( i = 0; i < order; i += 2 ) {
+
+            /* Output of allpass section */
+            tmp2_QS = silk_SMLAWB( state_QS[ i ], state_QS[ i + 1 ] - tmp1_QS, warping_Q16 );
+            state_QS[ i ] = tmp1_QS;
+            corr_QC[  i ] = MIPS_MADD( corr_QC[  i ], tmp1_QS,   state_QS[ 0 ]);
+
+            /* Output of allpass section */
+            tmp1_QS = silk_SMLAWB( state_QS[ i + 1 ], state_QS[ i + 2 ] - tmp2_QS, warping_Q16 );
+            state_QS[ i + 1 ]  = tmp2_QS;
+            corr_QC[  i+1 ] = MIPS_MADD( corr_QC[  i+1 ], tmp2_QS,   state_QS[ 0 ]);
+        }
+        state_QS[ order ] = tmp1_QS;
+        corr_QC[  order ] = MIPS_MADD( corr_QC[  order ], tmp1_QS,   state_QS[ 0 ]);
+    }
+
+    temp64 =  corr_QC[ 0 ];
+    temp64 = MIPS_SHILO(temp64, val);
+
+    lsh = silk_CLZ64( temp64 ) - 35;
+    lsh = silk_LIMIT( lsh, -12 - QC, 30 - QC );
+    *scale = -( QC + lsh );
+    silk_assert( *scale >= -30 && *scale <= 12 );
+    if( lsh >= 0 ) {
+        for( i = 0; i < order + 1; i++ ) {
+            temp64 = corr_QC[ i ];
+            //temp64 = MIPS_SHILO(temp64, val);
+            temp64 = (val >= 0) ? (temp64 >> val) : (temp64 << -val);
+            corr[ i ] = (opus_int32)silk_CHECK_FIT32( MIPS_SHILO( temp64, -lsh ) );
+        }
+    } else {
+        for( i = 0; i < order + 1; i++ ) {
+            temp64 = corr_QC[ i ];
+            //temp64 = MIPS_SHILO(temp64, val);
+            temp64 = (val >= 0) ? (temp64 >> val) : (temp64 << -val);
+            corr[ i ] = (opus_int32)silk_CHECK_FIT32( MIPS_SHILO( temp64, -lsh ) );
+        }
+    }
+
+     corr_QC[ 0 ] = MIPS_SHILO(corr_QC[ 0 ], val);
+
+     silk_assert( corr_QC[ 0 ] >= 0 ); /* If breaking, decrease QC*/
+}
+#endif /* OVERRIDE_silk_warped_autocorrelation_FIX_c */
+
+#endif /* __WARPED_AUTOCORRELATION_FIX_MIPSR1_H__ */
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/warped_autocorrelation_FIX_neon_intr.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/fixed/warped_autocorrelation_FIX_neon_intr.c
new file mode 100644 (file)
index 0000000..6f3be02
--- /dev/null
@@ -0,0 +1,265 @@
+/***********************************************************************
+Copyright (c) 2017 Google Inc., Jean-Marc Valin
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <arm_neon.h>
+#ifdef OPUS_CHECK_ASM
+# include <string.h>
+#endif
+#include "stack_alloc.h"
+#include "main_FIX.h"
+
+static OPUS_INLINE void calc_corr( const opus_int32 *const input_QS, opus_int64 *const corr_QC, const opus_int offset, const int32x4_t state_QS_s32x4 )
+{
+    int64x2_t corr_QC_s64x2[ 2 ], t_s64x2[ 2 ];
+    const int32x4_t input_QS_s32x4 = vld1q_s32( input_QS + offset );
+    corr_QC_s64x2[ 0 ] = vld1q_s64( corr_QC + offset + 0 );
+    corr_QC_s64x2[ 1 ] = vld1q_s64( corr_QC + offset + 2 );
+    t_s64x2[ 0 ] = vmull_s32( vget_low_s32( state_QS_s32x4 ), vget_low_s32( input_QS_s32x4 ) );
+    t_s64x2[ 1 ] = vmull_s32( vget_high_s32( state_QS_s32x4 ), vget_high_s32( input_QS_s32x4 ) );
+    corr_QC_s64x2[ 0 ] = vsraq_n_s64( corr_QC_s64x2[ 0 ], t_s64x2[ 0 ], 2 * QS - QC );
+    corr_QC_s64x2[ 1 ] = vsraq_n_s64( corr_QC_s64x2[ 1 ], t_s64x2[ 1 ], 2 * QS - QC );
+    vst1q_s64( corr_QC + offset + 0, corr_QC_s64x2[ 0 ] );
+    vst1q_s64( corr_QC + offset + 2, corr_QC_s64x2[ 1 ] );
+}
+
+static OPUS_INLINE int32x4_t calc_state( const int32x4_t state_QS0_s32x4, const int32x4_t state_QS0_1_s32x4, const int32x4_t state_QS1_1_s32x4, const int32x4_t warping_Q16_s32x4 )
+{
+    int32x4_t t_s32x4 = vsubq_s32( state_QS0_s32x4, state_QS0_1_s32x4 );
+    t_s32x4 = vqdmulhq_s32( t_s32x4, warping_Q16_s32x4 );
+    return vaddq_s32( state_QS1_1_s32x4, t_s32x4 );
+}
+
+void silk_warped_autocorrelation_FIX_neon(
+          opus_int32                *corr,                                  /* O    Result [order + 1]                                                          */
+          opus_int                  *scale,                                 /* O    Scaling of the correlation vector                                           */
+    const opus_int16                *input,                                 /* I    Input data to correlate                                                     */
+    const opus_int                  warping_Q16,                            /* I    Warping coefficient                                                         */
+    const opus_int                  length,                                 /* I    Length of input                                                             */
+    const opus_int                  order                                   /* I    Correlation order (even)                                                    */
+)
+{
+    if( ( MAX_SHAPE_LPC_ORDER > 24 ) || ( order < 6 ) ) {
+        silk_warped_autocorrelation_FIX_c( corr, scale, input, warping_Q16, length, order );
+    } else {
+        opus_int       n, i, lsh;
+        opus_int64     corr_QC[ MAX_SHAPE_LPC_ORDER + 1 ] = { 0 }; /* In reverse order */
+        opus_int64     corr_QC_orderT;
+        int64x2_t      lsh_s64x2;
+        const opus_int orderT = ( order + 3 ) & ~3;
+        opus_int64     *corr_QCT;
+        opus_int32     *input_QS;
+        VARDECL( opus_int32, input_QST );
+        VARDECL( opus_int32, state );
+        SAVE_STACK;
+
+        /* Order must be even */
+        silk_assert( ( order & 1 ) == 0 );
+        silk_assert( 2 * QS - QC >= 0 );
+
+        /* The additional +4 is to ensure a later vld1q_s32 call does not overflow.               */
+        /* Strictly, only +3 is needed but +4 simplifies initialization using the 4x32 neon load. */
+        ALLOC( input_QST, length + 2 * MAX_SHAPE_LPC_ORDER + 4, opus_int32 );
+
+        input_QS = input_QST;
+        /* input_QS has zero paddings in the beginning and end. */
+        vst1q_s32( input_QS, vdupq_n_s32( 0 ) );
+        input_QS += 4;
+        vst1q_s32( input_QS, vdupq_n_s32( 0 ) );
+        input_QS += 4;
+        vst1q_s32( input_QS, vdupq_n_s32( 0 ) );
+        input_QS += 4;
+        vst1q_s32( input_QS, vdupq_n_s32( 0 ) );
+        input_QS += 4;
+        vst1q_s32( input_QS, vdupq_n_s32( 0 ) );
+        input_QS += 4;
+        vst1q_s32( input_QS, vdupq_n_s32( 0 ) );
+        input_QS += 4;
+
+        /* Loop over samples */
+        for( n = 0; n < length - 7; n += 8, input_QS += 8 ) {
+            const int16x8_t t0_s16x4 = vld1q_s16( input + n );
+            vst1q_s32( input_QS + 0, vshll_n_s16( vget_low_s16( t0_s16x4 ), QS ) );
+            vst1q_s32( input_QS + 4, vshll_n_s16( vget_high_s16( t0_s16x4 ), QS ) );
+        }
+        for( ; n < length; n++, input_QS++ ) {
+            input_QS[ 0 ] = silk_LSHIFT32( (opus_int32)input[ n ], QS );
+        }
+        vst1q_s32( input_QS, vdupq_n_s32( 0 ) );
+        input_QS += 4;
+        vst1q_s32( input_QS, vdupq_n_s32( 0 ) );
+        input_QS += 4;
+        vst1q_s32( input_QS, vdupq_n_s32( 0 ) );
+        input_QS += 4;
+        vst1q_s32( input_QS, vdupq_n_s32( 0 ) );
+        input_QS += 4;
+        vst1q_s32( input_QS, vdupq_n_s32( 0 ) );
+        input_QS += 4;
+        vst1q_s32( input_QS, vdupq_n_s32( 0 ) );
+        input_QS += 4;
+        vst1q_s32( input_QS, vdupq_n_s32( 0 ) );
+        input_QS = input_QST + MAX_SHAPE_LPC_ORDER - orderT;
+
+        /* The following loop runs ( length + order ) times, with ( order ) extra epilogues.                  */
+        /* The zero paddings in input_QS guarantee corr_QC's correctness even with the extra epilogues.       */
+        /* The values of state_QS will be polluted by the extra epilogues, however they are temporary values. */
+
+        /* Keep the C code here to help understand the intrinsics optimization. */
+        /*
+        {
+            opus_int32 state_QS[ 2 ][ MAX_SHAPE_LPC_ORDER + 1 ] = { 0 };
+            opus_int32 *state_QST[ 3 ];
+            state_QST[ 0 ] = state_QS[ 0 ];
+            state_QST[ 1 ] = state_QS[ 1 ];
+            for( n = 0; n < length + order; n++, input_QS++ ) {
+                state_QST[ 0 ][ orderT ] = input_QS[ orderT ];
+                for( i = 0; i < orderT; i++ ) {
+                    corr_QC[ i ] += silk_RSHIFT64( silk_SMULL( state_QST[ 0 ][ i ], input_QS[ i ] ), 2 * QS - QC );
+                    state_QST[ 1 ][ i ] = silk_SMLAWB( state_QST[ 1 ][ i + 1 ], state_QST[ 0 ][ i ] - state_QST[ 0 ][ i + 1 ], warping_Q16 );
+                }
+                state_QST[ 2 ] = state_QST[ 0 ];
+                state_QST[ 0 ] = state_QST[ 1 ];
+                state_QST[ 1 ] = state_QST[ 2 ];
+            }
+        }
+        */
+
+        {
+            const int32x4_t warping_Q16_s32x4 = vdupq_n_s32( warping_Q16 << 15 );
+            const opus_int32 *in = input_QS + orderT;
+            opus_int o = orderT;
+            int32x4_t state_QS_s32x4[ 3 ][ 2 ];
+
+            /* The additional +4 is to ensure a later vld1q_s32 call does not overflow. */
+            ALLOC( state, length + order + 4, opus_int32 );
+            state_QS_s32x4[ 2 ][ 1 ] = vdupq_n_s32( 0 );
+
+            /* Calculate 8 taps of all inputs in each loop. */
+            do {
+                state_QS_s32x4[ 0 ][ 0 ] = state_QS_s32x4[ 0 ][ 1 ] =
+                state_QS_s32x4[ 1 ][ 0 ] = state_QS_s32x4[ 1 ][ 1 ] = vdupq_n_s32( 0 );
+                n = 0;
+                do {
+                    calc_corr( input_QS + n, corr_QC, o - 8, state_QS_s32x4[ 0 ][ 0 ] );
+                    calc_corr( input_QS + n, corr_QC, o - 4, state_QS_s32x4[ 0 ][ 1 ] );
+                    state_QS_s32x4[ 2 ][ 1 ] = vld1q_s32( in + n );
+                    vst1q_lane_s32( state + n, state_QS_s32x4[ 0 ][ 0 ], 0 );
+                    state_QS_s32x4[ 2 ][ 0 ] = vextq_s32( state_QS_s32x4[ 0 ][ 0 ], state_QS_s32x4[ 0 ][ 1 ], 1 );
+                    state_QS_s32x4[ 2 ][ 1 ] = vextq_s32( state_QS_s32x4[ 0 ][ 1 ], state_QS_s32x4[ 2 ][ 1 ], 1 );
+                    state_QS_s32x4[ 0 ][ 0 ] = calc_state( state_QS_s32x4[ 0 ][ 0 ], state_QS_s32x4[ 2 ][ 0 ], state_QS_s32x4[ 1 ][ 0 ], warping_Q16_s32x4 );
+                    state_QS_s32x4[ 0 ][ 1 ] = calc_state( state_QS_s32x4[ 0 ][ 1 ], state_QS_s32x4[ 2 ][ 1 ], state_QS_s32x4[ 1 ][ 1 ], warping_Q16_s32x4 );
+                    state_QS_s32x4[ 1 ][ 0 ] = state_QS_s32x4[ 2 ][ 0 ];
+                    state_QS_s32x4[ 1 ][ 1 ] = state_QS_s32x4[ 2 ][ 1 ];
+                } while( ++n < ( length + order ) );
+                in = state;
+                o -= 8;
+            } while( o > 4 );
+
+            if( o ) {
+                /* Calculate the last 4 taps of all inputs. */
+                opus_int32 *stateT = state;
+                silk_assert( o == 4 );
+                state_QS_s32x4[ 0 ][ 0 ] = state_QS_s32x4[ 1 ][ 0 ] = vdupq_n_s32( 0 );
+                n = length + order;
+                do {
+                    calc_corr( input_QS, corr_QC, 0, state_QS_s32x4[ 0 ][ 0 ] );
+                    state_QS_s32x4[ 2 ][ 0 ] = vld1q_s32( stateT );
+                    vst1q_lane_s32( stateT, state_QS_s32x4[ 0 ][ 0 ], 0 );
+                    state_QS_s32x4[ 2 ][ 0 ] = vextq_s32( state_QS_s32x4[ 0 ][ 0 ], state_QS_s32x4[ 2 ][ 0 ], 1 );
+                    state_QS_s32x4[ 0 ][ 0 ] = calc_state( state_QS_s32x4[ 0 ][ 0 ], state_QS_s32x4[ 2 ][ 0 ], state_QS_s32x4[ 1 ][ 0 ], warping_Q16_s32x4 );
+                    state_QS_s32x4[ 1 ][ 0 ] = state_QS_s32x4[ 2 ][ 0 ];
+                    input_QS++;
+                    stateT++;
+                } while( --n );
+            }
+        }
+
+        {
+            const opus_int16 *inputT = input;
+            int32x4_t t_s32x4;
+            int64x1_t t_s64x1;
+            int64x2_t t_s64x2 = vdupq_n_s64( 0 );
+            for( n = 0; n <= length - 8; n += 8 ) {
+                int16x8_t input_s16x8 = vld1q_s16( inputT );
+                t_s32x4 = vmull_s16( vget_low_s16( input_s16x8 ), vget_low_s16( input_s16x8 ) );
+                t_s32x4 = vmlal_s16( t_s32x4, vget_high_s16( input_s16x8 ), vget_high_s16( input_s16x8 ) );
+                t_s64x2 = vaddw_s32( t_s64x2, vget_low_s32( t_s32x4 ) );
+                t_s64x2 = vaddw_s32( t_s64x2, vget_high_s32( t_s32x4 ) );
+                inputT += 8;
+            }
+            t_s64x1 = vadd_s64( vget_low_s64( t_s64x2 ), vget_high_s64( t_s64x2 ) );
+            corr_QC_orderT = vget_lane_s64( t_s64x1, 0 );
+            for( ; n < length; n++ ) {
+                corr_QC_orderT += silk_SMULL( input[ n ], input[ n ] );
+            }
+            corr_QC_orderT = silk_LSHIFT64( corr_QC_orderT, QC );
+            corr_QC[ orderT ] = corr_QC_orderT;
+        }
+
+        corr_QCT = corr_QC + orderT - order;
+        lsh = silk_CLZ64( corr_QC_orderT ) - 35;
+        lsh = silk_LIMIT( lsh, -12 - QC, 30 - QC );
+        *scale = -( QC + lsh );
+        silk_assert( *scale >= -30 && *scale <= 12 );
+        lsh_s64x2 = vdupq_n_s64( lsh );
+        for( i = 0; i <= order - 3; i += 4 ) {
+            int32x4_t corr_s32x4;
+            int64x2_t corr_QC0_s64x2, corr_QC1_s64x2;
+            corr_QC0_s64x2 = vld1q_s64( corr_QCT + i );
+            corr_QC1_s64x2 = vld1q_s64( corr_QCT + i + 2 );
+            corr_QC0_s64x2 = vshlq_s64( corr_QC0_s64x2, lsh_s64x2 );
+            corr_QC1_s64x2 = vshlq_s64( corr_QC1_s64x2, lsh_s64x2 );
+            corr_s32x4     = vcombine_s32( vmovn_s64( corr_QC1_s64x2 ), vmovn_s64( corr_QC0_s64x2 ) );
+            corr_s32x4     = vrev64q_s32( corr_s32x4 );
+            vst1q_s32( corr + order - i - 3, corr_s32x4 );
+        }
+        if( lsh >= 0 ) {
+            for( ; i < order + 1; i++ ) {
+                corr[ order - i ] = (opus_int32)silk_CHECK_FIT32( silk_LSHIFT64( corr_QCT[ i ], lsh ) );
+            }
+        } else {
+            for( ; i < order + 1; i++ ) {
+                corr[ order - i ] = (opus_int32)silk_CHECK_FIT32( silk_RSHIFT64( corr_QCT[ i ], -lsh ) );
+            }
+        }
+        silk_assert( corr_QCT[ order ] >= 0 ); /* If breaking, decrease QC*/
+        RESTORE_STACK;
+    }
+
+#ifdef OPUS_CHECK_ASM
+    {
+        opus_int32 corr_c[ MAX_SHAPE_LPC_ORDER + 1 ];
+        opus_int   scale_c;
+        silk_warped_autocorrelation_FIX_c( corr_c, &scale_c, input, warping_Q16, length, order );
+        silk_assert( !memcmp( corr_c, corr, sizeof( corr_c[ 0 ] ) * ( order + 1 ) ) );
+        silk_assert( scale_c == *scale );
+    }
+#endif
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/float/SigProc_FLP.h b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/float/SigProc_FLP.h
new file mode 100644 (file)
index 0000000..783099b
--- /dev/null
@@ -0,0 +1,197 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifndef SILK_SIGPROC_FLP_H
+#define SILK_SIGPROC_FLP_H
+
+#include "SigProc_FIX.h"
+#include "float_cast.h"
+#include "main.h"
+#include <math.h>
+
+
+/********************************************************************/
+/*                    SIGNAL PROCESSING FUNCTIONS                   */
+/********************************************************************/
+
+/* Chirp (bw expand) LP AR filter */
+void silk_bwexpander_FLP(
+    silk_float          *ar,                /* I/O  AR filter to be expanded (without leading 1)                */
+    const opus_int      d,                  /* I    length of ar                                                */
+    const silk_float    chirp               /* I    chirp factor (typically in range (0..1) )                   */
+);
+
+/* compute inverse of LPC prediction gain, and                          */
+/* test if LPC coefficients are stable (all poles within unit circle)   */
+/* this code is based on silk_FLP_a2k()                                 */
+silk_float silk_LPC_inverse_pred_gain_FLP(  /* O    return inverse prediction gain, energy domain               */
+    const silk_float    *A,                 /* I    prediction coefficients [order]                             */
+    opus_int32          order               /* I    prediction order                                            */
+);
+
+silk_float silk_schur_FLP(                  /* O    returns residual energy                                     */
+    silk_float          refl_coef[],        /* O    reflection coefficients (length order)                      */
+    const silk_float    auto_corr[],        /* I    autocorrelation sequence (length order+1)                   */
+    opus_int            order               /* I    order                                                       */
+);
+
+void silk_k2a_FLP(
+    silk_float          *A,                 /* O     prediction coefficients [order]                            */
+    const silk_float    *rc,                /* I     reflection coefficients [order]                            */
+    opus_int32          order               /* I     prediction order                                           */
+);
+
+/* compute autocorrelation */
+void silk_autocorrelation_FLP(
+    silk_float          *results,           /* O    result (length correlationCount)                            */
+    const silk_float    *inputData,         /* I    input data to correlate                                     */
+    opus_int            inputDataSize,      /* I    length of input                                             */
+    opus_int            correlationCount,    /* I    number of correlation taps to compute                       */
+    int                 arch
+);
+
+opus_int silk_pitch_analysis_core_FLP(      /* O    Voicing estimate: 0 voiced, 1 unvoiced                      */
+    const silk_float    *frame,             /* I    Signal of length PE_FRAME_LENGTH_MS*Fs_kHz                  */
+    opus_int            *pitch_out,         /* O    Pitch lag values [nb_subfr]                                 */
+    opus_int16          *lagIndex,          /* O    Lag Index                                                   */
+    opus_int8           *contourIndex,      /* O    Pitch contour Index                                         */
+    silk_float          *LTPCorr,           /* I/O  Normalized correlation; input: value from previous frame    */
+    opus_int            prevLag,            /* I    Last lag of previous frame; set to zero is unvoiced         */
+    const silk_float    search_thres1,      /* I    First stage threshold for lag candidates 0 - 1              */
+    const silk_float    search_thres2,      /* I    Final threshold for lag candidates 0 - 1                    */
+    const opus_int      Fs_kHz,             /* I    sample frequency (kHz)                                      */
+    const opus_int      complexity,         /* I    Complexity setting, 0-2, where 2 is highest                 */
+    const opus_int      nb_subfr,           /* I    Number of 5 ms subframes                                    */
+    int                 arch                /* I    Run-time architecture                                       */
+);
+
+void silk_insertion_sort_decreasing_FLP(
+    silk_float          *a,                 /* I/O  Unsorted / Sorted vector                                    */
+    opus_int            *idx,               /* O    Index vector for the sorted elements                        */
+    const opus_int      L,                  /* I    Vector length                                               */
+    const opus_int      K                   /* I    Number of correctly sorted positions                        */
+);
+
+/* Compute reflection coefficients from input signal */
+silk_float silk_burg_modified_FLP(          /* O    returns residual energy                                     */
+    silk_float          A[],                /* O    prediction coefficients (length order)                      */
+    const silk_float    x[],                /* I    input signal, length: nb_subfr*(D+L_sub)                    */
+    const silk_float    minInvGain,         /* I    minimum inverse prediction gain                             */
+    const opus_int      subfr_length,       /* I    input signal subframe length (incl. D preceding samples)    */
+    const opus_int      nb_subfr,           /* I    number of subframes stacked in x                            */
+    const opus_int      D,                  /* I    order                                                       */
+    int                 arch
+);
+
+/* multiply a vector by a constant */
+void silk_scale_vector_FLP(
+    silk_float          *data1,
+    silk_float          gain,
+    opus_int            dataSize
+);
+
+/* copy and multiply a vector by a constant */
+void silk_scale_copy_vector_FLP(
+    silk_float          *data_out,
+    const silk_float    *data_in,
+    silk_float          gain,
+    opus_int            dataSize
+);
+
+/* inner product of two silk_float arrays, with result as double */
+double silk_inner_product_FLP_c(
+    const silk_float    *data1,
+    const silk_float    *data2,
+    opus_int            dataSize
+);
+
+#ifndef OVERRIDE_inner_product_FLP
+#define silk_inner_product_FLP(data1, data2, dataSize, arch) ((void)arch,silk_inner_product_FLP_c(data1, data2, dataSize))
+#endif
+
+
+/* sum of squares of a silk_float array, with result as double */
+double silk_energy_FLP(
+    const silk_float    *data,
+    opus_int            dataSize
+);
+
+/********************************************************************/
+/*                                MACROS                            */
+/********************************************************************/
+
+#define PI              (3.1415926536f)
+
+#define silk_min_float( a, b )                  (((a) < (b)) ? (a) :  (b))
+#define silk_max_float( a, b )                  (((a) > (b)) ? (a) :  (b))
+#define silk_abs_float( a )                     ((silk_float)fabs(a))
+
+/* sigmoid function */
+static OPUS_INLINE silk_float silk_sigmoid( silk_float x )
+{
+    return (silk_float)(1.0 / (1.0 + exp(-x)));
+}
+
+/* floating-point to integer conversion (rounding) */
+static OPUS_INLINE opus_int32 silk_float2int( silk_float x )
+{
+    return (opus_int32)float2int( x );
+}
+
+/* floating-point to integer conversion (rounding) */
+static OPUS_INLINE void silk_float2short_array(
+    opus_int16       *out,
+    const silk_float *in,
+    opus_int32       length
+)
+{
+    opus_int32 k;
+    for( k = length - 1; k >= 0; k-- ) {
+        out[k] = silk_SAT16( (opus_int32)float2int( in[k] ) );
+    }
+}
+
+/* integer to floating-point conversion */
+static OPUS_INLINE void silk_short2float_array(
+    silk_float       *out,
+    const opus_int16 *in,
+    opus_int32       length
+)
+{
+    opus_int32 k;
+    for( k = length - 1; k >= 0; k-- ) {
+        out[k] = (silk_float)in[k];
+    }
+}
+
+/* using log2() helps the fixed-point conversion */
+static OPUS_INLINE silk_float silk_log2( double x )
+{
+    return ( silk_float )( 3.32192809488736 * log10( x ) );
+}
+
+#endif /* SILK_SIGPROC_FLP_H */
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/float/main_FLP.h b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/float/main_FLP.h
new file mode 100644 (file)
index 0000000..8173bb3
--- /dev/null
@@ -0,0 +1,282 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifndef SILK_MAIN_FLP_H
+#define SILK_MAIN_FLP_H
+
+#include "SigProc_FLP.h"
+#include "SigProc_FIX.h"
+#include "structs_FLP.h"
+#include "main.h"
+#include "define.h"
+#include "debug.h"
+#include "entenc.h"
+
+
+#define silk_encoder_state_Fxx      silk_encoder_state_FLP
+#define silk_encode_do_VAD_Fxx      silk_encode_do_VAD_FLP
+#define silk_encode_frame_Fxx       silk_encode_frame_FLP
+
+/*********************/
+/* Encoder Functions */
+/*********************/
+
+/* High-pass filter with cutoff frequency adaptation based on pitch lag statistics */
+void silk_HP_variable_cutoff(
+    silk_encoder_state_Fxx          state_Fxx[]                         /* I/O  Encoder states                              */
+);
+
+/* Encoder main function */
+void silk_encode_do_VAD_FLP(
+    silk_encoder_state_FLP          *psEnc,                             /* I/O  Encoder state FLP                           */
+    opus_int                        activity                            /* I    Decision of Opus voice activity detector    */
+);
+
+/* Encoder main function */
+opus_int silk_encode_frame_FLP(
+    silk_encoder_state_FLP          *psEnc,                             /* I/O  Encoder state FLP                           */
+    opus_int32                      *pnBytesOut,                        /* O    Number of payload bytes;                    */
+    ec_enc                          *psRangeEnc,                        /* I/O  compressor data structure                   */
+    opus_int                        condCoding,                         /* I    The type of conditional coding to use       */
+    opus_int                        maxBits,                            /* I    If > 0: maximum number of output bits       */
+    opus_int                        useCBR                              /* I    Flag to force constant-bitrate operation    */
+);
+
+/* Initializes the Silk encoder state */
+opus_int silk_init_encoder(
+    silk_encoder_state_FLP          *psEnc,                             /* I/O  Encoder state FLP                           */
+    int                              arch                               /* I    Run-tim architecture                        */
+);
+
+/* Control the Silk encoder */
+opus_int silk_control_encoder(
+    silk_encoder_state_FLP          *psEnc,                             /* I/O  Pointer to Silk encoder state FLP           */
+    silk_EncControlStruct           *encControl,                        /* I    Control structure                           */
+    const opus_int                  allow_bw_switch,                    /* I    Flag to allow switching audio bandwidth     */
+    const opus_int                  channelNb,                          /* I    Channel number                              */
+    const opus_int                  force_fs_kHz
+);
+
+/**************************/
+/* Noise shaping analysis */
+/**************************/
+/* Compute noise shaping coefficients and initial gain values */
+void silk_noise_shape_analysis_FLP(
+    silk_encoder_state_FLP          *psEnc,                             /* I/O  Encoder state FLP                           */
+    silk_encoder_control_FLP        *psEncCtrl,                         /* I/O  Encoder control FLP                         */
+    const silk_float                *pitch_res,                         /* I    LPC residual from pitch analysis            */
+    const silk_float                *x                                  /* I    Input signal [frame_length + la_shape]      */
+);
+
+/* Autocorrelations for a warped frequency axis */
+void silk_warped_autocorrelation_FLP(
+    silk_float                      *corr,                              /* O    Result [order + 1]                          */
+    const silk_float                *input,                             /* I    Input data to correlate                     */
+    const silk_float                warping,                            /* I    Warping coefficient                         */
+    const opus_int                  length,                             /* I    Length of input                             */
+    const opus_int                  order                               /* I    Correlation order (even)                    */
+);
+
+/* Calculation of LTP state scaling */
+void silk_LTP_scale_ctrl_FLP(
+    silk_encoder_state_FLP          *psEnc,                             /* I/O  Encoder state FLP                           */
+    silk_encoder_control_FLP        *psEncCtrl,                         /* I/O  Encoder control FLP                         */
+    opus_int                        condCoding                          /* I    The type of conditional coding to use       */
+);
+
+/**********************************************/
+/* Prediction Analysis                        */
+/**********************************************/
+/* Find pitch lags */
+void silk_find_pitch_lags_FLP(
+    silk_encoder_state_FLP          *psEnc,                             /* I/O  Encoder state FLP                           */
+    silk_encoder_control_FLP        *psEncCtrl,                         /* I/O  Encoder control FLP                         */
+    silk_float                      res[],                              /* O    Residual                                    */
+    const silk_float                x[],                                /* I    Speech signal                               */
+    int                             arch                                /* I    Run-time architecture                       */
+);
+
+/* Find LPC and LTP coefficients */
+void silk_find_pred_coefs_FLP(
+    silk_encoder_state_FLP          *psEnc,                             /* I/O  Encoder state FLP                           */
+    silk_encoder_control_FLP        *psEncCtrl,                         /* I/O  Encoder control FLP                         */
+    const silk_float                res_pitch[],                        /* I    Residual from pitch analysis                */
+    const silk_float                x[],                                /* I    Speech signal                               */
+    opus_int                        condCoding                          /* I    The type of conditional coding to use       */
+);
+
+/* LPC analysis */
+void silk_find_LPC_FLP(
+    silk_encoder_state              *psEncC,                            /* I/O  Encoder state                               */
+    opus_int16                      NLSF_Q15[],                         /* O    NLSFs                                       */
+    const silk_float                x[],                                /* I    Input signal                                */
+    const silk_float                minInvGain,                         /* I    Prediction gain from LTP (dB)               */
+    int                             arch
+);
+
+/* LTP analysis */
+void silk_find_LTP_FLP(
+    silk_float                      XX[ MAX_NB_SUBFR * LTP_ORDER * LTP_ORDER ], /* O    Weight for LTP quantization         */
+    silk_float                      xX[ MAX_NB_SUBFR * LTP_ORDER ],     /* O    Weight for LTP quantization                 */
+    const silk_float                r_ptr[],                            /* I    LPC residual                                */
+    const opus_int                  lag[  MAX_NB_SUBFR ],               /* I    LTP lags                                    */
+    const opus_int                  subfr_length,                       /* I    Subframe length                             */
+    const opus_int                  nb_subfr,                           /* I    number of subframes                         */
+    int                             arch
+);
+
+void silk_LTP_analysis_filter_FLP(
+    silk_float                      *LTP_res,                           /* O    LTP res MAX_NB_SUBFR*(pre_lgth+subfr_lngth) */
+    const silk_float                *x,                                 /* I    Input signal, with preceding samples        */
+    const silk_float                B[ LTP_ORDER * MAX_NB_SUBFR ],      /* I    LTP coefficients for each subframe          */
+    const opus_int                  pitchL[   MAX_NB_SUBFR ],           /* I    Pitch lags                                  */
+    const silk_float                invGains[ MAX_NB_SUBFR ],           /* I    Inverse quantization gains                  */
+    const opus_int                  subfr_length,                       /* I    Length of each subframe                     */
+    const opus_int                  nb_subfr,                           /* I    number of subframes                         */
+    const opus_int                  pre_length                          /* I    Preceding samples for each subframe         */
+);
+
+/* Calculates residual energies of input subframes where all subframes have LPC_order   */
+/* of preceding samples                                                                 */
+void silk_residual_energy_FLP(
+    silk_float                      nrgs[ MAX_NB_SUBFR ],               /* O    Residual energy per subframe                */
+    const silk_float                x[],                                /* I    Input signal                                */
+    silk_float                      a[ 2 ][ MAX_LPC_ORDER ],            /* I    AR coefs for each frame half                */
+    const silk_float                gains[],                            /* I    Quantization gains                          */
+    const opus_int                  subfr_length,                       /* I    Subframe length                             */
+    const opus_int                  nb_subfr,                           /* I    number of subframes                         */
+    const opus_int                  LPC_order                           /* I    LPC order                                   */
+);
+
+/* 16th order LPC analysis filter */
+void silk_LPC_analysis_filter_FLP(
+    silk_float                      r_LPC[],                            /* O    LPC residual signal                         */
+    const silk_float                PredCoef[],                         /* I    LPC coefficients                            */
+    const silk_float                s[],                                /* I    Input signal                                */
+    const opus_int                  length,                             /* I    Length of input signal                      */
+    const opus_int                  Order                               /* I    LPC order                                   */
+);
+
+/* LTP tap quantizer */
+void silk_quant_LTP_gains_FLP(
+    silk_float                      B[ MAX_NB_SUBFR * LTP_ORDER ],      /* O    Quantized LTP gains                         */
+    opus_int8                       cbk_index[ MAX_NB_SUBFR ],          /* O    Codebook index                              */
+    opus_int8                       *periodicity_index,                 /* O    Periodicity index                           */
+    opus_int32                      *sum_log_gain_Q7,                   /* I/O  Cumulative max prediction gain  */
+    silk_float                      *pred_gain_dB,                      /* O    LTP prediction gain                         */
+    const silk_float                XX[ MAX_NB_SUBFR * LTP_ORDER * LTP_ORDER ], /* I    Correlation matrix                  */
+    const silk_float                xX[ MAX_NB_SUBFR * LTP_ORDER ],     /* I    Correlation vector                          */
+    const opus_int                  subfr_len,                          /* I    Number of samples per subframe              */
+    const opus_int                  nb_subfr,                           /* I    Number of subframes                         */
+    int                             arch                                /* I    Run-time architecture                       */
+);
+
+/* Residual energy: nrg = wxx - 2 * wXx * c + c' * wXX * c */
+silk_float silk_residual_energy_covar_FLP(                              /* O    Weighted residual energy                    */
+    const silk_float                *c,                                 /* I    Filter coefficients                         */
+    silk_float                      *wXX,                               /* I/O  Weighted correlation matrix, reg. out       */
+    const silk_float                *wXx,                               /* I    Weighted correlation vector                 */
+    const silk_float                wxx,                                /* I    Weighted correlation value                  */
+    const opus_int                  D                                   /* I    Dimension                                   */
+);
+
+/* Processing of gains */
+void silk_process_gains_FLP(
+    silk_encoder_state_FLP          *psEnc,                             /* I/O  Encoder state FLP                           */
+    silk_encoder_control_FLP        *psEncCtrl,                         /* I/O  Encoder control FLP                         */
+    opus_int                        condCoding                          /* I    The type of conditional coding to use       */
+);
+
+/******************/
+/* Linear Algebra */
+/******************/
+/* Calculates correlation matrix X'*X */
+void silk_corrMatrix_FLP(
+    const silk_float                *x,                                 /* I    x vector [ L+order-1 ] used to create X     */
+    const opus_int                  L,                                  /* I    Length of vectors                           */
+    const opus_int                  Order,                              /* I    Max lag for correlation                     */
+    silk_float                      *XX,                                /* O    X'*X correlation matrix [order x order]     */
+    int                             arch
+);
+
+/* Calculates correlation vector X'*t */
+void silk_corrVector_FLP(
+    const silk_float                *x,                                 /* I    x vector [L+order-1] used to create X       */
+    const silk_float                *t,                                 /* I    Target vector [L]                           */
+    const opus_int                  L,                                  /* I    Length of vecors                            */
+    const opus_int                  Order,                              /* I    Max lag for correlation                     */
+    silk_float                      *Xt,                                /* O    X'*t correlation vector [order]             */
+    int                             arch
+);
+
+/* Apply sine window to signal vector.  */
+/* Window types:                        */
+/*  1 -> sine window from 0 to pi/2     */
+/*  2 -> sine window from pi/2 to pi    */
+void silk_apply_sine_window_FLP(
+    silk_float                      px_win[],                           /* O    Pointer to windowed signal                  */
+    const silk_float                px[],                               /* I    Pointer to input signal                     */
+    const opus_int                  win_type,                           /* I    Selects a window type                       */
+    const opus_int                  length                              /* I    Window length, multiple of 4                */
+);
+
+/* Wrapper functions. Call flp / fix code */
+
+/* Convert AR filter coefficients to NLSF parameters */
+void silk_A2NLSF_FLP(
+    opus_int16                      *NLSF_Q15,                          /* O    NLSF vector      [ LPC_order ]              */
+    const silk_float                *pAR,                               /* I    LPC coefficients [ LPC_order ]              */
+    const opus_int                  LPC_order                           /* I    LPC order                                   */
+);
+
+/* Convert NLSF parameters to AR prediction filter coefficients */
+void silk_NLSF2A_FLP(
+    silk_float                      *pAR,                               /* O    LPC coefficients [ LPC_order ]              */
+    const opus_int16                *NLSF_Q15,                          /* I    NLSF vector      [ LPC_order ]              */
+    const opus_int                  LPC_order,                          /* I    LPC order                                   */
+    int                             arch                                /* I    Run-time architecture                       */
+);
+
+/* Limit, stabilize, and quantize NLSFs */
+void silk_process_NLSFs_FLP(
+    silk_encoder_state              *psEncC,                            /* I/O  Encoder state                               */
+    silk_float                      PredCoef[ 2 ][ MAX_LPC_ORDER ],     /* O    Prediction coefficients                     */
+    opus_int16                      NLSF_Q15[      MAX_LPC_ORDER ],     /* I/O  Normalized LSFs (quant out) (0 - (2^15-1))  */
+    const opus_int16                prev_NLSF_Q15[ MAX_LPC_ORDER ]      /* I    Previous Normalized LSFs (0 - (2^15-1))     */
+);
+
+/* Floating-point Silk NSQ wrapper      */
+void silk_NSQ_wrapper_FLP(
+    silk_encoder_state_FLP          *psEnc,                             /* I/O  Encoder state FLP                           */
+    silk_encoder_control_FLP        *psEncCtrl,                         /* I/O  Encoder control FLP                         */
+    SideInfoIndices                 *psIndices,                         /* I/O  Quantization indices                        */
+    silk_nsq_state                  *psNSQ,                             /* I/O  Noise Shaping Quantzation state             */
+    opus_int8                       pulses[],                           /* O    Quantized pulse signal                      */
+    const silk_float                x[]                                 /* I    Prefiltered input signal                    */
+);
+
+#endif
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/float/structs_FLP.h b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/float/structs_FLP.h
new file mode 100644 (file)
index 0000000..8eac15c
--- /dev/null
@@ -0,0 +1,105 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifndef SILK_STRUCTS_FLP_H
+#define SILK_STRUCTS_FLP_H
+
+#include "typedef.h"
+#include "main.h"
+#include "structs.h"
+
+
+/********************************/
+/* Noise shaping analysis state */
+/********************************/
+typedef struct {
+    opus_int8                   LastGainIndex;
+    silk_float                  HarmShapeGain_smth;
+    silk_float                  Tilt_smth;
+} silk_shape_state_FLP;
+
+/********************************/
+/* Encoder state FLP            */
+/********************************/
+typedef struct {
+    silk_encoder_state          sCmn;                               /* Common struct, shared with fixed-point code */
+    silk_shape_state_FLP        sShape;                             /* Noise shaping state */
+
+    /* Buffer for find pitch and noise shape analysis */
+    silk_float                  x_buf[ 2 * MAX_FRAME_LENGTH + LA_SHAPE_MAX ];/* Buffer for find pitch and noise shape analysis */
+    silk_float                  LTPCorr;                            /* Normalized correlation from pitch lag estimator */
+} silk_encoder_state_FLP;
+
+/************************/
+/* Encoder control FLP  */
+/************************/
+typedef struct {
+    /* Prediction and coding parameters */
+    silk_float                  Gains[ MAX_NB_SUBFR ];
+    silk_float                  PredCoef[ 2 ][ MAX_LPC_ORDER ];     /* holds interpolated and final coefficients */
+    silk_float                  LTPCoef[LTP_ORDER * MAX_NB_SUBFR];
+    silk_float                  LTP_scale;
+    opus_int                    pitchL[ MAX_NB_SUBFR ];
+
+    /* Noise shaping parameters */
+    silk_float                  AR[ MAX_NB_SUBFR * MAX_SHAPE_LPC_ORDER ];
+    silk_float                  LF_MA_shp[     MAX_NB_SUBFR ];
+    silk_float                  LF_AR_shp[     MAX_NB_SUBFR ];
+    silk_float                  Tilt[          MAX_NB_SUBFR ];
+    silk_float                  HarmShapeGain[ MAX_NB_SUBFR ];
+    silk_float                  Lambda;
+    silk_float                  input_quality;
+    silk_float                  coding_quality;
+
+    /* Measures */
+    silk_float                  predGain;
+    silk_float                  LTPredCodGain;
+    silk_float                  ResNrg[ MAX_NB_SUBFR ];             /* Residual energy per subframe */
+
+    /* Parameters for CBR mode */
+    opus_int32                  GainsUnq_Q16[ MAX_NB_SUBFR ];
+    opus_int8                   lastGainIndexPrev;
+} silk_encoder_control_FLP;
+
+/************************/
+/* Encoder Super Struct */
+/************************/
+typedef struct {
+    stereo_enc_state            sStereo;
+    opus_int32                  nBitsUsedLBRR;
+    opus_int32                  nBitsExceeded;
+    opus_int                    nChannelsAPI;
+    opus_int                    nChannelsInternal;
+    opus_int                    nPrevChannelsInternal;
+    opus_int                    timeSinceSwitchAllowed_ms;
+    opus_int                    allowBandwidthSwitch;
+    opus_int                    prev_decode_only_middle;
+    /* This needs to be last so we can skip the second state for mono. */
+    silk_encoder_state_FLP      state_Fxx[ ENCODER_NUM_CHANNELS ];
+} silk_encoder;
+
+#endif
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/init_encoder.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/init_encoder.c
new file mode 100644 (file)
index 0000000..10d4128
--- /dev/null
@@ -0,0 +1,68 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#ifdef FIXED_POINT
+#include "main_FIX.h"
+#else
+#include "main_FLP.h"
+#endif
+#include "tuning_parameters.h"
+#include "cpu_support.h"
+
+#ifdef ENABLE_DRED
+#include "dred_encoder.h"
+#endif
+
+/*********************************/
+/* Initialize Silk Encoder state */
+/*********************************/
+opus_int silk_init_encoder(
+    silk_encoder_state_Fxx          *psEnc,                                 /* I/O  Pointer to Silk FIX encoder state                                           */
+    int                              arch                                   /* I    Run-time architecture                                                       */
+)
+{
+    opus_int ret = 0;
+
+    /* Clear the entire encoder state */
+    silk_memset( psEnc, 0, sizeof( silk_encoder_state_Fxx ) );
+
+    psEnc->sCmn.arch = arch;
+
+    psEnc->sCmn.variable_HP_smth1_Q15 = silk_LSHIFT( silk_lin2log( SILK_FIX_CONST( VARIABLE_HP_MIN_CUTOFF_HZ, 16 ) ) - ( 16 << 7 ), 8 );
+    psEnc->sCmn.variable_HP_smth2_Q15 = psEnc->sCmn.variable_HP_smth1_Q15;
+
+    /* Used to deactivate LSF interpolation, pitch prediction */
+    psEnc->sCmn.first_frame_after_reset = 1;
+
+    /* Initialize Silk VAD */
+    ret += silk_VAD_Init( &psEnc->sCmn.sVAD );
+
+    return  ret;
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/process_NLSFs.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/process_NLSFs.c
new file mode 100644 (file)
index 0000000..d130809
--- /dev/null
@@ -0,0 +1,107 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main.h"
+
+/* Limit, stabilize, convert and quantize NLSFs */
+void silk_process_NLSFs(
+    silk_encoder_state          *psEncC,                            /* I/O  Encoder state                               */
+    opus_int16                  PredCoef_Q12[ 2 ][ MAX_LPC_ORDER ], /* O    Prediction coefficients                     */
+    opus_int16                  pNLSF_Q15[         MAX_LPC_ORDER ], /* I/O  Normalized LSFs (quant out) (0 - (2^15-1))  */
+    const opus_int16            prev_NLSFq_Q15[    MAX_LPC_ORDER ]  /* I    Previous Normalized LSFs (0 - (2^15-1))     */
+)
+{
+    opus_int     i, doInterpolate;
+    opus_int     NLSF_mu_Q20;
+    opus_int16   i_sqr_Q15;
+    opus_int16   pNLSF0_temp_Q15[ MAX_LPC_ORDER ];
+    opus_int16   pNLSFW_QW[ MAX_LPC_ORDER ];
+    opus_int16   pNLSFW0_temp_QW[ MAX_LPC_ORDER ];
+
+    silk_assert( psEncC->speech_activity_Q8 >=   0 );
+    silk_assert( psEncC->speech_activity_Q8 <= SILK_FIX_CONST( 1.0, 8 ) );
+    celt_assert( psEncC->useInterpolatedNLSFs == 1 || psEncC->indices.NLSFInterpCoef_Q2 == ( 1 << 2 ) );
+
+    /***********************/
+    /* Calculate mu values */
+    /***********************/
+    /* NLSF_mu  = 0.003 - 0.0015 * psEnc->speech_activity; */
+    NLSF_mu_Q20 = silk_SMLAWB( SILK_FIX_CONST( 0.003, 20 ), SILK_FIX_CONST( -0.001, 28 ), psEncC->speech_activity_Q8 );
+    if( psEncC->nb_subfr == 2 ) {
+        /* Multiply by 1.5 for 10 ms packets */
+        NLSF_mu_Q20 = silk_ADD_RSHIFT( NLSF_mu_Q20, NLSF_mu_Q20, 1 );
+    }
+
+    celt_assert( NLSF_mu_Q20 >  0 );
+    silk_assert( NLSF_mu_Q20 <= SILK_FIX_CONST( 0.005, 20 ) );
+
+    /* Calculate NLSF weights */
+    silk_NLSF_VQ_weights_laroia( pNLSFW_QW, pNLSF_Q15, psEncC->predictLPCOrder );
+
+    /* Update NLSF weights for interpolated NLSFs */
+    doInterpolate = ( psEncC->useInterpolatedNLSFs == 1 ) && ( psEncC->indices.NLSFInterpCoef_Q2 < 4 );
+    if( doInterpolate ) {
+        /* Calculate the interpolated NLSF vector for the first half */
+        silk_interpolate( pNLSF0_temp_Q15, prev_NLSFq_Q15, pNLSF_Q15,
+            psEncC->indices.NLSFInterpCoef_Q2, psEncC->predictLPCOrder );
+
+        /* Calculate first half NLSF weights for the interpolated NLSFs */
+        silk_NLSF_VQ_weights_laroia( pNLSFW0_temp_QW, pNLSF0_temp_Q15, psEncC->predictLPCOrder );
+
+        /* Update NLSF weights with contribution from first half */
+        i_sqr_Q15 = silk_LSHIFT( silk_SMULBB( psEncC->indices.NLSFInterpCoef_Q2, psEncC->indices.NLSFInterpCoef_Q2 ), 11 );
+        for( i = 0; i < psEncC->predictLPCOrder; i++ ) {
+            pNLSFW_QW[ i ] = silk_ADD16( silk_RSHIFT( pNLSFW_QW[ i ], 1 ), silk_RSHIFT(
+                  silk_SMULBB( pNLSFW0_temp_QW[ i ], i_sqr_Q15 ), 16) );
+            silk_assert( pNLSFW_QW[ i ] >= 1 );
+        }
+    }
+
+    silk_NLSF_encode( psEncC->indices.NLSFIndices, pNLSF_Q15, psEncC->psNLSF_CB, pNLSFW_QW,
+        NLSF_mu_Q20, psEncC->NLSF_MSVQ_Survivors, psEncC->indices.signalType );
+
+    /* Convert quantized NLSFs back to LPC coefficients */
+    silk_NLSF2A( PredCoef_Q12[ 1 ], pNLSF_Q15, psEncC->predictLPCOrder, psEncC->arch );
+
+    if( doInterpolate ) {
+        /* Calculate the interpolated, quantized LSF vector for the first half */
+        silk_interpolate( pNLSF0_temp_Q15, prev_NLSFq_Q15, pNLSF_Q15,
+            psEncC->indices.NLSFInterpCoef_Q2, psEncC->predictLPCOrder );
+
+        /* Convert back to LPC coefficients */
+        silk_NLSF2A( PredCoef_Q12[ 0 ], pNLSF0_temp_Q15, psEncC->predictLPCOrder, psEncC->arch );
+
+    } else {
+        /* Copy LPC coefficients for first half from second half */
+        celt_assert( psEncC->predictLPCOrder <= MAX_LPC_ORDER );
+        silk_memcpy( PredCoef_Q12[ 0 ], PredCoef_Q12[ 1 ], psEncC->predictLPCOrder * sizeof( opus_int16 ) );
+    }
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/quant_LTP_gains.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/quant_LTP_gains.c
new file mode 100644 (file)
index 0000000..d6b8eff
--- /dev/null
@@ -0,0 +1,132 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main.h"
+#include "tuning_parameters.h"
+
+void silk_quant_LTP_gains(
+    opus_int16                  B_Q14[ MAX_NB_SUBFR * LTP_ORDER ],          /* O    Quantized LTP gains             */
+    opus_int8                   cbk_index[ MAX_NB_SUBFR ],                  /* O    Codebook Index                  */
+    opus_int8                   *periodicity_index,                         /* O    Periodicity Index               */
+    opus_int32                  *sum_log_gain_Q7,                           /* I/O  Cumulative max prediction gain  */
+    opus_int                    *pred_gain_dB_Q7,                           /* O    LTP prediction gain             */
+    const opus_int32            XX_Q17[ MAX_NB_SUBFR*LTP_ORDER*LTP_ORDER ], /* I    Correlation matrix in Q18       */
+    const opus_int32            xX_Q17[ MAX_NB_SUBFR*LTP_ORDER ],           /* I    Correlation vector in Q18       */
+    const opus_int              subfr_len,                                  /* I    Number of samples per subframe  */
+    const opus_int              nb_subfr,                                   /* I    Number of subframes             */
+    int                         arch                                        /* I    Run-time architecture           */
+)
+{
+    opus_int             j, k, cbk_size;
+    opus_int8            temp_idx[ MAX_NB_SUBFR ];
+    const opus_uint8     *cl_ptr_Q5;
+    const opus_int8      *cbk_ptr_Q7;
+    const opus_uint8     *cbk_gain_ptr_Q7;
+    const opus_int32     *XX_Q17_ptr, *xX_Q17_ptr;
+    opus_int32           res_nrg_Q15_subfr, res_nrg_Q15, rate_dist_Q7_subfr, rate_dist_Q7, min_rate_dist_Q7;
+    opus_int32           sum_log_gain_tmp_Q7, best_sum_log_gain_Q7, max_gain_Q7;
+    opus_int             gain_Q7;
+
+    /***************************************************/
+    /* iterate over different codebooks with different */
+    /* rates/distortions, and choose best */
+    /***************************************************/
+    min_rate_dist_Q7 = silk_int32_MAX;
+    best_sum_log_gain_Q7 = 0;
+    for( k = 0; k < 3; k++ ) {
+        /* Safety margin for pitch gain control, to take into account factors
+           such as state rescaling/rewhitening. */
+        opus_int32 gain_safety = SILK_FIX_CONST( 0.4, 7 );
+
+        cl_ptr_Q5  = silk_LTP_gain_BITS_Q5_ptrs[ k ];
+        cbk_ptr_Q7 = silk_LTP_vq_ptrs_Q7[        k ];
+        cbk_gain_ptr_Q7 = silk_LTP_vq_gain_ptrs_Q7[ k ];
+        cbk_size   = silk_LTP_vq_sizes[          k ];
+
+        /* Set up pointers to first subframe */
+        XX_Q17_ptr = XX_Q17;
+        xX_Q17_ptr = xX_Q17;
+
+        res_nrg_Q15 = 0;
+        rate_dist_Q7 = 0;
+        sum_log_gain_tmp_Q7 = *sum_log_gain_Q7;
+        for( j = 0; j < nb_subfr; j++ ) {
+            max_gain_Q7 = silk_log2lin( ( SILK_FIX_CONST( MAX_SUM_LOG_GAIN_DB / 6.0, 7 ) - sum_log_gain_tmp_Q7 )
+                                        + SILK_FIX_CONST( 7, 7 ) ) - gain_safety;
+            silk_VQ_WMat_EC(
+                &temp_idx[ j ],         /* O    index of best codebook vector                           */
+                &res_nrg_Q15_subfr,     /* O    residual energy                                         */
+                &rate_dist_Q7_subfr,    /* O    best weighted quantization error + mu * rate            */
+                &gain_Q7,               /* O    sum of absolute LTP coefficients                        */
+                XX_Q17_ptr,             /* I    correlation matrix                                      */
+                xX_Q17_ptr,             /* I    correlation vector                                      */
+                cbk_ptr_Q7,             /* I    codebook                                                */
+                cbk_gain_ptr_Q7,        /* I    codebook effective gains                                */
+                cl_ptr_Q5,              /* I    code length for each codebook vector                    */
+                subfr_len,              /* I    number of samples per subframe                          */
+                max_gain_Q7,            /* I    maximum sum of absolute LTP coefficients                */
+                cbk_size,               /* I    number of vectors in codebook                           */
+                arch                    /* I    Run-time architecture                                   */
+            );
+
+            res_nrg_Q15  = silk_ADD_POS_SAT32( res_nrg_Q15, res_nrg_Q15_subfr );
+            rate_dist_Q7 = silk_ADD_POS_SAT32( rate_dist_Q7, rate_dist_Q7_subfr );
+            sum_log_gain_tmp_Q7 = silk_max(0, sum_log_gain_tmp_Q7
+                                + silk_lin2log( gain_safety + gain_Q7 ) - SILK_FIX_CONST( 7, 7 ));
+
+            XX_Q17_ptr += LTP_ORDER * LTP_ORDER;
+            xX_Q17_ptr += LTP_ORDER;
+        }
+
+        if( rate_dist_Q7 <= min_rate_dist_Q7 ) {
+            min_rate_dist_Q7 = rate_dist_Q7;
+            *periodicity_index = (opus_int8)k;
+            silk_memcpy( cbk_index, temp_idx, nb_subfr * sizeof( opus_int8 ) );
+            best_sum_log_gain_Q7 = sum_log_gain_tmp_Q7;
+        }
+    }
+
+    cbk_ptr_Q7 = silk_LTP_vq_ptrs_Q7[ *periodicity_index ];
+    for( j = 0; j < nb_subfr; j++ ) {
+        for( k = 0; k < LTP_ORDER; k++ ) {
+            B_Q14[ j * LTP_ORDER + k ] = silk_LSHIFT( cbk_ptr_Q7[ cbk_index[ j ] * LTP_ORDER + k ], 7 );
+        }
+    }
+
+    if( nb_subfr == 2 ) {
+        res_nrg_Q15 = silk_RSHIFT32( res_nrg_Q15, 1 );
+    } else {
+        res_nrg_Q15 = silk_RSHIFT32( res_nrg_Q15, 2 );
+    }
+
+    *sum_log_gain_Q7 = best_sum_log_gain_Q7;
+    *pred_gain_dB_Q7 = (opus_int)silk_SMULBB( -3, silk_lin2log( res_nrg_Q15 ) - ( 15 << 7 ) );
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/stereo_LR_to_MS.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/stereo_LR_to_MS.c
new file mode 100644 (file)
index 0000000..751452c
--- /dev/null
@@ -0,0 +1,229 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main.h"
+#include "stack_alloc.h"
+
+/* Convert Left/Right stereo signal to adaptive Mid/Side representation */
+void silk_stereo_LR_to_MS(
+    stereo_enc_state            *state,                         /* I/O  State                                       */
+    opus_int16                  x1[],                           /* I/O  Left input signal, becomes mid signal       */
+    opus_int16                  x2[],                           /* I/O  Right input signal, becomes side signal     */
+    opus_int8                   ix[ 2 ][ 3 ],                   /* O    Quantization indices                        */
+    opus_int8                   *mid_only_flag,                 /* O    Flag: only mid signal coded                 */
+    opus_int32                  mid_side_rates_bps[],           /* O    Bitrates for mid and side signals           */
+    opus_int32                  total_rate_bps,                 /* I    Total bitrate                               */
+    opus_int                    prev_speech_act_Q8,             /* I    Speech activity level in previous frame     */
+    opus_int                    toMono,                         /* I    Last frame before a stereo->mono transition */
+    opus_int                    fs_kHz,                         /* I    Sample rate (kHz)                           */
+    opus_int                    frame_length                    /* I    Number of samples                           */
+)
+{
+    opus_int   n, is10msFrame, denom_Q16, delta0_Q13, delta1_Q13;
+    opus_int32 sum, diff, smooth_coef_Q16, pred_Q13[ 2 ], pred0_Q13, pred1_Q13;
+    opus_int32 LP_ratio_Q14, HP_ratio_Q14, frac_Q16, frac_3_Q16, min_mid_rate_bps, width_Q14, w_Q24, deltaw_Q24;
+    VARDECL( opus_int16, side );
+    VARDECL( opus_int16, LP_mid );
+    VARDECL( opus_int16, HP_mid );
+    VARDECL( opus_int16, LP_side );
+    VARDECL( opus_int16, HP_side );
+    opus_int16 *mid = &x1[ -2 ];
+    SAVE_STACK;
+
+    ALLOC( side, frame_length + 2, opus_int16 );
+    /* Convert to basic mid/side signals */
+    for( n = 0; n < frame_length + 2; n++ ) {
+        sum  = x1[ n - 2 ] + (opus_int32)x2[ n - 2 ];
+        diff = x1[ n - 2 ] - (opus_int32)x2[ n - 2 ];
+        mid[  n ] = (opus_int16)silk_RSHIFT_ROUND( sum, 1 );
+        side[ n ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( diff, 1 ) );
+    }
+
+    /* Buffering */
+    silk_memcpy( mid,  state->sMid,  2 * sizeof( opus_int16 ) );
+    silk_memcpy( side, state->sSide, 2 * sizeof( opus_int16 ) );
+    silk_memcpy( state->sMid,  &mid[  frame_length ], 2 * sizeof( opus_int16 ) );
+    silk_memcpy( state->sSide, &side[ frame_length ], 2 * sizeof( opus_int16 ) );
+
+    /* LP and HP filter mid signal */
+    ALLOC( LP_mid, frame_length, opus_int16 );
+    ALLOC( HP_mid, frame_length, opus_int16 );
+    for( n = 0; n < frame_length; n++ ) {
+        sum = silk_RSHIFT_ROUND( silk_ADD_LSHIFT32( mid[ n ] + (opus_int32)mid[ n + 2 ], mid[ n + 1 ], 1 ), 2 );
+        LP_mid[ n ] = sum;
+        HP_mid[ n ] = mid[ n + 1 ] - sum;
+    }
+
+    /* LP and HP filter side signal */
+    ALLOC( LP_side, frame_length, opus_int16 );
+    ALLOC( HP_side, frame_length, opus_int16 );
+    for( n = 0; n < frame_length; n++ ) {
+        sum = silk_RSHIFT_ROUND( silk_ADD_LSHIFT32( side[ n ] + (opus_int32)side[ n + 2 ], side[ n + 1 ], 1 ), 2 );
+        LP_side[ n ] = sum;
+        HP_side[ n ] = side[ n + 1 ] - sum;
+    }
+
+    /* Find energies and predictors */
+    is10msFrame = frame_length == 10 * fs_kHz;
+    smooth_coef_Q16 = is10msFrame ?
+        SILK_FIX_CONST( STEREO_RATIO_SMOOTH_COEF / 2, 16 ) :
+        SILK_FIX_CONST( STEREO_RATIO_SMOOTH_COEF,     16 );
+    smooth_coef_Q16 = silk_SMULWB( silk_SMULBB( prev_speech_act_Q8, prev_speech_act_Q8 ), smooth_coef_Q16 );
+
+    pred_Q13[ 0 ] = silk_stereo_find_predictor( &LP_ratio_Q14, LP_mid, LP_side, &state->mid_side_amp_Q0[ 0 ], frame_length, smooth_coef_Q16 );
+    pred_Q13[ 1 ] = silk_stereo_find_predictor( &HP_ratio_Q14, HP_mid, HP_side, &state->mid_side_amp_Q0[ 2 ], frame_length, smooth_coef_Q16 );
+    /* Ratio of the norms of residual and mid signals */
+    frac_Q16 = silk_SMLABB( HP_ratio_Q14, LP_ratio_Q14, 3 );
+    frac_Q16 = silk_min( frac_Q16, SILK_FIX_CONST( 1, 16 ) );
+
+    /* Determine bitrate distribution between mid and side, and possibly reduce stereo width */
+    total_rate_bps -= is10msFrame ? 1200 : 600;      /* Subtract approximate bitrate for coding stereo parameters */
+    if( total_rate_bps < 1 ) {
+        total_rate_bps = 1;
+    }
+    min_mid_rate_bps = silk_SMLABB( 2000, fs_kHz, 600 );
+    silk_assert( min_mid_rate_bps < 32767 );
+    /* Default bitrate distribution: 8 parts for Mid and (5+3*frac) parts for Side. so: mid_rate = ( 8 / ( 13 + 3 * frac ) ) * total_ rate */
+    frac_3_Q16 = silk_MUL( 3, frac_Q16 );
+    mid_side_rates_bps[ 0 ] = silk_DIV32_varQ( total_rate_bps, SILK_FIX_CONST( 8 + 5, 16 ) + frac_3_Q16, 16+3 );
+    /* If Mid bitrate below minimum, reduce stereo width */
+    if( mid_side_rates_bps[ 0 ] < min_mid_rate_bps ) {
+        mid_side_rates_bps[ 0 ] = min_mid_rate_bps;
+        mid_side_rates_bps[ 1 ] = total_rate_bps - mid_side_rates_bps[ 0 ];
+        /* width = 4 * ( 2 * side_rate - min_rate ) / ( ( 1 + 3 * frac ) * min_rate ) */
+        width_Q14 = silk_DIV32_varQ( silk_LSHIFT( mid_side_rates_bps[ 1 ], 1 ) - min_mid_rate_bps,
+            silk_SMULWB( SILK_FIX_CONST( 1, 16 ) + frac_3_Q16, min_mid_rate_bps ), 14+2 );
+        width_Q14 = silk_LIMIT( width_Q14, 0, SILK_FIX_CONST( 1, 14 ) );
+    } else {
+        mid_side_rates_bps[ 1 ] = total_rate_bps - mid_side_rates_bps[ 0 ];
+        width_Q14 = SILK_FIX_CONST( 1, 14 );
+    }
+
+    /* Smoother */
+    state->smth_width_Q14 = (opus_int16)silk_SMLAWB( state->smth_width_Q14, width_Q14 - state->smth_width_Q14, smooth_coef_Q16 );
+
+    /* At very low bitrates or for inputs that are nearly amplitude panned, switch to panned-mono coding */
+    *mid_only_flag = 0;
+    if( toMono ) {
+        /* Last frame before stereo->mono transition; collapse stereo width */
+        width_Q14 = 0;
+        pred_Q13[ 0 ] = 0;
+        pred_Q13[ 1 ] = 0;
+        silk_stereo_quant_pred( pred_Q13, ix );
+    } else if( state->width_prev_Q14 == 0 &&
+        ( 8 * total_rate_bps < 13 * min_mid_rate_bps || silk_SMULWB( frac_Q16, state->smth_width_Q14 ) < SILK_FIX_CONST( 0.05, 14 ) ) )
+    {
+        /* Code as panned-mono; previous frame already had zero width */
+        /* Scale down and quantize predictors */
+        pred_Q13[ 0 ] = silk_RSHIFT( silk_SMULBB( state->smth_width_Q14, pred_Q13[ 0 ] ), 14 );
+        pred_Q13[ 1 ] = silk_RSHIFT( silk_SMULBB( state->smth_width_Q14, pred_Q13[ 1 ] ), 14 );
+        silk_stereo_quant_pred( pred_Q13, ix );
+        /* Collapse stereo width */
+        width_Q14 = 0;
+        pred_Q13[ 0 ] = 0;
+        pred_Q13[ 1 ] = 0;
+        mid_side_rates_bps[ 0 ] = total_rate_bps;
+        mid_side_rates_bps[ 1 ] = 0;
+        *mid_only_flag = 1;
+    } else if( state->width_prev_Q14 != 0 &&
+        ( 8 * total_rate_bps < 11 * min_mid_rate_bps || silk_SMULWB( frac_Q16, state->smth_width_Q14 ) < SILK_FIX_CONST( 0.02, 14 ) ) )
+    {
+        /* Transition to zero-width stereo */
+        /* Scale down and quantize predictors */
+        pred_Q13[ 0 ] = silk_RSHIFT( silk_SMULBB( state->smth_width_Q14, pred_Q13[ 0 ] ), 14 );
+        pred_Q13[ 1 ] = silk_RSHIFT( silk_SMULBB( state->smth_width_Q14, pred_Q13[ 1 ] ), 14 );
+        silk_stereo_quant_pred( pred_Q13, ix );
+        /* Collapse stereo width */
+        width_Q14 = 0;
+        pred_Q13[ 0 ] = 0;
+        pred_Q13[ 1 ] = 0;
+    } else if( state->smth_width_Q14 > SILK_FIX_CONST( 0.95, 14 ) ) {
+        /* Full-width stereo coding */
+        silk_stereo_quant_pred( pred_Q13, ix );
+        width_Q14 = SILK_FIX_CONST( 1, 14 );
+    } else {
+        /* Reduced-width stereo coding; scale down and quantize predictors */
+        pred_Q13[ 0 ] = silk_RSHIFT( silk_SMULBB( state->smth_width_Q14, pred_Q13[ 0 ] ), 14 );
+        pred_Q13[ 1 ] = silk_RSHIFT( silk_SMULBB( state->smth_width_Q14, pred_Q13[ 1 ] ), 14 );
+        silk_stereo_quant_pred( pred_Q13, ix );
+        width_Q14 = state->smth_width_Q14;
+    }
+
+    /* Make sure to keep on encoding until the tapered output has been transmitted */
+    if( *mid_only_flag == 1 ) {
+        state->silent_side_len += frame_length - STEREO_INTERP_LEN_MS * fs_kHz;
+        if( state->silent_side_len < LA_SHAPE_MS * fs_kHz ) {
+            *mid_only_flag = 0;
+        } else {
+            /* Limit to avoid wrapping around */
+            state->silent_side_len = 10000;
+        }
+    } else {
+        state->silent_side_len = 0;
+    }
+
+    if( *mid_only_flag == 0 && mid_side_rates_bps[ 1 ] < 1 ) {
+        mid_side_rates_bps[ 1 ] = 1;
+        mid_side_rates_bps[ 0 ] = silk_max_int( 1, total_rate_bps - mid_side_rates_bps[ 1 ]);
+    }
+
+    /* Interpolate predictors and subtract prediction from side channel */
+    pred0_Q13  = -state->pred_prev_Q13[ 0 ];
+    pred1_Q13  = -state->pred_prev_Q13[ 1 ];
+    w_Q24      =  silk_LSHIFT( state->width_prev_Q14, 10 );
+    denom_Q16  = silk_DIV32_16( (opus_int32)1 << 16, STEREO_INTERP_LEN_MS * fs_kHz );
+    delta0_Q13 = -silk_RSHIFT_ROUND( silk_SMULBB( pred_Q13[ 0 ] - state->pred_prev_Q13[ 0 ], denom_Q16 ), 16 );
+    delta1_Q13 = -silk_RSHIFT_ROUND( silk_SMULBB( pred_Q13[ 1 ] - state->pred_prev_Q13[ 1 ], denom_Q16 ), 16 );
+    deltaw_Q24 =  silk_LSHIFT( silk_SMULWB( width_Q14 - state->width_prev_Q14, denom_Q16 ), 10 );
+    for( n = 0; n < STEREO_INTERP_LEN_MS * fs_kHz; n++ ) {
+        pred0_Q13 += delta0_Q13;
+        pred1_Q13 += delta1_Q13;
+        w_Q24   += deltaw_Q24;
+        sum = silk_LSHIFT( silk_ADD_LSHIFT32( mid[ n ] + (opus_int32)mid[ n + 2 ], mid[ n + 1 ], 1 ), 9 );    /* Q11 */
+        sum = silk_SMLAWB( silk_SMULWB( w_Q24, side[ n + 1 ] ), sum, pred0_Q13 );               /* Q8  */
+        sum = silk_SMLAWB( sum, silk_LSHIFT( (opus_int32)mid[ n + 1 ], 11 ), pred1_Q13 );       /* Q8  */
+        x2[ n - 1 ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( sum, 8 ) );
+    }
+
+    pred0_Q13 = -pred_Q13[ 0 ];
+    pred1_Q13 = -pred_Q13[ 1 ];
+    w_Q24     =  silk_LSHIFT( width_Q14, 10 );
+    for( n = STEREO_INTERP_LEN_MS * fs_kHz; n < frame_length; n++ ) {
+        sum = silk_LSHIFT( silk_ADD_LSHIFT32( mid[ n ] + (opus_int32)mid[ n + 2 ], mid[ n + 1 ], 1 ), 9 );    /* Q11 */
+        sum = silk_SMLAWB( silk_SMULWB( w_Q24, side[ n + 1 ] ), sum, pred0_Q13 );               /* Q8  */
+        sum = silk_SMLAWB( sum, silk_LSHIFT( (opus_int32)mid[ n + 1 ], 11 ), pred1_Q13 );       /* Q8  */
+        x2[ n - 1 ] = (opus_int16)silk_SAT16( silk_RSHIFT_ROUND( sum, 8 ) );
+    }
+    state->pred_prev_Q13[ 0 ] = (opus_int16)pred_Q13[ 0 ];
+    state->pred_prev_Q13[ 1 ] = (opus_int16)pred_Q13[ 1 ];
+    state->width_prev_Q14     = (opus_int16)width_Q14;
+    RESTORE_STACK;
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/stereo_encode_pred.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/stereo_encode_pred.c
new file mode 100644 (file)
index 0000000..03becb6
--- /dev/null
@@ -0,0 +1,62 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main.h"
+
+/* Entropy code the mid/side quantization indices */
+void silk_stereo_encode_pred(
+    ec_enc                      *psRangeEnc,                    /* I/O  Compressor data structure                   */
+    opus_int8                   ix[ 2 ][ 3 ]                    /* I    Quantization indices                        */
+)
+{
+    opus_int   n;
+
+    /* Entropy coding */
+    n = 5 * ix[ 0 ][ 2 ] + ix[ 1 ][ 2 ];
+    celt_assert( n < 25 );
+    ec_enc_icdf( psRangeEnc, n, silk_stereo_pred_joint_iCDF, 8 );
+    for( n = 0; n < 2; n++ ) {
+        celt_assert( ix[ n ][ 0 ] < 3 );
+        celt_assert( ix[ n ][ 1 ] < STEREO_QUANT_SUB_STEPS );
+        ec_enc_icdf( psRangeEnc, ix[ n ][ 0 ], silk_uniform3_iCDF, 8 );
+        ec_enc_icdf( psRangeEnc, ix[ n ][ 1 ], silk_uniform5_iCDF, 8 );
+    }
+}
+
+/* Entropy code the mid-only flag */
+void silk_stereo_encode_mid_only(
+    ec_enc                      *psRangeEnc,                    /* I/O  Compressor data structure                   */
+    opus_int8                   mid_only_flag
+)
+{
+    /* Encode flag that only mid channel is coded */
+    ec_enc_icdf( psRangeEnc, mid_only_flag, silk_stereo_only_code_mid_iCDF, 8 );
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/silk/stereo_quant_pred.c b/code/sdk/zhihui/test_opus_frame_to_pcm/silk/stereo_quant_pred.c
new file mode 100644 (file)
index 0000000..d4ced6c
--- /dev/null
@@ -0,0 +1,73 @@
+/***********************************************************************
+Copyright (c) 2006-2011, Skype Limited. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+- Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "main.h"
+
+/* Quantize mid/side predictors */
+void silk_stereo_quant_pred(
+    opus_int32                  pred_Q13[],                     /* I/O  Predictors (out: quantized)                 */
+    opus_int8                   ix[ 2 ][ 3 ]                    /* O    Quantization indices                        */
+)
+{
+    opus_int   i, j, n;
+    opus_int32 low_Q13, step_Q13, lvl_Q13, err_min_Q13, err_Q13, quant_pred_Q13 = 0;
+
+    /* Quantize */
+    for( n = 0; n < 2; n++ ) {
+        /* Brute-force search over quantization levels */
+        err_min_Q13 = silk_int32_MAX;
+        for( i = 0; i < STEREO_QUANT_TAB_SIZE - 1; i++ ) {
+            low_Q13 = silk_stereo_pred_quant_Q13[ i ];
+            step_Q13 = silk_SMULWB( silk_stereo_pred_quant_Q13[ i + 1 ] - low_Q13,
+                SILK_FIX_CONST( 0.5 / STEREO_QUANT_SUB_STEPS, 16 ) );
+            for( j = 0; j < STEREO_QUANT_SUB_STEPS; j++ ) {
+                lvl_Q13 = silk_SMLABB( low_Q13, step_Q13, 2 * j + 1 );
+                err_Q13 = silk_abs( pred_Q13[ n ] - lvl_Q13 );
+                if( err_Q13 < err_min_Q13 ) {
+                    err_min_Q13 = err_Q13;
+                    quant_pred_Q13 = lvl_Q13;
+                    ix[ n ][ 0 ] = i;
+                    ix[ n ][ 1 ] = j;
+                } else {
+                    /* Error increasing, so we're past the optimum */
+                    goto done;
+                }
+            }
+        }
+        done:
+        ix[ n ][ 2 ]  = silk_DIV32_16( ix[ n ][ 0 ], 3 );
+        ix[ n ][ 0 ] -= ix[ n ][ 2 ] * 3;
+        pred_Q13[ n ] = quant_pred_Q13;
+    }
+
+    /* Subtract second from first predictor (helps when actually applying these) */
+    pred_Q13[ 0 ] -= pred_Q13[ 1 ];
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/src/opus_pcm_to_frame.c b/code/sdk/zhihui/test_opus_frame_to_pcm/src/opus_pcm_to_frame.c
new file mode 100644 (file)
index 0000000..da1b411
--- /dev/null
@@ -0,0 +1,228 @@
+/* Copyright (c) 2007-2008 CSIRO\r
+   Copyright (c) 2007-2009 Xiph.Org Foundation\r
+   Written by Jean-Marc Valin */\r
+/*\r
+   Redistribution and use in source and binary forms, with or without\r
+   modification, are permitted provided that the following conditions\r
+   are met:\r
+\r
+   - Redistributions of source code must retain the above copyright\r
+   notice, this list of conditions and the following disclaimer.\r
+\r
+   - Redistributions in binary form must reproduce the above copyright\r
+   notice, this list of conditions and the following disclaimer in the\r
+   documentation and/or other materials provided with the distribution.\r
+\r
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r
+   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\r
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\r
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER\r
+   OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\r
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\r
+   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\r
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\r
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\r
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+*/\r
+\r
+#ifdef HAVE_CONFIG_H\r
+#include "config.h"\r
+#endif\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <stdint.h>\r
+#include "opus.h"\r
+#include "opus_frame_to_pcm.h"\r
+\r
+#define MAX_PACKET_SIZE 15000\r
+#define MAX_PCM_SAMPLES 5760  /* 120ms at 48kHz */\r
+\r
+/* Encode PCM buffer to Opus packet - no file I/O */\r
+int opus_pcm_to_frame_encode_packet(const short *pcm, int pcm_samples_per_channel,\r
+                                    int sample_rate, int channels,\r
+                                    int bitrate, int application,\r
+                                    unsigned char *out_packet, int out_packet_max_len,\r
+                                    int *out_encoded_len) {\r
+    OpusEncoder *encoder = NULL;\r
+    int error = OPUS_OK;\r
+    int encoded_bytes = 0;\r
+\r
+    if (!pcm || !out_packet || out_packet_max_len <= 0 ||\r
+        pcm_samples_per_channel <= 0 || sample_rate <= 0 || channels <= 0) {\r
+        return 1;\r
+    }\r
+\r
+    encoder = opus_encoder_create(sample_rate, channels, application, &error);\r
+    if (error != OPUS_OK || !encoder) {\r
+        if (encoder) opus_encoder_destroy(encoder);\r
+        printf("[opus_pcm_to_frame] opus_encoder_create failed: %s (code=%d), rate=%d ch=%d\n",\r
+               opus_strerror(error), error, sample_rate, channels);\r
+        return 1;\r
+    }\r
+\r
+    error = opus_encoder_ctl(encoder, OPUS_SET_BITRATE(bitrate));\r
+    if (error != OPUS_OK) {\r
+        opus_encoder_destroy(encoder);\r
+        return 1;\r
+    }\r
+    error = opus_encoder_ctl(encoder, OPUS_SET_LSB_DEPTH(16));\r
+    if (error != OPUS_OK) {\r
+        opus_encoder_destroy(encoder);\r
+        return 1;\r
+    }\r
+    opus_encoder_ctl(encoder, OPUS_RESET_STATE);\r
+\r
+    encoded_bytes = opus_encode(encoder, (const opus_int16 *)pcm, pcm_samples_per_channel,\r
+                                out_packet, out_packet_max_len);\r
+    opus_encoder_destroy(encoder);\r
+\r
+    if (encoded_bytes <= 0) {\r
+        if (encoded_bytes < 0) {\r
+            printf("[opus_pcm_to_frame] opus_encode error: %s (code=%d), samples=%d, rate=%d\n",\r
+                   opus_strerror(encoded_bytes), encoded_bytes, pcm_samples_per_channel, sample_rate);\r
+        }\r
+        return 1;\r
+    }\r
+    if (out_encoded_len) {\r
+        *out_encoded_len = encoded_bytes;\r
+    }\r
+    return 0;\r
+}\r
+\r
+/* Encode PCM data to a single Opus frame file - external API */\r
+int opus_pcm_to_frame_encode_file(const char *input_file, int sample_rate, int channels,\r
+                                  int bitrate, int application, const char *output_file) {\r
+    OpusEncoder *encoder = NULL;\r
+    int error = OPUS_OK;\r
+    opus_int16 *pcm = NULL;\r
+    FILE *fin = NULL;\r
+    FILE *fout = NULL;\r
+    unsigned char *packet_data = NULL;\r
+    int encoded_bytes = 0;\r
+    int pcm_samples_read = 0;\r
+    \r
+    printf("=== Encoding PCM to Opus Frame ===\n");\r
+    printf("Input: %s\n", input_file);\r
+    printf("Output: %s\n", output_file ? output_file : "(stdout)");\r
+    printf("Sample rate: %d Hz, Channels: %d\n", sample_rate, channels);\r
+    printf("Bitrate: %d bps, Application: %d\n", bitrate, application);\r
+    \r
+    /* Read input file */\r
+    fin = fopen(input_file, "rb");\r
+    if (!fin) {\r
+        fprintf(stderr, "Error opening input file: %s\n", input_file);\r
+        return 1;\r
+    }\r
+    \r
+    /* Allocate buffer for PCM data */\r
+    pcm = (opus_int16*)malloc(MAX_PCM_SAMPLES * channels * sizeof(opus_int16));\r
+    if (!pcm) {\r
+        fprintf(stderr, "Error allocating PCM buffer\n");\r
+        fclose(fin);\r
+        return 1;\r
+    }\r
+    \r
+    /* Read PCM data from file */\r
+    pcm_samples_read = fread(pcm, sizeof(opus_int16), MAX_PCM_SAMPLES * channels, fin);\r
+    fclose(fin);\r
+    \r
+    if (pcm_samples_read <= 0) {\r
+        fprintf(stderr, "Error reading PCM file or file is empty\n");\r
+        free(pcm);\r
+        return 1;\r
+    }\r
+    \r
+    /* Calculate actual samples per channel */\r
+    int samples_per_channel = pcm_samples_read / channels;\r
+    if (samples_per_channel <= 0) {\r
+        fprintf(stderr, "Error: Invalid PCM data (samples per channel: %d)\n", samples_per_channel);\r
+        free(pcm);\r
+        return 1;\r
+    }\r
+    \r
+    printf("Read %d samples per channel (%d total samples)\n", samples_per_channel, pcm_samples_read);\r
+    \r
+    /* Create encoder */\r
+    encoder = opus_encoder_create(sample_rate, channels, application, &error);\r
+    if (error != OPUS_OK) {\r
+        fprintf(stderr, "Error creating encoder: %s\n", opus_strerror(error));\r
+        free(pcm);\r
+        return 1;\r
+    }\r
+    \r
+    /* Set encoder parameters */\r
+    error = opus_encoder_ctl(encoder, OPUS_SET_BITRATE(bitrate));\r
+    if (error != OPUS_OK) {\r
+        fprintf(stderr, "Warning: Failed to set bitrate: %s\n", opus_strerror(error));\r
+    }\r
+    \r
+    /* Set LSB depth to 16 bits for 16-bit PCM input */\r
+    error = opus_encoder_ctl(encoder, OPUS_SET_LSB_DEPTH(16));\r
+    if (error != OPUS_OK) {\r
+        fprintf(stderr, "Warning: Failed to set LSB depth: %s\n", opus_strerror(error));\r
+    }\r
+    \r
+    /* Reset encoder state to ensure clean encoding */\r
+    opus_encoder_ctl(encoder, OPUS_RESET_STATE);\r
+    \r
+    /* Allocate buffer for encoded packet */\r
+    packet_data = (unsigned char*)malloc(MAX_PACKET_SIZE);\r
+    if (!packet_data) {\r
+        fprintf(stderr, "Error allocating packet buffer\n");\r
+        opus_encoder_destroy(encoder);\r
+        free(pcm);\r
+        return 1;\r
+    }\r
+    \r
+    /* Encode PCM data */\r
+    encoded_bytes = opus_encode(encoder, pcm, samples_per_channel, packet_data, MAX_PACKET_SIZE);\r
+    if (encoded_bytes <= 0) {\r
+        fprintf(stderr, "Error encoding PCM data: %s\n", opus_strerror(encoded_bytes));\r
+        opus_encoder_destroy(encoder);\r
+        free(pcm);\r
+        free(packet_data);\r
+        return 1;\r
+    }\r
+    \r
+    printf("Encoded %d bytes\n", encoded_bytes);\r
+    \r
+    /* Open output file */\r
+    if (output_file) {\r
+        fout = fopen(output_file, "wb");\r
+        if (!fout) {\r
+            fprintf(stderr, "Error opening output file: %s\n", output_file);\r
+            opus_encoder_destroy(encoder);\r
+            free(pcm);\r
+            free(packet_data);\r
+            return 1;\r
+        }\r
+    } else {\r
+        fout = stdout;\r
+    }\r
+    \r
+    /* Write encoded packet data */\r
+    if (fwrite(packet_data, 1, encoded_bytes, fout) != encoded_bytes) {\r
+        fprintf(stderr, "Error writing Opus packet data\n");\r
+        if (fout != stdout) fclose(fout);\r
+        opus_encoder_destroy(encoder);\r
+        free(pcm);\r
+        free(packet_data);\r
+        return 1;\r
+    }\r
+    \r
+    if (fout != stdout) {\r
+        fclose(fout);\r
+        printf("Saved Opus frame to: %s\n", output_file);\r
+        printf("Opus file size: %d bytes\n", encoded_bytes);\r
+    }\r
+    \r
+    /* Cleanup */\r
+    opus_encoder_destroy(encoder);\r
+    free(pcm);\r
+    free(packet_data);\r
+    \r
+    return 0;\r
+}\r
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/src_lib/analysis.c b/code/sdk/zhihui/test_opus_frame_to_pcm/src_lib/analysis.c
new file mode 100644 (file)
index 0000000..c353230
--- /dev/null
@@ -0,0 +1,982 @@
+/* Copyright (c) 2011 Xiph.Org Foundation
+   Written by Jean-Marc Valin */
+/*
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+   - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+   - Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#define ANALYSIS_C
+
+#ifdef MLP_TRAINING
+#include <stdio.h>
+#endif
+
+#include "mathops.h"
+#include "kiss_fft.h"
+#include "celt.h"
+#include "modes.h"
+#include "arch.h"
+#include "quant_bands.h"
+#include "analysis.h"
+#include "mlp.h"
+#include "stack_alloc.h"
+#include "float_cast.h"
+
+#ifndef M_PI
+#define M_PI 3.141592653
+#endif
+
+#ifndef DISABLE_FLOAT_API
+
+#define TRANSITION_PENALTY 10
+
+static const float dct_table[128] = {
+        0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f,
+        0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f, 0.250000f,
+        0.351851f, 0.338330f, 0.311806f, 0.273300f, 0.224292f, 0.166664f, 0.102631f, 0.034654f,
+       -0.034654f,-0.102631f,-0.166664f,-0.224292f,-0.273300f,-0.311806f,-0.338330f,-0.351851f,
+        0.346760f, 0.293969f, 0.196424f, 0.068975f,-0.068975f,-0.196424f,-0.293969f,-0.346760f,
+       -0.346760f,-0.293969f,-0.196424f,-0.068975f, 0.068975f, 0.196424f, 0.293969f, 0.346760f,
+        0.338330f, 0.224292f, 0.034654f,-0.166664f,-0.311806f,-0.351851f,-0.273300f,-0.102631f,
+        0.102631f, 0.273300f, 0.351851f, 0.311806f, 0.166664f,-0.034654f,-0.224292f,-0.338330f,
+        0.326641f, 0.135299f,-0.135299f,-0.326641f,-0.326641f,-0.135299f, 0.135299f, 0.326641f,
+        0.326641f, 0.135299f,-0.135299f,-0.326641f,-0.326641f,-0.135299f, 0.135299f, 0.326641f,
+        0.311806f, 0.034654f,-0.273300f,-0.338330f,-0.102631f, 0.224292f, 0.351851f, 0.166664f,
+       -0.166664f,-0.351851f,-0.224292f, 0.102631f, 0.338330f, 0.273300f,-0.034654f,-0.311806f,
+        0.293969f,-0.068975f,-0.346760f,-0.196424f, 0.196424f, 0.346760f, 0.068975f,-0.293969f,
+       -0.293969f, 0.068975f, 0.346760f, 0.196424f,-0.196424f,-0.346760f,-0.068975f, 0.293969f,
+        0.273300f,-0.166664f,-0.338330f, 0.034654f, 0.351851f, 0.102631f,-0.311806f,-0.224292f,
+        0.224292f, 0.311806f,-0.102631f,-0.351851f,-0.034654f, 0.338330f, 0.166664f,-0.273300f,
+};
+
+static const float analysis_window[240] = {
+      0.000043f, 0.000171f, 0.000385f, 0.000685f, 0.001071f, 0.001541f, 0.002098f, 0.002739f,
+      0.003466f, 0.004278f, 0.005174f, 0.006156f, 0.007222f, 0.008373f, 0.009607f, 0.010926f,
+      0.012329f, 0.013815f, 0.015385f, 0.017037f, 0.018772f, 0.020590f, 0.022490f, 0.024472f,
+      0.026535f, 0.028679f, 0.030904f, 0.033210f, 0.035595f, 0.038060f, 0.040604f, 0.043227f,
+      0.045928f, 0.048707f, 0.051564f, 0.054497f, 0.057506f, 0.060591f, 0.063752f, 0.066987f,
+      0.070297f, 0.073680f, 0.077136f, 0.080665f, 0.084265f, 0.087937f, 0.091679f, 0.095492f,
+      0.099373f, 0.103323f, 0.107342f, 0.111427f, 0.115579f, 0.119797f, 0.124080f, 0.128428f,
+      0.132839f, 0.137313f, 0.141849f, 0.146447f, 0.151105f, 0.155823f, 0.160600f, 0.165435f,
+      0.170327f, 0.175276f, 0.180280f, 0.185340f, 0.190453f, 0.195619f, 0.200838f, 0.206107f,
+      0.211427f, 0.216797f, 0.222215f, 0.227680f, 0.233193f, 0.238751f, 0.244353f, 0.250000f,
+      0.255689f, 0.261421f, 0.267193f, 0.273005f, 0.278856f, 0.284744f, 0.290670f, 0.296632f,
+      0.302628f, 0.308658f, 0.314721f, 0.320816f, 0.326941f, 0.333097f, 0.339280f, 0.345492f,
+      0.351729f, 0.357992f, 0.364280f, 0.370590f, 0.376923f, 0.383277f, 0.389651f, 0.396044f,
+      0.402455f, 0.408882f, 0.415325f, 0.421783f, 0.428254f, 0.434737f, 0.441231f, 0.447736f,
+      0.454249f, 0.460770f, 0.467298f, 0.473832f, 0.480370f, 0.486912f, 0.493455f, 0.500000f,
+      0.506545f, 0.513088f, 0.519630f, 0.526168f, 0.532702f, 0.539230f, 0.545751f, 0.552264f,
+      0.558769f, 0.565263f, 0.571746f, 0.578217f, 0.584675f, 0.591118f, 0.597545f, 0.603956f,
+      0.610349f, 0.616723f, 0.623077f, 0.629410f, 0.635720f, 0.642008f, 0.648271f, 0.654508f,
+      0.660720f, 0.666903f, 0.673059f, 0.679184f, 0.685279f, 0.691342f, 0.697372f, 0.703368f,
+      0.709330f, 0.715256f, 0.721144f, 0.726995f, 0.732807f, 0.738579f, 0.744311f, 0.750000f,
+      0.755647f, 0.761249f, 0.766807f, 0.772320f, 0.777785f, 0.783203f, 0.788573f, 0.793893f,
+      0.799162f, 0.804381f, 0.809547f, 0.814660f, 0.819720f, 0.824724f, 0.829673f, 0.834565f,
+      0.839400f, 0.844177f, 0.848895f, 0.853553f, 0.858151f, 0.862687f, 0.867161f, 0.871572f,
+      0.875920f, 0.880203f, 0.884421f, 0.888573f, 0.892658f, 0.896677f, 0.900627f, 0.904508f,
+      0.908321f, 0.912063f, 0.915735f, 0.919335f, 0.922864f, 0.926320f, 0.929703f, 0.933013f,
+      0.936248f, 0.939409f, 0.942494f, 0.945503f, 0.948436f, 0.951293f, 0.954072f, 0.956773f,
+      0.959396f, 0.961940f, 0.964405f, 0.966790f, 0.969096f, 0.971321f, 0.973465f, 0.975528f,
+      0.977510f, 0.979410f, 0.981228f, 0.982963f, 0.984615f, 0.986185f, 0.987671f, 0.989074f,
+      0.990393f, 0.991627f, 0.992778f, 0.993844f, 0.994826f, 0.995722f, 0.996534f, 0.997261f,
+      0.997902f, 0.998459f, 0.998929f, 0.999315f, 0.999615f, 0.999829f, 0.999957f, 1.000000f,
+};
+
+static const int tbands[NB_TBANDS+1] = {
+      4, 8, 12, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 136, 160, 192, 240
+};
+
+#define NB_TONAL_SKIP_BANDS 9
+
+static opus_val32 silk_resampler_down2_hp(
+    opus_val32                  *S,                 /* I/O  State vector [ 2 ]                                          */
+    opus_val32                  *out,               /* O    Output signal [ floor(len/2) ]                              */
+    const opus_val32            *in,                /* I    Input signal [ len ]                                        */
+    int                         inLen               /* I    Number of input samples                                     */
+)
+{
+    int k, len2 = inLen/2;
+    opus_val32 in32, out32, out32_hp, Y, X;
+    opus_val64 hp_ener = 0;
+    /* Internal variables and state are in Q10 format */
+    for( k = 0; k < len2; k++ ) {
+        /* Convert to Q10 */
+        in32 = in[ 2 * k ];
+
+        /* All-pass section for even input sample */
+        Y      = SUB32( in32, S[ 0 ] );
+        X      = MULT16_32_Q15(QCONST16(0.6074371f, 15), Y);
+        out32  = ADD32( S[ 0 ], X );
+        S[ 0 ] = ADD32( in32, X );
+        out32_hp = out32;
+        /* Convert to Q10 */
+        in32 = in[ 2 * k + 1 ];
+
+        /* All-pass section for odd input sample, and add to output of previous section */
+        Y      = SUB32( in32, S[ 1 ] );
+        X      = MULT16_32_Q15(QCONST16(0.15063f, 15), Y);
+        out32  = ADD32( out32, S[ 1 ] );
+        out32  = ADD32( out32, X );
+        S[ 1 ] = ADD32( in32, X );
+
+        Y      = SUB32( -in32, S[ 2 ] );
+        X      = MULT16_32_Q15(QCONST16(0.15063f, 15), Y);
+        out32_hp  = ADD32( out32_hp, S[ 2 ] );
+        out32_hp  = ADD32( out32_hp, X );
+        S[ 2 ] = ADD32( -in32, X );
+
+        /* len2 can be up to 480, so we shift by 8 to make it fit. */
+        hp_ener += SHR64(out32_hp*(opus_val64)out32_hp, 8);
+        /* Add, convert back to int16 and store to output */
+        out[ k ] = HALF32(out32);
+    }
+#ifdef FIXED_POINT
+    /* Fitting in 32 bits. */
+    hp_ener = hp_ener >> (2*SIG_SHIFT);
+    if (hp_ener > 2147483647) hp_ener = 2147483647;
+#endif
+    return (opus_val32)hp_ener;
+}
+
+static opus_val32 downmix_and_resample(downmix_func downmix, const void *_x, opus_val32 *y, opus_val32 S[3], int subframe, int offset, int c1, int c2, int C, int Fs)
+{
+   VARDECL(opus_val32, tmp);
+   int j;
+   opus_val32 ret = 0;
+   SAVE_STACK;
+
+   if (subframe==0) return 0;
+   if (Fs == 48000)
+   {
+      subframe *= 2;
+      offset *= 2;
+   } else if (Fs == 16000) {
+      subframe = subframe*2/3;
+      offset = offset*2/3;
+   }
+   else if (Fs != 24000) celt_assert(0);
+   ALLOC(tmp, subframe, opus_val32);
+
+   downmix(_x, tmp, subframe, offset, c1, c2, C);
+   if ((c2==-2 && C==2) || c2>-1) {
+      for (j=0;j<subframe;j++) {
+         tmp[j] = HALF32(tmp[j]);
+      }
+   }
+   if (Fs == 48000)
+   {
+      ret = silk_resampler_down2_hp(S, y, tmp, subframe);
+   } else if (Fs == 24000) {
+      OPUS_COPY(y, tmp, subframe);
+   } else if (Fs == 16000) {
+      VARDECL(opus_val32, tmp3x);
+      ALLOC(tmp3x, 3*subframe, opus_val32);
+      /* Don't do this at home! This resampler is horrible and it's only (barely)
+         usable for the purpose of the analysis because we don't care about all
+         the aliasing between 8 kHz and 12 kHz. */
+      for (j=0;j<subframe;j++)
+      {
+         tmp3x[3*j] = tmp[j];
+         tmp3x[3*j+1] = tmp[j];
+         tmp3x[3*j+2] = tmp[j];
+      }
+      silk_resampler_down2_hp(S, y, tmp3x, 3*subframe);
+   }
+   RESTORE_STACK;
+#ifndef FIXED_POINT
+   ret *= 1.f/32768/32768;
+#endif
+   return ret;
+}
+
+void tonality_analysis_init(TonalityAnalysisState *tonal, opus_int32 Fs)
+{
+  /* Initialize reusable fields. */
+  tonal->arch = opus_select_arch();
+  tonal->Fs = Fs;
+  /* Clear remaining fields. */
+  tonality_analysis_reset(tonal);
+}
+
+void tonality_analysis_reset(TonalityAnalysisState *tonal)
+{
+  /* Clear non-reusable fields. */
+  char *start = (char*)&tonal->TONALITY_ANALYSIS_RESET_START;
+  OPUS_CLEAR(start, sizeof(TonalityAnalysisState) - (start - (char*)tonal));
+}
+
+void tonality_get_info(TonalityAnalysisState *tonal, AnalysisInfo *info_out, int len)
+{
+   int pos;
+   int curr_lookahead;
+   float tonality_max;
+   float tonality_avg;
+   int tonality_count;
+   int i;
+   int pos0;
+   float prob_avg;
+   float prob_count;
+   float prob_min, prob_max;
+   float vad_prob;
+   int mpos, vpos;
+   int bandwidth_span;
+
+   pos = tonal->read_pos;
+   curr_lookahead = tonal->write_pos-tonal->read_pos;
+   if (curr_lookahead<0)
+      curr_lookahead += DETECT_SIZE;
+
+   tonal->read_subframe += len/(tonal->Fs/400);
+   while (tonal->read_subframe>=8)
+   {
+      tonal->read_subframe -= 8;
+      tonal->read_pos++;
+   }
+   if (tonal->read_pos>=DETECT_SIZE)
+      tonal->read_pos-=DETECT_SIZE;
+
+   /* On long frames, look at the second analysis window rather than the first. */
+   if (len > tonal->Fs/50 && pos != tonal->write_pos)
+   {
+      pos++;
+      if (pos==DETECT_SIZE)
+         pos=0;
+   }
+   if (pos == tonal->write_pos)
+      pos--;
+   if (pos<0)
+      pos = DETECT_SIZE-1;
+   pos0 = pos;
+   OPUS_COPY(info_out, &tonal->info[pos], 1);
+   if (!info_out->valid)
+      return;
+   tonality_max = tonality_avg = info_out->tonality;
+   tonality_count = 1;
+   /* Look at the neighbouring frames and pick largest bandwidth found (to be safe). */
+   bandwidth_span = 6;
+   /* If possible, look ahead for a tone to compensate for the delay in the tone detector. */
+   for (i=0;i<3;i++)
+   {
+      pos++;
+      if (pos==DETECT_SIZE)
+         pos = 0;
+      if (pos == tonal->write_pos)
+         break;
+      tonality_max = MAX32(tonality_max, tonal->info[pos].tonality);
+      tonality_avg += tonal->info[pos].tonality;
+      tonality_count++;
+      info_out->bandwidth = IMAX(info_out->bandwidth, tonal->info[pos].bandwidth);
+      bandwidth_span--;
+   }
+   pos = pos0;
+   /* Look back in time to see if any has a wider bandwidth than the current frame. */
+   for (i=0;i<bandwidth_span;i++)
+   {
+      pos--;
+      if (pos < 0)
+         pos = DETECT_SIZE-1;
+      if (pos == tonal->write_pos)
+         break;
+      info_out->bandwidth = IMAX(info_out->bandwidth, tonal->info[pos].bandwidth);
+   }
+   info_out->tonality = MAX32(tonality_avg/tonality_count, tonality_max-.2f);
+
+   mpos = vpos = pos0;
+   /* If we have enough look-ahead, compensate for the ~5-frame delay in the music prob and
+      ~1 frame delay in the VAD prob. */
+   if (curr_lookahead > 15)
+   {
+      mpos += 5;
+      if (mpos>=DETECT_SIZE)
+         mpos -= DETECT_SIZE;
+      vpos += 1;
+      if (vpos>=DETECT_SIZE)
+         vpos -= DETECT_SIZE;
+   }
+
+   /* The following calculations attempt to minimize a "badness function"
+      for the transition. When switching from speech to music, the badness
+      of switching at frame k is
+      b_k = S*v_k + \sum_{i=0}^{k-1} v_i*(p_i - T)
+      where
+      v_i is the activity probability (VAD) at frame i,
+      p_i is the music probability at frame i
+      T is the probability threshold for switching
+      S is the penalty for switching during active audio rather than silence
+      the current frame has index i=0
+
+      Rather than apply badness to directly decide when to switch, what we compute
+      instead is the threshold for which the optimal switching point is now. When
+      considering whether to switch now (frame 0) or at frame k, we have:
+      S*v_0 = S*v_k + \sum_{i=0}^{k-1} v_i*(p_i - T)
+      which gives us:
+      T = ( \sum_{i=0}^{k-1} v_i*p_i + S*(v_k-v_0) ) / ( \sum_{i=0}^{k-1} v_i )
+      We take the min threshold across all positive values of k (up to the maximum
+      amount of lookahead we have) to give us the threshold for which the current
+      frame is the optimal switch point.
+
+      The last step is that we need to consider whether we want to switch at all.
+      For that we use the average of the music probability over the entire window.
+      If the threshold is higher than that average we're not going to
+      switch, so we compute a min with the average as well. The result of all these
+      min operations is music_prob_min, which gives the threshold for switching to music
+      if we're currently encoding for speech.
+
+      We do the exact opposite to compute music_prob_max which is used for switching
+      from music to speech.
+    */
+   prob_min = 1.f;
+   prob_max = 0.f;
+   vad_prob = tonal->info[vpos].activity_probability;
+   prob_count = MAX16(.1f, vad_prob);
+   prob_avg = MAX16(.1f, vad_prob)*tonal->info[mpos].music_prob;
+   while (1)
+   {
+      float pos_vad;
+      mpos++;
+      if (mpos==DETECT_SIZE)
+         mpos = 0;
+      if (mpos == tonal->write_pos)
+         break;
+      vpos++;
+      if (vpos==DETECT_SIZE)
+         vpos = 0;
+      if (vpos == tonal->write_pos)
+         break;
+      pos_vad = tonal->info[vpos].activity_probability;
+      prob_min = MIN16((prob_avg - TRANSITION_PENALTY*(vad_prob - pos_vad))/prob_count, prob_min);
+      prob_max = MAX16((prob_avg + TRANSITION_PENALTY*(vad_prob - pos_vad))/prob_count, prob_max);
+      prob_count += MAX16(.1f, pos_vad);
+      prob_avg += MAX16(.1f, pos_vad)*tonal->info[mpos].music_prob;
+   }
+   info_out->music_prob = prob_avg/prob_count;
+   prob_min = MIN16(prob_avg/prob_count, prob_min);
+   prob_max = MAX16(prob_avg/prob_count, prob_max);
+   prob_min = MAX16(prob_min, 0.f);
+   prob_max = MIN16(prob_max, 1.f);
+
+   /* If we don't have enough look-ahead, do our best to make a decent decision. */
+   if (curr_lookahead < 10)
+   {
+      float pmin, pmax;
+      pmin = prob_min;
+      pmax = prob_max;
+      pos = pos0;
+      /* Look for min/max in the past. */
+      for (i=0;i<IMIN(tonal->count-1, 15);i++)
+      {
+         pos--;
+         if (pos < 0)
+            pos = DETECT_SIZE-1;
+         pmin = MIN16(pmin, tonal->info[pos].music_prob);
+         pmax = MAX16(pmax, tonal->info[pos].music_prob);
+      }
+      /* Bias against switching on active audio. */
+      pmin = MAX16(0.f, pmin - .1f*vad_prob);
+      pmax = MIN16(1.f, pmax + .1f*vad_prob);
+      prob_min += (1.f-.1f*curr_lookahead)*(pmin - prob_min);
+      prob_max += (1.f-.1f*curr_lookahead)*(pmax - prob_max);
+   }
+   info_out->music_prob_min = prob_min;
+   info_out->music_prob_max = prob_max;
+
+   /* printf("%f %f %f %f %f\n", prob_min, prob_max, prob_avg/prob_count, vad_prob, info_out->music_prob); */
+}
+
+static const float std_feature_bias[9] = {
+      5.684947f, 3.475288f, 1.770634f, 1.599784f, 3.773215f,
+      2.163313f, 1.260756f, 1.116868f, 1.918795f
+};
+
+#define LEAKAGE_OFFSET 2.5f
+#define LEAKAGE_SLOPE 2.f
+
+#ifdef FIXED_POINT
+/* For fixed-point, the input is +/-2^15 shifted up by SIG_SHIFT, so we need to
+   compensate for that in the energy. */
+#define SCALE_COMPENS (1.f/((opus_int32)1<<(15+SIG_SHIFT)))
+#define SCALE_ENER(e) ((SCALE_COMPENS*SCALE_COMPENS)*(e))
+#else
+#define SCALE_ENER(e) ((1.f/32768/32768)*e)
+#endif
+
+#ifdef FIXED_POINT
+static int is_digital_silence32(const opus_val32* pcm, int frame_size, int channels, int lsb_depth)
+{
+   int silence = 0;
+   opus_val32 sample_max = 0;
+#ifdef MLP_TRAINING
+   return 0;
+#endif
+   sample_max = celt_maxabs32(pcm, frame_size*channels);
+
+   silence = (sample_max == 0);
+   (void)lsb_depth;
+   return silence;
+}
+#else
+#define is_digital_silence32(pcm, frame_size, channels, lsb_depth) is_digital_silence(pcm, frame_size, channels, lsb_depth)
+#endif
+
+static void tonality_analysis(TonalityAnalysisState *tonal, const CELTMode *celt_mode, const void *x, int len, int offset, int c1, int c2, int C, int lsb_depth, downmix_func downmix)
+{
+    int i, b;
+    const kiss_fft_state *kfft;
+    VARDECL(kiss_fft_cpx, in);
+    VARDECL(kiss_fft_cpx, out);
+    int N = 480, N2=240;
+    float * OPUS_RESTRICT A = tonal->angle;
+    float * OPUS_RESTRICT dA = tonal->d_angle;
+    float * OPUS_RESTRICT d2A = tonal->d2_angle;
+    VARDECL(float, tonality);
+    VARDECL(float, noisiness);
+    float band_tonality[NB_TBANDS];
+    float logE[NB_TBANDS];
+    float BFCC[8];
+    float features[25];
+    float frame_tonality;
+    float max_frame_tonality;
+    /*float tw_sum=0;*/
+    float frame_noisiness;
+    const float pi4 = (float)(M_PI*M_PI*M_PI*M_PI);
+    float slope=0;
+    float frame_stationarity;
+    float relativeE;
+    float frame_probs[2];
+    float alpha, alphaE, alphaE2;
+    float frame_loudness;
+    float bandwidth_mask;
+    int is_masked[NB_TBANDS+1];
+    int bandwidth=0;
+    float maxE = 0;
+    float noise_floor;
+    int remaining;
+    AnalysisInfo *info;
+    float hp_ener;
+    float tonality2[240];
+    float midE[8];
+    float spec_variability=0;
+    float band_log2[NB_TBANDS+1];
+    float leakage_from[NB_TBANDS+1];
+    float leakage_to[NB_TBANDS+1];
+    float layer_out[MAX_NEURONS];
+    float below_max_pitch;
+    float above_max_pitch;
+    int is_silence;
+    SAVE_STACK;
+
+    if (!tonal->initialized)
+    {
+       tonal->mem_fill = 240;
+       tonal->initialized = 1;
+    }
+    alpha = 1.f/IMIN(10, 1+tonal->count);
+    alphaE = 1.f/IMIN(25, 1+tonal->count);
+    /* Noise floor related decay for bandwidth detection: -2.2 dB/second */
+    alphaE2 = 1.f/IMIN(100, 1+tonal->count);
+    if (tonal->count <= 1) alphaE2 = 1;
+
+    if (tonal->Fs == 48000)
+    {
+       /* len and offset are now at 24 kHz. */
+       len/= 2;
+       offset /= 2;
+    } else if (tonal->Fs == 16000) {
+       len = 3*len/2;
+       offset = 3*offset/2;
+    }
+
+    kfft = celt_mode->mdct.kfft[0];
+    tonal->hp_ener_accum += (float)downmix_and_resample(downmix, x,
+          &tonal->inmem[tonal->mem_fill], tonal->downmix_state,
+          IMIN(len, ANALYSIS_BUF_SIZE-tonal->mem_fill), offset, c1, c2, C, tonal->Fs);
+    if (tonal->mem_fill+len < ANALYSIS_BUF_SIZE)
+    {
+       tonal->mem_fill += len;
+       /* Don't have enough to update the analysis */
+       RESTORE_STACK;
+       return;
+    }
+    hp_ener = tonal->hp_ener_accum;
+    info = &tonal->info[tonal->write_pos++];
+    if (tonal->write_pos>=DETECT_SIZE)
+       tonal->write_pos-=DETECT_SIZE;
+
+    is_silence = is_digital_silence32(tonal->inmem, ANALYSIS_BUF_SIZE, 1, lsb_depth);
+
+    ALLOC(in, 480, kiss_fft_cpx);
+    ALLOC(out, 480, kiss_fft_cpx);
+    ALLOC(tonality, 240, float);
+    ALLOC(noisiness, 240, float);
+    for (i=0;i<N2;i++)
+    {
+       float w = analysis_window[i];
+       in[i].r = (kiss_fft_scalar)(w*tonal->inmem[i]);
+       in[i].i = (kiss_fft_scalar)(w*tonal->inmem[N2+i]);
+       in[N-i-1].r = (kiss_fft_scalar)(w*tonal->inmem[N-i-1]);
+       in[N-i-1].i = (kiss_fft_scalar)(w*tonal->inmem[N+N2-i-1]);
+    }
+    OPUS_MOVE(tonal->inmem, tonal->inmem+ANALYSIS_BUF_SIZE-240, 240);
+    remaining = len - (ANALYSIS_BUF_SIZE-tonal->mem_fill);
+    tonal->hp_ener_accum = (float)downmix_and_resample(downmix, x,
+          &tonal->inmem[240], tonal->downmix_state, remaining,
+          offset+ANALYSIS_BUF_SIZE-tonal->mem_fill, c1, c2, C, tonal->Fs);
+    tonal->mem_fill = 240 + remaining;
+    if (is_silence)
+    {
+       /* On silence, copy the previous analysis. */
+       int prev_pos = tonal->write_pos-2;
+       if (prev_pos < 0)
+          prev_pos += DETECT_SIZE;
+       OPUS_COPY(info, &tonal->info[prev_pos], 1);
+       RESTORE_STACK;
+       return;
+    }
+    opus_fft(kfft, in, out, tonal->arch);
+#ifndef FIXED_POINT
+    /* If there's any NaN on the input, the entire output will be NaN, so we only need to check one value. */
+    if (celt_isnan(out[0].r))
+    {
+       info->valid = 0;
+       RESTORE_STACK;
+       return;
+    }
+#endif
+
+    for (i=1;i<N2;i++)
+    {
+       float X1r, X2r, X1i, X2i;
+       float angle, d_angle, d2_angle;
+       float angle2, d_angle2, d2_angle2;
+       float mod1, mod2, avg_mod;
+       X1r = (float)out[i].r+out[N-i].r;
+       X1i = (float)out[i].i-out[N-i].i;
+       X2r = (float)out[i].i+out[N-i].i;
+       X2i = (float)out[N-i].r-out[i].r;
+
+       angle = (float)(.5f/M_PI)*fast_atan2f(X1i, X1r);
+       d_angle = angle - A[i];
+       d2_angle = d_angle - dA[i];
+
+       angle2 = (float)(.5f/M_PI)*fast_atan2f(X2i, X2r);
+       d_angle2 = angle2 - angle;
+       d2_angle2 = d_angle2 - d_angle;
+
+       mod1 = d2_angle - (float)float2int(d2_angle);
+       noisiness[i] = ABS16(mod1);
+       mod1 *= mod1;
+       mod1 *= mod1;
+
+       mod2 = d2_angle2 - (float)float2int(d2_angle2);
+       noisiness[i] += ABS16(mod2);
+       mod2 *= mod2;
+       mod2 *= mod2;
+
+       avg_mod = .25f*(d2A[i]+mod1+2*mod2);
+       /* This introduces an extra delay of 2 frames in the detection. */
+       tonality[i] = 1.f/(1.f+40.f*16.f*pi4*avg_mod)-.015f;
+       /* No delay on this detection, but it's less reliable. */
+       tonality2[i] = 1.f/(1.f+40.f*16.f*pi4*mod2)-.015f;
+
+       A[i] = angle2;
+       dA[i] = d_angle2;
+       d2A[i] = mod2;
+    }
+    for (i=2;i<N2-1;i++)
+    {
+       float tt = MIN32(tonality2[i], MAX32(tonality2[i-1], tonality2[i+1]));
+       tonality[i] = .9f*MAX32(tonality[i], tt-.1f);
+    }
+    frame_tonality = 0;
+    max_frame_tonality = 0;
+    /*tw_sum = 0;*/
+    info->activity = 0;
+    frame_noisiness = 0;
+    frame_stationarity = 0;
+    if (!tonal->count)
+    {
+       for (b=0;b<NB_TBANDS;b++)
+       {
+          tonal->lowE[b] = 1e10;
+          tonal->highE[b] = -1e10;
+       }
+    }
+    relativeE = 0;
+    frame_loudness = 0;
+    /* The energy of the very first band is special because of DC. */
+    {
+       float E = 0;
+       float X1r, X2r;
+       X1r = 2*(float)out[0].r;
+       X2r = 2*(float)out[0].i;
+       E = X1r*X1r + X2r*X2r;
+       for (i=1;i<4;i++)
+       {
+          float binE = out[i].r*(float)out[i].r + out[N-i].r*(float)out[N-i].r
+                     + out[i].i*(float)out[i].i + out[N-i].i*(float)out[N-i].i;
+          E += binE;
+       }
+       E = SCALE_ENER(E);
+       band_log2[0] = .5f*1.442695f*(float)log(E+1e-10f);
+    }
+    for (b=0;b<NB_TBANDS;b++)
+    {
+       float E=0, tE=0, nE=0;
+       float L1, L2;
+       float stationarity;
+       for (i=tbands[b];i<tbands[b+1];i++)
+       {
+          float binE = out[i].r*(float)out[i].r + out[N-i].r*(float)out[N-i].r
+                     + out[i].i*(float)out[i].i + out[N-i].i*(float)out[N-i].i;
+          binE = SCALE_ENER(binE);
+          E += binE;
+          tE += binE*MAX32(0, tonality[i]);
+          nE += binE*2.f*(.5f-noisiness[i]);
+       }
+#ifndef FIXED_POINT
+       /* Check for extreme band energies that could cause NaNs later. */
+       if (!(E<1e9f) || celt_isnan(E))
+       {
+          info->valid = 0;
+          RESTORE_STACK;
+          return;
+       }
+#endif
+
+       tonal->E[tonal->E_count][b] = E;
+       frame_noisiness += nE/(1e-15f+E);
+
+       frame_loudness += (float)sqrt(E+1e-10f);
+       logE[b] = (float)log(E+1e-10f);
+       band_log2[b+1] = .5f*1.442695f*(float)log(E+1e-10f);
+       tonal->logE[tonal->E_count][b] = logE[b];
+       if (tonal->count==0)
+          tonal->highE[b] = tonal->lowE[b] = logE[b];
+       if (tonal->highE[b] > tonal->lowE[b] + 7.5)
+       {
+          if (tonal->highE[b] - logE[b] > logE[b] - tonal->lowE[b])
+             tonal->highE[b] -= .01f;
+          else
+             tonal->lowE[b] += .01f;
+       }
+       if (logE[b] > tonal->highE[b])
+       {
+          tonal->highE[b] = logE[b];
+          tonal->lowE[b] = MAX32(tonal->highE[b]-15, tonal->lowE[b]);
+       } else if (logE[b] < tonal->lowE[b])
+       {
+          tonal->lowE[b] = logE[b];
+          tonal->highE[b] = MIN32(tonal->lowE[b]+15, tonal->highE[b]);
+       }
+       relativeE += (logE[b]-tonal->lowE[b])/(1e-5f + (tonal->highE[b]-tonal->lowE[b]));
+
+       L1=L2=0;
+       for (i=0;i<NB_FRAMES;i++)
+       {
+          L1 += (float)sqrt(tonal->E[i][b]);
+          L2 += tonal->E[i][b];
+       }
+
+       stationarity = MIN16(0.99f,L1/(float)sqrt(1e-15+NB_FRAMES*L2));
+       stationarity *= stationarity;
+       stationarity *= stationarity;
+       frame_stationarity += stationarity;
+       /*band_tonality[b] = tE/(1e-15+E)*/;
+       band_tonality[b] = MAX16(tE/(1e-15f+E), stationarity*tonal->prev_band_tonality[b]);
+#if 0
+       if (b>=NB_TONAL_SKIP_BANDS)
+       {
+          frame_tonality += tweight[b]*band_tonality[b];
+          tw_sum += tweight[b];
+       }
+#else
+       frame_tonality += band_tonality[b];
+       if (b>=NB_TBANDS-NB_TONAL_SKIP_BANDS)
+          frame_tonality -= band_tonality[b-NB_TBANDS+NB_TONAL_SKIP_BANDS];
+#endif
+       max_frame_tonality = MAX16(max_frame_tonality, (1.f+.03f*(b-NB_TBANDS))*frame_tonality);
+       slope += band_tonality[b]*(b-8);
+       /*printf("%f %f ", band_tonality[b], stationarity);*/
+       tonal->prev_band_tonality[b] = band_tonality[b];
+    }
+
+    leakage_from[0] = band_log2[0];
+    leakage_to[0] = band_log2[0] - LEAKAGE_OFFSET;
+    for (b=1;b<NB_TBANDS+1;b++)
+    {
+       float leak_slope = LEAKAGE_SLOPE*(tbands[b]-tbands[b-1])/4;
+       leakage_from[b] = MIN16(leakage_from[b-1]+leak_slope, band_log2[b]);
+       leakage_to[b] = MAX16(leakage_to[b-1]-leak_slope, band_log2[b]-LEAKAGE_OFFSET);
+    }
+    for (b=NB_TBANDS-2;b>=0;b--)
+    {
+       float leak_slope = LEAKAGE_SLOPE*(tbands[b+1]-tbands[b])/4;
+       leakage_from[b] = MIN16(leakage_from[b+1]+leak_slope, leakage_from[b]);
+       leakage_to[b] = MAX16(leakage_to[b+1]-leak_slope, leakage_to[b]);
+    }
+    celt_assert(NB_TBANDS+1 <= LEAK_BANDS);
+    for (b=0;b<NB_TBANDS+1;b++)
+    {
+       /* leak_boost[] is made up of two terms. The first, based on leakage_to[],
+          represents the boost needed to overcome the amount of analysis leakage
+          cause in a weaker band b by louder neighbouring bands.
+          The second, based on leakage_from[], applies to a loud band b for
+          which the quantization noise causes synthesis leakage to the weaker
+          neighbouring bands. */
+       float boost = MAX16(0, leakage_to[b] - band_log2[b]) +
+             MAX16(0, band_log2[b] - (leakage_from[b]+LEAKAGE_OFFSET));
+       info->leak_boost[b] = IMIN(255, (int)floor(.5 + 64.f*boost));
+    }
+    for (;b<LEAK_BANDS;b++) info->leak_boost[b] = 0;
+
+    for (i=0;i<NB_FRAMES;i++)
+    {
+       int j;
+       float mindist = 1e15f;
+       for (j=0;j<NB_FRAMES;j++)
+       {
+          int k;
+          float dist=0;
+          for (k=0;k<NB_TBANDS;k++)
+          {
+             float tmp;
+             tmp = tonal->logE[i][k] - tonal->logE[j][k];
+             dist += tmp*tmp;
+          }
+          if (j!=i)
+             mindist = MIN32(mindist, dist);
+       }
+       spec_variability += mindist;
+    }
+    spec_variability = (float)sqrt(spec_variability/NB_FRAMES/NB_TBANDS);
+    bandwidth_mask = 0;
+    bandwidth = 0;
+    maxE = 0;
+    noise_floor = 5.7e-4f/(1<<(IMAX(0,lsb_depth-8)));
+    noise_floor *= noise_floor;
+    below_max_pitch=0;
+    above_max_pitch=0;
+    for (b=0;b<NB_TBANDS;b++)
+    {
+       float E=0;
+       float Em;
+       int band_start, band_end;
+       /* Keep a margin of 300 Hz for aliasing */
+       band_start = tbands[b];
+       band_end = tbands[b+1];
+       for (i=band_start;i<band_end;i++)
+       {
+          float binE = out[i].r*(float)out[i].r + out[N-i].r*(float)out[N-i].r
+                     + out[i].i*(float)out[i].i + out[N-i].i*(float)out[N-i].i;
+          E += binE;
+       }
+       E = SCALE_ENER(E);
+       maxE = MAX32(maxE, E);
+       if (band_start < 64)
+       {
+          below_max_pitch += E;
+       } else {
+          above_max_pitch += E;
+       }
+       tonal->meanE[b] = MAX32((1-alphaE2)*tonal->meanE[b], E);
+       Em = MAX32(E, tonal->meanE[b]);
+       /* Consider the band "active" only if all these conditions are met:
+          1) less than 90 dB below the peak band (maximal masking possible considering
+             both the ATH and the loudness-dependent slope of the spreading function)
+          2) above the PCM quantization noise floor
+          We use b+1 because the first CELT band isn't included in tbands[]
+       */
+       if (E*1e9f > maxE && (Em > 3*noise_floor*(band_end-band_start) || E > noise_floor*(band_end-band_start)))
+          bandwidth = b+1;
+       /* Check if the band is masked (see below). */
+       is_masked[b] = E < (tonal->prev_bandwidth >= b+1  ? .01f : .05f)*bandwidth_mask;
+       /* Use a simple follower with 13 dB/Bark slope for spreading function. */
+       bandwidth_mask = MAX32(.05f*bandwidth_mask, E);
+    }
+    /* Special case for the last two bands, for which we don't have spectrum but only
+       the energy above 12 kHz. The difficulty here is that the high-pass we use
+       leaks some LF energy, so we need to increase the threshold without accidentally cutting
+       off the band. */
+    if (tonal->Fs == 48000) {
+       float noise_ratio;
+       float Em;
+       float E = hp_ener*(1.f/(60*60));
+       noise_ratio = tonal->prev_bandwidth==20 ? 10.f : 30.f;
+
+#ifdef FIXED_POINT
+       /* silk_resampler_down2_hp() shifted right by an extra 8 bits. */
+       E *= 256.f*(1.f/Q15ONE)*(1.f/Q15ONE);
+#endif
+       above_max_pitch += E;
+       tonal->meanE[b] = MAX32((1-alphaE2)*tonal->meanE[b], E);
+       Em = MAX32(E, tonal->meanE[b]);
+       if (Em > 3*noise_ratio*noise_floor*160 || E > noise_ratio*noise_floor*160)
+          bandwidth = 20;
+       /* Check if the band is masked (see below). */
+       is_masked[b] = E < (tonal->prev_bandwidth == 20  ? .01f : .05f)*bandwidth_mask;
+    }
+    if (above_max_pitch > below_max_pitch)
+       info->max_pitch_ratio = below_max_pitch/above_max_pitch;
+    else
+       info->max_pitch_ratio = 1;
+    /* In some cases, resampling aliasing can create a small amount of energy in the first band
+       being cut. So if the last band is masked, we don't include it.  */
+    if (bandwidth == 20 && is_masked[NB_TBANDS])
+       bandwidth-=2;
+    else if (bandwidth > 0 && bandwidth <= NB_TBANDS && is_masked[bandwidth-1])
+       bandwidth--;
+    if (tonal->count<=2)
+       bandwidth = 20;
+    frame_loudness = 20*(float)log10(frame_loudness);
+    tonal->Etracker = MAX32(tonal->Etracker-.003f, frame_loudness);
+    tonal->lowECount *= (1-alphaE);
+    if (frame_loudness < tonal->Etracker-30)
+       tonal->lowECount += alphaE;
+
+    for (i=0;i<8;i++)
+    {
+       float sum=0;
+       for (b=0;b<16;b++)
+          sum += dct_table[i*16+b]*logE[b];
+       BFCC[i] = sum;
+    }
+    for (i=0;i<8;i++)
+    {
+       float sum=0;
+       for (b=0;b<16;b++)
+          sum += dct_table[i*16+b]*.5f*(tonal->highE[b]+tonal->lowE[b]);
+       midE[i] = sum;
+    }
+
+    frame_stationarity /= NB_TBANDS;
+    relativeE /= NB_TBANDS;
+    if (tonal->count<10)
+       relativeE = .5f;
+    frame_noisiness /= NB_TBANDS;
+#if 1
+    info->activity = frame_noisiness + (1-frame_noisiness)*relativeE;
+#else
+    info->activity = .5*(1+frame_noisiness-frame_stationarity);
+#endif
+    frame_tonality = (max_frame_tonality/(NB_TBANDS-NB_TONAL_SKIP_BANDS));
+    frame_tonality = MAX16(frame_tonality, tonal->prev_tonality*.8f);
+    tonal->prev_tonality = frame_tonality;
+
+    slope /= 8*8;
+    info->tonality_slope = slope;
+
+    tonal->E_count = (tonal->E_count+1)%NB_FRAMES;
+    tonal->count = IMIN(tonal->count+1, ANALYSIS_COUNT_MAX);
+    info->tonality = frame_tonality;
+
+    for (i=0;i<4;i++)
+       features[i] = -0.12299f*(BFCC[i]+tonal->mem[i+24]) + 0.49195f*(tonal->mem[i]+tonal->mem[i+16]) + 0.69693f*tonal->mem[i+8] - 1.4349f*tonal->cmean[i];
+
+    for (i=0;i<4;i++)
+       tonal->cmean[i] = (1-alpha)*tonal->cmean[i] + alpha*BFCC[i];
+
+    for (i=0;i<4;i++)
+        features[4+i] = 0.63246f*(BFCC[i]-tonal->mem[i+24]) + 0.31623f*(tonal->mem[i]-tonal->mem[i+16]);
+    for (i=0;i<3;i++)
+        features[8+i] = 0.53452f*(BFCC[i]+tonal->mem[i+24]) - 0.26726f*(tonal->mem[i]+tonal->mem[i+16]) -0.53452f*tonal->mem[i+8];
+
+    if (tonal->count > 5)
+    {
+       for (i=0;i<9;i++)
+          tonal->std[i] = (1-alpha)*tonal->std[i] + alpha*features[i]*features[i];
+    }
+    for (i=0;i<4;i++)
+       features[i] = BFCC[i]-midE[i];
+
+    for (i=0;i<8;i++)
+    {
+       tonal->mem[i+24] = tonal->mem[i+16];
+       tonal->mem[i+16] = tonal->mem[i+8];
+       tonal->mem[i+8] = tonal->mem[i];
+       tonal->mem[i] = BFCC[i];
+    }
+    for (i=0;i<9;i++)
+       features[11+i] = (float)sqrt(tonal->std[i]) - std_feature_bias[i];
+    features[18] = spec_variability - 0.78f;
+    features[20] = info->tonality - 0.154723f;
+    features[21] = info->activity - 0.724643f;
+    features[22] = frame_stationarity - 0.743717f;
+    features[23] = info->tonality_slope + 0.069216f;
+    features[24] = tonal->lowECount - 0.067930f;
+
+    analysis_compute_dense(&layer0, layer_out, features);
+    analysis_compute_gru(&layer1, tonal->rnn_state, layer_out);
+    analysis_compute_dense(&layer2, frame_probs, tonal->rnn_state);
+
+    /* Probability of speech or music vs noise */
+    info->activity_probability = frame_probs[1];
+    info->music_prob = frame_probs[0];
+
+    /*printf("%f %f %f\n", frame_probs[0], frame_probs[1], info->music_prob);*/
+#ifdef MLP_TRAINING
+    for (i=0;i<25;i++)
+       printf("%f ", features[i]);
+    printf("\n");
+#endif
+
+    info->bandwidth = bandwidth;
+    tonal->prev_bandwidth = bandwidth;
+    /*printf("%d %d\n", info->bandwidth, info->opus_bandwidth);*/
+    info->noisiness = frame_noisiness;
+    info->valid = 1;
+    RESTORE_STACK;
+}
+
+void run_analysis(TonalityAnalysisState *analysis, const CELTMode *celt_mode, const void *analysis_pcm,
+                 int analysis_frame_size, int frame_size, int c1, int c2, int C, opus_int32 Fs,
+                 int lsb_depth, downmix_func downmix, AnalysisInfo *analysis_info)
+{
+   int offset;
+   int pcm_len;
+
+   analysis_frame_size -= analysis_frame_size&1;
+   if (analysis_pcm != NULL)
+   {
+      /* Avoid overflow/wrap-around of the analysis buffer */
+      analysis_frame_size = IMIN((DETECT_SIZE-5)*Fs/50, analysis_frame_size);
+
+      pcm_len = analysis_frame_size - analysis->analysis_offset;
+      offset = analysis->analysis_offset;
+      while (pcm_len>0) {
+         tonality_analysis(analysis, celt_mode, analysis_pcm, IMIN(Fs/50, pcm_len), offset, c1, c2, C, lsb_depth, downmix);
+         offset += Fs/50;
+         pcm_len -= Fs/50;
+      }
+      analysis->analysis_offset = analysis_frame_size;
+
+      analysis->analysis_offset -= frame_size;
+   }
+
+   tonality_get_info(analysis, analysis_info, frame_size);
+}
+
+#endif /* DISABLE_FLOAT_API */
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/src_lib/mlp.c b/code/sdk/zhihui/test_opus_frame_to_pcm/src_lib/mlp.c
new file mode 100644 (file)
index 0000000..b1dbab5
--- /dev/null
@@ -0,0 +1,131 @@
+/* Copyright (c) 2008-2011 Octasic Inc.
+                 2012-2017 Jean-Marc Valin */
+/*
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+   - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+   - Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <math.h>
+#include "opus_types.h"
+#include "opus_defines.h"
+#include "arch.h"
+#include "mlp.h"
+
+#define fmadd(a, b, c) ((a)*(b)+(c))
+static OPUS_INLINE float tansig_approx(float x)
+{
+    const float N0 = 952.52801514f;
+    const float N1 = 96.39235687f;
+    const float N2 = 0.60863042f;
+    const float D0 = 952.72399902f;
+    const float D1 = 413.36801147f;
+    const float D2 = 11.88600922f;
+    float X2, num, den;
+    X2 = x*x;
+    num = fmadd(fmadd(N2, X2, N1), X2, N0);
+    den = fmadd(fmadd(D2, X2, D1), X2, D0);
+    num = num*x/den;
+    return MAX32(-1.f, MIN32(1.f, num));
+}
+
+static OPUS_INLINE float sigmoid_approx(float x)
+{
+   return .5f + .5f*tansig_approx(.5f*x);
+}
+
+static void gemm_accum(float *out, const opus_int8 *weights, int rows, int cols, int col_stride, const float *x)
+{
+   int i, j;
+   for (i=0;i<rows;i++)
+   {
+      for (j=0;j<cols;j++)
+         out[i] += weights[j*col_stride + i]*x[j];
+   }
+}
+
+void analysis_compute_dense(const AnalysisDenseLayer *layer, float *output, const float *input)
+{
+   int i;
+   int N, M;
+   int stride;
+   M = layer->nb_inputs;
+   N = layer->nb_neurons;
+   stride = N;
+   for (i=0;i<N;i++)
+      output[i] = layer->bias[i];
+   gemm_accum(output, layer->input_weights, N, M, stride, input);
+   for (i=0;i<N;i++)
+      output[i] *= WEIGHTS_SCALE;
+   if (layer->sigmoid) {
+      for (i=0;i<N;i++)
+         output[i] = sigmoid_approx(output[i]);
+   } else {
+      for (i=0;i<N;i++)
+         output[i] = tansig_approx(output[i]);
+   }
+}
+
+void analysis_compute_gru(const AnalysisGRULayer *gru, float *state, const float *input)
+{
+   int i;
+   int N, M;
+   int stride;
+   float tmp[MAX_NEURONS];
+   float z[MAX_NEURONS];
+   float r[MAX_NEURONS];
+   float h[MAX_NEURONS];
+   M = gru->nb_inputs;
+   N = gru->nb_neurons;
+   stride = 3*N;
+   /* Compute update gate. */
+   for (i=0;i<N;i++)
+      z[i] = gru->bias[i];
+   gemm_accum(z, gru->input_weights, N, M, stride, input);
+   gemm_accum(z, gru->recurrent_weights, N, N, stride, state);
+   for (i=0;i<N;i++)
+      z[i] = sigmoid_approx(WEIGHTS_SCALE*z[i]);
+
+   /* Compute reset gate. */
+   for (i=0;i<N;i++)
+      r[i] = gru->bias[N + i];
+   gemm_accum(r, &gru->input_weights[N], N, M, stride, input);
+   gemm_accum(r, &gru->recurrent_weights[N], N, N, stride, state);
+   for (i=0;i<N;i++)
+      r[i] = sigmoid_approx(WEIGHTS_SCALE*r[i]);
+
+   /* Compute output. */
+   for (i=0;i<N;i++)
+      h[i] = gru->bias[2*N + i];
+   for (i=0;i<N;i++)
+      tmp[i] = state[i] * r[i];
+   gemm_accum(h, &gru->input_weights[2*N], N, M, stride, input);
+   gemm_accum(h, &gru->recurrent_weights[2*N], N, N, stride, tmp);
+   for (i=0;i<N;i++)
+      h[i] = z[i]*state[i] + (1-z[i])*tansig_approx(WEIGHTS_SCALE*h[i]);
+   for (i=0;i<N;i++)
+      state[i] = h[i];
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/src_lib/mlp_data.c b/code/sdk/zhihui/test_opus_frame_to_pcm/src_lib/mlp_data.c
new file mode 100644 (file)
index 0000000..30477cb
--- /dev/null
@@ -0,0 +1,671 @@
+/*This file is automatically generated from a Keras model*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "mlp.h"
+
+static const opus_int8 layer0_weights[800] = {
+   -30, -9, 2, -12, 5, -1, 8, 9,
+   9, 8, -13, 18, -17, -34, -5, 17,
+   -11, 0, -4, 10, 2, 10, 15, -8,
+   2, -1, 0, 5, 13, -3, -16, 1,
+   -5, 3, 7, -28, -13, 6, 36, -3,
+   19, -60, -17, -28, 7, -11, -30, -7,
+   2, -42, -21, -3, 6, -22, 33, -9,
+   7, -30, 21, -14, 24, -11, -20, -18,
+   -5, -12, 12, -49, -50, -49, 16, 9,
+   -37, -1, 9, 34, -13, -31, -31, 12,
+   16, 44, -42, 2, -9, 8, -18, -6,
+   9, 36, 19, 11, 13, 12, -21, 3,
+   -28, -12, 3, 33, 25, -14, 11, 1,
+   -94, -39, 18, -12, -11, -15, -7, 49,
+   52, 10, -43, 9, 57, 8, 21, -6,
+   14, -15, 44, -8, 7, -30, -13, -2,
+   -9, 25, -2, -127, 18, -11, -52, 26,
+   -27, 27, 10, -10, 7, 43, 6, -24,
+   41, 10, -18, -27, 10, 17, 9, 10,
+   -17, -10, 20, -6, 22, 55, 35, -80,
+   36, 25, -24, -36, 15, 9, -19, 88,
+   19, 64, -51, -35, 17, 0, -7, 41,
+   -16, 27, 4, 15, -1, 18, -16, 47,
+   -39, -54, -8, 13, -25, -20, 102, -18,
+   -5, 44, 11, -28, 71, 2, -51, -5,
+   5, 2, -83, -9, -29, 8, 21, -53,
+   58, -37, -7, 13, 38, 9, 34, -1,
+   -41, 21, 4, -24, -36, -33, -21, 32,
+   75, -2, 1, -68, -1, 47, -29, 32,
+   20, 12, -65, -87, 5, 16, -12, 24,
+   40, 15, 7, 19, -26, -17, 17, 6,
+   -2, -37, -30, -9, 32, -127, -39, 0,
+   -31, -27, 4, -22, 23, -6, -77, 35,
+   -61, 32, -37, -24, 13, -11, -1, -40,
+   -3, 17, -7, 13, 11, 59, -19, 10,
+   6, -18, 0, 13, 3, -6, -23, 19,
+   11, -17, 13, -1, -80, 40, -53, 69,
+   -29, -54, 0, -4, 33, -25, -2, 38,
+   35, 36, -15, 46, 2, -13, -16, -8,
+   -8, 12, -24, -9, -55, -5, -9, 32,
+   11, 7, 12, -18, -10, -86, -38, 54,
+   37, -25, 18, -43, 7, -27, -27, -54,
+   13, 9, 22, 70, 6, 35, -7, 23,
+   -15, -44, -6, 7, -66, -85, 32, 40,
+   -19, -9, -7, 12, -15, 7, 2, 6,
+   -35, 11, 28, 0, 26, 14, 1, 1,
+   4, 12, 18, 35, 22, -18, -3, 14,
+   -1, 7, 14, -8, -14, -3, 4, -3,
+   -19, -7, -1, -25, -27, 25, -26, -2,
+   33, -22, -27, -25, 4, -9, 7, 21,
+   26, -30, 10, -9, -20, 11, 27, 10,
+   5, -18, 14, -4, 2, -17, -5, -7,
+   -9, -13, 15, 29, 1, -10, -16, -10,
+   35, 36, -7, -22, -44, 17, 30, 22,
+   21, -1, 22, -11, 32, -8, -7, 5,
+   -10, 5, 30, -20, 29, -20, -34, 12,
+   -4, -6, 6, -13, 10, -5, -68, -1,
+   24, 9, 19, -24, -64, 31, 19, 27,
+   -26, 75, -45, 41, 39, -42, 8, 6,
+   23, -30, 16, -25, 30, 34, 8, -38,
+   -3, 18, 16, -31, 22, -4, -9, 1,
+   20, 9, 38, -32, 0, -45, 0, -6,
+   -13, 11, -25, -32, -22, 31, -24, -11,
+   -11, -4, -4, 20, -34, 22, 20, 9,
+   -25, 27, -5, 28, -29, 29, 6, 21,
+   -6, -18, 54, 4, -46, 23, 21, -14,
+   -31, 36, -41, -24, 4, 22, 10, 11,
+   7, 36, -32, -13, -52, -17, 24, 28,
+   -37, -36, -1, 24, 9, -38, 35, 48,
+   18, 2, -1, 45, 10, 39, 24, -38,
+   13, 8, -16, 8, 25, 11, 7, -29,
+   -11, 7, 20, -30, -38, -45, 14, -18,
+   -28, -9, 65, 61, 22, -53, -38, -16,
+   36, 46, 20, -39, 32, -61, -6, -6,
+   -36, -33, -18, -28, 56, 101, 45, 11,
+   -28, -23, -29, -61, 20, -47, 2, 48,
+   27, -17, 1, 40, 1, 3, -51, 15,
+   35, 28, 22, 35, 53, -61, -29, 12,
+   -6, -21, 10, 3, -20, 2, -25, 1,
+   -6, 31, 11, -3, 1, -10, -52, 6,
+   126, -105, 122, 127, -128, 127, 127, -128,
+   127, 108, 12, 127, 48, -128, -36, -128,
+   127, 127, -128, -128, 127, 89, -128, 127,
+   -128, -128, -128, 127, 127, -128, -128, -93,
+   -82, 20, 125, 65, -82, 127, 38, -74,
+   81, 88, -88, 79, 51, -47, -111, -26,
+   14, 83, -88, -112, 24, 35, -101, 98,
+   -99, -48, -45, 46, 83, -60, -79, 45,
+   -20, -41, 9, 4, 52, 54, 93, -10,
+   4, 13, 3, 123, 6, 94, -111, -69,
+   -14, -31, 10, 12, 53, -79, -11, -21,
+   -2, -44, -72, 92, 65, -57, 56, -38,
+   127, -56, -128, 127, 127, -128, 86, 117,
+   -75, -128, 127, -19, -99, -112, 127, -128,
+   127, -48, 114, 118, -128, -128, 117, -17,
+   -6, 121, -128, 127, -128, 82, 54, -106,
+   127, 127, -33, 100, -39, -23, 18, -78,
+   -34, -29, -1, -30, 127, -26, 127, -128,
+   126, -128, 27, -23, -79, -120, -127, 127,
+   72, 66, 29, 7, -66, -56, -117, -128
+};
+
+static const opus_int8 layer0_bias[32] = {
+   51, -16, 1, 13, -5, -6, -16, -7,
+   11, -6, 106, 26, 28, -14, 21, -29,
+   7, 18, -18, -17, 21, -17, -9, 20,
+   -25, -3, -34, 48, 11, -13, -31, -20
+};
+
+static const opus_int8 layer1_weights[2304] = {
+   22, -1, -7, 7, 29, -27, -31, -17,
+   -13, 33, 44, -8, 11, 33, 24, 78,
+   15, 19, 30, -2, -24, 5, 49, 5,
+   36, 29, -14, -11, -48, -33, 21, -42,
+   -38, -12, 55, -37, 54, -8, 1, 36,
+   17, 0, 51, 31, 59, 7, -12, 53,
+   4, 32, -14, 48, 5, -10, -16, -8,
+   1, -16, -56, -24, -6, 18, -2, 23,
+   6, 46, -6, -10, 20, 35, -44, -15,
+   -49, 36, 16, 5, -7, -79, -67, 12,
+   70, -3, -79, -54, -85, -24, 47, -22,
+   33, 21, 69, -1, 11, 22, 14, -16,
+   -16, -22, -28, -11, 11, -41, 31, -26,
+   -33, -19, -4, 27, 32, -50, 5, -10,
+   -38, -22, -8, 35, -31, 1, -41, -15,
+   -11, 44, 28, -17, -41, -23, 17, 2,
+   -23, -26, -13, -13, -17, 6, 14, -31,
+   -25, 9, -19, 39, -8, 4, 31, -1,
+   -45, -11, -28, -92, -46, -15, 21, 118,
+   -22, 45, -51, 11, -20, -20, -15, 13,
+   -21, -97, -29, -32, -23, -42, 94, 1,
+   23, -8, 63, -3, -46, 19, -26, 32,
+   -40, -74, -26, 26, -4, -13, 30, -20,
+   -30, -25, -14, -31, -45, -43, 4, -60,
+   -48, -12, -34, 2, 2, 3, 13, 15,
+   11, 16, 5, 46, -9, -55, -16, -57,
+   29, 14, 38, -50, -2, -44, -11, -8,
+   52, -27, -38, -7, 20, 47, 17, -59,
+   0, 47, 46, -63, 35, -17, 19, 33,
+   68, -19, 2, 15, -16, 28, -16, -103,
+   26, -35, 47, -39, -60, 30, 31, -23,
+   -52, -13, 116, 47, -25, 30, 40, 30,
+   -22, 2, 12, -27, -18, 31, -10, 27,
+   -8, -66, 12, 14, 4, -26, -28, -13,
+   3, 13, -26, -51, 37, 5, 2, -21,
+   47, 3, 13, 25, -41, -27, -8, -4,
+   5, -76, -33, 28, 10, 9, -46, -74,
+   19, 28, 25, 31, 54, -55, 68, 38,
+   -24, -32, 2, 4, 68, 11, -1, 99,
+   5, 16, -2, -74, 40, 26, -26, 33,
+   31, -1, -68, 14, -6, 25, 9, 29,
+   60, 61, 7, -7, 0, -24, 7, 77,
+   4, -1, 16, -7, 13, -15, -19, 28,
+   -31, -24, -16, 37, 24, 13, 30, 10,
+   -30, 11, 11, -10, 22, 60, 28, 45,
+   -3, -40, -62, -5, -102, 9, -32, -27,
+   -54, 21, 15, -5, 37, -43, -11, 37,
+   -19, 47, -64, -128, -27, -114, 21, -66,
+   59, 46, -3, -12, -87, -9, 4, 19,
+   -113, -36, 78, 57, -26, -38, -77, -10,
+   6, 6, -75, 25, -97, -11, 33, -46,
+   1, 13, -21, -33, -20, 16, -6, -3,
+   -11, -4, -27, 38, 8, -41, -2, -33,
+   18, 19, -26, 1, -29, -22, -4, -14,
+   -55, -11, -80, -3, 11, 34, 90, 51,
+   11, 17, 43, 36, 127, -32, 29, 103,
+   9, 27, 13, 64, 56, 70, -14, 3,
+   -12, 10, 37, 3, 12, -22, -10, 46,
+   28, 10, 20, 26, -24, 18, 9, 7,
+   14, 34, -5, -7, 31, -14, -56, 11,
+   -18, -8, -17, -7, -10, -40, 10, -33,
+   -32, -43, 5, 9, 11, -4, 10, 50,
+   -12, -5, 46, 9, 7, 1, 11, 15,
+   91, -17, 7, -50, 23, 6, -30, -99,
+   0, -17, 14, 8, -10, -25, -30, -69,
+   -62, 31, 127, 114, -23, 101, -5, -54,
+   -6, -22, 7, -56, 39, 18, -29, 0,
+   46, 8, -79, 4, -21, 18, -32, 62,
+   -12, -8, -12, -58, 31, -32, 17, 6,
+   -24, 25, 24, 9, -4, -19, 45, 6,
+   17, -14, 5, -27, 16, -4, -41, 25,
+   -36, 5, 15, 12, 50, 27, 25, 23,
+   -44, -69, -9, -19, -48, -8, 4, 12,
+   -6, 13, -19, -30, -36, 26, 37, -1,
+   -3, -30, -42, -14, -10, -20, 26, -54,
+   -27, -44, 4, 73, -26, 90, 32, -69,
+   -29, -16, 3, 103, 15, -17, 37, 24,
+   -23, -31, 33, -37, -64, 25, 13, -81,
+   -28, -32, 27, 5, -35, -23, 15, -22,
+   19, -7, 9, 30, 19, -23, 27, -13,
+   43, 29, -29, -6, 9, -40, -33, -33,
+   -32, 9, 11, -48, -8, -23, -52, 46,
+   17, -22, -42, 35, -15, -41, 16, 34,
+   31, -42, -19, -11, 55, 7, -39, 89,
+   -11, -33, 20, -14, 22, 32, 3, -17,
+   -6, 14, 34, 1, 55, -21, -90, -8,
+   18, 27, 13, -29, 21, 15, -33, -51,
+   -9, -11, 4, -16, -18, 23, -4, -4,
+   48, 1, 7, 29, -14, -12, -16, 17,
+   35, 8, 0, -7, -2, 9, 8, 17,
+   -6, 53, -32, -21, -50, 5, 99, -60,
+   -5, -53, 10, -31, 12, -5, 7, 80,
+   36, 18, -31, 9, 98, 36, -63, -35,
+   4, -13, -28, -24, 28, -13, 18, 16,
+   -1, -18, -34, 10, 20, 7, 4, 29,
+   11, 25, -7, 36, 14, 45, 24, 1,
+   -16, 30, 6, 35, -6, -11, -24, 13,
+   -1, 27, 39, 20, 48, -11, -4, -13,
+   28, 11, -31, -18, 31, -29, 22, -2,
+   -20, -16, 5, 30, -12, -28, -3, 93,
+   -16, 23, 18, -29, 6, -54, -37, 28,
+   -3, -3, -47, -3, -36, -55, -3, 41,
+   -10, 47, -2, 23, 42, -7, -71, -27,
+   83, -64, 7, -24, 8, 26, -17, 15,
+   12, 31, -30, -38, -13, -33, -56, 4,
+   -17, 20, 18, 1, -30, -5, -6, -31,
+   -14, -37, 0, 22, 10, -30, 37, -17,
+   18, 6, 5, 23, -36, -32, 14, 18,
+   -13, -61, -52, -69, 44, -30, 16, 18,
+   -4, -25, 14, 81, 26, -8, -23, -59,
+   52, -104, 17, 119, -32, 26, 17, 1,
+   23, 45, 29, -64, -57, -14, 73, 21,
+   -13, -13, 9, -68, -7, -52, 3, 24,
+   -39, 44, -15, 27, 14, 19, -9, -28,
+   -11, 5, 3, -34, -2, 2, 22, -6,
+   -23, 4, 3, 13, -22, -13, -10, -18,
+   29, 6, 44, -13, -24, -8, 2, 30,
+   14, 43, 6, 17, -73, -6, -7, 20,
+   -80, -7, -7, -28, 15, -69, -38, -5,
+   -100, -35, 15, -79, 23, 29, -18, -27,
+   21, -66, -37, 8, -22, -39, 48, 4,
+   -13, 1, -9, 11, -29, 22, 6, -49,
+   32, -14, 47, -18, -4, 44, -52, -74,
+   43, 30, 23, -14, 5, 0, -27, 4,
+   -7, 10, -4, 10, 1, -16, 11, -18,
+   -2, -5, 2, -11, 0, -20, -4, 38,
+   74, 59, 39, 64, -10, 26, -3, -40,
+   -68, 3, -30, -51, 8, -19, -27, -46,
+   51, 52, 54, 36, 90, 92, 14, 13,
+   -5, 0, 16, -62, 16, 11, -47, -37,
+   -6, -5, 21, 54, -57, 32, 42, -6,
+   62, -9, 16, 21, 24, 9, -10, -4,
+   33, 50, 13, -15, 1, -35, -48, 18,
+   -11, -17, -67, -13, 21, 38, -44, 36,
+   -16, 29, 17, 5, -10, 18, 17, -32,
+   2, 8, 22, -56, -15, -32, 40, 43,
+   19, 46, -7, -100, -96, 19, 53, 24,
+   21, -26, -48, -101, -82, 61, 38, -85,
+   -28, -34, -1, 63, -5, -5, 39, 39,
+   -38, 32, -12, -28, 20, 40, -8, 2,
+   31, 12, -35, -13, 20, -25, 30, 8,
+   3, -13, -9, -20, 2, -13, 24, 37,
+   -10, 33, 6, 20, -16, -24, -6, -6,
+   -19, -5, 22, 21, 10, 11, -4, -39,
+   -1, 6, 49, 41, -15, -57, 21, -62,
+   77, -69, -13, 0, -74, 1, -7, -38,
+   -8, 6, 63, 28, 4, 26, -52, 82,
+   63, 13, 45, -33, 44, -52, -65, -21,
+   -46, -49, 64, -17, 32, 24, 68, -39,
+   -16, -5, -26, 28, 5, -61, -28, 2,
+   24, 11, -12, -33, 9, -37, -3, -28,
+   22, -37, -12, 19, 0, -18, -2, 14,
+   1, 4, 8, -9, -2, 43, -17, -2,
+   -66, -31, 56, -40, -87, -36, -2, -4,
+   -42, -45, -1, 31, -43, -15, 27, 63,
+   -11, 32, -10, -33, 27, -19, 4, 15,
+   -26, -34, 29, -4, -39, -65, 14, -20,
+   -21, -17, -36, 13, 59, 47, -38, -33,
+   13, -37, -8, -37, -7, -6, -76, -31,
+   -12, -46, 7, 24, -21, -30, -14, 9,
+   15, -12, -13, 47, -27, -25, -1, -39,
+   0, 20, -9, 6, 7, 4, 3, 7,
+   39, 50, 22, -7, 14, -20, 1, 70,
+   -28, 29, -41, 10, -16, -5, -28, -2,
+   -37, 32, -18, 17, 62, -11, -20, -50,
+   36, 21, -62, -12, -56, 52, 50, 17,
+   3, 48, 44, -41, -25, 3, 16, -3,
+   0, 33, -6, 15, 27, 34, -25, 22,
+   9, 17, -11, 36, 16, -2, 12, 21,
+   -52, 45, -2, -10, 46, 21, -18, 67,
+   -28, -13, 30, 37, 42, 16, -9, 11,
+   75, 7, -64, -40, -10, 29, 57, -23,
+   5, 53, -77, 3, -17, -5, 47, -55,
+   -35, -36, -13, 52, -53, -71, 52, -111,
+   -23, -26, -28, 29, -43, 55, -19, 43,
+   -19, 54, -12, -33, -44, -39, -19, -10,
+   -31, -10, 21, 38, -57, -20, 2, -25,
+   8, -6, 50, 12, 15, 25, -25, 15,
+   -30, -6, 9, 25, 37, 19, -4, 31,
+   -22, 2, 4, 2, 36, 7, 3, -34,
+   -80, 36, -10, -2, -5, 31, -36, 49,
+   -70, 20, -36, 21, 24, 25, -46, -51,
+   36, -58, -48, -40, -10, 55, 71, 47,
+   10, -1, 1, 2, -46, -68, 16, 13,
+   0, -74, -29, 73, -52, -18, -11, 7,
+   -44, -82, -32, -70, -28, -1, -39, -68,
+   -6, -41, 12, -22, -16, 40, -11, -25,
+   51, -9, 21, 4, 4, -34, 7, -78,
+   16, 6, -38, -30, -2, -44, 32, 0,
+   22, 64, 5, -72, -2, -14, -10, -16,
+   -8, -25, 12, 102, -58, 37, -10, -23,
+   15, 49, 7, -7, 2, -20, -32, 45,
+   -6, 48, 28, 30, 33, -1, 22, -6,
+   30, 65, -17, 29, 74, 37, -26, -10,
+   15, -24, 19, -66, 22, -10, -31, -1,
+   -18, -9, 11, 37, -4, 45, 5, 41,
+   17, 1, 1, 24, -58, 41, 5, -51,
+   14, 8, 43, 16, -10, -1, 45, 32,
+   -64, 3, -33, -25, -3, -27, -68, 12,
+   23, -11, -13, -37, -40, 4, -21, -12,
+   32, -23, -19, 76, 41, -23, -24, -44,
+   -65, -1, -15, 1, 71, 63, 5, 20,
+   -3, 21, -23, 31, -32, 18, -2, 27,
+   31, 46, -5, -39, -5, -35, 18, -18,
+   -40, -10, 3, 12, 2, -2, -22, 40,
+   5, -6, 60, 36, 3, 29, -27, 10,
+   25, -54, 5, 26, 39, 35, -24, -37,
+   30, -91, 28, -4, -21, -27, -39, -6,
+   5, 12, -128, 38, -16, 29, -95, -29,
+   82, -2, 35, 2, 12, 8, -22, 10,
+   80, -47, 2, -25, -73, -79, 16, -30,
+   -32, -66, 48, 21, -45, -11, -47, 14,
+   -27, -17, -7, 15, -44, -14, -44, -26,
+   -32, 26, -23, 17, -7, -28, 26, -6,
+   28, 6, -26, 2, 13, -14, -23, -14,
+   19, 46, 16, 2, -33, -21, 28, -17,
+   -42, 44, -37, 1, -39, 28, 84, -46,
+   15, 10, 13, -44, 72, -26, 26, 32,
+   -28, -12, -83, 2, 10, -30, -44, -10,
+   -28, 53, 45, 65, 0, -25, 57, 36,
+   -33, 6, 29, 44, -53, 11, 19, -2,
+   -27, 35, 32, 49, 4, 23, 38, 36,
+   24, 10, 51, -39, 4, -7, 26, 37,
+   -35, 11, -47, -18, 28, 16, -35, 42,
+   17, -21, -41, 28, 14, -12, 11, -45,
+   7, -43, -15, 18, -5, 38, -40, -50,
+   -30, -21, 9, -98, 13, 12, 23, 75,
+   -56, -7, -3, -4, -1, -34, 12, -49,
+   11, 26, -18, -28, -17, 33, 13, -14,
+   40, 24, -72, -37, 10, 17, -6, 22,
+   16, 16, -6, -12, -30, -14, 10, 40,
+   -23, 12, 15, -3, -15, 13, -56, -4,
+   -30, 1, -3, -17, 27, 50, -5, 64,
+   -36, -19, 7, 29, 22, 25, 9, -16,
+   -58, -69, -40, -61, -71, -14, 42, 93,
+   26, 11, -6, -58, -11, 70, -52, 19,
+   9, -30, -33, 11, -37, -47, -21, -22,
+   -40, 10, 47, 4, -23, 17, 48, 41,
+   -48, 14, 10, 15, 34, -23, -2, -47,
+   23, -32, -13, -10, -26, -26, -4, 16,
+   38, -14, 0, -12, -7, -7, 20, 44,
+   -1, -32, -27, -16, 4, -6, -18, 14,
+   5, 4, -29, 28, 7, -7, 15, -11,
+   -20, -45, -36, 16, 84, 34, -59, -30,
+   22, 126, 8, 68, 79, -17, 21, -68,
+   37, 5, 15, 63, 49, 127, -90, 85,
+   43, 7, 16, 9, 6, -45, -57, -43,
+   57, 11, -23, -11, -29, 60, -26, 0,
+   7, 42, -24, 10, 23, -25, 8, -7,
+   -40, 19, -17, 35, 4, 27, -39, -91,
+   27, -36, 34, 2, 16, -24, 25, 7,
+   -21, 5, 17, 10, -22, -30, 9, -17,
+   -61, -26, 33, 21, 58, -51, -14, 69,
+   -38, 20, 7, 80, -4, -65, -6, -27,
+   53, -12, 47, -1, -15, 1, 60, 102,
+   -79, -4, 12, 9, 22, 37, -8, -4,
+   37, 2, -3, -15, -16, -11, -5, 19,
+   -6, -43, 20, -25, -18, 10, -27, 0,
+   -28, -27, -11, 10, -18, -2, -4, -16,
+   26, 14, -6, 7, -6, 1, 53, -2,
+   -29, 23, 9, -30, -6, -4, -6, 56,
+   70, 0, -33, -20, -17, -9, -24, 46,
+   -5, -105, 47, -46, -51, 20, 20, -53,
+   -81, -1, -7, 75, -5, -21, -65, 12,
+   -52, 22, -50, -12, 49, 54, 76, -81,
+   10, 45, -41, -59, 18, -19, 25, 14,
+   -31, -53, -5, 12, 31, 84, -23, 2,
+   7, 2, 10, -32, 39, -2, -12, 1,
+   -9, 0, -10, -11, 9, 15, -8, -2,
+   2, -1, 10, 14, -5, -40, 19, -7,
+   -7, 26, -4, 2, 1, -27, 35, 32,
+   21, -31, 26, 43, -9, 4, -32, 40,
+   -62, -52, 36, 22, 38, 22, 36, -96,
+   6, -10, -23, -49, 15, -33, -18, -3,
+   0, 41, 21, -19, 21, 23, -39, -23,
+   -6, 6, 47, 56, 4, 74, 0, -98,
+   29, -47, -14, -36, 21, -22, 22, 16,
+   13, 12, 16, -5, 13, 17, -13, -15,
+   1, -34, -26, 26, 12, 32, 27, 13,
+   -67, 27, 2, 8, 10, 18, 16, 20,
+   -17, -17, 57, -64, 5, 14, 19, 31,
+   -18, -44, -46, -16, 4, -25, 17, -126,
+   -24, 39, 4, 8, 55, -25, -34, 39,
+   -16, 3, 9, 71, 72, -31, -55, 6,
+   10, -25, 32, -85, -21, 18, -8, 15,
+   12, -27, -7, 1, -21, -2, -5, 48,
+   -16, 18, 1, -22, -26, 16, 14, -31,
+   27, -6, -15, -21, 4, -14, 18, -36
+};
+
+static const opus_int8 layer1_recur_weights[1728] = {
+   20, 67, -99, 12, 41, -25, 49, -44,
+   35, 81, 110, 47, 34, -66, -14, 14,
+   -60, 34, 29, -73, 10, 41, 35, 89,
+   7, -35, 22, 7, 27, -20, -6, 56,
+   26, 66, 6, 33, -55, 53, 1, -21,
+   14, 17, 68, 55, 59, 0, 18, -9,
+   5, -41, 6, -5, -114, -12, 29, 42,
+   -23, 10, 81, -27, 20, -53, -30, -62,
+   40, 95, 25, -4, 3, 18, -8, -15,
+   -29, -82, 2, -57, -3, -61, -29, -29,
+   49, 2, -55, 5, -69, -99, -49, -51,
+   6, -25, 12, 89, 44, -33, 5, 41,
+   1, 23, -37, -37, -28, -48, 3, 4,
+   -41, -30, -57, -35, -39, -1, -13, -56,
+   -5, 50, 49, 41, -4, -4, 33, -22,
+   -1, 33, 34, 18, 40, -42, 12, 1,
+   -6, -2, 18, 17, 39, 44, 11, 65,
+   -60, -45, 10, 91, 21, 9, -62, -11,
+   8, 69, 37, 24, -30, 21, 26, -27,
+   1, -28, 24, 66, -8, 6, -71, 34,
+   24, 44, 58, -78, -19, 57, 17, -60,
+   1, 12, -3, -1, -40, 22, 11, -5,
+   25, 12, 1, 72, 79, 7, -50, 23,
+   18, 13, 21, -11, -20, 5, 77, -94,
+   24, 15, 57, -51, 3, 36, 53, -1,
+   4, 14, 30, -31, 22, 40, 32, -11,
+   -34, -36, -59, 58, 25, 21, -54, -23,
+   40, 46, 18, 0, 12, 54, -96, -99,
+   -59, 5, 119, -38, 50, 55, 12, -16,
+   67, 0, 34, 35, 39, 35, -1, 69,
+   24, 27, -30, -35, -4, -70, 2, -44,
+   -7, -6, 19, -9, 60, 44, -21, -10,
+   37, 43, -16, -3, 30, -15, -65, 31,
+   -55, 18, -98, 76, 64, 25, 24, -18,
+   -7, -68, -10, 38, 27, -60, 36, 33,
+   16, 30, 34, -39, -37, 31, 12, 53,
+   -54, 14, -26, -49, -128, -13, -5, -22,
+   -11, -85, 55, -8, -51, -11, -33, -10,
+   -31, -76, -41, 23, 44, -40, -54, -127,
+   -101, 19, -23, -15, 15, 27, 58, -60,
+   8, 14, -33, 1, 48, -9, -11, -123,
+   3, 53, 23, 4, -28, 22, 2, -29,
+   -67, 36, 12, 7, 55, -21, 88, 20,
+   -1, -21, -17, 3, 41, 32, -10, -14,
+   -5, -57, 67, 57, 21, 23, -2, -27,
+   -73, -24, 120, 21, 18, -35, 42, -7,
+   3, -45, -25, 76, -34, 50, 11, -54,
+   -91, 3, -113, -20, -5, 47, 15, -47,
+   17, 27, -3, -26, -7, 10, 7, 74,
+   -40, 64, -7, -5, -24, -49, -24, -3,
+   -10, 27, -17, -8, -3, 14, -27, 33,
+   13, 39, 28, -7, -38, 29, 16, 44,
+   19, 55, -3, 9, -13, -57, 43, 43,
+   31, 0, -93, -17, 19, -56, 4, -12,
+   -25, 37, -85, -13, -118, 33, -17, 56,
+   71, -80, -4, 6, -11, -18, 47, -52,
+   25, 9, 48, -107, 1, 21, 20, -3,
+   10, -16, -4, 24, 17, 31, -61, -18,
+   -50, 24, -10, 12, 71, 26, 11, -3,
+   4, 1, 0, -7, -40, 18, 38, -34,
+   38, 17, 8, -34, 2, 21, 123, -32,
+   -26, 43, 14, -34, -1, -9, 37, -16,
+   6, -17, -62, 68, 22, 17, 11, -75,
+   33, -80, 62, -9, -75, 76, 36, -41,
+   -8, -40, -11, -71, 40, -39, 62, -49,
+   -81, 16, -9, -52, 52, 61, 17, -103,
+   -27, -10, -8, -54, -57, 21, 23, -16,
+   -52, 36, 18, 10, -5, 8, 15, -29,
+   5, -19, -37, 8, -53, 6, 19, -37,
+   38, -17, 48, 10, 0, 81, 46, 70,
+   -29, 101, 11, 44, -44, -3, 24, 11,
+   3, 14, -9, 11, 14, -45, 13, 46,
+   -3, -57, 68, 44, 63, 98, 25, -28,
+   -23, 15, 32, -10, 53, -6, -2, -9,
+   -6, 16, -107, -11, -11, -28, 59, 57,
+   -22, 38, 42, 83, 27, 5, 29, -30,
+   12, -21, -13, 31, 38, -21, 58, -10,
+   -10, -15, -2, -5, 11, 12, -73, -28,
+   -38, 22, 2, -25, 73, -52, -12, -55,
+   32, -63, 21, 51, 33, 52, -26, 55,
+   -26, -26, 57, -32, -4, -52, -61, 21,
+   -33, -91, -51, 69, -90, -53, -38, -44,
+   12, -76, -20, 77, -45, -7, 86, 43,
+   -109, -33, -105, -40, -121, -10, 0, -72,
+   45, -51, -75, -49, -38, -1, -62, 18,
+   -1, 30, -44, -14, -10, -67, 40, -10,
+   -34, 46, -64, -32, 29, -13, 33, 3,
+   -32, -5, 28, -27, -25, 93, 24, 68,
+   -40, 57, 23, -3, -21, -58, 17, -39,
+   -17, -22, -89, 11, 18, -46, 27, 24,
+   46, 127, 61, 87, 31, 127, -36, 47,
+   -23, 47, 127, -24, 110, 122, 30, 100,
+   0, 96, -12, 6, 50, 44, -13, 73,
+   4, 55, -11, -15, 49, 42, -6, 20,
+   -35, 58, 18, 38, 42, 72, 19, -21,
+   11, 9, -37, 7, 29, 31, 16, -17,
+   13, -50, 19, 5, -23, 51, -16, -5,
+   4, -24, 76, 10, -53, -28, -7, -65,
+   74, 40, -16, -29, 32, -16, -49, -35,
+   -3, 59, -96, -50, -43, -43, -61, -15,
+   -8, -36, -34, -33, -14, 11, -3, -39,
+   4, -114, -123, -11, -49, -21, 14, -56,
+   1, 43, -63, 26, 40, 18, -10, -26,
+   -14, -15, -35, -35, -11, 32, -44, -67,
+   2, 22, 7, 3, -9, -30, -51, -28,
+   28, 6, -22, 16, 34, -25, -52, -54,
+   -8, -6, 5, 8, 20, -16, -17, -44,
+   27, 3, 31, -5, -48, -1, -3, 116,
+   11, 71, -31, -47, 109, 50, -22, -12,
+   -57, 32, 66, 8, -25, -93, -54, -10,
+   19, -76, -34, 97, 48, -36, -18, -30,
+   -39, -26, -12, 28, 14, 12, -12, -31,
+   38, 2, 10, 4, -40, 20, 16, -61,
+   2, 64, 39, 5, 15, 33, 40, -61,
+   -49, 93, -10, 33, 28, -11, -27, -18,
+   39, -62, -6, -6, 62, 11, -8, 38,
+   -67, 12, 27, 39, -27, 123, -18, -6,
+   -65, 83, -64, 20, 19, -11, 33, 24,
+   17, 56, 78, 7, -15, 54, -101, -9,
+   115, -96, 50, 51, 35, 34, 27, 37,
+   -40, -11, 8, -36, 42, -45, 2, -23,
+   0, 67, -8, -9, -13, 50, -14, -27,
+   4, 0, -8, -14, 30, -9, 29, 15,
+   9, -38, 37, -8, 50, -46, 54, 41,
+   -11, -8, -11, -26, 39, 45, 14, -26,
+   -17, -27, 69, 38, 39, 98, 66, 0,
+   42, 123, -101, -19, -83, 117, -32, 56,
+   10, 12, -88, 79, -53, 56, 63, 95,
+   -62, 9, 36, -13, -79, -16, 37, -46,
+   35, -34, 14, 17, -54, 5, 21, -7,
+   7, 63, 56, 15, 27, -76, -25, 4,
+   -26, -63, 28, -67, -52, 43, -47, -70,
+   40, -12, 40, -66, -37, 0, 35, 37,
+   -53, 4, -17, -51, 11, 21, 14, -34,
+   -4, 24, -42, 29, 22, 7, 28, 12,
+   37, 39, -39, -19, 65, -60, -50, -2,
+   1, 82, 39, 19, -23, -43, -22, -67,
+   -35, -34, 32, 102, 81, 127, 36, 67,
+   -45, 1, -67, -52, -4, 35, 20, 28,
+   71, 86, -35, -9, -83, -34, 12, 9,
+   -23, 2, 14, 28, -23, 7, -25, 45,
+   7, 17, -37, 0, -19, 31, 26, 40,
+   -27, -16, 17, 5, -21, 23, 24, 96,
+   -55, 52, -19, -14, -6, 1, 50, -34,
+   86, -53, 38, 2, -52, -36, -13, 60,
+   -85, -120, 32, 7, -12, 22, 70, -7,
+   -94, 38, -76, -31, -20, 15, -28, 7,
+   6, 40, 53, 88, 3, 38, 18, -8,
+   -22, -23, 51, 37, -9, 13, -32, 25,
+   -21, 27, 31, 20, 18, -9, -13, 1,
+   21, -24, -13, 39, 15, -11, -29, -36,
+   18, 15, 8, 27, 21, -94, -1, -22,
+   49, 66, -1, 6, -3, -40, -18, 6,
+   28, 12, 33, -59, 62, 60, -48, 90,
+   -1, 108, 9, 18, -2, 27, 77, -65,
+   82, -48, -38, -19, -11, 127, 50, 66,
+   18, -13, -22, 60, -38, 40, -14, -26,
+   -13, 38, 67, 57, 30, 33, 26, 36,
+   38, -17, 27, -28, 20, 12, -64, 18,
+   5, -33, -27, 13, -26, 32, 35, -5,
+   -48, -14, 92, 43, -47, -14, 40, 11,
+   51, 66, 22, -63, -16, -61, 4, -28,
+   27, 20, -33, -30, -21, -29, -53, 31,
+   -40, 24, 43, -4, -19, 21, 67, 20,
+   100, -16, -93, 78, -6, -18, -52, -37,
+   -9, 66, -31, -8, 26, 18, 4, 24,
+   -22, 17, -2, -13, 27, 0, 8, -18,
+   -25, 5, -21, -24, -7, 18, -93, 21,
+   7, 2, -75, 69, 50, -5, -15, -17,
+   60, -42, 55, 1, -4, 3, 10, 46,
+   16, -13, 45, -7, -10, -44, -108, 49,
+   2, -15, -64, -12, -72, 32, -38, -45,
+   10, -54, 13, -13, -27, -36, -64, 58,
+   -62, -101, 88, -86, -71, -39, -9, -128,
+   32, 15, -4, 54, -16, -39, -26, -36,
+   46, 48, -64, -10, 19, 30, -13, 34,
+   -8, 50, 60, -22, -6, -11, -30, 5,
+   50, 32, 56, 0, 25, 6, 68, 11,
+   -29, 45, -9, -12, 4, 1, 18, -49,
+   0, -38, -19, 90, 29, 35, 51, 8,
+   -48, 96, -1, -12, -9, -32, -63, -65,
+   -7, 38, 89, 28, -85, -28, -23, -25,
+   -128, 56, 79, -36, 99, -6, -37, 7,
+   -13, -69, -46, -29, 25, 64, -21, 17,
+   1, 42, -66, 1, 80, 26, -32, 21,
+   15, 15, 6, 6, -10, 15, 127, 5,
+   38, 27, 87, -57, -25, 11, 72, -21,
+   -5, 11, -13, -66, 78, 36, -3, 41,
+   -21, 8, -33, 23, 73, 28, 57, -25,
+   -5, 4, -22, -47, 15, 4, -57, -72,
+   33, 1, 18, 2, 53, -71, -99, -21,
+   -3, -111, 108, 71, -14, 82, 25, 61,
+   -48, 5, 9, -51, -20, -25, -3, 14,
+   -33, 14, -3, -34, 22, 12, -19, -38,
+   -16, 2, 21, 16, 26, -31, 75, 44,
+   -31, 16, 26, 66, 17, -9, -22, -22,
+   22, -44, 22, 27, 2, 58, -14, 10,
+   -73, -42, 55, -25, -61, 72, -1, 30,
+   -58, -25, 63, 26, -48, -40, 26, -30,
+   60, 8, -17, -1, -18, -20, 43, -20,
+   -4, -28, 127, -106, 29, 70, 64, -27,
+   39, -33, -5, -88, -40, -52, 26, 44,
+   -17, 23, 2, -49, 22, -9, -8, 86,
+   49, -43, -60, 1, 10, 45, 36, -53,
+   -4, 33, 38, 48, -72, 1, 19, 21,
+   -65, 4, -5, -62, 27, -25, 17, -6,
+   6, -45, -39, -46, 4, 26, 127, -9,
+   18, -33, -18, -3, 33, 2, -5, 15,
+   -26, -22, -117, -63, -17, -59, 61, -74,
+   7, -47, -58, -128, -67, 15, -16, -128,
+   12, 2, 20, 9, -48, -40, 43, 3,
+   -40, -16, -38, -6, -22, -28, -16, -59,
+   -22, 6, -5, 11, -12, -66, -40, 27,
+   -62, -44, -19, 38, -3, 39, -8, 40,
+   -24, 13, 21, 50, -60, -22, 53, -29,
+   -6, 1, 22, -59, 0, 17, -39, 115
+};
+
+static const opus_int8 layer1_bias[72] = {
+   -42, 20, 16, 0, 105, 60, 1, -97,
+   24, 60, 18, 13, 62, 25, 127, 34,
+   79, 55, 118, 127, 95, 31, -4, 87,
+   21, 12, 2, -14, 18, 23, 8, 17,
+   -1, -8, 5, 4, 24, 37, 21, 13,
+   36, 13, 17, 18, 37, 30, 33, 1,
+   8, -16, -11, -5, -31, -3, -5, 0,
+   6, 3, 58, -7, -1, -16, 5, -13,
+   16, 10, -2, -14, 11, -4, 3, -11
+};
+
+static const opus_int8 layer2_weights[48] = {
+   -113, -88, 31, -128, -126, -61, 85, -35,
+   118, -128, -61, 127, -128, -17, -128, 127,
+   104, -9, -128, 33, 45, 127, 5, 83,
+   84, -128, -85, -128, -45, 48, -53, -128,
+   46, 127, -17, 125, 117, -41, -117, -91,
+   -127, -68, -1, -89, -80, 32, 106, 7
+};
+
+static const opus_int8 layer2_bias[2] = {
+   14, 117
+};
+
+const AnalysisDenseLayer layer0 = {
+   layer0_bias,
+   layer0_weights,
+   25, 32, 0
+};
+
+const AnalysisGRULayer layer1 = {
+   layer1_bias,
+   layer1_weights,
+   layer1_recur_weights,
+   32, 24
+};
+
+const AnalysisDenseLayer layer2 = {
+   layer2_bias,
+   layer2_weights,
+   24, 2, 1
+};
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/src_lib/opus_encoder.c b/code/sdk/zhihui/test_opus_frame_to_pcm/src_lib/opus_encoder.c
new file mode 100644 (file)
index 0000000..a74631f
--- /dev/null
@@ -0,0 +1,3365 @@
+/* Copyright (c) 2010-2011 Xiph.Org Foundation, Skype Limited
+   Written by Jean-Marc Valin and Koen Vos */
+/*
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+   - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+   - Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+   OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdarg.h>
+#include "celt.h"
+#include "entenc.h"
+#include "modes.h"
+#include "API.h"
+#include "stack_alloc.h"
+#include "float_cast.h"
+#include "opus.h"
+#include "arch.h"
+#include "pitch.h"
+#include "opus_private.h"
+#include "os_support.h"
+#include "cpu_support.h"
+#include "analysis.h"
+#include "mathops.h"
+#include "tuning_parameters.h"
+
+#ifdef ENABLE_DRED
+#include "dred_coding.h"
+#endif
+
+#ifdef FIXED_POINT
+#include "fixed/structs_FIX.h"
+#else
+#include "float/structs_FLP.h"
+#endif
+#ifdef ENABLE_OSCE_TRAINING_DATA
+#include <stdio.h>
+#endif
+
+#ifdef ENABLE_QEXT
+#define MAX_ENCODER_BUFFER 960
+#else
+#define MAX_ENCODER_BUFFER 480
+#endif
+
+#define PSEUDO_SNR_THRESHOLD 316.23f    /* 10^(25/10) */
+
+typedef struct {
+   opus_val32 XX, XY, YY;
+   opus_val16 smoothed_width;
+   opus_val16 max_follower;
+} StereoWidthState;
+
+struct OpusEncoder {
+    int          celt_enc_offset;
+    int          silk_enc_offset;
+    silk_EncControlStruct silk_mode;
+#ifdef ENABLE_DRED
+    DREDEnc      dred_encoder;
+#endif
+    int          application;
+    int          channels;
+    int          delay_compensation;
+    int          force_channels;
+    int          signal_type;
+    int          user_bandwidth;
+    int          max_bandwidth;
+    int          user_forced_mode;
+    int          voice_ratio;
+    opus_int32   Fs;
+    int          use_vbr;
+    int          vbr_constraint;
+    int          variable_duration;
+    opus_int32   bitrate_bps;
+    opus_int32   user_bitrate_bps;
+    int          lsb_depth;
+    int          encoder_buffer;
+    int          lfe;
+    int          arch;
+    int          use_dtx;                 /* general DTX for both SILK and CELT */
+    int          fec_config;
+#ifndef DISABLE_FLOAT_API
+    TonalityAnalysisState analysis;
+#endif
+#ifdef ENABLE_QEXT
+   int enable_qext;
+#endif
+
+#define OPUS_ENCODER_RESET_START stream_channels
+    int          stream_channels;
+    opus_int16   hybrid_stereo_width_Q14;
+    opus_int32   variable_HP_smth2_Q15;
+    opus_val16   prev_HB_gain;
+    opus_val32   hp_mem[4];
+    int          mode;
+    int          prev_mode;
+    int          prev_channels;
+    int          prev_framesize;
+    int          bandwidth;
+    /* Bandwidth determined automatically from the rate (before any other adjustment) */
+    int          auto_bandwidth;
+    int          silk_bw_switch;
+    /* Sampling rate (at the API level) */
+    int          first;
+    celt_glog * energy_masking;
+    StereoWidthState width_mem;
+#ifndef DISABLE_FLOAT_API
+    int          detected_bandwidth;
+#endif
+    int          nb_no_activity_ms_Q1;
+    opus_val32   peak_signal_energy;
+#ifdef ENABLE_DRED
+    int          dred_duration;
+    int          dred_q0;
+    int          dred_dQ;
+    int          dred_qmax;
+    int          dred_target_chunks;
+    unsigned char activity_mem[DRED_MAX_FRAMES*4]; /* 2.5ms resolution*/
+#endif
+    int          nonfinal_frame; /* current frame is not the final in a packet */
+    opus_uint32  rangeFinal;
+    /* Needs to be the last field because it may be partially or completely omitted. */
+    opus_res     delay_buffer[MAX_ENCODER_BUFFER*2];
+};
+
+/* Transition tables for the voice and music. First column is the
+   middle (memoriless) threshold. The second column is the hysteresis
+   (difference with the middle) */
+static const opus_int32 mono_voice_bandwidth_thresholds[8] = {
+         9000,  700, /* NB<->MB */
+         9000,  700, /* MB<->WB */
+        13500, 1000, /* WB<->SWB */
+        14000, 2000, /* SWB<->FB */
+};
+static const opus_int32 mono_music_bandwidth_thresholds[8] = {
+         9000,  700, /* NB<->MB */
+         9000,  700, /* MB<->WB */
+        11000, 1000, /* WB<->SWB */
+        12000, 2000, /* SWB<->FB */
+};
+static const opus_int32 stereo_voice_bandwidth_thresholds[8] = {
+         9000,  700, /* NB<->MB */
+         9000,  700, /* MB<->WB */
+        13500, 1000, /* WB<->SWB */
+        14000, 2000, /* SWB<->FB */
+};
+static const opus_int32 stereo_music_bandwidth_thresholds[8] = {
+         9000,  700, /* NB<->MB */
+         9000,  700, /* MB<->WB */
+        11000, 1000, /* WB<->SWB */
+        12000, 2000, /* SWB<->FB */
+};
+/* Threshold bit-rates for switching between mono and stereo */
+static const opus_int32 stereo_voice_threshold = 19000;
+static const opus_int32 stereo_music_threshold = 17000;
+
+/* Threshold bit-rate for switching between SILK/hybrid and CELT-only */
+static const opus_int32 mode_thresholds[2][2] = {
+      /* voice */ /* music */
+      {  64000,      10000}, /* mono */
+      {  44000,      10000}, /* stereo */
+};
+
+static const opus_int32 fec_thresholds[] = {
+        12000, 1000, /* NB */
+        14000, 1000, /* MB */
+        16000, 1000, /* WB */
+        20000, 1000, /* SWB */
+        22000, 1000, /* FB */
+};
+
+int opus_encoder_get_size(int channels)
+{
+    int ret;
+    ret = opus_encoder_init(NULL, 48000, channels, OPUS_APPLICATION_AUDIO);
+    if (ret < 0)
+        return 0;
+    else
+        return ret;
+}
+
+int opus_encoder_init(OpusEncoder* st, opus_int32 Fs, int channels, int application)
+{
+    void *silk_enc=NULL;
+    CELTEncoder *celt_enc=NULL;
+    int err;
+    int ret, silkEncSizeBytes, celtEncSizeBytes=0;
+    int tot_size;
+    int base_size;
+
+   if((Fs!=48000&&Fs!=24000&&Fs!=16000&&Fs!=12000&&Fs!=8000
+#ifdef ENABLE_QEXT
+         &&Fs!=96000
+#endif
+         )||(channels!=1&&channels!=2)||
+        (application != OPUS_APPLICATION_VOIP && application != OPUS_APPLICATION_AUDIO
+        && application != OPUS_APPLICATION_RESTRICTED_LOWDELAY
+        && application != OPUS_APPLICATION_RESTRICTED_SILK
+        && application != OPUS_APPLICATION_RESTRICTED_CELT))
+        return OPUS_BAD_ARG;
+
+    /* Create SILK encoder */
+    ret = silk_Get_Encoder_Size( &silkEncSizeBytes, channels );
+    if (ret)
+        return OPUS_BAD_ARG;
+    silkEncSizeBytes = align(silkEncSizeBytes);
+    if (application == OPUS_APPLICATION_RESTRICTED_CELT)
+        silkEncSizeBytes = 0;
+    if (application != OPUS_APPLICATION_RESTRICTED_SILK)
+        celtEncSizeBytes = celt_encoder_get_size(channels);
+    base_size = align(sizeof(OpusEncoder));
+    if (application == OPUS_APPLICATION_RESTRICTED_SILK || application == OPUS_APPLICATION_RESTRICTED_CELT) {
+       base_size = align(base_size - MAX_ENCODER_BUFFER*2*sizeof(opus_res));
+    } else if (channels==1)
+       base_size = align(base_size - MAX_ENCODER_BUFFER*sizeof(opus_res));
+    tot_size = base_size+silkEncSizeBytes+celtEncSizeBytes;
+    if (st == NULL) {
+        return tot_size;
+    }
+    OPUS_CLEAR((char*)st, tot_size);
+    st->silk_enc_offset = base_size;
+    st->celt_enc_offset = st->silk_enc_offset+silkEncSizeBytes;
+
+    st->stream_channels = st->channels = channels;
+
+    st->Fs = Fs;
+
+    st->arch = opus_select_arch();
+
+    if (application != OPUS_APPLICATION_RESTRICTED_CELT)
+    {
+       silk_enc = (char*)st+st->silk_enc_offset;
+       ret = silk_InitEncoder( silk_enc, st->channels, st->arch, &st->silk_mode );
+    }
+    if(ret)return OPUS_INTERNAL_ERROR;
+
+    /* default SILK parameters */
+    st->silk_mode.nChannelsAPI              = channels;
+    st->silk_mode.nChannelsInternal         = channels;
+    st->silk_mode.API_sampleRate            = st->Fs;
+    st->silk_mode.maxInternalSampleRate     = 16000;
+    st->silk_mode.minInternalSampleRate     = 8000;
+    st->silk_mode.desiredInternalSampleRate = 16000;
+    st->silk_mode.payloadSize_ms            = 20;
+    st->silk_mode.bitRate                   = 25000;
+    st->silk_mode.packetLossPercentage      = 0;
+    st->silk_mode.complexity                = 9;
+    st->silk_mode.useInBandFEC              = 0;
+    st->silk_mode.useDRED                   = 0;
+    st->silk_mode.useDTX                    = 0;
+    st->silk_mode.useCBR                    = 0;
+    st->silk_mode.reducedDependency         = 0;
+
+    /* Create CELT encoder */
+    /* Initialize CELT encoder */
+    if (application != OPUS_APPLICATION_RESTRICTED_SILK)
+    {
+       celt_enc = (CELTEncoder*)((char*)st+st->celt_enc_offset);
+       err = celt_encoder_init(celt_enc, Fs, channels, st->arch);
+       if(err!=OPUS_OK)return OPUS_INTERNAL_ERROR;
+       celt_encoder_ctl(celt_enc, CELT_SET_SIGNALLING(0));
+       celt_encoder_ctl(celt_enc, OPUS_SET_COMPLEXITY(st->silk_mode.complexity));
+    }
+
+#ifdef ENABLE_DRED
+    /* Initialize DRED Encoder */
+    dred_encoder_init( &st->dred_encoder, Fs, channels );
+#endif
+
+    st->use_vbr = 1;
+    /* Makes constrained VBR the default (safer for real-time use) */
+    st->vbr_constraint = 1;
+    st->user_bitrate_bps = OPUS_AUTO;
+    st->bitrate_bps = 3000+Fs*channels;
+    st->application = application;
+    st->signal_type = OPUS_AUTO;
+    st->user_bandwidth = OPUS_AUTO;
+    st->max_bandwidth = OPUS_BANDWIDTH_FULLBAND;
+    st->force_channels = OPUS_AUTO;
+    st->user_forced_mode = OPUS_AUTO;
+    st->voice_ratio = -1;
+    if (application != OPUS_APPLICATION_RESTRICTED_CELT && application != OPUS_APPLICATION_RESTRICTED_SILK)
+       st->encoder_buffer = st->Fs/100;
+    else
+       st->encoder_buffer = 0;
+    st->lsb_depth = 24;
+    st->variable_duration = OPUS_FRAMESIZE_ARG;
+
+    /* Delay compensation of 4 ms (2.5 ms for SILK's extra look-ahead
+       + 1.5 ms for SILK resamplers and stereo prediction) */
+    st->delay_compensation = st->Fs/250;
+
+    st->hybrid_stereo_width_Q14 = 1 << 14;
+    st->prev_HB_gain = Q15ONE;
+    st->variable_HP_smth2_Q15 = silk_LSHIFT( silk_lin2log( VARIABLE_HP_MIN_CUTOFF_HZ ), 8 );
+    st->first = 1;
+    st->mode = MODE_HYBRID;
+    st->bandwidth = OPUS_BANDWIDTH_FULLBAND;
+
+#ifndef DISABLE_FLOAT_API
+    tonality_analysis_init(&st->analysis, st->Fs);
+    st->analysis.application = st->application;
+#endif
+
+    return OPUS_OK;
+}
+
+static unsigned char gen_toc(int mode, int framerate, int bandwidth, int channels)
+{
+   int period;
+   unsigned char toc;
+   period = 0;
+   while (framerate < 400)
+   {
+       framerate <<= 1;
+       period++;
+   }
+   if (mode == MODE_SILK_ONLY)
+   {
+       toc = (bandwidth-OPUS_BANDWIDTH_NARROWBAND)<<5;
+       toc |= (period-2)<<3;
+   } else if (mode == MODE_CELT_ONLY)
+   {
+       int tmp = bandwidth-OPUS_BANDWIDTH_MEDIUMBAND;
+       if (tmp < 0)
+           tmp = 0;
+       toc = 0x80;
+       toc |= tmp << 5;
+       toc |= period<<3;
+   } else /* Hybrid */
+   {
+       toc = 0x60;
+       toc |= (bandwidth-OPUS_BANDWIDTH_SUPERWIDEBAND)<<4;
+       toc |= (period-2)<<3;
+   }
+   toc |= (channels==2)<<2;
+   return toc;
+}
+
+#ifdef FIXED_POINT
+/* Second order ARMA filter, alternative implementation */
+void silk_biquad_res(
+    const opus_res              *in,                /* I     input signal                                               */
+    const opus_int32            *B_Q28,             /* I     MA coefficients [3]                                        */
+    const opus_int32            *A_Q28,             /* I     AR coefficients [2]                                        */
+    opus_int32                  *S,                 /* I/O   State vector [2]                                           */
+    opus_res                    *out,               /* O     output signal                                              */
+    const opus_int32            len,                /* I     signal length (must be even)                               */
+    int stride
+)
+{
+    /* DIRECT FORM II TRANSPOSED (uses 2 element state vector) */
+    opus_int   k;
+    opus_int32 inval, A0_U_Q28, A0_L_Q28, A1_U_Q28, A1_L_Q28, out32_Q14;
+
+    /* Negate A_Q28 values and split in two parts */
+    A0_L_Q28 = ( -A_Q28[ 0 ] ) & 0x00003FFF;        /* lower part */
+    A0_U_Q28 = silk_RSHIFT( -A_Q28[ 0 ], 14 );      /* upper part */
+    A1_L_Q28 = ( -A_Q28[ 1 ] ) & 0x00003FFF;        /* lower part */
+    A1_U_Q28 = silk_RSHIFT( -A_Q28[ 1 ], 14 );      /* upper part */
+
+    for( k = 0; k < len; k++ ) {
+        /* S[ 0 ], S[ 1 ]: Q12 */
+        inval = RES2INT16(in[ k*stride ]);
+        out32_Q14 = silk_LSHIFT( silk_SMLAWB( S[ 0 ], B_Q28[ 0 ], inval ), 2 );
+
+        S[ 0 ] = S[1] + silk_RSHIFT_ROUND( silk_SMULWB( out32_Q14, A0_L_Q28 ), 14 );
+        S[ 0 ] = silk_SMLAWB( S[ 0 ], out32_Q14, A0_U_Q28 );
+        S[ 0 ] = silk_SMLAWB( S[ 0 ], B_Q28[ 1 ], inval);
+
+        S[ 1 ] = silk_RSHIFT_ROUND( silk_SMULWB( out32_Q14, A1_L_Q28 ), 14 );
+        S[ 1 ] = silk_SMLAWB( S[ 1 ], out32_Q14, A1_U_Q28 );
+        S[ 1 ] = silk_SMLAWB( S[ 1 ], B_Q28[ 2 ], inval );
+
+        /* Scale back to Q0 and saturate */
+        out[ k*stride ] = INT16TORES( silk_SAT16( silk_RSHIFT( out32_Q14 + (1<<14) - 1, 14 ) ) );
+    }
+}
+#else
+static void silk_biquad_res(
+    const opus_res        *in,            /* I:    Input signal                   */
+    const opus_int32      *B_Q28,         /* I:    MA coefficients [3]            */
+    const opus_int32      *A_Q28,         /* I:    AR coefficients [2]            */
+    opus_val32            *S,             /* I/O:  State vector [2]               */
+    opus_res              *out,           /* O:    Output signal                  */
+    const opus_int32      len,            /* I:    Signal length (must be even)   */
+    int stride
+)
+{
+    /* DIRECT FORM II TRANSPOSED (uses 2 element state vector) */
+    opus_int   k;
+    opus_val32 vout;
+    opus_val32 inval;
+    opus_val32 A[2], B[3];
+
+    A[0] = (opus_val32)(A_Q28[0] * (1.f/((opus_int32)1<<28)));
+    A[1] = (opus_val32)(A_Q28[1] * (1.f/((opus_int32)1<<28)));
+    B[0] = (opus_val32)(B_Q28[0] * (1.f/((opus_int32)1<<28)));
+    B[1] = (opus_val32)(B_Q28[1] * (1.f/((opus_int32)1<<28)));
+    B[2] = (opus_val32)(B_Q28[2] * (1.f/((opus_int32)1<<28)));
+
+    /* Negate A_Q28 values and split in two parts */
+
+    for( k = 0; k < len; k++ ) {
+        /* S[ 0 ], S[ 1 ]: Q12 */
+        inval = in[ k*stride ];
+        vout = S[ 0 ] + B[0]*inval;
+
+        S[ 0 ] = S[1] - vout*A[0] + B[1]*inval;
+
+        S[ 1 ] = - vout*A[1] + B[2]*inval + VERY_SMALL;
+
+        /* Scale back to Q0 and saturate */
+        out[ k*stride ] = vout;
+    }
+}
+#endif
+
+static void hp_cutoff(const opus_res *in, opus_int32 cutoff_Hz, opus_res *out, opus_val32 *hp_mem, int len, int channels, opus_int32 Fs, int arch)
+{
+   opus_int32 B_Q28[ 3 ], A_Q28[ 2 ];
+   opus_int32 Fc_Q19, r_Q28, r_Q22;
+   (void)arch;
+
+   silk_assert( cutoff_Hz <= silk_int32_MAX / SILK_FIX_CONST( 1.5 * 3.14159 / 1000, 19 ) );
+   Fc_Q19 = silk_DIV32_16( silk_SMULBB( SILK_FIX_CONST( 1.5 * 3.14159 / 1000, 19 ), cutoff_Hz ), Fs/1000 );
+   silk_assert( Fc_Q19 > 0 && Fc_Q19 < 32768 );
+
+   r_Q28 = SILK_FIX_CONST( 1.0, 28 ) - silk_MUL( SILK_FIX_CONST( 0.92, 9 ), Fc_Q19 );
+
+   /* b = r * [ 1; -2; 1 ]; */
+   /* a = [ 1; -2 * r * ( 1 - 0.5 * Fc^2 ); r^2 ]; */
+   B_Q28[ 0 ] = r_Q28;
+   B_Q28[ 1 ] = silk_LSHIFT( -r_Q28, 1 );
+   B_Q28[ 2 ] = r_Q28;
+
+   /* -r * ( 2 - Fc * Fc ); */
+   r_Q22  = silk_RSHIFT( r_Q28, 6 );
+   A_Q28[ 0 ] = silk_SMULWW( r_Q22, silk_SMULWW( Fc_Q19, Fc_Q19 ) - SILK_FIX_CONST( 2.0,  22 ) );
+   A_Q28[ 1 ] = silk_SMULWW( r_Q22, r_Q22 );
+
+#if defined(FIXED_POINT) && !defined(ENABLE_RES24)
+   if( channels == 1 ) {
+      silk_biquad_alt_stride1( in, B_Q28, A_Q28, hp_mem, out, len );
+   } else {
+      silk_biquad_alt_stride2( in, B_Q28, A_Q28, hp_mem, out, len, arch );
+   }
+#else
+   silk_biquad_res( in, B_Q28, A_Q28, hp_mem, out, len, channels );
+   if( channels == 2 ) {
+       silk_biquad_res( in+1, B_Q28, A_Q28, hp_mem+2, out+1, len, channels );
+   }
+#endif
+}
+
+#ifdef FIXED_POINT
+static void dc_reject(const opus_res *in, opus_int32 cutoff_Hz, opus_res *out, opus_val32 *hp_mem, int len, int channels, opus_int32 Fs)
+{
+   int c, i;
+   int shift;
+
+   /* Approximates -round(log2(6.3*cutoff_Hz/Fs)) */
+   shift=celt_ilog2(Fs/(cutoff_Hz*4));
+   for (c=0;c<channels;c++)
+   {
+      for (i=0;i<len;i++)
+      {
+         opus_val32 x, y;
+         /* Saturate at +6 dBFS to avoid any wrap-around. */
+         x = SATURATE((opus_val32)in[channels*i+c], (1<<16<<RES_SHIFT)-1);
+         x = SHL32(x, 14-RES_SHIFT);
+         y = x-hp_mem[2*c];
+         hp_mem[2*c] = hp_mem[2*c] + PSHR32(x - hp_mem[2*c], shift);
+#ifdef ENABLE_RES24
+         /* Don't saturate if we have the headroom to avoid it. */
+         out[channels*i+c] = PSHR32(y, 14-RES_SHIFT);
+#else
+         out[channels*i+c] = SATURATE(PSHR32(y, 14-RES_SHIFT), 32767);
+#endif
+      }
+   }
+}
+
+#else
+static void dc_reject(const opus_val16 *in, opus_int32 cutoff_Hz, opus_val16 *out, opus_val32 *hp_mem, int len, int channels, opus_int32 Fs)
+{
+   int i;
+   float coef, coef2;
+   coef = 6.3f*cutoff_Hz/Fs;
+   coef2 = 1-coef;
+   if (channels==2)
+   {
+      float m0, m2;
+      m0 = hp_mem[0];
+      m2 = hp_mem[2];
+      for (i=0;i<len;i++)
+      {
+         opus_val32 x0, x1, out0, out1;
+         x0 = in[2*i+0];
+         x1 = in[2*i+1];
+         out0 = x0-m0;
+         out1 = x1-m2;
+         m0 = coef*x0 + VERY_SMALL + coef2*m0;
+         m2 = coef*x1 + VERY_SMALL + coef2*m2;
+         out[2*i+0] = out0;
+         out[2*i+1] = out1;
+      }
+      hp_mem[0] = m0;
+      hp_mem[2] = m2;
+   } else {
+      float m0;
+      m0 = hp_mem[0];
+      for (i=0;i<len;i++)
+      {
+         opus_val32 x, y;
+         x = in[i];
+         y = x-m0;
+         m0 = coef*x + VERY_SMALL + coef2*m0;
+         out[i] = y;
+      }
+      hp_mem[0] = m0;
+   }
+}
+#endif
+
+static void stereo_fade(const opus_res *in, opus_res *out, opus_val16 g1, opus_val16 g2,
+        int overlap48, int frame_size, int channels, const celt_coef *window, opus_int32 Fs)
+{
+    int i;
+    int overlap;
+    int inc;
+    inc = IMAX(1, 48000/Fs);
+    overlap=overlap48/inc;
+    g1 = Q15ONE-g1;
+    g2 = Q15ONE-g2;
+    for (i=0;i<overlap;i++)
+    {
+       opus_val32 diff;
+       opus_val16 g, w;
+       w = COEF2VAL16(window[i*inc]);
+       w = MULT16_16_Q15(w, w);
+       g = SHR32(MAC16_16(MULT16_16(w,g2),
+             Q15ONE-w, g1), 15);
+       diff = HALF32((opus_val32)in[i*channels] - (opus_val32)in[i*channels+1]);
+       diff = MULT16_RES_Q15(g, diff);
+       out[i*channels] = out[i*channels] - diff;
+       out[i*channels+1] = out[i*channels+1] + diff;
+    }
+    for (;i<frame_size;i++)
+    {
+       opus_val32 diff;
+       diff = HALF32((opus_val32)in[i*channels] - (opus_val32)in[i*channels+1]);
+       diff = MULT16_RES_Q15(g2, diff);
+       out[i*channels] = out[i*channels] - diff;
+       out[i*channels+1] = out[i*channels+1] + diff;
+    }
+}
+
+static void gain_fade(const opus_res *in, opus_res *out, opus_val16 g1, opus_val16 g2,
+        int overlap48, int frame_size, int channels, const celt_coef *window, opus_int32 Fs)
+{
+    int i;
+    int inc;
+    int overlap;
+    int c;
+    inc = IMAX(1, 48000/Fs);
+    overlap=overlap48/inc;
+    if (channels==1)
+    {
+       for (i=0;i<overlap;i++)
+       {
+          opus_val16 g, w;
+          w = COEF2VAL16(window[i*inc]);
+          w = MULT16_16_Q15(w, w);
+          g = SHR32(MAC16_16(MULT16_16(w,g2),
+                Q15ONE-w, g1), 15);
+          out[i] = MULT16_RES_Q15(g, in[i]);
+       }
+    } else {
+       for (i=0;i<overlap;i++)
+       {
+          opus_val16 g, w;
+          w = COEF2VAL16(window[i*inc]);
+          w = MULT16_16_Q15(w, w);
+          g = SHR32(MAC16_16(MULT16_16(w,g2),
+                Q15ONE-w, g1), 15);
+          out[i*2] = MULT16_RES_Q15(g, in[i*2]);
+          out[i*2+1] = MULT16_RES_Q15(g, in[i*2+1]);
+       }
+    }
+    c=0;do {
+       for (i=overlap;i<frame_size;i++)
+       {
+          out[i*channels+c] = MULT16_RES_Q15(g2, in[i*channels+c]);
+       }
+    }
+    while (++c<channels);
+}
+
+OpusEncoder *opus_encoder_create(opus_int32 Fs, int channels, int application, int *error)
+{
+   int ret;
+   OpusEncoder *st;
+   int size;
+   if((Fs!=48000&&Fs!=24000&&Fs!=16000&&Fs!=12000&&Fs!=8000
+#ifdef ENABLE_QEXT
+         &&Fs!=96000
+#endif
+         )||(channels!=1&&channels!=2)||
+       (application != OPUS_APPLICATION_VOIP && application != OPUS_APPLICATION_AUDIO
+       && application != OPUS_APPLICATION_RESTRICTED_LOWDELAY
+       && application != OPUS_APPLICATION_RESTRICTED_SILK
+       && application != OPUS_APPLICATION_RESTRICTED_CELT))
+   {
+      if (error)
+         *error = OPUS_BAD_ARG;
+      return NULL;
+   }
+   size = opus_encoder_init(NULL, Fs, channels, application);
+   if (size <= 0)
+   {
+      if (error)
+         *error = OPUS_INTERNAL_ERROR;
+      return NULL;
+   }
+   st = (OpusEncoder *)opus_alloc(size);
+   if (st == NULL)
+   {
+      if (error)
+         *error = OPUS_ALLOC_FAIL;
+      return NULL;
+   }
+   ret = opus_encoder_init(st, Fs, channels, application);
+   if (error)
+      *error = ret;
+   if (ret != OPUS_OK)
+   {
+      opus_free(st);
+      st = NULL;
+   }
+   return st;
+}
+
+#ifdef ENABLE_DRED
+
+static const float dred_bits_table[16] = {73.2f, 68.1f, 62.5f, 57.0f, 51.5f, 45.7f, 39.9f, 32.4f, 26.4f, 20.4f, 16.3f, 13.f, 9.3f, 8.2f, 7.2f, 6.4f};
+static int estimate_dred_bitrate(int q0, int dQ, int qmax, int duration, opus_int32 target_bits, int *target_chunks) {
+   int dred_chunks;
+   int i;
+   float bits;
+   /* Signaling DRED costs 3 bytes. */
+   bits = 8*(3+DRED_EXPERIMENTAL_BYTES);
+   /* Approximation for the size of the IS. */
+   bits += 50.f+dred_bits_table[q0];
+   dred_chunks = IMIN((duration+5)/4, DRED_NUM_REDUNDANCY_FRAMES/2);
+   if (target_chunks != NULL) *target_chunks = 0;
+   for (i=0;i<dred_chunks;i++) {
+      int q = compute_quantizer(q0, dQ, qmax, i);
+      bits += dred_bits_table[q];
+      if (target_chunks != NULL && bits < target_bits) *target_chunks = i+1;
+   }
+   return (int)floor(.5f+bits);
+}
+
+static opus_int32 compute_dred_bitrate(OpusEncoder *st, opus_int32 bitrate_bps, int frame_size)
+{
+   float dred_frac;
+   int bitrate_offset;
+   opus_int32 dred_bitrate;
+   opus_int32 target_dred_bitrate;
+   int target_chunks;
+   opus_int32 max_dred_bits;
+   int q0, dQ, qmax;
+   if (st->silk_mode.useInBandFEC) {
+      dred_frac = MIN16(.7f, 3.f*st->silk_mode.packetLossPercentage/100.f);
+      bitrate_offset = 20000;
+   } else {
+      if (st->silk_mode.packetLossPercentage > 5) {
+         dred_frac = MIN16(.8f, .55f + st->silk_mode.packetLossPercentage/100.f);
+      } else {
+         dred_frac = 12*st->silk_mode.packetLossPercentage/100.f;
+      }
+      bitrate_offset = 12000;
+   }
+   /* Account for the fact that longer packets require less redundancy. */
+   dred_frac = dred_frac/(dred_frac + (1-dred_frac)*(frame_size*50.f)/st->Fs);
+   /* Approximate fit based on a few experiments. Could probably be improved. */
+   q0 = IMIN(15, IMAX(4, 51 - 3*EC_ILOG(IMAX(1, bitrate_bps-bitrate_offset))));
+   dQ = bitrate_bps-bitrate_offset > 36000 ? 3 : 5;
+   qmax = 15;
+   target_dred_bitrate = IMAX(0, (int)(dred_frac*(bitrate_bps-bitrate_offset)));
+   if (st->dred_duration > 0) {
+      opus_int32 target_bits = bitrate_to_bits(target_dred_bitrate, st->Fs, frame_size);
+      max_dred_bits = estimate_dred_bitrate(q0, dQ, qmax, st->dred_duration, target_bits, &target_chunks);
+   } else {
+      max_dred_bits = 0;
+      target_chunks=0;
+   }
+   dred_bitrate = IMIN(target_dred_bitrate, bits_to_bitrate(max_dred_bits, st->Fs, frame_size));
+   /* If we can't afford enough bits, don't bother with DRED at all. */
+   if (target_chunks < 2)
+      dred_bitrate = 0;
+   st->dred_q0 = q0;
+   st->dred_dQ = dQ;
+   st->dred_qmax = qmax;
+   st->dred_target_chunks = target_chunks;
+   return dred_bitrate;
+}
+#endif
+
+static opus_int32 user_bitrate_to_bitrate(OpusEncoder *st, int frame_size, int max_data_bytes)
+{
+  opus_int32 max_bitrate, user_bitrate;
+  if(!frame_size)frame_size=st->Fs/400;
+  max_bitrate = bits_to_bitrate(max_data_bytes*8, st->Fs, frame_size);
+  if (st->user_bitrate_bps==OPUS_AUTO)
+     user_bitrate = 60*st->Fs/frame_size + st->Fs*st->channels;
+  else if (st->user_bitrate_bps==OPUS_BITRATE_MAX)
+     user_bitrate = 1500000;
+  else
+     user_bitrate = st->user_bitrate_bps;
+  return IMIN(user_bitrate, max_bitrate);
+}
+
+#ifndef DISABLE_FLOAT_API
+void downmix_float(const void *_x, opus_val32 *y, int subframe, int offset, int c1, int c2, int C)
+{
+   const float *x;
+   int j;
+
+   x = (const float *)_x;
+   for (j=0;j<subframe;j++)
+      y[j] = FLOAT2SIG(x[(j+offset)*C+c1]);
+   if (c2>-1)
+   {
+      for (j=0;j<subframe;j++)
+         y[j] += FLOAT2SIG(x[(j+offset)*C+c2]);
+   } else if (c2==-2)
+   {
+      int c;
+      for (c=1;c<C;c++)
+      {
+         for (j=0;j<subframe;j++)
+            y[j] += FLOAT2SIG(x[(j+offset)*C+c]);
+      }
+   }
+#ifndef FIXED_POINT
+   /* Cap signal to +6 dBFS to avoid problems in the analysis. */
+   for (j=0;j<subframe;j++)
+   {
+      if (y[j] < -65536.f) y[j] = -65536.f;
+      if (y[j] >  65536.f) y[j] =  65536.f;
+      if (celt_isnan(y[j])) y[j] = 0;
+   }
+#endif
+}
+#endif
+
+void downmix_int(const void *_x, opus_val32 *y, int subframe, int offset, int c1, int c2, int C)
+{
+   const opus_int16 *x;
+   int j;
+
+   x = (const opus_int16 *)_x;
+   for (j=0;j<subframe;j++)
+      y[j] = INT16TOSIG(x[(j+offset)*C+c1]);
+   if (c2>-1)
+   {
+      for (j=0;j<subframe;j++)
+         y[j] += INT16TOSIG(x[(j+offset)*C+c2]);
+   } else if (c2==-2)
+   {
+      int c;
+      for (c=1;c<C;c++)
+      {
+         for (j=0;j<subframe;j++)
+            y[j] += INT16TOSIG(x[(j+offset)*C+c]);
+      }
+   }
+}
+
+void downmix_int24(const void *_x, opus_val32 *y, int subframe, int offset, int c1, int c2, int C)
+{
+   const opus_int32 *x;
+   int j;
+
+   x = (const opus_int32 *)_x;
+   for (j=0;j<subframe;j++)
+      y[j] = INT24TOSIG(x[(j+offset)*C+c1]);
+   if (c2>-1)
+   {
+      for (j=0;j<subframe;j++)
+         y[j] += INT24TOSIG(x[(j+offset)*C+c2]);
+   } else if (c2==-2)
+   {
+      int c;
+      for (c=1;c<C;c++)
+      {
+         for (j=0;j<subframe;j++)
+            y[j] += INT24TOSIG(x[(j+offset)*C+c]);
+      }
+   }
+}
+
+opus_int32 frame_size_select(int application, opus_int32 frame_size, int variable_duration, opus_int32 Fs)
+{
+   int new_size;
+   if (frame_size<Fs/400)
+      return -1;
+   if (variable_duration == OPUS_FRAMESIZE_ARG)
+      new_size = frame_size;
+   else if (variable_duration >= OPUS_FRAMESIZE_2_5_MS && variable_duration <= OPUS_FRAMESIZE_120_MS)
+   {
+      if (variable_duration <= OPUS_FRAMESIZE_40_MS)
+         new_size = (Fs/400)<<(variable_duration-OPUS_FRAMESIZE_2_5_MS);
+      else
+         new_size = (variable_duration-OPUS_FRAMESIZE_2_5_MS-2)*Fs/50;
+   }
+   else
+      return -1;
+   if (new_size>frame_size)
+      return -1;
+   if (400*new_size!=Fs   && 200*new_size!=Fs   && 100*new_size!=Fs   &&
+        50*new_size!=Fs   &&  25*new_size!=Fs   &&  50*new_size!=3*Fs &&
+        50*new_size!=4*Fs &&  50*new_size!=5*Fs &&  50*new_size!=6*Fs)
+      return -1;
+   if (application == OPUS_APPLICATION_RESTRICTED_SILK && new_size < Fs/100)
+      return -1;
+   return new_size;
+}
+
+opus_val16 compute_stereo_width(const opus_res *pcm, int frame_size, opus_int32 Fs, StereoWidthState *mem)
+{
+   opus_val32 xx, xy, yy;
+   opus_val16 sqrt_xx, sqrt_yy;
+   opus_val16 qrrt_xx, qrrt_yy;
+   int frame_rate;
+   int i;
+   opus_val16 short_alpha;
+#ifdef FIXED_POINT
+   int shift = celt_ilog2(frame_size)-2;
+#endif
+   frame_rate = Fs/frame_size;
+   short_alpha = MULT16_16(25, Q15ONE)/IMAX(50,frame_rate);
+   xx=xy=yy=0;
+   /* Unroll by 4. The frame size is always a multiple of 4 *except* for
+      2.5 ms frames at 12 kHz. Since this setting is very rare (and very
+      stupid), we just discard the last two samples. */
+   for (i=0;i<frame_size-3;i+=4)
+   {
+      opus_val32 pxx=0;
+      opus_val32 pxy=0;
+      opus_val32 pyy=0;
+      opus_val16 x, y;
+      x = RES2VAL16(pcm[2*i]);
+      y = RES2VAL16(pcm[2*i+1]);
+      pxx = SHR32(MULT16_16(x,x),2);
+      pxy = SHR32(MULT16_16(x,y),2);
+      pyy = SHR32(MULT16_16(y,y),2);
+      x = RES2VAL16(pcm[2*i+2]);
+      y = RES2VAL16(pcm[2*i+3]);
+      pxx += SHR32(MULT16_16(x,x),2);
+      pxy += SHR32(MULT16_16(x,y),2);
+      pyy += SHR32(MULT16_16(y,y),2);
+      x = RES2VAL16(pcm[2*i+4]);
+      y = RES2VAL16(pcm[2*i+5]);
+      pxx += SHR32(MULT16_16(x,x),2);
+      pxy += SHR32(MULT16_16(x,y),2);
+      pyy += SHR32(MULT16_16(y,y),2);
+      x = RES2VAL16(pcm[2*i+6]);
+      y = RES2VAL16(pcm[2*i+7]);
+      pxx += SHR32(MULT16_16(x,x),2);
+      pxy += SHR32(MULT16_16(x,y),2);
+      pyy += SHR32(MULT16_16(y,y),2);
+
+      xx += SHR32(pxx, shift);
+      xy += SHR32(pxy, shift);
+      yy += SHR32(pyy, shift);
+   }
+#ifndef FIXED_POINT
+   if (!(xx < 1e9f) || celt_isnan(xx) || !(yy < 1e9f) || celt_isnan(yy))
+   {
+      xy = xx = yy = 0;
+   }
+#endif
+   mem->XX += MULT16_32_Q15(short_alpha, xx-mem->XX);
+   /*mem->XY += MULT16_32_Q15(short_alpha, xy-mem->XY);*/
+   /* Rewritten to avoid overflows on abrupt sign change. */
+   mem->XY = MULT16_32_Q15(Q15ONE - short_alpha, mem->XY) + MULT16_32_Q15(short_alpha, xy);
+   mem->YY += MULT16_32_Q15(short_alpha, yy-mem->YY);
+   mem->XX = MAX32(0, mem->XX);
+   mem->XY = MAX32(0, mem->XY);
+   mem->YY = MAX32(0, mem->YY);
+   if (MAX32(mem->XX, mem->YY)>QCONST16(8e-4f, 18))
+   {
+      opus_val16 corr;
+      opus_val16 ldiff;
+      opus_val16 width;
+      sqrt_xx = celt_sqrt(mem->XX);
+      sqrt_yy = celt_sqrt(mem->YY);
+      qrrt_xx = celt_sqrt(sqrt_xx);
+      qrrt_yy = celt_sqrt(sqrt_yy);
+      /* Inter-channel correlation */
+      mem->XY = MIN32(mem->XY, sqrt_xx*sqrt_yy);
+      corr = SHR32(frac_div32(mem->XY,EPSILON+MULT16_16(sqrt_xx,sqrt_yy)),16);
+      /* Approximate loudness difference */
+      ldiff = MULT16_16(Q15ONE, ABS16(qrrt_xx-qrrt_yy))/(EPSILON+qrrt_xx+qrrt_yy);
+      width = MULT16_16_Q15(MIN16(Q15ONE, celt_sqrt(QCONST32(1.f,30)-MULT16_16(corr,corr))), ldiff);
+      /* Smoothing over one second */
+      mem->smoothed_width += (width-mem->smoothed_width)/frame_rate;
+      /* Peak follower */
+      mem->max_follower = MAX16(mem->max_follower-QCONST16(.02f,15)/frame_rate, mem->smoothed_width);
+   }
+   /*printf("%f %f %f %f %f ", corr/(float)Q15ONE, ldiff/(float)Q15ONE, width/(float)Q15ONE, mem->smoothed_width/(float)Q15ONE, mem->max_follower/(float)Q15ONE);*/
+   return EXTRACT16(MIN32(Q15ONE, MULT16_16(20, mem->max_follower)));
+}
+
+static int decide_fec(int useInBandFEC, int PacketLoss_perc, int last_fec, int mode, int *bandwidth, opus_int32 rate)
+{
+   int orig_bandwidth;
+   if (!useInBandFEC || PacketLoss_perc == 0 || mode == MODE_CELT_ONLY)
+      return 0;
+   orig_bandwidth = *bandwidth;
+   for (;;)
+   {
+      opus_int32 hysteresis;
+      opus_int32 LBRR_rate_thres_bps;
+      /* Compute threshold for using FEC at the current bandwidth setting */
+      LBRR_rate_thres_bps = fec_thresholds[2*(*bandwidth - OPUS_BANDWIDTH_NARROWBAND)];
+      hysteresis = fec_thresholds[2*(*bandwidth - OPUS_BANDWIDTH_NARROWBAND) + 1];
+      if (last_fec == 1) LBRR_rate_thres_bps -= hysteresis;
+      if (last_fec == 0) LBRR_rate_thres_bps += hysteresis;
+      LBRR_rate_thres_bps = silk_SMULWB( silk_MUL( LBRR_rate_thres_bps,
+            125 - silk_min( PacketLoss_perc, 25 ) ), SILK_FIX_CONST( 0.01, 16 ) );
+      /* If loss <= 5%, we look at whether we have enough rate to enable FEC.
+         If loss > 5%, we decrease the bandwidth until we can enable FEC. */
+      if (rate > LBRR_rate_thres_bps)
+         return 1;
+      else if (PacketLoss_perc <= 5)
+         return 0;
+      else if (*bandwidth > OPUS_BANDWIDTH_NARROWBAND)
+         (*bandwidth)--;
+      else
+         break;
+   }
+   /* Couldn't find any bandwidth to enable FEC, keep original bandwidth. */
+   *bandwidth = orig_bandwidth;
+   return 0;
+}
+
+static int compute_silk_rate_for_hybrid(int rate, int bandwidth, int frame20ms, int vbr, int fec, int channels) {
+   int entry;
+   int i;
+   int N;
+   int silk_rate;
+   static const int rate_table[][5] = {
+  /*  |total| |-------- SILK------------|
+              |-- No FEC -| |--- FEC ---|
+               10ms   20ms   10ms   20ms */
+      {    0,     0,     0,     0,     0},
+      {12000, 10000, 10000, 11000, 11000},
+      {16000, 13500, 13500, 15000, 15000},
+      {20000, 16000, 16000, 18000, 18000},
+      {24000, 18000, 18000, 21000, 21000},
+      {32000, 22000, 22000, 28000, 28000},
+      {64000, 38000, 38000, 50000, 50000}
+   };
+   /* Do the allocation per-channel. */
+   rate /= channels;
+   entry = 1 + frame20ms + 2*fec;
+   N = sizeof(rate_table)/sizeof(rate_table[0]);
+   for (i=1;i<N;i++)
+   {
+      if (rate_table[i][0] > rate) break;
+   }
+   if (i == N)
+   {
+      silk_rate = rate_table[i-1][entry];
+      /* For now, just give 50% of the extra bits to SILK. */
+      silk_rate += (rate-rate_table[i-1][0])/2;
+   } else {
+      opus_int32 lo, hi, x0, x1;
+      lo = rate_table[i-1][entry];
+      hi = rate_table[i][entry];
+      x0 = rate_table[i-1][0];
+      x1 = rate_table[i][0];
+      silk_rate = (lo*(x1-rate) + hi*(rate-x0))/(x1-x0);
+   }
+   if (!vbr)
+   {
+      /* Tiny boost to SILK for CBR. We should probably tune this better. */
+      silk_rate += 100;
+   }
+   if (bandwidth==OPUS_BANDWIDTH_SUPERWIDEBAND)
+      silk_rate += 300;
+   silk_rate *= channels;
+   /* Small adjustment for stereo (calibrated for 32 kb/s, haven't tried other bitrates). */
+   if (channels == 2 && rate >= 12000)
+      silk_rate -= 1000;
+   return silk_rate;
+}
+
+/* Returns the equivalent bitrate corresponding to 20 ms frames,
+   complexity 10 VBR operation. */
+static opus_int32 compute_equiv_rate(opus_int32 bitrate, int channels,
+      int frame_rate, int vbr, int mode, int complexity, int loss)
+{
+   opus_int32 equiv;
+   equiv = bitrate;
+   /* Take into account overhead from smaller frames. */
+   if (frame_rate > 50)
+      equiv -= (40*channels+20)*(frame_rate - 50);
+   /* CBR is about a 8% penalty for both SILK and CELT. */
+   if (!vbr)
+      equiv -= equiv/12;
+   /* Complexity makes about 10% difference (from 0 to 10) in general. */
+   equiv = equiv * (90+complexity)/100;
+   if (mode == MODE_SILK_ONLY || mode == MODE_HYBRID)
+   {
+      /* SILK complexity 0-1 uses the non-delayed-decision NSQ, which
+         costs about 20%. */
+      if (complexity<2)
+         equiv = equiv*4/5;
+      equiv -= equiv*loss/(6*loss + 10);
+   } else if (mode == MODE_CELT_ONLY) {
+      /* CELT complexity 0-4 doesn't have the pitch filter, which costs
+         about 10%. */
+      if (complexity<5)
+         equiv = equiv*9/10;
+   } else {
+      /* Mode not known yet */
+      /* Half the SILK loss*/
+      equiv -= equiv*loss/(12*loss + 20);
+   }
+   return equiv;
+}
+
+int is_digital_silence(const opus_res* pcm, int frame_size, int channels, int lsb_depth)
+{
+   int silence = 0;
+   opus_val32 sample_max = 0;
+#ifdef MLP_TRAINING
+   return 0;
+#endif
+   sample_max = celt_maxabs_res(pcm, frame_size*channels);
+
+#ifdef FIXED_POINT
+   silence = (sample_max == 0);
+   (void)lsb_depth;
+#else
+   silence = (sample_max <= (opus_val16) 1 / (1 << lsb_depth));
+#endif
+
+   return silence;
+}
+
+#ifdef FIXED_POINT
+static opus_val32 compute_frame_energy(const opus_res *pcm, int frame_size, int channels, int arch)
+{
+   int i;
+   opus_val32 sample_max;
+   int max_shift;
+   int shift;
+   opus_val32 energy = 0;
+   int len = frame_size*channels;
+   (void)arch;
+   /* Max amplitude in the signal */
+   sample_max = RES2INT16(celt_maxabs_res(pcm, len));
+
+   /* Compute the right shift required in the MAC to avoid an overflow */
+   max_shift = celt_ilog2(len);
+   shift = IMAX(0, (celt_ilog2(1+sample_max) << 1) + max_shift - 28);
+
+   /* Compute the energy */
+   for (i=0; i<len; i++)
+      energy += SHR32(MULT16_16(RES2INT16(pcm[i]), RES2INT16(pcm[i])), shift);
+
+   /* Normalize energy by the frame size and left-shift back to the original position */
+   energy /= len;
+   energy = SHL32(energy, shift);
+
+   return energy;
+}
+#else
+static opus_val32 compute_frame_energy(const opus_val16 *pcm, int frame_size, int channels, int arch)
+{
+   int len = frame_size*channels;
+   return celt_inner_prod(pcm, pcm, len, arch)/len;
+}
+#endif
+
+/* Decides if DTX should be turned on (=1) or off (=0) */
+static int decide_dtx_mode(opus_int activity,            /* indicates if this frame contains speech/music */
+                           int *nb_no_activity_ms_Q1,    /* number of consecutive milliseconds with no activity, in Q1 */
+                           int frame_size_ms_Q1          /* number of milliseconds in this update, in Q1 */
+                           )
+
+{
+   if (!activity)
+   {
+      /* The number of consecutive DTX frames should be within the allowed bounds.
+         Note that the allowed bound is defined in the SILK headers and assumes 20 ms
+         frames. As this function can be called with any frame length, a conversion to
+         milliseconds is done before the comparisons. */
+      (*nb_no_activity_ms_Q1) += frame_size_ms_Q1;
+      if (*nb_no_activity_ms_Q1 > NB_SPEECH_FRAMES_BEFORE_DTX*20*2)
+      {
+         if (*nb_no_activity_ms_Q1 <= (NB_SPEECH_FRAMES_BEFORE_DTX + MAX_CONSECUTIVE_DTX)*20*2)
+            /* Valid frame for DTX! */
+            return 1;
+         else
+            (*nb_no_activity_ms_Q1) = NB_SPEECH_FRAMES_BEFORE_DTX*20*2;
+      }
+   } else
+      (*nb_no_activity_ms_Q1) = 0;
+
+   return 0;
+}
+
+static int compute_redundancy_bytes(opus_int32 max_data_bytes, opus_int32 bitrate_bps, int frame_rate, int channels)
+{
+   int redundancy_bytes_cap;
+   int redundancy_bytes;
+   opus_int32 redundancy_rate;
+   int base_bits;
+   opus_int32 available_bits;
+   base_bits = (40*channels+20);
+
+   /* Equivalent rate for 5 ms frames. */
+   redundancy_rate = bitrate_bps + base_bits*(200 - frame_rate);
+   /* For VBR, further increase the bitrate if we can afford it. It's pretty short
+      and we'll avoid artefacts. */
+   redundancy_rate = 3*redundancy_rate/2;
+   redundancy_bytes = redundancy_rate/1600;
+
+   /* Compute the max rate we can use given CBR or VBR with cap. */
+   available_bits = max_data_bytes*8 - 2*base_bits;
+   redundancy_bytes_cap = (available_bits*240/(240+48000/frame_rate) + base_bits)/8;
+   redundancy_bytes = IMIN(redundancy_bytes, redundancy_bytes_cap);
+   /* It we can't get enough bits for redundancy to be worth it, rely on the decoder PLC. */
+   if (redundancy_bytes > 4 + 8*channels)
+      redundancy_bytes = IMIN(257, redundancy_bytes);
+   else
+      redundancy_bytes = 0;
+   return redundancy_bytes;
+}
+
+static opus_int32 opus_encode_frame_native(OpusEncoder *st, const opus_res *pcm, int frame_size,
+                unsigned char *data, opus_int32 max_data_bytes,
+                int float_api, int first_frame,
+#ifdef ENABLE_DRED
+                opus_int32 dred_bitrate_bps,
+#endif
+#ifndef DISABLE_FLOAT_API
+                AnalysisInfo *analysis_info,
+#endif
+                int is_silence, int redundancy, int celt_to_silk, int prefill,
+                opus_int32 equiv_rate, int to_celt);
+
+opus_int32 opus_encode_native(OpusEncoder *st, const opus_res *pcm, int frame_size,
+                unsigned char *data, opus_int32 out_data_bytes, int lsb_depth,
+                const void *analysis_pcm, opus_int32 analysis_size, int c1, int c2,
+                int analysis_channels, downmix_func downmix, int float_api)
+{
+    void *silk_enc=NULL;
+    CELTEncoder *celt_enc=NULL;
+    int i;
+    int ret=0;
+    int prefill=0;
+    int redundancy = 0;
+    int celt_to_silk = 0;
+    int to_celt = 0;
+    int voice_est; /* Probability of voice in Q7 */
+    opus_int32 equiv_rate;
+    int frame_rate;
+    opus_int32 max_rate; /* Max bitrate we're allowed to use */
+    int curr_bandwidth;
+    opus_int32 max_data_bytes; /* Max number of bytes we're allowed to use */
+    opus_int32 cbr_bytes=-1;
+    opus_val16 stereo_width;
+    const CELTMode *celt_mode=NULL;
+    int packet_size_cap = 1276;
+#ifndef DISABLE_FLOAT_API
+    AnalysisInfo analysis_info;
+    int analysis_read_pos_bak=-1;
+    int analysis_read_subframe_bak=-1;
+#endif
+    int is_silence = 0;
+#ifdef ENABLE_DRED
+    opus_int32 dred_bitrate_bps;
+#endif
+    ALLOC_STACK;
+
+#ifdef ENABLE_QEXT
+   if (st->enable_qext) packet_size_cap = QEXT_PACKET_SIZE_CAP;
+#endif
+
+    /* Just avoid insane packet sizes here, but the real bounds are applied later on. */
+    max_data_bytes = IMIN(packet_size_cap*6, out_data_bytes);
+
+    st->rangeFinal = 0;
+    if (frame_size <= 0 || max_data_bytes <= 0)
+    {
+       RESTORE_STACK;
+       return OPUS_BAD_ARG;
+    }
+
+    /* Cannot encode 100 ms in 1 byte */
+    if (max_data_bytes==1 && st->Fs==(frame_size*10))
+    {
+      RESTORE_STACK;
+      return OPUS_BUFFER_TOO_SMALL;
+    }
+
+    if (st->application != OPUS_APPLICATION_RESTRICTED_CELT)
+        silk_enc = (char*)st+st->silk_enc_offset;
+    if (st->application != OPUS_APPLICATION_RESTRICTED_SILK)
+        celt_enc = (CELTEncoder*)((char*)st+st->celt_enc_offset);
+
+    lsb_depth = IMIN(lsb_depth, st->lsb_depth);
+
+    if (st->application != OPUS_APPLICATION_RESTRICTED_SILK)
+        celt_encoder_ctl(celt_enc, CELT_GET_MODE(&celt_mode));
+    is_silence = is_digital_silence(pcm, frame_size, st->channels, lsb_depth);
+#ifndef DISABLE_FLOAT_API
+    analysis_info.valid = 0;
+#ifdef FIXED_POINT
+    if (st->silk_mode.complexity >= 10 && st->Fs>=16000 && st->Fs<=48000 && st->application != OPUS_APPLICATION_RESTRICTED_SILK)
+#else
+    if (st->silk_mode.complexity >= 7 && st->Fs>=16000 && st->Fs<=48000 && st->application != OPUS_APPLICATION_RESTRICTED_SILK)
+#endif
+    {
+       analysis_read_pos_bak = st->analysis.read_pos;
+       analysis_read_subframe_bak = st->analysis.read_subframe;
+       run_analysis(&st->analysis, celt_mode, analysis_pcm, analysis_size, frame_size,
+             c1, c2, analysis_channels, st->Fs,
+             lsb_depth, downmix, &analysis_info);
+
+    } else if (st->analysis.initialized) {
+       tonality_analysis_reset(&st->analysis);
+    }
+#else
+    (void)analysis_pcm;
+    (void)analysis_size;
+    (void)c1;
+    (void)c2;
+    (void)analysis_channels;
+    (void)downmix;
+#endif
+
+    /* Reset voice_ratio if this frame is not silent or if analysis is disabled.
+     * Otherwise, preserve voice_ratio from the last non-silent frame */
+    if (!is_silence)
+      st->voice_ratio = -1;
+#ifndef DISABLE_FLOAT_API
+    st->detected_bandwidth = 0;
+    if (analysis_info.valid)
+    {
+       int analysis_bandwidth;
+       if (st->signal_type == OPUS_AUTO)
+       {
+          float prob;
+          if (st->prev_mode == 0)
+             prob = analysis_info.music_prob;
+          else if (st->prev_mode == MODE_CELT_ONLY)
+             prob = analysis_info.music_prob_max;
+          else
+             prob = analysis_info.music_prob_min;
+          st->voice_ratio = (int)floor(.5+100*(1-prob));
+       }
+
+       analysis_bandwidth = analysis_info.bandwidth;
+       if (analysis_bandwidth<=12)
+          st->detected_bandwidth = OPUS_BANDWIDTH_NARROWBAND;
+       else if (analysis_bandwidth<=14)
+          st->detected_bandwidth = OPUS_BANDWIDTH_MEDIUMBAND;
+       else if (analysis_bandwidth<=16)
+          st->detected_bandwidth = OPUS_BANDWIDTH_WIDEBAND;
+       else if (analysis_bandwidth<=18)
+          st->detected_bandwidth = OPUS_BANDWIDTH_SUPERWIDEBAND;
+       else
+          st->detected_bandwidth = OPUS_BANDWIDTH_FULLBAND;
+    }
+#else
+    st->voice_ratio = -1;
+#endif
+
+    /* Track the peak signal energy */
+#ifndef DISABLE_FLOAT_API
+    if (!analysis_info.valid || analysis_info.activity_probability > DTX_ACTIVITY_THRESHOLD)
+#endif
+    {
+       if (!is_silence)
+       {
+          st->peak_signal_energy = MAX32(MULT16_32_Q15(QCONST16(0.999f, 15), st->peak_signal_energy),
+                compute_frame_energy(pcm, frame_size, st->channels, st->arch));
+       }
+    }
+    if (st->channels==2 && st->force_channels!=1)
+       stereo_width = compute_stereo_width(pcm, frame_size, st->Fs, &st->width_mem);
+    else
+       stereo_width = 0;
+    st->bitrate_bps = user_bitrate_to_bitrate(st, frame_size, max_data_bytes);
+
+    frame_rate = st->Fs/frame_size;
+    if (!st->use_vbr)
+    {
+       cbr_bytes = IMIN((bitrate_to_bits(st->bitrate_bps, st->Fs, frame_size)+4)/8, max_data_bytes);
+       st->bitrate_bps = bits_to_bitrate(cbr_bytes*8, st->Fs, frame_size);
+       /* Make sure we provide at least one byte to avoid failing. */
+       max_data_bytes = IMAX(1, cbr_bytes);
+    }
+#ifdef ENABLE_DRED
+    /* Allocate some of the bits to DRED if needed. */
+    dred_bitrate_bps = compute_dred_bitrate(st, st->bitrate_bps, frame_size);
+    st->bitrate_bps -= dred_bitrate_bps;
+#endif
+    if (max_data_bytes<3 || st->bitrate_bps < 3*frame_rate*8
+       || (frame_rate<50 && (max_data_bytes*(opus_int32)frame_rate<300 || st->bitrate_bps < 2400)))
+    {
+       /*If the space is too low to do something useful, emit 'PLC' frames.*/
+       int tocmode = st->mode;
+       int bw = st->bandwidth == 0 ? OPUS_BANDWIDTH_NARROWBAND : st->bandwidth;
+       int packet_code = 0;
+       int num_multiframes = 0;
+
+       if (tocmode==0)
+          tocmode = MODE_SILK_ONLY;
+       if (frame_rate>100)
+          tocmode = MODE_CELT_ONLY;
+       /* 40 ms -> 2 x 20 ms if in CELT_ONLY or HYBRID mode */
+       if (frame_rate==25 && tocmode!=MODE_SILK_ONLY)
+       {
+          frame_rate = 50;
+          packet_code = 1;
+       }
+
+       /* >= 60 ms frames */
+       if (frame_rate<=16)
+       {
+          /* 1 x 60 ms, 2 x 40 ms, 2 x 60 ms */
+          if (out_data_bytes==1 || (tocmode==MODE_SILK_ONLY && frame_rate!=10))
+          {
+             tocmode = MODE_SILK_ONLY;
+
+             packet_code = frame_rate <= 12;
+             frame_rate = frame_rate == 12 ? 25 : 16;
+          }
+          else
+          {
+             num_multiframes = 50/frame_rate;
+             frame_rate = 50;
+             packet_code = 3;
+          }
+       }
+
+       if(tocmode==MODE_SILK_ONLY&&bw>OPUS_BANDWIDTH_WIDEBAND)
+          bw=OPUS_BANDWIDTH_WIDEBAND;
+       else if (tocmode==MODE_CELT_ONLY&&bw==OPUS_BANDWIDTH_MEDIUMBAND)
+          bw=OPUS_BANDWIDTH_NARROWBAND;
+       else if (tocmode==MODE_HYBRID&&bw<=OPUS_BANDWIDTH_SUPERWIDEBAND)
+          bw=OPUS_BANDWIDTH_SUPERWIDEBAND;
+
+       data[0] = gen_toc(tocmode, frame_rate, bw, st->stream_channels);
+       data[0] |= packet_code;
+
+       ret = packet_code <= 1 ? 1 : 2;
+
+       max_data_bytes = IMAX(max_data_bytes, ret);
+
+       if (packet_code==3)
+          data[1] = num_multiframes;
+
+       if (!st->use_vbr)
+       {
+          ret = opus_packet_pad(data, ret, max_data_bytes);
+          if (ret == OPUS_OK)
+             ret = max_data_bytes;
+          else
+             ret = OPUS_INTERNAL_ERROR;
+       }
+       RESTORE_STACK;
+       return ret;
+    }
+    max_rate = bits_to_bitrate(max_data_bytes*8, st->Fs, frame_size);
+
+    /* Equivalent 20-ms rate for mode/channel/bandwidth decisions */
+    equiv_rate = compute_equiv_rate(st->bitrate_bps, st->channels, st->Fs/frame_size,
+          st->use_vbr, 0, st->silk_mode.complexity, st->silk_mode.packetLossPercentage);
+
+    if (st->signal_type == OPUS_SIGNAL_VOICE)
+       voice_est = 127;
+    else if (st->signal_type == OPUS_SIGNAL_MUSIC)
+       voice_est = 0;
+    else if (st->voice_ratio >= 0)
+    {
+       voice_est = st->voice_ratio*327>>8;
+       /* For AUDIO, never be more than 90% confident of having speech */
+       if (st->application == OPUS_APPLICATION_AUDIO)
+          voice_est = IMIN(voice_est, 115);
+    } else if (st->application == OPUS_APPLICATION_VOIP)
+       voice_est = 115;
+    else
+       voice_est = 48;
+
+    if (st->force_channels!=OPUS_AUTO && st->channels == 2)
+    {
+        st->stream_channels = st->force_channels;
+    } else {
+#ifdef FUZZING
+        (void)stereo_music_threshold;
+        (void)stereo_voice_threshold;
+       /* Random mono/stereo decision */
+       if (st->channels == 2 && (rand()&0x1F)==0)
+          st->stream_channels = 3-st->stream_channels;
+#else
+       /* Rate-dependent mono-stereo decision */
+       if (st->channels == 2)
+       {
+          opus_int32 stereo_threshold;
+          stereo_threshold = stereo_music_threshold + ((voice_est*voice_est*(stereo_voice_threshold-stereo_music_threshold))>>14);
+          if (st->stream_channels == 2)
+             stereo_threshold -= 1000;
+          else
+             stereo_threshold += 1000;
+          st->stream_channels = (equiv_rate > stereo_threshold) ? 2 : 1;
+       } else {
+          st->stream_channels = st->channels;
+       }
+#endif
+    }
+    /* Update equivalent rate for channels decision. */
+    equiv_rate = compute_equiv_rate(st->bitrate_bps, st->stream_channels, st->Fs/frame_size,
+          st->use_vbr, 0, st->silk_mode.complexity, st->silk_mode.packetLossPercentage);
+
+    /* Allow SILK DTX if DTX is enabled but the generalized DTX cannot be used,
+       e.g. because of the complexity setting or sample rate. */
+#ifndef DISABLE_FLOAT_API
+    st->silk_mode.useDTX = st->use_dtx && !(analysis_info.valid || is_silence);
+#else
+    st->silk_mode.useDTX = st->use_dtx && !is_silence;
+#endif
+
+    /* Mode selection depending on application and signal type */
+    if (st->application == OPUS_APPLICATION_RESTRICTED_SILK)
+    {
+       st->mode = MODE_SILK_ONLY;
+    } else if (st->application == OPUS_APPLICATION_RESTRICTED_LOWDELAY || st->application == OPUS_APPLICATION_RESTRICTED_CELT)
+    {
+       st->mode = MODE_CELT_ONLY;
+    } else if (st->user_forced_mode == OPUS_AUTO)
+    {
+#ifdef FUZZING
+        (void)stereo_width;
+        (void)mode_thresholds;
+       /* Random mode switching */
+       if ((rand()&0xF)==0)
+       {
+          if ((rand()&0x1)==0)
+             st->mode = MODE_CELT_ONLY;
+          else
+             st->mode = MODE_SILK_ONLY;
+       } else {
+          if (st->prev_mode==MODE_CELT_ONLY)
+             st->mode = MODE_CELT_ONLY;
+          else
+             st->mode = MODE_SILK_ONLY;
+       }
+#else
+       opus_int32 mode_voice, mode_music;
+       opus_int32 threshold;
+
+       /* Interpolate based on stereo width */
+       mode_voice = (opus_int32)(MULT16_32_Q15(Q15ONE-stereo_width,mode_thresholds[0][0])
+             + MULT16_32_Q15(stereo_width,mode_thresholds[1][0]));
+       mode_music = (opus_int32)(MULT16_32_Q15(Q15ONE-stereo_width,mode_thresholds[1][1])
+             + MULT16_32_Q15(stereo_width,mode_thresholds[1][1]));
+       /* Interpolate based on speech/music probability */
+       threshold = mode_music + ((voice_est*voice_est*(mode_voice-mode_music))>>14);
+       /* Bias towards SILK for VoIP because of some useful features */
+       if (st->application == OPUS_APPLICATION_VOIP)
+          threshold += 8000;
+
+       /*printf("%f %d\n", stereo_width/(float)Q15ONE, threshold);*/
+       /* Hysteresis */
+       if (st->prev_mode == MODE_CELT_ONLY)
+           threshold -= 4000;
+       else if (st->prev_mode>0)
+           threshold += 4000;
+
+       st->mode = (equiv_rate >= threshold) ? MODE_CELT_ONLY: MODE_SILK_ONLY;
+
+       /* When FEC is enabled and there's enough packet loss, use SILK.
+          Unless the FEC is set to 2, in which case we don't switch to SILK if we're confident we have music. */
+       if (st->silk_mode.useInBandFEC && st->silk_mode.packetLossPercentage > (128-voice_est)>>4 && (st->fec_config != 2 || voice_est > 25))
+          st->mode = MODE_SILK_ONLY;
+       /* When encoding voice and DTX is enabled but the generalized DTX cannot be used,
+          use SILK in order to make use of its DTX. */
+       if (st->silk_mode.useDTX && voice_est > 100)
+          st->mode = MODE_SILK_ONLY;
+#endif
+
+       /* If max_data_bytes represents less than 6 kb/s, switch to CELT-only mode */
+       if (max_data_bytes < bitrate_to_bits(frame_rate > 50 ? 9000 : 6000, st->Fs, frame_size)/8)
+          st->mode = MODE_CELT_ONLY;
+    } else {
+       st->mode = st->user_forced_mode;
+    }
+
+    /* Override the chosen mode to make sure we meet the requested frame size */
+    if (st->mode != MODE_CELT_ONLY && frame_size < st->Fs/100)
+    {
+       celt_assert(st->application != OPUS_APPLICATION_RESTRICTED_SILK);
+       st->mode = MODE_CELT_ONLY;
+    }
+    if (st->lfe && st->application != OPUS_APPLICATION_RESTRICTED_SILK)
+       st->mode = MODE_CELT_ONLY;
+
+    if (st->prev_mode > 0 &&
+        ((st->mode != MODE_CELT_ONLY && st->prev_mode == MODE_CELT_ONLY) ||
+    (st->mode == MODE_CELT_ONLY && st->prev_mode != MODE_CELT_ONLY)))
+    {
+        redundancy = 1;
+        celt_to_silk = (st->mode != MODE_CELT_ONLY);
+        if (!celt_to_silk)
+        {
+            /* Switch to SILK/hybrid if frame size is 10 ms or more*/
+            if (frame_size >= st->Fs/100)
+            {
+                st->mode = st->prev_mode;
+                to_celt = 1;
+            } else {
+                redundancy=0;
+            }
+        }
+    }
+
+    /* When encoding multiframes, we can ask for a switch to CELT only in the last frame. This switch
+     * is processed above as the requested mode shouldn't interrupt stereo->mono transition. */
+    if (st->stream_channels == 1 && st->prev_channels ==2 && st->silk_mode.toMono==0
+          && st->mode != MODE_CELT_ONLY && st->prev_mode != MODE_CELT_ONLY)
+    {
+       /* Delay stereo->mono transition by two frames so that SILK can do a smooth downmix */
+       st->silk_mode.toMono = 1;
+       st->stream_channels = 2;
+    } else {
+       st->silk_mode.toMono = 0;
+    }
+
+    /* Update equivalent rate with mode decision. */
+    equiv_rate = compute_equiv_rate(st->bitrate_bps, st->stream_channels, st->Fs/frame_size,
+          st->use_vbr, st->mode, st->silk_mode.complexity, st->silk_mode.packetLossPercentage);
+
+    if (st->mode != MODE_CELT_ONLY && st->prev_mode == MODE_CELT_ONLY)
+    {
+        silk_EncControlStruct dummy;
+        silk_InitEncoder( silk_enc, st->channels, st->arch, &dummy);
+        prefill=1;
+    }
+
+    /* Automatic (rate-dependent) bandwidth selection */
+    if (st->mode == MODE_CELT_ONLY || st->first || st->silk_mode.allowBandwidthSwitch)
+    {
+        const opus_int32 *voice_bandwidth_thresholds, *music_bandwidth_thresholds;
+        opus_int32 bandwidth_thresholds[8];
+        int bandwidth = OPUS_BANDWIDTH_FULLBAND;
+
+        if (st->channels==2 && st->force_channels!=1)
+        {
+           voice_bandwidth_thresholds = stereo_voice_bandwidth_thresholds;
+           music_bandwidth_thresholds = stereo_music_bandwidth_thresholds;
+        } else {
+           voice_bandwidth_thresholds = mono_voice_bandwidth_thresholds;
+           music_bandwidth_thresholds = mono_music_bandwidth_thresholds;
+        }
+        /* Interpolate bandwidth thresholds depending on voice estimation */
+        for (i=0;i<8;i++)
+        {
+           bandwidth_thresholds[i] = music_bandwidth_thresholds[i]
+                    + ((voice_est*voice_est*(voice_bandwidth_thresholds[i]-music_bandwidth_thresholds[i]))>>14);
+        }
+        do {
+            int threshold, hysteresis;
+            threshold = bandwidth_thresholds[2*(bandwidth-OPUS_BANDWIDTH_MEDIUMBAND)];
+            hysteresis = bandwidth_thresholds[2*(bandwidth-OPUS_BANDWIDTH_MEDIUMBAND)+1];
+            if (!st->first)
+            {
+                if (st->auto_bandwidth >= bandwidth)
+                    threshold -= hysteresis;
+                else
+                    threshold += hysteresis;
+            }
+            if (equiv_rate >= threshold)
+                break;
+        } while (--bandwidth>OPUS_BANDWIDTH_NARROWBAND);
+        /* We don't use mediumband anymore, except when explicitly requested or during
+           mode transitions. */
+        if (bandwidth == OPUS_BANDWIDTH_MEDIUMBAND)
+           bandwidth = OPUS_BANDWIDTH_WIDEBAND;
+        st->bandwidth = st->auto_bandwidth = bandwidth;
+        /* Prevents any transition to SWB/FB until the SILK layer has fully
+           switched to WB mode and turned the variable LP filter off */
+        if (!st->first && st->mode != MODE_CELT_ONLY && !st->silk_mode.inWBmodeWithoutVariableLP && st->bandwidth > OPUS_BANDWIDTH_WIDEBAND)
+            st->bandwidth = OPUS_BANDWIDTH_WIDEBAND;
+    }
+
+    if (st->bandwidth>st->max_bandwidth)
+       st->bandwidth = st->max_bandwidth;
+
+    if (st->user_bandwidth != OPUS_AUTO)
+        st->bandwidth = st->user_bandwidth;
+
+    /* This prevents us from using hybrid at unsafe CBR/max rates */
+    if (st->mode != MODE_CELT_ONLY && max_rate < 15000)
+    {
+       st->bandwidth = IMIN(st->bandwidth, OPUS_BANDWIDTH_WIDEBAND);
+    }
+
+    /* Prevents Opus from wasting bits on frequencies that are above
+       the Nyquist rate of the input signal */
+    if (st->Fs <= 24000 && st->bandwidth > OPUS_BANDWIDTH_SUPERWIDEBAND)
+        st->bandwidth = OPUS_BANDWIDTH_SUPERWIDEBAND;
+    if (st->Fs <= 16000 && st->bandwidth > OPUS_BANDWIDTH_WIDEBAND)
+        st->bandwidth = OPUS_BANDWIDTH_WIDEBAND;
+    if (st->Fs <= 12000 && st->bandwidth > OPUS_BANDWIDTH_MEDIUMBAND)
+        st->bandwidth = OPUS_BANDWIDTH_MEDIUMBAND;
+    if (st->Fs <= 8000 && st->bandwidth > OPUS_BANDWIDTH_NARROWBAND)
+        st->bandwidth = OPUS_BANDWIDTH_NARROWBAND;
+#ifndef DISABLE_FLOAT_API
+    /* Use detected bandwidth to reduce the encoded bandwidth. */
+    if (st->detected_bandwidth && st->user_bandwidth == OPUS_AUTO)
+    {
+       int min_detected_bandwidth;
+       /* Makes bandwidth detection more conservative just in case the detector
+          gets it wrong when we could have coded a high bandwidth transparently.
+          When operating in SILK/hybrid mode, we don't go below wideband to avoid
+          more complicated switches that require redundancy. */
+       if (equiv_rate <= 18000*st->stream_channels && st->mode == MODE_CELT_ONLY)
+          min_detected_bandwidth = OPUS_BANDWIDTH_NARROWBAND;
+       else if (equiv_rate <= 24000*st->stream_channels && st->mode == MODE_CELT_ONLY)
+          min_detected_bandwidth = OPUS_BANDWIDTH_MEDIUMBAND;
+       else if (equiv_rate <= 30000*st->stream_channels)
+          min_detected_bandwidth = OPUS_BANDWIDTH_WIDEBAND;
+       else if (equiv_rate <= 44000*st->stream_channels)
+          min_detected_bandwidth = OPUS_BANDWIDTH_SUPERWIDEBAND;
+       else
+          min_detected_bandwidth = OPUS_BANDWIDTH_FULLBAND;
+
+       st->detected_bandwidth = IMAX(st->detected_bandwidth, min_detected_bandwidth);
+       st->bandwidth = IMIN(st->bandwidth, st->detected_bandwidth);
+    }
+#endif
+    st->silk_mode.LBRR_coded = decide_fec(st->silk_mode.useInBandFEC, st->silk_mode.packetLossPercentage,
+          st->silk_mode.LBRR_coded, st->mode, &st->bandwidth, equiv_rate);
+    if (st->application != OPUS_APPLICATION_RESTRICTED_SILK)
+        celt_encoder_ctl(celt_enc, OPUS_SET_LSB_DEPTH(lsb_depth));
+
+    /* CELT mode doesn't support mediumband, use wideband instead */
+    if (st->mode == MODE_CELT_ONLY && st->bandwidth == OPUS_BANDWIDTH_MEDIUMBAND)
+        st->bandwidth = OPUS_BANDWIDTH_WIDEBAND;
+    if (st->lfe)
+       st->bandwidth = OPUS_BANDWIDTH_NARROWBAND;
+
+    curr_bandwidth = st->bandwidth;
+
+    if (st->application == OPUS_APPLICATION_RESTRICTED_SILK && curr_bandwidth > OPUS_BANDWIDTH_WIDEBAND)
+       st->bandwidth = curr_bandwidth = OPUS_BANDWIDTH_WIDEBAND;
+    /* Chooses the appropriate mode for speech
+       *NEVER* switch to/from CELT-only mode here as this will invalidate some assumptions */
+    if (st->mode == MODE_SILK_ONLY && curr_bandwidth > OPUS_BANDWIDTH_WIDEBAND)
+        st->mode = MODE_HYBRID;
+    if (st->mode == MODE_HYBRID && curr_bandwidth <= OPUS_BANDWIDTH_WIDEBAND)
+        st->mode = MODE_SILK_ONLY;
+
+    /* Can't support higher than >60 ms frames, and >20 ms when in Hybrid or CELT-only modes */
+    if ((frame_size > st->Fs/50 && (st->mode != MODE_SILK_ONLY)) || frame_size > 3*st->Fs/50)
+    {
+       int enc_frame_size;
+       int nb_frames;
+       VARDECL(unsigned char, tmp_data);
+       VARDECL(OpusRepacketizer, rp);
+       int max_header_bytes;
+       opus_int32 repacketize_len;
+       opus_int32 max_len_sum;
+       opus_int32 tot_size=0;
+       unsigned char *curr_data;
+       int tmp_len;
+       int dtx_count = 0;
+       int bak_to_mono;
+
+       if (st->mode == MODE_SILK_ONLY)
+       {
+         if (frame_size == 2*st->Fs/25)  /* 80 ms -> 2x 40 ms */
+           enc_frame_size = st->Fs/25;
+         else if (frame_size == 3*st->Fs/25)  /* 120 ms -> 2x 60 ms */
+           enc_frame_size = 3*st->Fs/50;
+         else                            /* 100 ms -> 5x 20 ms */
+           enc_frame_size = st->Fs/50;
+       }
+       else
+         enc_frame_size = st->Fs/50;
+
+       nb_frames = frame_size/enc_frame_size;
+
+#ifndef DISABLE_FLOAT_API
+       if (analysis_read_pos_bak!= -1)
+       {
+          /* Reset analysis position to the beginning of the first frame so we
+             can use it one frame at a time. */
+          st->analysis.read_pos = analysis_read_pos_bak;
+          st->analysis.read_subframe = analysis_read_subframe_bak;
+       }
+#endif
+
+       /* Worst cases:
+        * 2 frames: Code 2 with different compressed sizes
+        * >2 frames: Code 3 VBR */
+       max_header_bytes = nb_frames == 2 ? 3 : (2+(nb_frames-1)*2);
+#ifdef ENABLE_QEXT
+       /* Cover the use of the separators that are the only thing that can get us over
+          once we consider that we need to subtract the extension overhead in each
+          of the individual frames. Also consider that a separator can get our padding
+          from 254 to 255, which costs an extra length byte (at most once). */
+       if (st->enable_qext) max_header_bytes += (nb_frames-1) + 1;
+#endif
+
+       if (st->use_vbr || st->user_bitrate_bps==OPUS_BITRATE_MAX)
+          repacketize_len = out_data_bytes;
+       else {
+          celt_assert(cbr_bytes>=0);
+          repacketize_len = IMIN(cbr_bytes, out_data_bytes);
+       }
+       max_len_sum = nb_frames + repacketize_len - max_header_bytes;
+
+       ALLOC(tmp_data, max_len_sum, unsigned char);
+       curr_data = tmp_data;
+       ALLOC(rp, 1, OpusRepacketizer);
+       opus_repacketizer_init(rp);
+
+
+       bak_to_mono = st->silk_mode.toMono;
+       if (bak_to_mono)
+          st->force_channels = 1;
+       else
+          st->prev_channels = st->stream_channels;
+
+       for (i=0;i<nb_frames;i++)
+       {
+          int first_frame;
+          int frame_to_celt;
+          int frame_redundancy;
+          opus_int32 curr_max;
+          /* Attempt DRED encoding until we have a non-DTX frame. In case of DTX refresh,
+             that allows for DRED not to be in the first frame. */
+          first_frame = (i == 0) || (i == dtx_count);
+          st->silk_mode.toMono = 0;
+          st->nonfinal_frame = i<(nb_frames-1);
+
+          /* When switching from SILK/Hybrid to CELT, only ask for a switch at the last frame */
+          frame_to_celt = to_celt && i==nb_frames-1;
+          frame_redundancy = redundancy && (frame_to_celt || (!to_celt && i==0));
+
+          curr_max = IMIN(bitrate_to_bits(st->bitrate_bps, st->Fs, enc_frame_size)/8, max_len_sum/nb_frames);
+#ifdef ENABLE_DRED
+          curr_max = IMIN(curr_max, (max_len_sum-bitrate_to_bits(dred_bitrate_bps, st->Fs, frame_size)/8)/nb_frames);
+          if (first_frame) curr_max += bitrate_to_bits(dred_bitrate_bps, st->Fs, frame_size)/8;
+#endif
+#ifdef ENABLE_QEXT
+          /* Leave room for signaling the extension size once we repacketize. */
+          if (st->enable_qext)
+             curr_max -= curr_max/254;
+#endif
+          curr_max = IMIN(max_len_sum-tot_size, curr_max);
+#ifndef DISABLE_FLOAT_API
+          if (analysis_read_pos_bak != -1) {
+            /* Get analysis for current frame. */
+            tonality_get_info(&st->analysis, &analysis_info, enc_frame_size);
+          }
+#endif
+          is_silence = is_digital_silence(pcm+i*(st->channels*enc_frame_size), enc_frame_size, st->channels, lsb_depth);
+
+          tmp_len = opus_encode_frame_native(st, pcm+i*(st->channels*enc_frame_size), enc_frame_size, curr_data, curr_max, float_api, first_frame,
+#ifdef ENABLE_DRED
+                    dred_bitrate_bps,
+#endif
+#ifndef DISABLE_FLOAT_API
+                    &analysis_info,
+#endif
+                    is_silence, frame_redundancy, celt_to_silk, prefill,
+                    equiv_rate, frame_to_celt
+              );
+          if (tmp_len<0)
+          {
+             RESTORE_STACK;
+             return OPUS_INTERNAL_ERROR;
+          } else if (tmp_len==1) {
+             dtx_count++;
+          }
+          ret = opus_repacketizer_cat(rp, curr_data, tmp_len);
+
+          if (ret<0)
+          {
+             RESTORE_STACK;
+             return OPUS_INTERNAL_ERROR;
+          }
+          tot_size += tmp_len;
+          curr_data += tmp_len;
+       }
+       ret = opus_repacketizer_out_range_impl(rp, 0, nb_frames, data, repacketize_len, 0, !st->use_vbr && (dtx_count != nb_frames), NULL, 0);
+       if (ret<0)
+       {
+          ret = OPUS_INTERNAL_ERROR;
+       }
+       st->silk_mode.toMono = bak_to_mono;
+       RESTORE_STACK;
+       return ret;
+    } else {
+      ret = opus_encode_frame_native(st, pcm, frame_size, data, max_data_bytes, float_api, 1,
+#ifdef ENABLE_DRED
+                dred_bitrate_bps,
+#endif
+#ifndef DISABLE_FLOAT_API
+                &analysis_info,
+#endif
+                is_silence, redundancy, celt_to_silk, prefill,
+                equiv_rate, to_celt
+          );
+      RESTORE_STACK;
+      return ret;
+    }
+}
+
+static opus_int32 opus_encode_frame_native(OpusEncoder *st, const opus_res *pcm, int frame_size,
+                unsigned char *data, opus_int32 orig_max_data_bytes,
+                int float_api, int first_frame,
+#ifdef ENABLE_DRED
+                opus_int32 dred_bitrate_bps,
+#endif
+#ifndef DISABLE_FLOAT_API
+                AnalysisInfo *analysis_info,
+#endif
+                int is_silence, int redundancy, int celt_to_silk, int prefill,
+                opus_int32 equiv_rate, int to_celt)
+{
+    void *silk_enc=NULL;
+    CELTEncoder *celt_enc=NULL;
+    const CELTMode *celt_mode=NULL;
+    int i;
+    int ret=0;
+    int max_data_bytes;
+    opus_int32 nBytes;
+    ec_enc enc;
+    int bits_target;
+    int start_band = 0;
+    int redundancy_bytes = 0; /* Number of bytes to use for redundancy frame */
+    int nb_compr_bytes;
+    opus_uint32 redundant_rng = 0;
+    int cutoff_Hz;
+    int hp_freq_smth1;
+    opus_val16 HB_gain;
+    int apply_padding;
+    int frame_rate;
+    int curr_bandwidth;
+    int delay_compensation;
+    int total_buffer;
+    opus_int activity = VAD_NO_DECISION;
+    VARDECL(opus_res, pcm_buf);
+    VARDECL(opus_res, tmp_prefill);
+    SAVE_STACK;
+
+    max_data_bytes = IMIN(orig_max_data_bytes, 1276);
+    st->rangeFinal = 0;
+    if (st->application != OPUS_APPLICATION_RESTRICTED_CELT)
+       silk_enc = (char*)st+st->silk_enc_offset;
+    if (st->application != OPUS_APPLICATION_RESTRICTED_SILK)
+    {
+       celt_enc = (CELTEncoder*)((char*)st+st->celt_enc_offset);
+       celt_encoder_ctl(celt_enc, CELT_GET_MODE(&celt_mode));
+    }
+    curr_bandwidth = st->bandwidth;
+    if (st->application == OPUS_APPLICATION_RESTRICTED_LOWDELAY || st->application == OPUS_APPLICATION_RESTRICTED_CELT || st->application == OPUS_APPLICATION_RESTRICTED_SILK)
+       delay_compensation = 0;
+    else
+       delay_compensation = st->delay_compensation;
+    total_buffer = delay_compensation;
+
+    frame_rate = st->Fs/frame_size;
+
+    if (is_silence)
+    {
+       activity = !is_silence;
+    }
+#ifndef DISABLE_FLOAT_API
+    else if (analysis_info->valid) {
+       activity = analysis_info->activity_probability >= DTX_ACTIVITY_THRESHOLD;
+       if (!activity)
+       {
+           /* Mark as active if this noise frame is sufficiently loud */
+           opus_val32 noise_energy = compute_frame_energy(pcm, frame_size, st->channels, st->arch);
+           activity = st->peak_signal_energy < (PSEUDO_SNR_THRESHOLD * noise_energy);
+       }
+    }
+#endif
+    else if (st->mode == MODE_CELT_ONLY) {
+       opus_val32 noise_energy = compute_frame_energy(pcm, frame_size, st->channels, st->arch);
+       /* Boosting peak energy a bit because we didn't just average the active frames. */
+       activity = st->peak_signal_energy < (QCONST16(PSEUDO_SNR_THRESHOLD, 0) * (opus_val64)HALF32(noise_energy));
+    }
+
+    /* For the first frame at a new SILK bandwidth */
+    if (st->silk_bw_switch)
+    {
+       redundancy = 1;
+       celt_to_silk = 1;
+       st->silk_bw_switch = 0;
+       /* Do a prefill without resetting the sampling rate control. */
+       prefill=2;
+    }
+
+    /* If we decided to go with CELT, make sure redundancy is off, no matter what
+       we decided earlier. */
+    if (st->mode == MODE_CELT_ONLY)
+        redundancy = 0;
+
+    if (redundancy)
+    {
+       redundancy_bytes = compute_redundancy_bytes(max_data_bytes, st->bitrate_bps, frame_rate, st->stream_channels);
+       if (redundancy_bytes == 0)
+          redundancy = 0;
+    }
+    if (st->application == OPUS_APPLICATION_RESTRICTED_SILK)
+    {
+       redundancy = 0;
+       redundancy_bytes = 0;
+    }
+
+    /* printf("%d %d %d %d\n", st->bitrate_bps, st->stream_channels, st->mode, curr_bandwidth); */
+    bits_target = IMIN(8*(max_data_bytes-redundancy_bytes), bitrate_to_bits(st->bitrate_bps, st->Fs, frame_size)) - 8;
+
+    data += 1;
+
+    ec_enc_init(&enc, data, orig_max_data_bytes-1);
+
+    ALLOC(pcm_buf, (total_buffer+frame_size)*st->channels, opus_res);
+    OPUS_COPY(pcm_buf, &st->delay_buffer[(st->encoder_buffer-total_buffer)*st->channels], total_buffer*st->channels);
+
+    if (st->mode == MODE_CELT_ONLY)
+       hp_freq_smth1 = silk_LSHIFT( silk_lin2log( VARIABLE_HP_MIN_CUTOFF_HZ ), 8 );
+    else
+       hp_freq_smth1 = ((silk_encoder*)silk_enc)->state_Fxx[0].sCmn.variable_HP_smth1_Q15;
+
+    st->variable_HP_smth2_Q15 = silk_SMLAWB( st->variable_HP_smth2_Q15,
+          hp_freq_smth1 - st->variable_HP_smth2_Q15, SILK_FIX_CONST( VARIABLE_HP_SMTH_COEF2, 16 ) );
+
+    /* convert from log scale to Hertz */
+    cutoff_Hz = silk_log2lin( silk_RSHIFT( st->variable_HP_smth2_Q15, 8 ) );
+
+    if (st->application == OPUS_APPLICATION_VOIP)
+    {
+       hp_cutoff(pcm, cutoff_Hz, &pcm_buf[total_buffer*st->channels], st->hp_mem, frame_size, st->channels, st->Fs, st->arch);
+
+#ifdef ENABLE_OSCE_TRAINING_DATA
+       /* write out high pass filtered clean signal*/
+       static FILE *fout =NULL;
+       if (fout == NULL)
+       {
+         fout = fopen("clean_hp.s16", "wb");
+       }
+
+       {
+         int idx;
+         opus_int16 tmp;
+         for (idx = 0; idx < frame_size; idx++)
+         {
+            tmp = (opus_int16) (32768 * pcm_buf[total_buffer + idx] + 0.5f);
+            fwrite(&tmp, sizeof(tmp), 1, fout);
+         }
+       }
+#endif
+    } else {
+#ifdef ENABLE_QEXT
+       /* FIXME: Avoid glitching when we switch qext on/off dynamically. */
+       if (st->enable_qext) OPUS_COPY(&pcm_buf[total_buffer*st->channels], pcm, frame_size*st->channels);
+       else
+#endif
+       dc_reject(pcm, 3, &pcm_buf[total_buffer*st->channels], st->hp_mem, frame_size, st->channels, st->Fs);
+    }
+#ifndef FIXED_POINT
+    if (float_api)
+    {
+       opus_val32 sum;
+       sum = celt_inner_prod(&pcm_buf[total_buffer*st->channels], &pcm_buf[total_buffer*st->channels], frame_size*st->channels, st->arch);
+       /* This should filter out both NaNs and ridiculous signals that could
+          cause NaNs further down. */
+       if (!(sum < 1e9f) || celt_isnan(sum))
+       {
+          OPUS_CLEAR(&pcm_buf[total_buffer*st->channels], frame_size*st->channels);
+          st->hp_mem[0] = st->hp_mem[1] = st->hp_mem[2] = st->hp_mem[3] = 0;
+       }
+    }
+#else
+    (void)float_api;
+#endif
+
+#ifdef ENABLE_DRED
+    /* Compute the DRED features. Needs to be before SILK because of DTX. */
+    if ( st->dred_duration > 0 && st->dred_encoder.loaded ) {
+        int frame_size_400Hz;
+        /* DRED Encoder */
+        dred_compute_latents( &st->dred_encoder, &pcm_buf[total_buffer*st->channels], frame_size, total_buffer, st->arch );
+        frame_size_400Hz = frame_size*400/st->Fs;
+        OPUS_MOVE(&st->activity_mem[frame_size_400Hz], st->activity_mem, 4*DRED_MAX_FRAMES-frame_size_400Hz);
+        for (i=0;i<frame_size_400Hz;i++)
+           st->activity_mem[i] = activity;
+    } else {
+        st->dred_encoder.latents_buffer_fill = 0;
+        OPUS_CLEAR(st->activity_mem, 4*DRED_MAX_FRAMES);
+    }
+#endif
+
+    /* SILK processing */
+    HB_gain = Q15ONE;
+    if (st->mode != MODE_CELT_ONLY)
+    {
+        opus_int32 total_bitRate, celt_rate;
+        const opus_res *pcm_silk;
+
+        /* Distribute bits between SILK and CELT */
+        total_bitRate = bits_to_bitrate(bits_target, st->Fs, frame_size);
+        if( st->mode == MODE_HYBRID ) {
+            /* Base rate for SILK */
+            st->silk_mode.bitRate = compute_silk_rate_for_hybrid(total_bitRate,
+                  curr_bandwidth, st->Fs == 50 * frame_size, st->use_vbr, st->silk_mode.LBRR_coded,
+                  st->stream_channels);
+            if (!st->energy_masking)
+            {
+               /* Increasingly attenuate high band when it gets allocated fewer bits */
+               celt_rate = total_bitRate - st->silk_mode.bitRate;
+               HB_gain = Q15ONE - SHR32(celt_exp2(-celt_rate * QCONST16(1.f/1024, 10)), 1);
+            }
+        } else {
+            /* SILK gets all bits */
+            st->silk_mode.bitRate = total_bitRate;
+        }
+
+        /* Surround masking for SILK */
+        if (st->energy_masking && st->use_vbr && !st->lfe)
+        {
+           opus_val32 mask_sum=0;
+           celt_glog masking_depth;
+           opus_int32 rate_offset;
+           int c;
+           int end = 17;
+           opus_int16 srate = 16000;
+           if (st->bandwidth == OPUS_BANDWIDTH_NARROWBAND)
+           {
+              end = 13;
+              srate = 8000;
+           } else if (st->bandwidth == OPUS_BANDWIDTH_MEDIUMBAND)
+           {
+              end = 15;
+              srate = 12000;
+           }
+           for (c=0;c<st->channels;c++)
+           {
+              for(i=0;i<end;i++)
+              {
+                 celt_glog mask;
+                 mask = MAXG(MING(st->energy_masking[21*c+i],
+                        GCONST(.5f)), -GCONST(2.0f));
+                 if (mask > 0)
+                    mask = HALF32(mask);
+                 mask_sum += mask;
+              }
+           }
+           /* Conservative rate reduction, we cut the masking in half */
+           masking_depth = mask_sum / end*st->channels;
+           masking_depth += GCONST(.2f);
+           rate_offset = (opus_int32)PSHR32(MULT16_16(srate, SHR32(masking_depth, DB_SHIFT-10)), 10);
+           rate_offset = MAX32(rate_offset, -2*st->silk_mode.bitRate/3);
+           /* Split the rate change between the SILK and CELT part for hybrid. */
+           if (st->bandwidth==OPUS_BANDWIDTH_SUPERWIDEBAND || st->bandwidth==OPUS_BANDWIDTH_FULLBAND)
+              st->silk_mode.bitRate += 3*rate_offset/5;
+           else
+              st->silk_mode.bitRate += rate_offset;
+        }
+
+        st->silk_mode.payloadSize_ms = 1000 * frame_size / st->Fs;
+        st->silk_mode.nChannelsAPI = st->channels;
+        st->silk_mode.nChannelsInternal = st->stream_channels;
+        if (curr_bandwidth == OPUS_BANDWIDTH_NARROWBAND) {
+            st->silk_mode.desiredInternalSampleRate = 8000;
+        } else if (curr_bandwidth == OPUS_BANDWIDTH_MEDIUMBAND) {
+            st->silk_mode.desiredInternalSampleRate = 12000;
+        } else {
+            celt_assert( st->mode == MODE_HYBRID || curr_bandwidth == OPUS_BANDWIDTH_WIDEBAND );
+            st->silk_mode.desiredInternalSampleRate = 16000;
+        }
+        if( st->mode == MODE_HYBRID ) {
+            /* Don't allow bandwidth reduction at lowest bitrates in hybrid mode */
+            st->silk_mode.minInternalSampleRate = 16000;
+        } else {
+            st->silk_mode.minInternalSampleRate = 8000;
+        }
+
+        st->silk_mode.maxInternalSampleRate = 16000;
+        if (st->mode == MODE_SILK_ONLY)
+        {
+           opus_int32 effective_max_rate = bits_to_bitrate(max_data_bytes*8, st->Fs, frame_size);
+           if (frame_rate > 50)
+              effective_max_rate = effective_max_rate*2/3;
+           if (effective_max_rate < 8000)
+           {
+              st->silk_mode.maxInternalSampleRate = 12000;
+              st->silk_mode.desiredInternalSampleRate = IMIN(12000, st->silk_mode.desiredInternalSampleRate);
+           }
+           if (effective_max_rate < 7000)
+           {
+              st->silk_mode.maxInternalSampleRate = 8000;
+              st->silk_mode.desiredInternalSampleRate = IMIN(8000, st->silk_mode.desiredInternalSampleRate);
+           }
+#ifdef ENABLE_QEXT
+           /* At 96 kHz, we don't have the input resampler to do 8 or 12 kHz. */
+           if (st->Fs==96000) st->silk_mode.maxInternalSampleRate = st->silk_mode.desiredInternalSampleRate = 16000;
+#endif
+        }
+
+        st->silk_mode.useCBR = !st->use_vbr;
+
+        /* Call SILK encoder for the low band */
+
+        /* Max bits for SILK, counting ToC, redundancy bytes, and optionally redundancy. */
+        st->silk_mode.maxBits = (max_data_bytes-1)*8;
+        if (redundancy && redundancy_bytes >= 2)
+        {
+           /* Counting 1 bit for redundancy position and 20 bits for flag+size (only for hybrid). */
+           st->silk_mode.maxBits -= redundancy_bytes*8 + 1;
+           if (st->mode == MODE_HYBRID)
+              st->silk_mode.maxBits -= 20;
+        }
+        if (st->silk_mode.useCBR)
+        {
+           /* When we're in CBR mode, but we have non-SILK data to encode, switch SILK to VBR with cap to
+              save on complexity. Any variations will be absorbed by CELT and/or DRED and we can still
+              produce a constant bitrate without wasting bits. */
+#ifdef ENABLE_DRED
+           if (st->mode == MODE_HYBRID || dred_bitrate_bps > 0)
+#else
+           if (st->mode == MODE_HYBRID)
+#endif
+           {
+              /* Allow SILK to steal up to 25% of the remaining bits */
+              opus_int16 other_bits = IMAX(0, st->silk_mode.maxBits - st->silk_mode.bitRate * frame_size / st->Fs);
+              st->silk_mode.maxBits = IMAX(0, st->silk_mode.maxBits - other_bits*3/4);
+              st->silk_mode.useCBR = 0;
+           }
+        } else {
+           /* Constrained VBR. */
+           if (st->mode == MODE_HYBRID)
+           {
+              /* Compute SILK bitrate corresponding to the max total bits available */
+              opus_int32 maxBitRate = compute_silk_rate_for_hybrid(st->silk_mode.maxBits*st->Fs / frame_size,
+                    curr_bandwidth, st->Fs == 50 * frame_size, st->use_vbr, st->silk_mode.LBRR_coded,
+                    st->stream_channels);
+              st->silk_mode.maxBits = bitrate_to_bits(maxBitRate, st->Fs, frame_size);
+           }
+        }
+
+        if (prefill && st->application != OPUS_APPLICATION_RESTRICTED_SILK)
+        {
+            opus_int32 zero=0;
+            int prefill_offset;
+            /* Use a smooth onset for the SILK prefill to avoid the encoder trying to encode
+               a discontinuity. The exact location is what we need to avoid leaving any "gap"
+               in the audio when mixing with the redundant CELT frame. Here we can afford to
+               overwrite st->delay_buffer because the only thing that uses it before it gets
+               rewritten is tmp_prefill[] and even then only the part after the ramp really
+               gets used (rather than sent to the encoder and discarded) */
+            prefill_offset = st->channels*(st->encoder_buffer-st->delay_compensation-st->Fs/400);
+            gain_fade(st->delay_buffer+prefill_offset, st->delay_buffer+prefill_offset,
+                  0, Q15ONE, celt_mode->overlap, st->Fs/400, st->channels, celt_mode->window, st->Fs);
+            OPUS_CLEAR(st->delay_buffer, prefill_offset);
+            pcm_silk = st->delay_buffer;
+            silk_Encode( silk_enc, &st->silk_mode, pcm_silk, st->encoder_buffer, NULL, &zero, prefill, activity );
+            /* Prevent a second switch in the real encode call. */
+            st->silk_mode.opusCanSwitch = 0;
+        }
+
+        pcm_silk = pcm_buf+total_buffer*st->channels;
+        ret = silk_Encode( silk_enc, &st->silk_mode, pcm_silk, frame_size, &enc, &nBytes, 0, activity );
+        if( ret ) {
+            /*fprintf (stderr, "SILK encode error: %d\n", ret);*/
+            /* Handle error */
+           RESTORE_STACK;
+           return OPUS_INTERNAL_ERROR;
+        }
+
+        /* Extract SILK internal bandwidth for signaling in first byte */
+        if( st->mode == MODE_SILK_ONLY ) {
+            if( st->silk_mode.internalSampleRate == 8000 ) {
+               curr_bandwidth = OPUS_BANDWIDTH_NARROWBAND;
+            } else if( st->silk_mode.internalSampleRate == 12000 ) {
+               curr_bandwidth = OPUS_BANDWIDTH_MEDIUMBAND;
+            } else if( st->silk_mode.internalSampleRate == 16000 ) {
+               curr_bandwidth = OPUS_BANDWIDTH_WIDEBAND;
+            }
+        } else {
+            celt_assert( st->silk_mode.internalSampleRate == 16000 );
+        }
+
+        st->silk_mode.opusCanSwitch = st->silk_mode.switchReady && !st->nonfinal_frame;
+
+        if (activity == VAD_NO_DECISION) {
+           activity = (st->silk_mode.signalType != TYPE_NO_VOICE_ACTIVITY);
+#ifdef ENABLE_DRED
+           for (i=0;i<frame_size*400/st->Fs;i++)
+              st->activity_mem[i] = activity;
+#endif
+        }
+        if (nBytes==0)
+        {
+           st->rangeFinal = 0;
+           data[-1] = gen_toc(st->mode, st->Fs/frame_size, curr_bandwidth, st->stream_channels);
+           RESTORE_STACK;
+           return 1;
+        }
+
+        /* FIXME: How do we allocate the redundancy for CBR? */
+        if (st->silk_mode.opusCanSwitch)
+        {
+           if (st->application != OPUS_APPLICATION_RESTRICTED_SILK)
+           {
+              redundancy_bytes = compute_redundancy_bytes(max_data_bytes, st->bitrate_bps, frame_rate, st->stream_channels);
+              redundancy = (redundancy_bytes != 0);
+           }
+           celt_to_silk = 0;
+           st->silk_bw_switch = 1;
+        }
+    }
+
+    /* CELT processing */
+    if (st->application != OPUS_APPLICATION_RESTRICTED_SILK)
+    {
+        int endband=21;
+
+        switch(curr_bandwidth)
+        {
+            case OPUS_BANDWIDTH_NARROWBAND:
+                endband = 13;
+                break;
+            case OPUS_BANDWIDTH_MEDIUMBAND:
+            case OPUS_BANDWIDTH_WIDEBAND:
+                endband = 17;
+                break;
+            case OPUS_BANDWIDTH_SUPERWIDEBAND:
+                endband = 19;
+                break;
+            case OPUS_BANDWIDTH_FULLBAND:
+                endband = 21;
+                break;
+        }
+        celt_encoder_ctl(celt_enc, CELT_SET_END_BAND(endband));
+        celt_encoder_ctl(celt_enc, CELT_SET_CHANNELS(st->stream_channels));
+        celt_encoder_ctl(celt_enc, OPUS_SET_BITRATE(OPUS_BITRATE_MAX));
+    }
+    if (st->mode != MODE_SILK_ONLY)
+    {
+        opus_val32 celt_pred=2;
+        /* We may still decide to disable prediction later */
+        if (st->silk_mode.reducedDependency)
+           celt_pred = 0;
+        celt_encoder_ctl(celt_enc, CELT_SET_PREDICTION(celt_pred));
+    }
+
+    ALLOC(tmp_prefill, st->channels*st->Fs/400, opus_res);
+    if (st->mode != MODE_SILK_ONLY && st->mode != st->prev_mode && st->prev_mode > 0
+          && st->application != OPUS_APPLICATION_RESTRICTED_CELT)
+    {
+       OPUS_COPY(tmp_prefill, &st->delay_buffer[(st->encoder_buffer-total_buffer-st->Fs/400)*st->channels], st->channels*st->Fs/400);
+    }
+
+    if (st->channels*(st->encoder_buffer-(frame_size+total_buffer)) > 0)
+    {
+       OPUS_MOVE(st->delay_buffer, &st->delay_buffer[st->channels*frame_size], st->channels*(st->encoder_buffer-frame_size-total_buffer));
+       OPUS_COPY(&st->delay_buffer[st->channels*(st->encoder_buffer-frame_size-total_buffer)],
+             &pcm_buf[0],
+             (frame_size+total_buffer)*st->channels);
+    } else {
+       OPUS_COPY(st->delay_buffer, &pcm_buf[(frame_size+total_buffer-st->encoder_buffer)*st->channels], st->encoder_buffer*st->channels);
+    }
+    /* gain_fade() and stereo_fade() need to be after the buffer copying
+       because we don't want any of this to affect the SILK part */
+    if( ( st->prev_HB_gain < Q15ONE || HB_gain < Q15ONE ) && celt_mode != NULL ) {
+       gain_fade(pcm_buf, pcm_buf,
+             st->prev_HB_gain, HB_gain, celt_mode->overlap, frame_size, st->channels, celt_mode->window, st->Fs);
+    }
+    st->prev_HB_gain = HB_gain;
+    if (st->mode != MODE_HYBRID || st->stream_channels==1)
+    {
+       if (equiv_rate > 32000)
+          st->silk_mode.stereoWidth_Q14 = 16384;
+       else if (equiv_rate < 16000)
+          st->silk_mode.stereoWidth_Q14 = 0;
+       else
+          st->silk_mode.stereoWidth_Q14 = 16384 - 2048*(opus_int32)(32000-equiv_rate)/(equiv_rate-14000);
+    }
+    if( !st->energy_masking && st->channels == 2 ) {
+        /* Apply stereo width reduction (at low bitrates) */
+        if( st->hybrid_stereo_width_Q14 < (1 << 14) || st->silk_mode.stereoWidth_Q14 < (1 << 14) ) {
+            opus_val16 g1, g2;
+            g1 = st->hybrid_stereo_width_Q14;
+            g2 = (opus_val16)(st->silk_mode.stereoWidth_Q14);
+#ifdef FIXED_POINT
+            g1 = g1==16384 ? Q15ONE : SHL16(g1,1);
+            g2 = g2==16384 ? Q15ONE : SHL16(g2,1);
+#else
+            g1 *= (1.f/16384);
+            g2 *= (1.f/16384);
+#endif
+            if ( celt_mode != NULL )
+            {
+                stereo_fade(pcm_buf, pcm_buf, g1, g2, celt_mode->overlap,
+                     frame_size, st->channels, celt_mode->window, st->Fs);
+            }
+            st->hybrid_stereo_width_Q14 = st->silk_mode.stereoWidth_Q14;
+        }
+    }
+
+    if ( st->mode != MODE_CELT_ONLY && ec_tell(&enc)+17+20*(st->mode == MODE_HYBRID) <= 8*(max_data_bytes-1))
+    {
+        /* For SILK mode, the redundancy is inferred from the length */
+        if (st->mode == MODE_HYBRID)
+           ec_enc_bit_logp(&enc, redundancy, 12);
+        if (redundancy)
+        {
+            int max_redundancy;
+            ec_enc_bit_logp(&enc, celt_to_silk, 1);
+            if (st->mode == MODE_HYBRID)
+            {
+               /* Reserve the 8 bits needed for the redundancy length,
+                  and at least a few bits for CELT if possible */
+               max_redundancy = (max_data_bytes-1)-((ec_tell(&enc)+8+3+7)>>3);
+            }
+            else
+               max_redundancy = (max_data_bytes-1)-((ec_tell(&enc)+7)>>3);
+            /* Target the same bit-rate for redundancy as for the rest,
+               up to a max of 257 bytes */
+            redundancy_bytes = IMIN(max_redundancy, redundancy_bytes);
+            redundancy_bytes = IMIN(257, IMAX(2, redundancy_bytes));
+            if (st->mode == MODE_HYBRID)
+                ec_enc_uint(&enc, redundancy_bytes-2, 256);
+        }
+    } else {
+        redundancy = 0;
+    }
+
+    if (!redundancy)
+    {
+       st->silk_bw_switch = 0;
+       redundancy_bytes = 0;
+    }
+    if (st->mode != MODE_CELT_ONLY)start_band=17;
+
+    if (st->mode == MODE_SILK_ONLY)
+    {
+        ret = (ec_tell(&enc)+7)>>3;
+        ec_enc_done(&enc);
+        nb_compr_bytes = ret;
+    } else {
+        nb_compr_bytes = (max_data_bytes-1)-redundancy_bytes;
+#ifdef ENABLE_QEXT
+        if (st->mode == MODE_CELT_ONLY && st->enable_qext) {
+           celt_assert(redundancy_bytes==0);
+           nb_compr_bytes = orig_max_data_bytes-1;
+        }
+#endif
+#ifdef ENABLE_DRED
+        if (st->dred_duration > 0)
+        {
+            int max_celt_bytes;
+            opus_int32 dred_bytes = bitrate_to_bits(dred_bitrate_bps, st->Fs, frame_size)/8;
+            /* Allow CELT to steal up to 25% of the remaining bits. */
+            max_celt_bytes = nb_compr_bytes - dred_bytes*3/4;
+            /* But try to give CELT at least 5 bytes to prevent a mismatch with
+               the redundancy signaling. */
+            max_celt_bytes = IMAX((ec_tell(&enc)+7)/8 + 5, max_celt_bytes);
+            /* Subject to the original max. */
+            nb_compr_bytes = IMIN(nb_compr_bytes, max_celt_bytes);
+        }
+#endif
+        ec_enc_shrink(&enc, nb_compr_bytes);
+    }
+
+#ifndef DISABLE_FLOAT_API
+    if (redundancy || st->mode != MODE_SILK_ONLY)
+       celt_encoder_ctl(celt_enc, CELT_SET_ANALYSIS(analysis_info));
+#endif
+    if (st->mode == MODE_HYBRID) {
+       SILKInfo info;
+       info.signalType = st->silk_mode.signalType;
+       info.offset = st->silk_mode.offset;
+       celt_encoder_ctl(celt_enc, CELT_SET_SILK_INFO(&info));
+    }
+
+    /* 5 ms redundant frame for CELT->SILK */
+    if (redundancy && celt_to_silk)
+    {
+        int err;
+        celt_encoder_ctl(celt_enc, CELT_SET_START_BAND(0));
+        celt_encoder_ctl(celt_enc, OPUS_SET_VBR(0));
+        celt_encoder_ctl(celt_enc, OPUS_SET_BITRATE(OPUS_BITRATE_MAX));
+        err = celt_encode_with_ec(celt_enc, pcm_buf, st->Fs/200, data+nb_compr_bytes, redundancy_bytes, NULL);
+        if (err < 0)
+        {
+           RESTORE_STACK;
+           return OPUS_INTERNAL_ERROR;
+        }
+        celt_encoder_ctl(celt_enc, OPUS_GET_FINAL_RANGE(&redundant_rng));
+        celt_encoder_ctl(celt_enc, OPUS_RESET_STATE);
+    }
+
+    if (st->application != OPUS_APPLICATION_RESTRICTED_SILK)
+       celt_encoder_ctl(celt_enc, CELT_SET_START_BAND(start_band));
+
+    data[-1] = 0;
+    if (st->mode != MODE_SILK_ONLY)
+    {
+        celt_encoder_ctl(celt_enc, OPUS_SET_VBR(st->use_vbr));
+        if (st->mode == MODE_HYBRID)
+        {
+            if( st->use_vbr ) {
+                celt_encoder_ctl(celt_enc, OPUS_SET_BITRATE(st->bitrate_bps-st->silk_mode.bitRate));
+                celt_encoder_ctl(celt_enc, OPUS_SET_VBR_CONSTRAINT(0));
+            }
+        } else {
+            if (st->use_vbr)
+            {
+                celt_encoder_ctl(celt_enc, OPUS_SET_VBR(1));
+                celt_encoder_ctl(celt_enc, OPUS_SET_VBR_CONSTRAINT(st->vbr_constraint));
+                celt_encoder_ctl(celt_enc, OPUS_SET_BITRATE(st->bitrate_bps));
+            }
+        }
+#ifdef ENABLE_DRED
+        /* When Using DRED CBR, we can actually make the CELT part VBR and have DRED pick up the slack. */
+        if (!st->use_vbr && st->dred_duration > 0)
+        {
+            opus_int32 celt_bitrate = st->bitrate_bps;
+            celt_encoder_ctl(celt_enc, OPUS_SET_VBR(1));
+            celt_encoder_ctl(celt_enc, OPUS_SET_VBR_CONSTRAINT(0));
+            if (st->mode == MODE_HYBRID) {
+                celt_bitrate -= st->silk_mode.bitRate;
+            }
+            celt_encoder_ctl(celt_enc, OPUS_SET_BITRATE(celt_bitrate));
+        }
+#endif
+        if (st->mode != st->prev_mode && st->prev_mode > 0 && st->application != OPUS_APPLICATION_RESTRICTED_CELT)
+        {
+           unsigned char dummy[2];
+           celt_encoder_ctl(celt_enc, OPUS_RESET_STATE);
+
+           /* Prefilling */
+           celt_encode_with_ec(celt_enc, tmp_prefill, st->Fs/400, dummy, 2, NULL);
+           celt_encoder_ctl(celt_enc, CELT_SET_PREDICTION(0));
+        }
+        /* If false, we already busted the budget and we'll end up with a "PLC frame" */
+        if (ec_tell(&enc) <= 8*nb_compr_bytes)
+        {
+#ifdef ENABLE_QEXT
+           if (st->mode == MODE_CELT_ONLY) celt_encoder_ctl(celt_enc, OPUS_SET_QEXT(st->enable_qext));
+#endif
+           ret = celt_encode_with_ec(celt_enc, pcm_buf, frame_size, NULL, nb_compr_bytes, &enc);
+#ifdef ENABLE_QEXT
+           celt_encoder_ctl(celt_enc, OPUS_SET_QEXT(0));
+#endif
+           if (ret < 0)
+           {
+              RESTORE_STACK;
+              return OPUS_INTERNAL_ERROR;
+           }
+           /* Put CELT->SILK redundancy data in the right place. */
+           if (redundancy && celt_to_silk && st->mode==MODE_HYBRID && nb_compr_bytes != ret)
+           {
+              OPUS_MOVE(data+ret, data+nb_compr_bytes, redundancy_bytes);
+              nb_compr_bytes = ret+redundancy_bytes;
+           }
+        }
+        celt_encoder_ctl(celt_enc, OPUS_GET_FINAL_RANGE(&st->rangeFinal));
+    } else {
+       st->rangeFinal = enc.rng;
+    }
+
+    /* 5 ms redundant frame for SILK->CELT */
+    if (redundancy && !celt_to_silk)
+    {
+        int err;
+        unsigned char dummy[2];
+        int N2, N4;
+        N2 = st->Fs/200;
+        N4 = st->Fs/400;
+
+        celt_encoder_ctl(celt_enc, OPUS_RESET_STATE);
+        celt_encoder_ctl(celt_enc, CELT_SET_START_BAND(0));
+        celt_encoder_ctl(celt_enc, CELT_SET_PREDICTION(0));
+        celt_encoder_ctl(celt_enc, OPUS_SET_VBR(0));
+        celt_encoder_ctl(celt_enc, OPUS_SET_BITRATE(OPUS_BITRATE_MAX));
+
+        if (st->mode == MODE_HYBRID)
+        {
+           /* Shrink packet to what the encoder actually used. */
+           nb_compr_bytes = ret;
+           ec_enc_shrink(&enc, nb_compr_bytes);
+        }
+        /* NOTE: We could speed this up slightly (at the expense of code size) by just adding a function that prefills the buffer */
+        celt_encode_with_ec(celt_enc, pcm_buf+st->channels*(frame_size-N2-N4), N4, dummy, 2, NULL);
+
+        err = celt_encode_with_ec(celt_enc, pcm_buf+st->channels*(frame_size-N2), N2, data+nb_compr_bytes, redundancy_bytes, NULL);
+        if (err < 0)
+        {
+           RESTORE_STACK;
+           return OPUS_INTERNAL_ERROR;
+        }
+        celt_encoder_ctl(celt_enc, OPUS_GET_FINAL_RANGE(&redundant_rng));
+    }
+
+
+
+    /* Signalling the mode in the first byte */
+    data--;
+    data[0] |= gen_toc(st->mode, st->Fs/frame_size, curr_bandwidth, st->stream_channels);
+
+    st->rangeFinal ^= redundant_rng;
+
+    if (to_celt)
+        st->prev_mode = MODE_CELT_ONLY;
+    else
+        st->prev_mode = st->mode;
+    st->prev_channels = st->stream_channels;
+    st->prev_framesize = frame_size;
+
+    st->first = 0;
+
+    /* DTX decision */
+    if (st->use_dtx && !st->silk_mode.useDTX)
+    {
+       if (decide_dtx_mode(activity, &st->nb_no_activity_ms_Q1, 2*1000*frame_size/st->Fs))
+       {
+          st->rangeFinal = 0;
+          data[0] = gen_toc(st->mode, st->Fs/frame_size, curr_bandwidth, st->stream_channels);
+          RESTORE_STACK;
+          return 1;
+       }
+    } else {
+       st->nb_no_activity_ms_Q1 = 0;
+    }
+
+    /* In the unlikely case that the SILK encoder busted its target, tell
+       the decoder to call the PLC */
+    if (ec_tell(&enc) > (max_data_bytes-1)*8)
+    {
+       if (max_data_bytes < 2)
+       {
+          RESTORE_STACK;
+          return OPUS_BUFFER_TOO_SMALL;
+       }
+       data[1] = 0;
+       ret = 1;
+       st->rangeFinal = 0;
+    } else if (st->mode==MODE_SILK_ONLY&&!redundancy)
+    {
+       /*When in LPC only mode it's perfectly
+         reasonable to strip off trailing zero bytes as
+         the required range decoder behavior is to
+         fill these in. This can't be done when the MDCT
+         modes are used because the decoder needs to know
+         the actual length for allocation purposes.*/
+       while(ret>2&&data[ret]==0)ret--;
+    }
+    /* Count ToC and redundancy */
+    ret += 1+redundancy_bytes;
+    apply_padding = !st->use_vbr;
+#ifdef ENABLE_DRED
+    if (st->dred_duration > 0 && st->dred_encoder.loaded && first_frame) {
+       opus_extension_data extension;
+       unsigned char buf[DRED_MAX_DATA_SIZE];
+       int dred_chunks;
+       int dred_bytes_left;
+       dred_chunks = IMIN((st->dred_duration+5)/4, DRED_NUM_REDUNDANCY_FRAMES/2);
+       if (st->use_vbr) dred_chunks = IMIN(dred_chunks, st->dred_target_chunks);
+       /* Remaining space for DRED, accounting for cost the 3 extra bytes for code 3, padding length, and extension number. */
+       dred_bytes_left = IMIN(DRED_MAX_DATA_SIZE, orig_max_data_bytes-ret-3);
+       /* Account for the extra bytes required to signal large padding length. */
+       dred_bytes_left -= (dred_bytes_left+1+DRED_EXPERIMENTAL_BYTES)/255;
+       /* Check whether we actually have something to encode. */
+       if (dred_chunks >= 1 && dred_bytes_left >= DRED_MIN_BYTES+DRED_EXPERIMENTAL_BYTES) {
+           int dred_bytes;
+#ifdef DRED_EXPERIMENTAL_VERSION
+           /* Add temporary extension type and version.
+              These bytes will be removed once extension is finalized. */
+           buf[0] = 'D';
+           buf[1] = DRED_EXPERIMENTAL_VERSION;
+#endif
+           dred_bytes = dred_encode_silk_frame(&st->dred_encoder, buf+DRED_EXPERIMENTAL_BYTES, dred_chunks, dred_bytes_left-DRED_EXPERIMENTAL_BYTES,
+                                               st->dred_q0, st->dred_dQ, st->dred_qmax, st->activity_mem, st->arch);
+           if (dred_bytes > 0) {
+              dred_bytes += DRED_EXPERIMENTAL_BYTES;
+              celt_assert(dred_bytes <= dred_bytes_left);
+              extension.id = DRED_EXTENSION_ID;
+              extension.frame = 0;
+              extension.data = buf;
+              extension.len = dred_bytes;
+              ret = opus_packet_pad_impl(data, ret, orig_max_data_bytes, !st->use_vbr, &extension, 1);
+              if (ret < 0)
+              {
+                 RESTORE_STACK;
+                 return OPUS_INTERNAL_ERROR;
+              }
+              apply_padding = 0;
+           }
+       }
+    }
+#else
+    (void)first_frame; /* Avoids a warning about first_frame being unused. */
+#endif
+    if (apply_padding)
+    {
+       if (opus_packet_pad(data, ret, orig_max_data_bytes) != OPUS_OK)
+       {
+          RESTORE_STACK;
+          return OPUS_INTERNAL_ERROR;
+       }
+       ret = orig_max_data_bytes;
+    }
+    RESTORE_STACK;
+    return ret;
+}
+
+
+
+#if defined(FIXED_POINT) && !defined(ENABLE_RES24)
+opus_int32 opus_encode(OpusEncoder *st, const opus_int16 *pcm, int analysis_frame_size,
+                unsigned char *data, opus_int32 max_data_bytes)
+{
+   int frame_size;
+   frame_size = frame_size_select(st->application, analysis_frame_size, st->variable_duration, st->Fs);
+   return opus_encode_native(st, pcm, frame_size, data, max_data_bytes, 16,
+                             pcm, analysis_frame_size, 0, -2, st->channels, downmix_int, 0);
+}
+#else
+opus_int32 opus_encode(OpusEncoder *st, const opus_int16 *pcm, int analysis_frame_size,
+                unsigned char *data, opus_int32 max_data_bytes)
+{
+   int i, ret;
+   int frame_size;
+   VARDECL(opus_res, in);
+   ALLOC_STACK;
+
+   frame_size = frame_size_select(st->application, analysis_frame_size, st->variable_duration, st->Fs);
+   if (frame_size <= 0)
+   {
+      RESTORE_STACK;
+      return OPUS_BAD_ARG;
+   }
+   ALLOC(in, frame_size*st->channels, opus_res);
+
+   for (i=0;i<frame_size*st->channels;i++)
+      in[i] = INT16TORES(pcm[i]);
+   ret = opus_encode_native(st, in, frame_size, data, max_data_bytes, 16,
+                            pcm, analysis_frame_size, 0, -2, st->channels, downmix_int, 1);
+   RESTORE_STACK;
+   return ret;
+}
+#endif
+
+#if defined(FIXED_POINT) && defined(ENABLE_RES24)
+opus_int32 opus_encode24(OpusEncoder *st, const opus_int32 *pcm, int analysis_frame_size,
+                unsigned char *data, opus_int32 max_data_bytes)
+{
+   int frame_size;
+   frame_size = frame_size_select(st->application, analysis_frame_size, st->variable_duration, st->Fs);
+   return opus_encode_native(st, pcm, frame_size, data, max_data_bytes, MAX_ENCODING_DEPTH,
+                             pcm, analysis_frame_size, 0, -2, st->channels, downmix_int24, 0);
+}
+#else
+opus_int32 opus_encode24(OpusEncoder *st, const opus_int32 *pcm, int analysis_frame_size,
+                unsigned char *data, opus_int32 max_data_bytes)
+{
+   int i, ret;
+   int frame_size;
+   VARDECL(opus_res, in);
+   ALLOC_STACK;
+
+   frame_size = frame_size_select(st->application, analysis_frame_size, st->variable_duration, st->Fs);
+   if (frame_size <= 0)
+   {
+      RESTORE_STACK;
+      return OPUS_BAD_ARG;
+   }
+   ALLOC(in, frame_size*st->channels, opus_res);
+
+   for (i=0;i<frame_size*st->channels;i++)
+      in[i] = INT24TORES(pcm[i]);
+   ret = opus_encode_native(st, in, frame_size, data, max_data_bytes, MAX_ENCODING_DEPTH,
+                            pcm, analysis_frame_size, 0, -2, st->channels, downmix_int24, 1);
+   RESTORE_STACK;
+   return ret;
+}
+#endif
+
+
+#ifndef DISABLE_FLOAT_API
+
+# if !defined(FIXED_POINT)
+opus_int32 opus_encode_float(OpusEncoder *st, const float *pcm, int analysis_frame_size,
+                      unsigned char *data, opus_int32 out_data_bytes)
+{
+   int frame_size;
+   frame_size = frame_size_select(st->application, analysis_frame_size, st->variable_duration, st->Fs);
+   return opus_encode_native(st, pcm, frame_size, data, out_data_bytes, MAX_ENCODING_DEPTH,
+                             pcm, analysis_frame_size, 0, -2, st->channels, downmix_float, 1);
+}
+# else
+opus_int32 opus_encode_float(OpusEncoder *st, const float *pcm, int analysis_frame_size,
+      unsigned char *data, opus_int32 max_data_bytes)
+{
+   int i, ret;
+   int frame_size;
+   VARDECL(opus_res, in);
+   ALLOC_STACK;
+
+   frame_size = frame_size_select(st->application, analysis_frame_size, st->variable_duration, st->Fs);
+   if (frame_size <= 0)
+   {
+      RESTORE_STACK;
+      return OPUS_BAD_ARG;
+   }
+   ALLOC(in, frame_size*st->channels, opus_res);
+
+   for (i=0;i<frame_size*st->channels;i++)
+      in[i] = FLOAT2RES(pcm[i]);
+   ret = opus_encode_native(st, in, frame_size, data, max_data_bytes, MAX_ENCODING_DEPTH,
+                            pcm, analysis_frame_size, 0, -2, st->channels, downmix_float, 1);
+   RESTORE_STACK;
+   return ret;
+}
+# endif
+
+#endif
+
+
+int opus_encoder_ctl(OpusEncoder *st, int request, ...)
+{
+    int ret;
+    CELTEncoder *celt_enc=NULL;
+    va_list ap;
+
+    ret = OPUS_OK;
+    va_start(ap, request);
+
+    if (st->application != OPUS_APPLICATION_RESTRICTED_SILK)
+       celt_enc = (CELTEncoder*)((char*)st+st->celt_enc_offset);
+
+    switch (request)
+    {
+        case OPUS_SET_APPLICATION_REQUEST:
+        {
+            opus_int32 value = va_arg(ap, opus_int32);
+            if (st->application == OPUS_APPLICATION_RESTRICTED_SILK || st->application == OPUS_APPLICATION_RESTRICTED_CELT)
+            {
+               ret = OPUS_BAD_ARG;
+               break;
+            }
+            if (   (value != OPUS_APPLICATION_VOIP && value != OPUS_APPLICATION_AUDIO
+                 && value != OPUS_APPLICATION_RESTRICTED_LOWDELAY)
+               || (!st->first && st->application != value))
+            {
+               ret = OPUS_BAD_ARG;
+               break;
+            }
+            st->application = value;
+#ifndef DISABLE_FLOAT_API
+            st->analysis.application = value;
+#endif
+        }
+        break;
+        case OPUS_GET_APPLICATION_REQUEST:
+        {
+            opus_int32 *value = va_arg(ap, opus_int32*);
+            if (!value)
+            {
+               goto bad_arg;
+            }
+            *value = st->application;
+        }
+        break;
+        case OPUS_SET_BITRATE_REQUEST:
+        {
+            opus_int32 value = va_arg(ap, opus_int32);
+            if (value != OPUS_AUTO && value != OPUS_BITRATE_MAX)
+            {
+                if (value <= 0)
+                    goto bad_arg;
+                else if (value <= 500)
+                    value = 500;
+                else if (value > (opus_int32)750000*st->channels)
+                    value = (opus_int32)750000*st->channels;
+            }
+            st->user_bitrate_bps = value;
+        }
+        break;
+        case OPUS_GET_BITRATE_REQUEST:
+        {
+            opus_int32 *value = va_arg(ap, opus_int32*);
+            if (!value)
+            {
+               goto bad_arg;
+            }
+            *value = user_bitrate_to_bitrate(st, st->prev_framesize, 1276);
+        }
+        break;
+        case OPUS_SET_FORCE_CHANNELS_REQUEST:
+        {
+            opus_int32 value = va_arg(ap, opus_int32);
+            if((value<1 || value>st->channels) && value != OPUS_AUTO)
+            {
+               goto bad_arg;
+            }
+            st->force_channels = value;
+        }
+        break;
+        case OPUS_GET_FORCE_CHANNELS_REQUEST:
+        {
+            opus_int32 *value = va_arg(ap, opus_int32*);
+            if (!value)
+            {
+               goto bad_arg;
+            }
+            *value = st->force_channels;
+        }
+        break;
+        case OPUS_SET_MAX_BANDWIDTH_REQUEST:
+        {
+            opus_int32 value = va_arg(ap, opus_int32);
+            if (value < OPUS_BANDWIDTH_NARROWBAND || value > OPUS_BANDWIDTH_FULLBAND)
+            {
+               goto bad_arg;
+            }
+            st->max_bandwidth = value;
+            if (st->max_bandwidth == OPUS_BANDWIDTH_NARROWBAND) {
+                st->silk_mode.maxInternalSampleRate = 8000;
+            } else if (st->max_bandwidth == OPUS_BANDWIDTH_MEDIUMBAND) {
+                st->silk_mode.maxInternalSampleRate = 12000;
+            } else {
+                st->silk_mode.maxInternalSampleRate = 16000;
+            }
+        }
+        break;
+        case OPUS_GET_MAX_BANDWIDTH_REQUEST:
+        {
+            opus_int32 *value = va_arg(ap, opus_int32*);
+            if (!value)
+            {
+               goto bad_arg;
+            }
+            *value = st->max_bandwidth;
+        }
+        break;
+        case OPUS_SET_BANDWIDTH_REQUEST:
+        {
+            opus_int32 value = va_arg(ap, opus_int32);
+            if ((value < OPUS_BANDWIDTH_NARROWBAND || value > OPUS_BANDWIDTH_FULLBAND) && value != OPUS_AUTO)
+            {
+               goto bad_arg;
+            }
+            st->user_bandwidth = value;
+            if (st->user_bandwidth == OPUS_BANDWIDTH_NARROWBAND) {
+                st->silk_mode.maxInternalSampleRate = 8000;
+            } else if (st->user_bandwidth == OPUS_BANDWIDTH_MEDIUMBAND) {
+                st->silk_mode.maxInternalSampleRate = 12000;
+            } else {
+                st->silk_mode.maxInternalSampleRate = 16000;
+            }
+        }
+        break;
+        case OPUS_GET_BANDWIDTH_REQUEST:
+        {
+            opus_int32 *value = va_arg(ap, opus_int32*);
+            if (!value)
+            {
+               goto bad_arg;
+            }
+            *value = st->bandwidth;
+        }
+        break;
+        case OPUS_SET_DTX_REQUEST:
+        {
+            opus_int32 value = va_arg(ap, opus_int32);
+            if(value<0 || value>1)
+            {
+               goto bad_arg;
+            }
+            st->use_dtx = value;
+        }
+        break;
+        case OPUS_GET_DTX_REQUEST:
+        {
+            opus_int32 *value = va_arg(ap, opus_int32*);
+            if (!value)
+            {
+               goto bad_arg;
+            }
+            *value = st->use_dtx;
+        }
+        break;
+        case OPUS_SET_COMPLEXITY_REQUEST:
+        {
+            opus_int32 value = va_arg(ap, opus_int32);
+            if(value<0 || value>10)
+            {
+               goto bad_arg;
+            }
+            st->silk_mode.complexity = value;
+            if (st->application != OPUS_APPLICATION_RESTRICTED_SILK)
+               celt_encoder_ctl(celt_enc, OPUS_SET_COMPLEXITY(value));
+        }
+        break;
+        case OPUS_GET_COMPLEXITY_REQUEST:
+        {
+            opus_int32 *value = va_arg(ap, opus_int32*);
+            if (!value)
+            {
+               goto bad_arg;
+            }
+            *value = st->silk_mode.complexity;
+        }
+        break;
+        case OPUS_SET_INBAND_FEC_REQUEST:
+        {
+            opus_int32 value = va_arg(ap, opus_int32);
+            if(value<0 || value>2)
+            {
+               goto bad_arg;
+            }
+            st->fec_config = value;
+            st->silk_mode.useInBandFEC = (value != 0);
+        }
+        break;
+        case OPUS_GET_INBAND_FEC_REQUEST:
+        {
+            opus_int32 *value = va_arg(ap, opus_int32*);
+            if (!value)
+            {
+               goto bad_arg;
+            }
+            *value = st->fec_config;
+        }
+        break;
+        case OPUS_SET_PACKET_LOSS_PERC_REQUEST:
+        {
+            opus_int32 value = va_arg(ap, opus_int32);
+            if (value < 0 || value > 100)
+            {
+               goto bad_arg;
+            }
+            st->silk_mode.packetLossPercentage = value;
+            if (st->application != OPUS_APPLICATION_RESTRICTED_SILK)
+               celt_encoder_ctl(celt_enc, OPUS_SET_PACKET_LOSS_PERC(value));
+        }
+        break;
+        case OPUS_GET_PACKET_LOSS_PERC_REQUEST:
+        {
+            opus_int32 *value = va_arg(ap, opus_int32*);
+            if (!value)
+            {
+               goto bad_arg;
+            }
+            *value = st->silk_mode.packetLossPercentage;
+        }
+        break;
+        case OPUS_SET_VBR_REQUEST:
+        {
+            opus_int32 value = va_arg(ap, opus_int32);
+            if(value<0 || value>1)
+            {
+               goto bad_arg;
+            }
+            st->use_vbr = value;
+            st->silk_mode.useCBR = 1-value;
+        }
+        break;
+        case OPUS_GET_VBR_REQUEST:
+        {
+            opus_int32 *value = va_arg(ap, opus_int32*);
+            if (!value)
+            {
+               goto bad_arg;
+            }
+            *value = st->use_vbr;
+        }
+        break;
+        case OPUS_SET_VOICE_RATIO_REQUEST:
+        {
+            opus_int32 value = va_arg(ap, opus_int32);
+            if (value<-1 || value>100)
+            {
+               goto bad_arg;
+            }
+            st->voice_ratio = value;
+        }
+        break;
+        case OPUS_GET_VOICE_RATIO_REQUEST:
+        {
+            opus_int32 *value = va_arg(ap, opus_int32*);
+            if (!value)
+            {
+               goto bad_arg;
+            }
+            *value = st->voice_ratio;
+        }
+        break;
+        case OPUS_SET_VBR_CONSTRAINT_REQUEST:
+        {
+            opus_int32 value = va_arg(ap, opus_int32);
+            if(value<0 || value>1)
+            {
+               goto bad_arg;
+            }
+            st->vbr_constraint = value;
+        }
+        break;
+        case OPUS_GET_VBR_CONSTRAINT_REQUEST:
+        {
+            opus_int32 *value = va_arg(ap, opus_int32*);
+            if (!value)
+            {
+               goto bad_arg;
+            }
+            *value = st->vbr_constraint;
+        }
+        break;
+        case OPUS_SET_SIGNAL_REQUEST:
+        {
+            opus_int32 value = va_arg(ap, opus_int32);
+            if(value!=OPUS_AUTO && value!=OPUS_SIGNAL_VOICE && value!=OPUS_SIGNAL_MUSIC)
+            {
+               goto bad_arg;
+            }
+            st->signal_type = value;
+        }
+        break;
+        case OPUS_GET_SIGNAL_REQUEST:
+        {
+            opus_int32 *value = va_arg(ap, opus_int32*);
+            if (!value)
+            {
+               goto bad_arg;
+            }
+            *value = st->signal_type;
+        }
+        break;
+        case OPUS_GET_LOOKAHEAD_REQUEST:
+        {
+            opus_int32 *value = va_arg(ap, opus_int32*);
+            if (!value)
+            {
+               goto bad_arg;
+            }
+            *value = st->Fs/400;
+            if (st->application != OPUS_APPLICATION_RESTRICTED_LOWDELAY && st->application != OPUS_APPLICATION_RESTRICTED_CELT)
+                *value += st->delay_compensation;
+        }
+        break;
+        case OPUS_GET_SAMPLE_RATE_REQUEST:
+        {
+            opus_int32 *value = va_arg(ap, opus_int32*);
+            if (!value)
+            {
+               goto bad_arg;
+            }
+            *value = st->Fs;
+        }
+        break;
+        case OPUS_GET_FINAL_RANGE_REQUEST:
+        {
+            opus_uint32 *value = va_arg(ap, opus_uint32*);
+            if (!value)
+            {
+               goto bad_arg;
+            }
+            *value = st->rangeFinal;
+        }
+        break;
+        case OPUS_SET_LSB_DEPTH_REQUEST:
+        {
+            opus_int32 value = va_arg(ap, opus_int32);
+            if (value<8 || value>24)
+            {
+               goto bad_arg;
+            }
+            st->lsb_depth=value;
+        }
+        break;
+        case OPUS_GET_LSB_DEPTH_REQUEST:
+        {
+            opus_int32 *value = va_arg(ap, opus_int32*);
+            if (!value)
+            {
+               goto bad_arg;
+            }
+            *value = st->lsb_depth;
+        }
+        break;
+        case OPUS_SET_EXPERT_FRAME_DURATION_REQUEST:
+        {
+            opus_int32 value = va_arg(ap, opus_int32);
+            if (value != OPUS_FRAMESIZE_ARG    && value != OPUS_FRAMESIZE_2_5_MS &&
+                value != OPUS_FRAMESIZE_5_MS   && value != OPUS_FRAMESIZE_10_MS  &&
+                value != OPUS_FRAMESIZE_20_MS  && value != OPUS_FRAMESIZE_40_MS  &&
+                value != OPUS_FRAMESIZE_60_MS  && value != OPUS_FRAMESIZE_80_MS  &&
+                value != OPUS_FRAMESIZE_100_MS && value != OPUS_FRAMESIZE_120_MS)
+            {
+               goto bad_arg;
+            }
+            st->variable_duration = value;
+        }
+        break;
+        case OPUS_GET_EXPERT_FRAME_DURATION_REQUEST:
+        {
+            opus_int32 *value = va_arg(ap, opus_int32*);
+            if (!value)
+            {
+               goto bad_arg;
+            }
+            *value = st->variable_duration;
+        }
+        break;
+        case OPUS_SET_PREDICTION_DISABLED_REQUEST:
+        {
+           opus_int32 value = va_arg(ap, opus_int32);
+           if (value > 1 || value < 0)
+              goto bad_arg;
+           st->silk_mode.reducedDependency = value;
+        }
+        break;
+        case OPUS_GET_PREDICTION_DISABLED_REQUEST:
+        {
+           opus_int32 *value = va_arg(ap, opus_int32*);
+           if (!value)
+              goto bad_arg;
+           *value = st->silk_mode.reducedDependency;
+        }
+        break;
+        case OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST:
+        {
+            opus_int32 value = va_arg(ap, opus_int32);
+            if(value<0 || value>1)
+            {
+               goto bad_arg;
+            }
+            if (st->application != OPUS_APPLICATION_RESTRICTED_SILK)
+               celt_encoder_ctl(celt_enc, OPUS_SET_PHASE_INVERSION_DISABLED(value));
+        }
+        break;
+        case OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST:
+        {
+            opus_int32 *value = va_arg(ap, opus_int32*);
+            if (!value)
+            {
+               goto bad_arg;
+            }
+            if (st->application != OPUS_APPLICATION_RESTRICTED_SILK)
+               celt_encoder_ctl(celt_enc, OPUS_GET_PHASE_INVERSION_DISABLED(value));
+            else
+               *value = 0;
+        }
+        break;
+#ifdef ENABLE_DRED
+        case OPUS_SET_DRED_DURATION_REQUEST:
+        {
+            opus_int32 value = va_arg(ap, opus_int32);
+            if(value<0 || value>DRED_MAX_FRAMES)
+            {
+               goto bad_arg;
+            }
+            st->dred_duration = value;
+            st->silk_mode.useDRED = !!value;
+        }
+        break;
+        case OPUS_GET_DRED_DURATION_REQUEST:
+        {
+            opus_int32 *value = va_arg(ap, opus_int32*);
+            if (!value)
+            {
+               goto bad_arg;
+            }
+            *value = st->dred_duration;
+        }
+        break;
+#endif
+#ifdef ENABLE_QEXT
+      case OPUS_SET_QEXT_REQUEST:
+      {
+          opus_int32 value = va_arg(ap, opus_int32);
+          if(value<0 || value>1)
+          {
+             goto bad_arg;
+          }
+          st->enable_qext = value;
+      }
+      break;
+      case OPUS_GET_QEXT_REQUEST:
+      {
+          opus_int32 *value = va_arg(ap, opus_int32*);
+          if (!value)
+          {
+             goto bad_arg;
+          }
+          *value = st->enable_qext;
+      }
+      break;
+#endif
+        case OPUS_RESET_STATE:
+        {
+           void *silk_enc;
+           silk_EncControlStruct dummy;
+           char *start;
+           silk_enc = (char*)st+st->silk_enc_offset;
+#ifndef DISABLE_FLOAT_API
+           tonality_analysis_reset(&st->analysis);
+#endif
+
+           start = (char*)&st->OPUS_ENCODER_RESET_START;
+           OPUS_CLEAR(start, st->silk_enc_offset - (start - (char*)st));
+
+           if (st->application != OPUS_APPLICATION_RESTRICTED_SILK)
+              celt_encoder_ctl(celt_enc, OPUS_RESET_STATE);
+           if (st->application != OPUS_APPLICATION_RESTRICTED_CELT)
+              silk_InitEncoder( silk_enc, st->channels, st->arch, &dummy );
+#ifdef ENABLE_DRED
+           /* Initialize DRED Encoder */
+           dred_encoder_reset( &st->dred_encoder );
+#endif
+           st->stream_channels = st->channels;
+           st->hybrid_stereo_width_Q14 = 1 << 14;
+           st->prev_HB_gain = Q15ONE;
+           st->first = 1;
+           st->mode = MODE_HYBRID;
+           st->bandwidth = OPUS_BANDWIDTH_FULLBAND;
+           st->variable_HP_smth2_Q15 = silk_LSHIFT( silk_lin2log( VARIABLE_HP_MIN_CUTOFF_HZ ), 8 );
+        }
+        break;
+        case OPUS_SET_FORCE_MODE_REQUEST:
+        {
+            opus_int32 value = va_arg(ap, opus_int32);
+            if ((value < MODE_SILK_ONLY || value > MODE_CELT_ONLY) && value != OPUS_AUTO)
+            {
+               goto bad_arg;
+            }
+            st->user_forced_mode = value;
+        }
+        break;
+        case OPUS_SET_LFE_REQUEST:
+        {
+            opus_int32 value = va_arg(ap, opus_int32);
+            st->lfe = value;
+            if (st->application != OPUS_APPLICATION_RESTRICTED_SILK)
+               ret = celt_encoder_ctl(celt_enc, OPUS_SET_LFE(value));
+        }
+        break;
+        case OPUS_SET_ENERGY_MASK_REQUEST:
+        {
+            celt_glog *value = va_arg(ap, celt_glog*);
+            st->energy_masking = value;
+            if (st->application != OPUS_APPLICATION_RESTRICTED_SILK)
+               ret = celt_encoder_ctl(celt_enc, OPUS_SET_ENERGY_MASK(value));
+        }
+        break;
+        case OPUS_GET_IN_DTX_REQUEST:
+        {
+            opus_int32 *value = va_arg(ap, opus_int32*);
+            if (!value)
+            {
+                goto bad_arg;
+            }
+            if (st->silk_mode.useDTX && (st->prev_mode == MODE_SILK_ONLY || st->prev_mode == MODE_HYBRID)) {
+                /* DTX determined by Silk. */
+                silk_encoder *silk_enc = (silk_encoder*)(void *)((char*)st+st->silk_enc_offset);
+                *value = silk_enc->state_Fxx[0].sCmn.noSpeechCounter >= NB_SPEECH_FRAMES_BEFORE_DTX;
+                /* Stereo: check second channel unless only the middle channel was encoded. */
+                if(*value == 1 && st->silk_mode.nChannelsInternal == 2 && silk_enc->prev_decode_only_middle == 0) {
+                    *value = silk_enc->state_Fxx[1].sCmn.noSpeechCounter >= NB_SPEECH_FRAMES_BEFORE_DTX;
+                }
+            }
+            else if (st->use_dtx) {
+                /* DTX determined by Opus. */
+                *value = st->nb_no_activity_ms_Q1 >= NB_SPEECH_FRAMES_BEFORE_DTX*20*2;
+            }
+            else {
+                *value = 0;
+            }
+        }
+        break;
+#ifdef USE_WEIGHTS_FILE
+        case OPUS_SET_DNN_BLOB_REQUEST:
+        {
+            const unsigned char *data = va_arg(ap, const unsigned char *);
+            opus_int32 len = va_arg(ap, opus_int32);
+            if(len<0 || data == NULL)
+            {
+               goto bad_arg;
+            }
+#ifdef ENABLE_DRED
+            ret = dred_encoder_load_model(&st->dred_encoder, data, len);
+#endif
+        }
+        break;
+#endif
+        case CELT_GET_MODE_REQUEST:
+        {
+           const CELTMode ** value = va_arg(ap, const CELTMode**);
+           if (!value)
+           {
+              goto bad_arg;
+           }
+           celt_assert(celt_enc != NULL);
+           ret = celt_encoder_ctl(celt_enc, CELT_GET_MODE(value));
+        }
+        break;
+        default:
+            /* fprintf(stderr, "unknown opus_encoder_ctl() request: %d", request);*/
+            ret = OPUS_UNIMPLEMENTED;
+            break;
+    }
+    va_end(ap);
+    return ret;
+bad_arg:
+    va_end(ap);
+    return OPUS_BAD_ARG;
+}
+
+void opus_encoder_destroy(OpusEncoder *st)
+{
+    opus_free(st);
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/src_lib/opus_multistream_encoder.c b/code/sdk/zhihui/test_opus_frame_to_pcm/src_lib/opus_multistream_encoder.c
new file mode 100644 (file)
index 0000000..79c0d3b
--- /dev/null
@@ -0,0 +1,1363 @@
+/* Copyright (c) 2011 Xiph.Org Foundation
+   Written by Jean-Marc Valin */
+/*
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+   - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+   - Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+   OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "opus_multistream.h"
+#include "opus.h"
+#include "opus_private.h"
+#include "stack_alloc.h"
+#include <stdarg.h>
+#include "float_cast.h"
+#include "os_support.h"
+#include "mathops.h"
+#include "mdct.h"
+#include "modes.h"
+#include "bands.h"
+#include "quant_bands.h"
+#include "pitch.h"
+
+typedef struct {
+   int nb_streams;
+   int nb_coupled_streams;
+   unsigned char mapping[8];
+} VorbisLayout;
+
+/* Index is nb_channel-1*/
+static const VorbisLayout vorbis_mappings[8] = {
+      {1, 0, {0}},                      /* 1: mono */
+      {1, 1, {0, 1}},                   /* 2: stereo */
+      {2, 1, {0, 2, 1}},                /* 3: 1-d surround */
+      {2, 2, {0, 1, 2, 3}},             /* 4: quadraphonic surround */
+      {3, 2, {0, 4, 1, 2, 3}},          /* 5: 5-channel surround */
+      {4, 2, {0, 4, 1, 2, 3, 5}},       /* 6: 5.1 surround */
+      {4, 3, {0, 4, 1, 2, 3, 5, 6}},    /* 7: 6.1 surround */
+      {5, 3, {0, 6, 1, 2, 3, 4, 5, 7}}, /* 8: 7.1 surround */
+};
+
+#ifdef ENABLE_QEXT
+#define MAX_OVERLAP 240
+#else
+#define MAX_OVERLAP 120
+#endif
+
+static opus_val32 *ms_get_preemph_mem(OpusMSEncoder *st)
+{
+   int s;
+   char *ptr;
+   int coupled_size, mono_size;
+
+   coupled_size = opus_encoder_init(NULL, st->Fs, 2, st->application);
+   mono_size = opus_encoder_init(NULL, st->Fs, 1, st->application);
+   ptr = (char*)st + align(sizeof(OpusMSEncoder));
+   for (s=0;s<st->layout.nb_streams;s++)
+   {
+      if (s < st->layout.nb_coupled_streams)
+         ptr += align(coupled_size);
+      else
+         ptr += align(mono_size);
+   }
+   /* void* cast avoids clang -Wcast-align warning */
+   return (opus_val32*)(void*)(ptr+st->layout.nb_channels*MAX_OVERLAP*sizeof(opus_val32));
+}
+
+static opus_val32 *ms_get_window_mem(OpusMSEncoder *st)
+{
+   int s;
+   char *ptr;
+   int coupled_size, mono_size;
+
+   coupled_size = opus_encoder_init(NULL, st->Fs, 2, st->application);
+   mono_size = opus_encoder_init(NULL, st->Fs, 1, st->application);
+   ptr = (char*)st + align(sizeof(OpusMSEncoder));
+   for (s=0;s<st->layout.nb_streams;s++)
+   {
+      if (s < st->layout.nb_coupled_streams)
+         ptr += align(coupled_size);
+      else
+         ptr += align(mono_size);
+   }
+   /* void* cast avoids clang -Wcast-align warning */
+   return (opus_val32*)(void*)ptr;
+}
+
+static int validate_ambisonics(int nb_channels, int *nb_streams, int *nb_coupled_streams)
+{
+   int order_plus_one;
+   int acn_channels;
+   int nondiegetic_channels;
+
+   if (nb_channels < 1 || nb_channels > 227)
+      return 0;
+
+   order_plus_one = isqrt32(nb_channels);
+   acn_channels = order_plus_one * order_plus_one;
+   nondiegetic_channels = nb_channels - acn_channels;
+
+   if (nondiegetic_channels != 0 && nondiegetic_channels != 2)
+      return 0;
+
+   if (nb_streams)
+      *nb_streams = acn_channels + (nondiegetic_channels != 0);
+   if (nb_coupled_streams)
+      *nb_coupled_streams = nondiegetic_channels != 0;
+   return 1;
+}
+
+static int validate_encoder_layout(const ChannelLayout *layout)
+{
+   int s;
+   for (s=0;s<layout->nb_streams;s++)
+   {
+      if (s < layout->nb_coupled_streams)
+      {
+         if (get_left_channel(layout, s, -1)==-1)
+            return 0;
+         if (get_right_channel(layout, s, -1)==-1)
+            return 0;
+      } else {
+         if (get_mono_channel(layout, s, -1)==-1)
+            return 0;
+      }
+   }
+   return 1;
+}
+
+static void channel_pos(int channels, int pos[8])
+{
+   /* Position in the mix: 0 don't mix, 1: left, 2: center, 3:right */
+   if (channels==4)
+   {
+      pos[0]=1;
+      pos[1]=3;
+      pos[2]=1;
+      pos[3]=3;
+   } else if (channels==3||channels==5||channels==6)
+   {
+      pos[0]=1;
+      pos[1]=2;
+      pos[2]=3;
+      pos[3]=1;
+      pos[4]=3;
+      pos[5]=0;
+   } else if (channels==7)
+   {
+      pos[0]=1;
+      pos[1]=2;
+      pos[2]=3;
+      pos[3]=1;
+      pos[4]=3;
+      pos[5]=2;
+      pos[6]=0;
+   } else if (channels==8)
+   {
+      pos[0]=1;
+      pos[1]=2;
+      pos[2]=3;
+      pos[3]=1;
+      pos[4]=3;
+      pos[5]=1;
+      pos[6]=3;
+      pos[7]=0;
+   }
+}
+
+#if 1
+/* Computes a rough approximation of log2(2^a + 2^b) */
+static opus_val16 logSum(celt_glog a, celt_glog b)
+{
+   celt_glog max;
+   celt_glog diff;
+   celt_glog frac;
+   static const celt_glog diff_table[17] = {
+         GCONST(0.5000000f), GCONST(0.2924813f), GCONST(0.1609640f), GCONST(0.0849625f),
+         GCONST(0.0437314f), GCONST(0.0221971f), GCONST(0.0111839f), GCONST(0.0056136f),
+         GCONST(0.0028123f)
+   };
+   int low;
+   if (a>b)
+   {
+      max = a;
+      diff = SUB32(a,b);
+   } else {
+      max = b;
+      diff = SUB32(b,a);
+   }
+   if (!(diff < GCONST(8.f)))  /* inverted to catch NaNs */
+      return max;
+#ifdef FIXED_POINT
+   low = SHR32(diff, DB_SHIFT-1);
+   frac = VSHR32(diff - SHL32(low, DB_SHIFT-1), DB_SHIFT-16);
+#else
+   low = (int)floor(2*diff);
+   frac = 2*diff - low;
+#endif
+   return max + diff_table[low] + MULT16_32_Q15(frac, SUB32(diff_table[low+1], diff_table[low]));
+}
+#else
+opus_val16 logSum(opus_val16 a, opus_val16 b)
+{
+   return log2(pow(4, a)+ pow(4, b))/2;
+}
+#endif
+
+void surround_analysis(const CELTMode *celt_mode, const void *pcm, celt_glog *bandLogE, opus_val32 *mem, opus_val32 *preemph_mem,
+      int len, int overlap, int channels, int rate, opus_copy_channel_in_func copy_channel_in, int arch
+)
+{
+   int c;
+   int i;
+   int LM;
+   int pos[8] = {0};
+   int upsample;
+   int frame_size;
+   int freq_size;
+   opus_val16 channel_offset;
+   opus_val32 bandE[21];
+   celt_glog maskLogE[3][21];
+   VARDECL(opus_val32, in);
+   VARDECL(opus_res, x);
+   VARDECL(opus_val32, freq);
+   SAVE_STACK;
+
+   upsample = resampling_factor(rate);
+   frame_size = len*upsample;
+
+   /* LM = log2(frame_size / 120) */
+   for (LM=0;LM<celt_mode->maxLM;LM++)
+      if (celt_mode->shortMdctSize<<LM==frame_size)
+         break;
+
+   freq_size = celt_mode->shortMdctSize<<LM;
+
+   ALLOC(in, frame_size+overlap, opus_val32);
+   ALLOC(x, len, opus_res);
+   ALLOC(freq, freq_size, opus_val32);
+
+   channel_pos(channels, pos);
+
+   for (c=0;c<3;c++)
+      for (i=0;i<21;i++)
+         maskLogE[c][i] = -GCONST(28.f);
+
+   for (c=0;c<channels;c++)
+   {
+      int frame;
+      int nb_frames = frame_size/freq_size;
+      celt_assert(nb_frames*freq_size == frame_size);
+      OPUS_COPY(in, mem+c*overlap, overlap);
+      (*copy_channel_in)(x, 1, pcm, channels, c, len, NULL);
+      celt_preemphasis(x, in+overlap, frame_size, 1, upsample, celt_mode->preemph, preemph_mem+c, 0);
+#ifndef FIXED_POINT
+      {
+         opus_val32 sum;
+         sum = celt_inner_prod(in, in, frame_size+overlap, 0);
+         /* This should filter out both NaNs and ridiculous signals that could
+            cause NaNs further down. */
+         if (!(sum < 1e18f) || celt_isnan(sum))
+         {
+            OPUS_CLEAR(in, frame_size+overlap);
+            preemph_mem[c] = 0;
+         }
+      }
+#endif
+      OPUS_CLEAR(bandE, 21);
+      for (frame=0;frame<nb_frames;frame++)
+      {
+         opus_val32 tmpE[21];
+         clt_mdct_forward(&celt_mode->mdct, in+freq_size*frame, freq, celt_mode->window,
+               overlap, celt_mode->maxLM-LM, 1, arch);
+         if (upsample != 1)
+         {
+            int bound = freq_size/upsample;
+            for (i=0;i<bound;i++)
+               freq[i] *= upsample;
+            for (;i<freq_size;i++)
+               freq[i] = 0;
+         }
+
+         compute_band_energies(celt_mode, freq, tmpE, 21, 1, LM, arch);
+         /* If we have multiple frames, take the max energy. */
+         for (i=0;i<21;i++)
+            bandE[i] = MAX32(bandE[i], tmpE[i]);
+      }
+      amp2Log2(celt_mode, 21, 21, bandE, bandLogE+21*c, 1);
+      /* Apply spreading function with -6 dB/band going up and -12 dB/band going down. */
+      for (i=1;i<21;i++)
+         bandLogE[21*c+i] = MAXG(bandLogE[21*c+i], bandLogE[21*c+i-1]-GCONST(1.f));
+      for (i=19;i>=0;i--)
+         bandLogE[21*c+i] = MAXG(bandLogE[21*c+i], bandLogE[21*c+i+1]-GCONST(2.f));
+      if (pos[c]==1)
+      {
+         for (i=0;i<21;i++)
+            maskLogE[0][i] = logSum(maskLogE[0][i], bandLogE[21*c+i]);
+      } else if (pos[c]==3)
+      {
+         for (i=0;i<21;i++)
+            maskLogE[2][i] = logSum(maskLogE[2][i], bandLogE[21*c+i]);
+      } else if (pos[c]==2)
+      {
+         for (i=0;i<21;i++)
+         {
+            maskLogE[0][i] = logSum(maskLogE[0][i], bandLogE[21*c+i]-GCONST(.5f));
+            maskLogE[2][i] = logSum(maskLogE[2][i], bandLogE[21*c+i]-GCONST(.5f));
+         }
+      }
+#if 0
+      for (i=0;i<21;i++)
+         printf("%f ", bandLogE[21*c+i]);
+      float sum=0;
+      for (i=0;i<21;i++)
+         sum += bandLogE[21*c+i];
+      printf("%f ", sum/21);
+#endif
+      OPUS_COPY(mem+c*overlap, in+frame_size, overlap);
+   }
+   for (i=0;i<21;i++)
+      maskLogE[1][i] = MIN32(maskLogE[0][i],maskLogE[2][i]);
+   channel_offset = HALF16(celt_log2(QCONST32(2.f,14)/(channels-1)));
+   for (c=0;c<3;c++)
+      for (i=0;i<21;i++)
+         maskLogE[c][i] += channel_offset;
+#if 0
+   for (c=0;c<3;c++)
+   {
+      for (i=0;i<21;i++)
+         printf("%f ", maskLogE[c][i]);
+   }
+#endif
+   for (c=0;c<channels;c++)
+   {
+      celt_glog *mask;
+      if (pos[c]!=0)
+      {
+         mask = &maskLogE[pos[c]-1][0];
+         for (i=0;i<21;i++)
+            bandLogE[21*c+i] = bandLogE[21*c+i] - mask[i];
+      } else {
+         for (i=0;i<21;i++)
+            bandLogE[21*c+i] = 0;
+      }
+#if 0
+      for (i=0;i<21;i++)
+         printf("%f ", bandLogE[21*c+i]);
+      printf("\n");
+#endif
+#if 0
+      float sum=0;
+      for (i=0;i<21;i++)
+         sum += bandLogE[21*c+i];
+      printf("%f ", sum/(float)QCONST32(21.f, DB_SHIFT));
+      printf("\n");
+#endif
+   }
+   RESTORE_STACK;
+}
+
+opus_int32 opus_multistream_encoder_get_size(int nb_streams, int nb_coupled_streams)
+{
+   int coupled_size;
+   int mono_size;
+
+   if(nb_streams<1||nb_coupled_streams>nb_streams||nb_coupled_streams<0)return 0;
+   coupled_size = opus_encoder_get_size(2);
+   mono_size = opus_encoder_get_size(1);
+   return align(sizeof(OpusMSEncoder))
+        + nb_coupled_streams * align(coupled_size)
+        + (nb_streams-nb_coupled_streams) * align(mono_size);
+}
+
+opus_int32 opus_multistream_surround_encoder_get_size(int channels, int mapping_family)
+{
+   int nb_streams;
+   int nb_coupled_streams;
+   opus_int32 size;
+
+   if (mapping_family==0)
+   {
+      if (channels==1)
+      {
+         nb_streams=1;
+         nb_coupled_streams=0;
+      } else if (channels==2)
+      {
+         nb_streams=1;
+         nb_coupled_streams=1;
+      } else
+         return 0;
+   } else if (mapping_family==1 && channels<=8 && channels>=1)
+   {
+      nb_streams=vorbis_mappings[channels-1].nb_streams;
+      nb_coupled_streams=vorbis_mappings[channels-1].nb_coupled_streams;
+   } else if (mapping_family==255)
+   {
+      nb_streams=channels;
+      nb_coupled_streams=0;
+   } else if (mapping_family==2)
+   {
+      if (!validate_ambisonics(channels, &nb_streams, &nb_coupled_streams))
+         return 0;
+   } else
+      return 0;
+   size = opus_multistream_encoder_get_size(nb_streams, nb_coupled_streams);
+   if (channels>2)
+   {
+      size += channels*(MAX_OVERLAP*sizeof(opus_val32) + sizeof(opus_val32));
+   }
+   return size;
+}
+
+static int opus_multistream_encoder_init_impl(
+      OpusMSEncoder *st,
+      opus_int32 Fs,
+      int channels,
+      int streams,
+      int coupled_streams,
+      const unsigned char *mapping,
+      int application,
+      MappingType mapping_type
+)
+{
+   int coupled_size;
+   int mono_size;
+   int i, ret;
+   char *ptr;
+
+   if ((channels>255) || (channels<1) || (coupled_streams>streams) ||
+       (streams<1) || (coupled_streams<0) || (streams>255-coupled_streams) ||
+       (streams+coupled_streams>channels))
+      return OPUS_BAD_ARG;
+
+   coupled_size = opus_encoder_init(NULL, Fs, 2, application);
+   if (coupled_size < 0)
+      return coupled_size;
+   mono_size = opus_encoder_init(NULL, Fs, 1, application);
+   if (mono_size < 0)
+      return mono_size;
+   if (st == NULL)
+   {
+      int surround_size=0;
+      if (mapping_type == MAPPING_TYPE_SURROUND) {
+         surround_size = channels*(MAX_OVERLAP*sizeof(opus_val32) + sizeof(opus_val32));
+      }
+
+      return align(sizeof(OpusMSEncoder)) + coupled_streams*align(coupled_size)
+             + (streams-coupled_streams)*align(mono_size) + surround_size;
+   }
+
+   st->arch = opus_select_arch();
+   st->layout.nb_channels = channels;
+   st->layout.nb_streams = streams;
+   st->layout.nb_coupled_streams = coupled_streams;
+   if (mapping_type != MAPPING_TYPE_SURROUND)
+      st->lfe_stream = -1;
+   st->bitrate_bps = OPUS_AUTO;
+   st->application = application;
+   st->Fs = Fs;
+   st->variable_duration = OPUS_FRAMESIZE_ARG;
+   for (i=0;i<st->layout.nb_channels;i++)
+      st->layout.mapping[i] = mapping[i];
+   if (!validate_layout(&st->layout))
+      return OPUS_BAD_ARG;
+   if (!validate_encoder_layout(&st->layout))
+      return OPUS_BAD_ARG;
+   if (mapping_type == MAPPING_TYPE_AMBISONICS &&
+       !validate_ambisonics(st->layout.nb_channels, NULL, NULL))
+      return OPUS_BAD_ARG;
+   ptr = (char*)st + align(sizeof(OpusMSEncoder));
+   for (i=0;i<st->layout.nb_coupled_streams;i++)
+   {
+      ret = opus_encoder_init((OpusEncoder*)ptr, Fs, 2, application);
+      if(ret!=OPUS_OK)return ret;
+      if (i==st->lfe_stream)
+         opus_encoder_ctl((OpusEncoder*)ptr, OPUS_SET_LFE(1));
+      ptr += align(coupled_size);
+   }
+   for (;i<st->layout.nb_streams;i++)
+   {
+      ret = opus_encoder_init((OpusEncoder*)ptr, Fs, 1, application);
+      if(ret!=OPUS_OK)return ret;
+      if (i==st->lfe_stream)
+         opus_encoder_ctl((OpusEncoder*)ptr, OPUS_SET_LFE(1));
+      ptr += align(mono_size);
+   }
+   if (mapping_type == MAPPING_TYPE_SURROUND)
+   {
+      OPUS_CLEAR(ms_get_preemph_mem(st), channels);
+      OPUS_CLEAR(ms_get_window_mem(st), channels*MAX_OVERLAP);
+   }
+   st->mapping_type = mapping_type;
+   return OPUS_OK;
+}
+
+int opus_multistream_encoder_init(
+      OpusMSEncoder *st,
+      opus_int32 Fs,
+      int channels,
+      int streams,
+      int coupled_streams,
+      const unsigned char *mapping,
+      int application
+)
+{
+   return opus_multistream_encoder_init_impl(st, Fs, channels, streams,
+                                             coupled_streams, mapping,
+                                             application, MAPPING_TYPE_NONE);
+}
+
+int opus_multistream_surround_encoder_init(
+      OpusMSEncoder *st,
+      opus_int32 Fs,
+      int channels,
+      int mapping_family,
+      int *streams,
+      int *coupled_streams,
+      unsigned char *mapping,
+      int application
+)
+{
+   MappingType mapping_type;
+   int lfe_stream;
+
+   if ((channels>255) || (channels<1))
+      return OPUS_BAD_ARG;
+   lfe_stream = -1;
+   if (mapping_family==0)
+   {
+      if (channels==1)
+      {
+         *streams=1;
+         *coupled_streams=0;
+         mapping[0]=0;
+      } else if (channels==2)
+      {
+         *streams=1;
+         *coupled_streams=1;
+         mapping[0]=0;
+         mapping[1]=1;
+      } else
+         return OPUS_UNIMPLEMENTED;
+   } else if (mapping_family==1 && channels<=8 && channels>=1)
+   {
+      int i;
+      *streams=vorbis_mappings[channels-1].nb_streams;
+      *coupled_streams=vorbis_mappings[channels-1].nb_coupled_streams;
+      for (i=0;i<channels;i++)
+         mapping[i] = vorbis_mappings[channels-1].mapping[i];
+      if (channels>=6)
+         lfe_stream = *streams-1;
+   } else if (mapping_family==255)
+   {
+      int i;
+      *streams=channels;
+      *coupled_streams=0;
+      for(i=0;i<channels;i++)
+         mapping[i] = i;
+   } else if (mapping_family==2)
+   {
+      int i;
+      if (!validate_ambisonics(channels, streams, coupled_streams))
+         return OPUS_BAD_ARG;
+      for(i = 0; i < (*streams - *coupled_streams); i++)
+         mapping[i] = i + (*coupled_streams * 2);
+      for(i = 0; i < *coupled_streams * 2; i++)
+         mapping[i + (*streams - *coupled_streams)] = i;
+   } else
+      return OPUS_UNIMPLEMENTED;
+
+   if (channels>2 && mapping_family==1) {
+      mapping_type = MAPPING_TYPE_SURROUND;
+   } else if (mapping_family==2)
+   {
+      mapping_type = MAPPING_TYPE_AMBISONICS;
+   } else
+   {
+      mapping_type = MAPPING_TYPE_NONE;
+   }
+   if (st != NULL) {
+      st->lfe_stream = lfe_stream;
+   }
+   return opus_multistream_encoder_init_impl(st, Fs, channels, *streams,
+                                             *coupled_streams, mapping,
+                                             application, mapping_type);
+}
+
+OpusMSEncoder *opus_multistream_encoder_create(
+      opus_int32 Fs,
+      int channels,
+      int streams,
+      int coupled_streams,
+      const unsigned char *mapping,
+      int application,
+      int *error
+)
+{
+   int ret;
+   OpusMSEncoder *st;
+   opus_int32 size;
+   if ((channels>255) || (channels<1) || (coupled_streams>streams) ||
+       (streams<1) || (coupled_streams<0) || (streams>255-coupled_streams) ||
+       (streams+coupled_streams>channels))
+   {
+      if (error)
+         *error = OPUS_BAD_ARG;
+      return NULL;
+   }
+   size = opus_multistream_encoder_init(NULL, Fs, channels, streams, coupled_streams, mapping, application);
+   if (size < 0)
+   {
+      if (error)
+         *error = size;
+      return NULL;
+   }
+   st = (OpusMSEncoder *)opus_alloc(size);
+   if (st==NULL)
+   {
+      if (error)
+         *error = OPUS_ALLOC_FAIL;
+      return NULL;
+   }
+   ret = opus_multistream_encoder_init(st, Fs, channels, streams, coupled_streams, mapping, application);
+   if (ret != OPUS_OK)
+   {
+      opus_free(st);
+      st = NULL;
+   }
+   if (error)
+      *error = ret;
+   return st;
+}
+
+OpusMSEncoder *opus_multistream_surround_encoder_create(
+      opus_int32 Fs,
+      int channels,
+      int mapping_family,
+      int *streams,
+      int *coupled_streams,
+      unsigned char *mapping,
+      int application,
+      int *error
+)
+{
+   int ret;
+   opus_int32 size;
+   OpusMSEncoder *st;
+   if ((channels>255) || (channels<1))
+   {
+      if (error)
+         *error = OPUS_BAD_ARG;
+      return NULL;
+   }
+   size = opus_multistream_surround_encoder_init(NULL, Fs, channels, mapping_family, streams, coupled_streams, mapping, application);
+   if (size < 0)
+   {
+      if (error)
+         *error = size;
+      return NULL;
+   }
+   st = (OpusMSEncoder *)opus_alloc(size);
+   if (st==NULL)
+   {
+      if (error)
+         *error = OPUS_ALLOC_FAIL;
+      return NULL;
+   }
+   ret = opus_multistream_surround_encoder_init(st, Fs, channels, mapping_family, streams, coupled_streams, mapping, application);
+   if (ret != OPUS_OK)
+   {
+      opus_free(st);
+      st = NULL;
+   }
+   if (error)
+      *error = ret;
+   return st;
+}
+
+static void surround_rate_allocation(
+      OpusMSEncoder *st,
+      opus_int32 *rate,
+      int frame_size,
+      opus_int32 Fs
+      )
+{
+   int i;
+   opus_int32 channel_rate;
+   int stream_offset;
+   int lfe_offset;
+   int coupled_ratio; /* Q8 */
+   int lfe_ratio;     /* Q8 */
+   int nb_lfe;
+   int nb_uncoupled;
+   int nb_coupled;
+   int nb_normal;
+   opus_int32 channel_offset;
+   opus_int32 bitrate;
+   int total;
+
+   nb_lfe = (st->lfe_stream!=-1);
+   nb_coupled = st->layout.nb_coupled_streams;
+   nb_uncoupled = st->layout.nb_streams-nb_coupled-nb_lfe;
+   nb_normal = 2*nb_coupled + nb_uncoupled;
+
+   /* Give each non-LFE channel enough bits per channel for coding band energy. */
+   channel_offset = 40*IMAX(50, Fs/frame_size);
+
+   if (st->bitrate_bps==OPUS_AUTO)
+   {
+      bitrate = nb_normal*(channel_offset + Fs + 10000) + 8000*nb_lfe;
+   } else if (st->bitrate_bps==OPUS_BITRATE_MAX)
+   {
+      bitrate = nb_normal*750000 + nb_lfe*128000;
+   } else {
+      bitrate = st->bitrate_bps;
+   }
+
+   /* Give LFE some basic stream_channel allocation but never exceed 1/20 of the
+      total rate for the non-energy part to avoid problems at really low rate. */
+   lfe_offset = IMIN(bitrate/20, 3000) + 15*IMAX(50, Fs/frame_size);
+
+   /* We give each stream (coupled or uncoupled) a starting bitrate.
+      This models the main saving of coupled channels over uncoupled. */
+   stream_offset = (bitrate - channel_offset*nb_normal - lfe_offset*nb_lfe)/nb_normal/2;
+   stream_offset = IMAX(0, IMIN(20000, stream_offset));
+
+   /* Coupled streams get twice the mono rate after the offset is allocated. */
+   coupled_ratio = 512;
+   /* Should depend on the bitrate, for now we assume LFE gets 1/8 the bits of mono */
+   lfe_ratio = 32;
+
+   total = (nb_uncoupled<<8)         /* mono */
+         + coupled_ratio*nb_coupled /* stereo */
+         + nb_lfe*lfe_ratio;
+   channel_rate = 256*(opus_int64)(bitrate - lfe_offset*nb_lfe - stream_offset*(nb_coupled+nb_uncoupled) - channel_offset*nb_normal)/total;
+
+   for (i=0;i<st->layout.nb_streams;i++)
+   {
+      if (i<st->layout.nb_coupled_streams)
+         rate[i] = 2*channel_offset + IMAX(0, stream_offset+(channel_rate*coupled_ratio>>8));
+      else if (i!=st->lfe_stream)
+         rate[i] = channel_offset + IMAX(0, stream_offset + channel_rate);
+      else
+         rate[i] = IMAX(0, lfe_offset+(channel_rate*lfe_ratio>>8));
+   }
+}
+
+static void ambisonics_rate_allocation(
+      OpusMSEncoder *st,
+      opus_int32 *rate,
+      int frame_size,
+      opus_int32 Fs
+      )
+{
+   int i;
+   opus_int32 total_rate;
+   opus_int32 per_stream_rate;
+
+   const int nb_channels = st->layout.nb_streams + st->layout.nb_coupled_streams;
+
+   if (st->bitrate_bps==OPUS_AUTO)
+   {
+      total_rate = (st->layout.nb_coupled_streams + st->layout.nb_streams) *
+         (Fs+60*Fs/frame_size) + st->layout.nb_streams * (opus_int32)15000;
+   } else if (st->bitrate_bps==OPUS_BITRATE_MAX)
+   {
+      total_rate = nb_channels * 750000;
+   } else
+   {
+      total_rate = st->bitrate_bps;
+   }
+
+   /* Allocate equal number of bits to Ambisonic (uncoupled) and non-diegetic
+    * (coupled) streams */
+   per_stream_rate = total_rate / st->layout.nb_streams;
+   for (i = 0; i < st->layout.nb_streams; i++)
+   {
+     rate[i] = per_stream_rate;
+   }
+}
+
+static opus_int32 rate_allocation(
+      OpusMSEncoder *st,
+      opus_int32 *rate,
+      int frame_size
+      )
+{
+   int i;
+   opus_int32 rate_sum=0;
+   opus_int32 Fs;
+   char *ptr;
+
+   ptr = (char*)st + align(sizeof(OpusMSEncoder));
+   opus_encoder_ctl((OpusEncoder*)ptr, OPUS_GET_SAMPLE_RATE(&Fs));
+
+   if (st->mapping_type == MAPPING_TYPE_AMBISONICS) {
+     ambisonics_rate_allocation(st, rate, frame_size, Fs);
+   } else
+   {
+     surround_rate_allocation(st, rate, frame_size, Fs);
+   }
+
+   for (i=0;i<st->layout.nb_streams;i++)
+   {
+      rate[i] = IMAX(rate[i], 500);
+      rate_sum += rate[i];
+   }
+   return rate_sum;
+}
+
+/* Max size in case the encoder decides to return six frames (6 x 20 ms = 120 ms) */
+#ifdef ENABLE_QEXT
+#define MS_FRAME_TMP (6*QEXT_PACKET_SIZE_CAP+12)
+#else
+#define MS_FRAME_TMP (6*1275+12)
+#endif
+
+int opus_multistream_encode_native
+(
+    OpusMSEncoder *st,
+    opus_copy_channel_in_func copy_channel_in,
+    const void *pcm,
+    int analysis_frame_size,
+    unsigned char *data,
+    opus_int32 max_data_bytes,
+    int lsb_depth,
+    downmix_func downmix,
+    int float_api,
+    void *user_data
+)
+{
+   opus_int32 Fs;
+   int coupled_size;
+   int mono_size;
+   int s;
+   char *ptr;
+   int tot_size;
+   VARDECL(opus_res, buf);
+   VARDECL(celt_glog, bandSMR);
+   unsigned char tmp_data[MS_FRAME_TMP];
+   OpusRepacketizer rp;
+   opus_int32 vbr;
+   const CELTMode *celt_mode=NULL;
+   opus_int32 bitrates[256];
+   celt_glog bandLogE[42];
+   opus_val32 *mem = NULL;
+   opus_val32 *preemph_mem=NULL;
+   int frame_size;
+   opus_int32 rate_sum;
+   opus_int32 smallest_packet;
+   ALLOC_STACK;
+
+   if (st->mapping_type == MAPPING_TYPE_SURROUND)
+   {
+      preemph_mem = ms_get_preemph_mem(st);
+      mem = ms_get_window_mem(st);
+   }
+
+   ptr = (char*)st + align(sizeof(OpusMSEncoder));
+   opus_encoder_ctl((OpusEncoder*)ptr, OPUS_GET_SAMPLE_RATE(&Fs));
+   opus_encoder_ctl((OpusEncoder*)ptr, OPUS_GET_VBR(&vbr));
+   if (st->application != OPUS_APPLICATION_RESTRICTED_SILK)
+      opus_encoder_ctl((OpusEncoder*)ptr, CELT_GET_MODE(&celt_mode));
+
+   frame_size = frame_size_select(st->application, analysis_frame_size, st->variable_duration, Fs);
+   if (frame_size <= 0)
+   {
+      RESTORE_STACK;
+      return OPUS_BAD_ARG;
+   }
+
+   /* Smallest packet the encoder can produce. */
+   smallest_packet = st->layout.nb_streams*2-1;
+   /* 100 ms needs an extra byte per stream for the ToC. */
+   if (Fs/frame_size == 10)
+     smallest_packet += st->layout.nb_streams;
+   if (max_data_bytes < smallest_packet)
+   {
+      RESTORE_STACK;
+      return OPUS_BUFFER_TOO_SMALL;
+   }
+   ALLOC(buf, 2*frame_size, opus_res);
+   coupled_size = opus_encoder_init(NULL, st->Fs, 2, st->application);
+   mono_size = opus_encoder_init(NULL, st->Fs, 1, st->application);
+
+   ALLOC(bandSMR, 21*st->layout.nb_channels, celt_glog);
+   if (st->mapping_type == MAPPING_TYPE_SURROUND && st->application != OPUS_APPLICATION_RESTRICTED_SILK)
+   {
+      surround_analysis(celt_mode, pcm, bandSMR, mem, preemph_mem, frame_size, celt_mode->overlap, st->layout.nb_channels, Fs, copy_channel_in, st->arch);
+   }
+
+   /* Compute bitrate allocation between streams (this could be a lot better) */
+   rate_sum = rate_allocation(st, bitrates, frame_size);
+
+   if (!vbr)
+   {
+      if (st->bitrate_bps == OPUS_AUTO)
+      {
+         max_data_bytes = IMIN(max_data_bytes, (bitrate_to_bits(rate_sum, Fs, frame_size)+4)/8);
+      } else if (st->bitrate_bps != OPUS_BITRATE_MAX)
+      {
+         max_data_bytes = IMIN(max_data_bytes, IMAX(smallest_packet,
+                          (bitrate_to_bits(st->bitrate_bps, Fs, frame_size)+4)/8));
+      }
+   }
+   ptr = (char*)st + align(sizeof(OpusMSEncoder));
+   for (s=0;s<st->layout.nb_streams;s++)
+   {
+      OpusEncoder *enc;
+      enc = (OpusEncoder*)ptr;
+      if (s < st->layout.nb_coupled_streams)
+         ptr += align(coupled_size);
+      else
+         ptr += align(mono_size);
+      opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrates[s]));
+      if (st->mapping_type == MAPPING_TYPE_SURROUND)
+      {
+         opus_int32 equiv_rate;
+         equiv_rate = st->bitrate_bps;
+         if (frame_size*50 < Fs)
+            equiv_rate -= 60*(Fs/frame_size - 50)*st->layout.nb_channels;
+         if (equiv_rate > 10000*st->layout.nb_channels)
+            opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND));
+         else if (equiv_rate > 7000*st->layout.nb_channels)
+            opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_SUPERWIDEBAND));
+         else if (equiv_rate > 5000*st->layout.nb_channels)
+            opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_WIDEBAND));
+         else
+            opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND));
+         if (s < st->layout.nb_coupled_streams)
+         {
+            /* To preserve the spatial image, force stereo CELT on coupled streams */
+            opus_encoder_ctl(enc, OPUS_SET_FORCE_MODE(MODE_CELT_ONLY));
+            opus_encoder_ctl(enc, OPUS_SET_FORCE_CHANNELS(2));
+         }
+      }
+      else if (st->mapping_type == MAPPING_TYPE_AMBISONICS) {
+        opus_encoder_ctl(enc, OPUS_SET_FORCE_MODE(MODE_CELT_ONLY));
+      }
+   }
+
+   ptr = (char*)st + align(sizeof(OpusMSEncoder));
+   /* Counting ToC */
+   tot_size = 0;
+   for (s=0;s<st->layout.nb_streams;s++)
+   {
+      OpusEncoder *enc;
+      int len;
+      int curr_max;
+      int c1, c2;
+      int ret;
+
+      opus_repacketizer_init(&rp);
+      enc = (OpusEncoder*)ptr;
+      if (s < st->layout.nb_coupled_streams)
+      {
+         int i;
+         int left, right;
+         left = get_left_channel(&st->layout, s, -1);
+         right = get_right_channel(&st->layout, s, -1);
+         (*copy_channel_in)(buf, 2,
+            pcm, st->layout.nb_channels, left, frame_size, user_data);
+         (*copy_channel_in)(buf+1, 2,
+            pcm, st->layout.nb_channels, right, frame_size, user_data);
+         ptr += align(coupled_size);
+         if (st->mapping_type == MAPPING_TYPE_SURROUND && st->application != OPUS_APPLICATION_RESTRICTED_SILK)
+         {
+            for (i=0;i<21;i++)
+            {
+               bandLogE[i] = bandSMR[21*left+i];
+               bandLogE[21+i] = bandSMR[21*right+i];
+            }
+         }
+         c1 = left;
+         c2 = right;
+      } else {
+         int i;
+         int chan = get_mono_channel(&st->layout, s, -1);
+         (*copy_channel_in)(buf, 1,
+            pcm, st->layout.nb_channels, chan, frame_size, user_data);
+         ptr += align(mono_size);
+         if (st->mapping_type == MAPPING_TYPE_SURROUND && st->application != OPUS_APPLICATION_RESTRICTED_SILK)
+         {
+            for (i=0;i<21;i++)
+               bandLogE[i] = bandSMR[21*chan+i];
+         }
+         c1 = chan;
+         c2 = -1;
+      }
+      if (st->mapping_type == MAPPING_TYPE_SURROUND && st->application != OPUS_APPLICATION_RESTRICTED_SILK)
+         opus_encoder_ctl(enc, OPUS_SET_ENERGY_MASK(bandLogE));
+      /* number of bytes left (+Toc) */
+      curr_max = max_data_bytes - tot_size;
+      /* Reserve one byte for the last stream and two for the others */
+      curr_max -= IMAX(0,2*(st->layout.nb_streams-s-1)-1);
+      /* For 100 ms, reserve an extra byte per stream for the ToC */
+      if (Fs/frame_size == 10)
+        curr_max -= st->layout.nb_streams-s-1;
+      curr_max = IMIN(curr_max,MS_FRAME_TMP);
+      /* Repacketizer will add one or two bytes for self-delimited frames */
+      if (s != st->layout.nb_streams-1) curr_max -=  curr_max>253 ? 2 : 1;
+      if (!vbr && s == st->layout.nb_streams-1)
+         opus_encoder_ctl(enc, OPUS_SET_BITRATE(bits_to_bitrate(curr_max*8, Fs, frame_size)));
+      len = opus_encode_native(enc, buf, frame_size, tmp_data, curr_max, lsb_depth,
+            pcm, analysis_frame_size, c1, c2, st->layout.nb_channels, downmix, float_api);
+      if (len<0)
+      {
+         RESTORE_STACK;
+         return len;
+      }
+      /* We need to use the repacketizer to add the self-delimiting lengths
+         while taking into account the fact that the encoder can now return
+         more than one frame at a time (e.g. 60 ms CELT-only) */
+      ret = opus_repacketizer_cat(&rp, tmp_data, len);
+      /* If the opus_repacketizer_cat() fails, then something's seriously wrong
+         with the encoder. */
+      if (ret != OPUS_OK)
+      {
+         RESTORE_STACK;
+         return OPUS_INTERNAL_ERROR;
+      }
+      len = opus_repacketizer_out_range_impl(&rp, 0, opus_repacketizer_get_nb_frames(&rp),
+            data, max_data_bytes-tot_size, s != st->layout.nb_streams-1, !vbr && s == st->layout.nb_streams-1, NULL, 0);
+      data += len;
+      tot_size += len;
+   }
+   /*printf("\n");*/
+   RESTORE_STACK;
+   return tot_size;
+}
+
+#if !defined(DISABLE_FLOAT_API)
+static void opus_copy_channel_in_float(
+  opus_res *dst,
+  int dst_stride,
+  const void *src,
+  int src_stride,
+  int src_channel,
+  int frame_size,
+  void *user_data
+)
+{
+   const float *float_src;
+   opus_int32 i;
+   (void)user_data;
+   float_src = (const float *)src;
+   for (i=0;i<frame_size;i++)
+      dst[i*dst_stride] = FLOAT2RES(float_src[i*src_stride+src_channel]);
+}
+#endif
+
+static void opus_copy_channel_in_short(
+  opus_res *dst,
+  int dst_stride,
+  const void *src,
+  int src_stride,
+  int src_channel,
+  int frame_size,
+  void *user_data
+)
+{
+   const opus_int16 *short_src;
+   opus_int32 i;
+   (void)user_data;
+   short_src = (const opus_int16 *)src;
+   for (i=0;i<frame_size;i++)
+      dst[i*dst_stride] = INT16TORES(short_src[i*src_stride+src_channel]);
+}
+
+static void opus_copy_channel_in_int24(
+  opus_res *dst,
+  int dst_stride,
+  const void *src,
+  int src_stride,
+  int src_channel,
+  int frame_size,
+  void *user_data
+)
+{
+   const opus_int32 *short_src;
+   opus_int32 i;
+   (void)user_data;
+   short_src = (const opus_int32 *)src;
+   for (i=0;i<frame_size;i++)
+      dst[i*dst_stride] = INT24TORES(short_src[i*src_stride+src_channel]);
+}
+
+int opus_multistream_encode(
+    OpusMSEncoder *st,
+    const opus_int16 *pcm,
+    int frame_size,
+    unsigned char *data,
+    opus_int32 max_data_bytes
+)
+{
+   return opus_multistream_encode_native(st, opus_copy_channel_in_short,
+      pcm, frame_size, data, max_data_bytes, 16, downmix_int, 0, NULL);
+}
+
+int opus_multistream_encode24(
+    OpusMSEncoder *st,
+    const opus_int32 *pcm,
+    int frame_size,
+    unsigned char *data,
+    opus_int32 max_data_bytes
+)
+{
+   return opus_multistream_encode_native(st, opus_copy_channel_in_int24,
+      pcm, frame_size, data, max_data_bytes, MAX_ENCODING_DEPTH, downmix_int24, 0, NULL);
+}
+
+#ifndef DISABLE_FLOAT_API
+int opus_multistream_encode_float(
+    OpusMSEncoder *st,
+    const float *pcm,
+    int frame_size,
+    unsigned char *data,
+    opus_int32 max_data_bytes
+)
+{
+   return opus_multistream_encode_native(st, opus_copy_channel_in_float,
+      pcm, frame_size, data, max_data_bytes, MAX_ENCODING_DEPTH, downmix_float, 1, NULL);
+}
+#endif
+
+int opus_multistream_encoder_ctl_va_list(OpusMSEncoder *st, int request,
+                                         va_list ap)
+{
+   int coupled_size, mono_size;
+   char *ptr;
+   int ret = OPUS_OK;
+
+   coupled_size = opus_encoder_init(NULL, st->Fs, 2, st->application);
+   mono_size = opus_encoder_init(NULL, st->Fs, 1, st->application);
+   ptr = (char*)st + align(sizeof(OpusMSEncoder));
+   switch (request)
+   {
+   case OPUS_SET_BITRATE_REQUEST:
+   {
+      opus_int32 value = va_arg(ap, opus_int32);
+      if (value != OPUS_AUTO && value != OPUS_BITRATE_MAX)
+      {
+         if (value <= 0)
+            goto bad_arg;
+         value = IMIN(750000*st->layout.nb_channels, IMAX(500*st->layout.nb_channels, value));
+      }
+      st->bitrate_bps = value;
+   }
+   break;
+   case OPUS_GET_BITRATE_REQUEST:
+   {
+      int s;
+      opus_int32 *value = va_arg(ap, opus_int32*);
+      if (!value)
+      {
+         goto bad_arg;
+      }
+      *value = 0;
+      for (s=0;s<st->layout.nb_streams;s++)
+      {
+         opus_int32 rate;
+         OpusEncoder *enc;
+         enc = (OpusEncoder*)ptr;
+         if (s < st->layout.nb_coupled_streams)
+            ptr += align(coupled_size);
+         else
+            ptr += align(mono_size);
+         opus_encoder_ctl(enc, request, &rate);
+         *value += rate;
+      }
+   }
+   break;
+   case OPUS_GET_LSB_DEPTH_REQUEST:
+   case OPUS_GET_VBR_REQUEST:
+   case OPUS_GET_APPLICATION_REQUEST:
+   case OPUS_GET_BANDWIDTH_REQUEST:
+   case OPUS_GET_COMPLEXITY_REQUEST:
+   case OPUS_GET_PACKET_LOSS_PERC_REQUEST:
+   case OPUS_GET_DTX_REQUEST:
+   case OPUS_GET_VOICE_RATIO_REQUEST:
+   case OPUS_GET_VBR_CONSTRAINT_REQUEST:
+   case OPUS_GET_SIGNAL_REQUEST:
+   case OPUS_GET_LOOKAHEAD_REQUEST:
+   case OPUS_GET_SAMPLE_RATE_REQUEST:
+   case OPUS_GET_INBAND_FEC_REQUEST:
+   case OPUS_GET_FORCE_CHANNELS_REQUEST:
+   case OPUS_GET_PREDICTION_DISABLED_REQUEST:
+   case OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST:
+   case OPUS_GET_QEXT_REQUEST:
+   {
+      OpusEncoder *enc;
+      /* For int32* GET params, just query the first stream */
+      opus_int32 *value = va_arg(ap, opus_int32*);
+      enc = (OpusEncoder*)ptr;
+      ret = opus_encoder_ctl(enc, request, value);
+   }
+   break;
+   case OPUS_GET_FINAL_RANGE_REQUEST:
+   {
+      int s;
+      opus_uint32 *value = va_arg(ap, opus_uint32*);
+      opus_uint32 tmp;
+      if (!value)
+      {
+         goto bad_arg;
+      }
+      *value=0;
+      for (s=0;s<st->layout.nb_streams;s++)
+      {
+         OpusEncoder *enc;
+         enc = (OpusEncoder*)ptr;
+         if (s < st->layout.nb_coupled_streams)
+            ptr += align(coupled_size);
+         else
+            ptr += align(mono_size);
+         ret = opus_encoder_ctl(enc, request, &tmp);
+         if (ret != OPUS_OK) break;
+         *value ^= tmp;
+      }
+   }
+   break;
+   case OPUS_SET_LSB_DEPTH_REQUEST:
+   case OPUS_SET_COMPLEXITY_REQUEST:
+   case OPUS_SET_VBR_REQUEST:
+   case OPUS_SET_VBR_CONSTRAINT_REQUEST:
+   case OPUS_SET_MAX_BANDWIDTH_REQUEST:
+   case OPUS_SET_BANDWIDTH_REQUEST:
+   case OPUS_SET_SIGNAL_REQUEST:
+   case OPUS_SET_APPLICATION_REQUEST:
+   case OPUS_SET_INBAND_FEC_REQUEST:
+   case OPUS_SET_PACKET_LOSS_PERC_REQUEST:
+   case OPUS_SET_DTX_REQUEST:
+   case OPUS_SET_FORCE_MODE_REQUEST:
+   case OPUS_SET_FORCE_CHANNELS_REQUEST:
+   case OPUS_SET_PREDICTION_DISABLED_REQUEST:
+   case OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST:
+   case OPUS_SET_QEXT_REQUEST:
+   {
+      int s;
+      /* This works for int32 params */
+      opus_int32 value = va_arg(ap, opus_int32);
+      for (s=0;s<st->layout.nb_streams;s++)
+      {
+         OpusEncoder *enc;
+
+         enc = (OpusEncoder*)ptr;
+         if (s < st->layout.nb_coupled_streams)
+            ptr += align(coupled_size);
+         else
+            ptr += align(mono_size);
+         ret = opus_encoder_ctl(enc, request, value);
+         if (ret != OPUS_OK)
+            break;
+      }
+   }
+   break;
+   case OPUS_MULTISTREAM_GET_ENCODER_STATE_REQUEST:
+   {
+      int s;
+      opus_int32 stream_id;
+      OpusEncoder **value;
+      stream_id = va_arg(ap, opus_int32);
+      if (stream_id<0 || stream_id >= st->layout.nb_streams)
+         goto bad_arg;
+      value = va_arg(ap, OpusEncoder**);
+      if (!value)
+      {
+         goto bad_arg;
+      }
+      for (s=0;s<stream_id;s++)
+      {
+         if (s < st->layout.nb_coupled_streams)
+            ptr += align(coupled_size);
+         else
+            ptr += align(mono_size);
+      }
+      *value = (OpusEncoder*)ptr;
+   }
+   break;
+   case OPUS_SET_EXPERT_FRAME_DURATION_REQUEST:
+   {
+       opus_int32 value = va_arg(ap, opus_int32);
+       st->variable_duration = value;
+   }
+   break;
+   case OPUS_GET_EXPERT_FRAME_DURATION_REQUEST:
+   {
+       opus_int32 *value = va_arg(ap, opus_int32*);
+       if (!value)
+       {
+          goto bad_arg;
+       }
+       *value = st->variable_duration;
+   }
+   break;
+   case OPUS_RESET_STATE:
+   {
+      int s;
+      if (st->mapping_type == MAPPING_TYPE_SURROUND)
+      {
+         OPUS_CLEAR(ms_get_preemph_mem(st), st->layout.nb_channels);
+         OPUS_CLEAR(ms_get_window_mem(st), st->layout.nb_channels*MAX_OVERLAP);
+      }
+      for (s=0;s<st->layout.nb_streams;s++)
+      {
+         OpusEncoder *enc;
+         enc = (OpusEncoder*)ptr;
+         if (s < st->layout.nb_coupled_streams)
+            ptr += align(coupled_size);
+         else
+            ptr += align(mono_size);
+         ret = opus_encoder_ctl(enc, OPUS_RESET_STATE);
+         if (ret != OPUS_OK)
+            break;
+      }
+   }
+   break;
+   default:
+      ret = OPUS_UNIMPLEMENTED;
+      break;
+   }
+   return ret;
+bad_arg:
+   return OPUS_BAD_ARG;
+}
+
+int opus_multistream_encoder_ctl(OpusMSEncoder *st, int request, ...)
+{
+   int ret;
+   va_list ap;
+   va_start(ap, request);
+   ret = opus_multistream_encoder_ctl_va_list(st, request, ap);
+   va_end(ap);
+   return ret;
+}
+
+void opus_multistream_encoder_destroy(OpusMSEncoder *st)
+{
+    opus_free(st);
+}
diff --git a/code/sdk/zhihui/test_opus_frame_to_pcm/src_lib/opus_projection_encoder.c b/code/sdk/zhihui/test_opus_frame_to_pcm/src_lib/opus_projection_encoder.c
new file mode 100644 (file)
index 0000000..9b03900
--- /dev/null
@@ -0,0 +1,521 @@
+/* Copyright (c) 2017 Google Inc.
+   Written by Andrew Allen */
+/*
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+
+   - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+   - Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+   OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "mathops.h"
+#include "os_support.h"
+#include "opus_private.h"
+#include "opus_defines.h"
+#include "opus_projection.h"
+#include "opus_multistream.h"
+#include "stack_alloc.h"
+#include "mapping_matrix.h"
+
+struct OpusProjectionEncoder
+{
+  opus_int32 mixing_matrix_size_in_bytes;
+  opus_int32 demixing_matrix_size_in_bytes;
+  /* Encoder states go here */
+};
+
+#if !defined(DISABLE_FLOAT_API)
+static void opus_projection_copy_channel_in_float(
+  opus_res *dst,
+  int dst_stride,
+  const void *src,
+  int src_stride,
+  int src_channel,
+  int frame_size,
+  void *user_data
+)
+{
+  mapping_matrix_multiply_channel_in_float((const MappingMatrix*)user_data,
+    (const float*)src, src_stride, dst, src_channel, dst_stride, frame_size);
+}
+#endif
+
+static void opus_projection_copy_channel_in_short(
+  opus_res *dst,
+  int dst_stride,
+  const void *src,
+  int src_stride,
+  int src_channel,
+  int frame_size,
+  void *user_data
+)
+{
+  mapping_matrix_multiply_channel_in_short((const MappingMatrix*)user_data,
+    (const opus_int16*)src, src_stride, dst, src_channel, dst_stride, frame_size);
+}
+
+static void opus_projection_copy_channel_in_int24(
+  opus_res *dst,
+  int dst_stride,
+  const void *src,
+  int src_stride,
+  int src_channel,
+  int frame_size,
+  void *user_data
+)
+{
+  mapping_matrix_multiply_channel_in_int24((const MappingMatrix*)user_data,
+    (const opus_int32*)src, src_stride, dst, src_channel, dst_stride, frame_size);
+}
+
+static int get_order_plus_one_from_channels(int channels, int *order_plus_one)
+{
+  int order_plus_one_;
+  int acn_channels;
+  int nondiegetic_channels;
+
+  /* Allowed numbers of channels:
+   * (1 + n)^2 + 2j, for n = 0...14 and j = 0 or 1.
+   */
+  if (channels < 1 || channels > 227)
+    return OPUS_BAD_ARG;
+
+  order_plus_one_ = isqrt32(channels);
+  acn_channels = order_plus_one_ * order_plus_one_;
+  nondiegetic_channels = channels - acn_channels;
+  if (nondiegetic_channels != 0 && nondiegetic_channels != 2)
+    return OPUS_BAD_ARG;
+
+  if (order_plus_one)
+    *order_plus_one = order_plus_one_;
+  return OPUS_OK;
+}
+
+static int get_streams_from_channels(int channels, int mapping_family,
+                                     int *streams, int *coupled_streams,
+                                     int *order_plus_one)
+{
+  if (mapping_family == 3)
+  {
+    if (get_order_plus_one_from_channels(channels, order_plus_one) != OPUS_OK)
+      return OPUS_BAD_ARG;
+    if (streams)
+      *streams = (channels + 1) / 2;
+    if (coupled_streams)
+      *coupled_streams = channels / 2;
+    return OPUS_OK;
+  }
+  return OPUS_BAD_ARG;
+}
+
+static MappingMatrix *get_mixing_matrix(OpusProjectionEncoder *st)
+{
+  /* void* cast avoids clang -Wcast-align warning */
+  return (MappingMatrix *)(void*)((char*)st +
+    align(sizeof(OpusProjectionEncoder)));
+}
+
+static MappingMatrix *get_enc_demixing_matrix(OpusProjectionEncoder *st)
+{
+  /* void* cast avoids clang -Wcast-align warning */
+  return (MappingMatrix *)(void*)((char*)st +
+    align(sizeof(OpusProjectionEncoder) +
+    st->mixing_matrix_size_in_bytes));
+}
+
+static OpusMSEncoder *get_multistream_encoder(OpusProjectionEncoder *st)
+{
+  /* void* cast avoids clang -Wcast-align warning */
+  return (OpusMSEncoder *)(void*)((char*)st +
+    align(sizeof(OpusProjectionEncoder) +
+    st->mixing_matrix_size_in_bytes +
+    st->demixing_matrix_size_in_bytes));
+}
+
+opus_int32 opus_projection_ambisonics_encoder_get_size(int channels,
+                                                       int mapping_family)
+{
+  int nb_streams;
+  int nb_coupled_streams;
+  int order_plus_one;
+  int mixing_matrix_rows, mixing_matrix_cols;
+  int demixing_matrix_rows, demixing_matrix_cols;
+  opus_int32 mixing_matrix_size, demixing_matrix_size;
+  opus_int32 encoder_size;
+  int ret;
+
+  ret = get_streams_from_channels(channels, mapping_family, &nb_streams,
+                                  &nb_coupled_streams, &order_plus_one);
+  if (ret != OPUS_OK)
+    return 0;
+
+  if (order_plus_one == 2)
+  {
+    mixing_matrix_rows = mapping_matrix_foa_mixing.rows;
+    mixing_matrix_cols = mapping_matrix_foa_mixing.cols;
+    demixing_matrix_rows = mapping_matrix_foa_demixing.rows;
+    demixing_matrix_cols = mapping_matrix_foa_demixing.cols;
+  }
+  else if (order_plus_one == 3)
+  {
+    mixing_matrix_rows = mapping_matrix_soa_mixing.rows;
+    mixing_matrix_cols = mapping_matrix_soa_mixing.cols;
+    demixing_matrix_rows = mapping_matrix_soa_demixing.rows;
+    demixing_matrix_cols = mapping_matrix_soa_demixing.cols;
+  }
+  else if (order_plus_one == 4)
+  {
+    mixing_matrix_rows = mapping_matrix_toa_mixing.rows;
+    mixing_matrix_cols = mapping_matrix_toa_mixing.cols;
+    demixing_matrix_rows = mapping_matrix_toa_demixing.rows;
+    demixing_matrix_cols = mapping_matrix_toa_demixing.cols;
+  }
+  else if (order_plus_one == 5)
+  {
+    mixing_matrix_rows = mapping_matrix_fourthoa_mixing.rows;
+    mixing_matrix_cols = mapping_matrix_fourthoa_mixing.cols;
+    demixing_matrix_rows = mapping_matrix_fourthoa_demixing.rows;
+    demixing_matrix_cols = mapping_matrix_fourthoa_demixing.cols;
+  }
+  else if (order_plus_one == 6)
+  {
+    mixing_matrix_rows = mapping_matrix_fifthoa_mixing.rows;
+    mixing_matrix_cols = mapping_matrix_fifthoa_mixing.cols;
+    demixing_matrix_rows = mapping_matrix_fifthoa_demixing.rows;
+    demixing_matrix_cols = mapping_matrix_fifthoa_demixing.cols;
+  }
+  else
+    return 0;
+
+  mixing_matrix_size =
+    mapping_matrix_get_size(mixing_matrix_rows, mixing_matrix_cols);
+  if (!mixing_matrix_size)
+    return 0;
+
+  demixing_matrix_size =
+    mapping_matrix_get_size(demixing_matrix_rows, demixing_matrix_cols);
+  if (!demixing_matrix_size)
+    return 0;
+
+  encoder_size =
+      opus_multistream_encoder_get_size(nb_streams, nb_coupled_streams);
+  if (!encoder_size)
+    return 0;
+
+  return align(sizeof(OpusProjectionEncoder)) +
+    mixing_matrix_size + demixing_matrix_size + encoder_size;
+}
+
+int opus_projection_ambisonics_encoder_init(OpusProjectionEncoder *st, opus_int32 Fs,
+                                            int channels, int mapping_family,
+                                            int *streams, int *coupled_streams,
+                                            int application)
+{
+  MappingMatrix *mixing_matrix;
+  MappingMatrix *demixing_matrix;
+  OpusMSEncoder *ms_encoder;
+  int i;
+  int ret;
+  int order_plus_one;
+  unsigned char mapping[255];
+
+  if (streams == NULL || coupled_streams == NULL) {
+    return OPUS_BAD_ARG;
+  }
+
+  if (get_streams_from_channels(channels, mapping_family, streams,
+    coupled_streams, &order_plus_one) != OPUS_OK)
+    return OPUS_BAD_ARG;
+
+  if (mapping_family == 3)
+  {
+    /* Assign mixing matrix based on available pre-computed matrices. */
+    mixing_matrix = get_mixing_matrix(st);
+    if (order_plus_one == 2)
+    {
+      mapping_matrix_init(mixing_matrix, mapping_matrix_foa_mixing.rows,
+        mapping_matrix_foa_mixing.cols, mapping_matrix_foa_mixing.gain,
+        mapping_matrix_foa_mixing_data,
+        sizeof(mapping_matrix_foa_mixing_data));
+    }
+    else if (order_plus_one == 3)
+    {
+      mapping_matrix_init(mixing_matrix, mapping_matrix_soa_mixing.rows,
+        mapping_matrix_soa_mixing.cols, mapping_matrix_soa_mixing.gain,
+        mapping_matrix_soa_mixing_data,
+        sizeof(mapping_matrix_soa_mixing_data));
+    }
+    else if (order_plus_one == 4)
+    {
+      mapping_matrix_init(mixing_matrix, mapping_matrix_toa_mixing.rows,
+        mapping_matrix_toa_mixing.cols, mapping_matrix_toa_mixing.gain,
+        mapping_matrix_toa_mixing_data,
+        sizeof(mapping_matrix_toa_mixing_data));
+    }
+    else if (order_plus_one == 5)
+    {
+      mapping_matrix_init(mixing_matrix, mapping_matrix_fourthoa_mixing.rows,
+        mapping_matrix_fourthoa_mixing.cols, mapping_matrix_fourthoa_mixing.gain,
+        mapping_matrix_fourthoa_mixing_data,
+        sizeof(mapping_matrix_fourthoa_mixing_data));
+    }
+    else if (order_plus_one == 6)
+    {
+      mapping_matrix_init(mixing_matrix, mapping_matrix_fifthoa_mixing.rows,
+        mapping_matrix_fifthoa_mixing.cols, mapping_matrix_fifthoa_mixing.gain,
+        mapping_matrix_fifthoa_mixing_data,
+        sizeof(mapping_matrix_fifthoa_mixing_data));
+    }
+    else
+      return OPUS_BAD_ARG;
+
+    st->mixing_matrix_size_in_bytes = mapping_matrix_get_size(
+      mixing_matrix->rows, mixing_matrix->cols);
+    if (!st->mixing_matrix_size_in_bytes)
+      return OPUS_BAD_ARG;
+
+    /* Assign demixing matrix based on available pre-computed matrices. */
+    demixing_matrix = get_enc_demixing_matrix(st);
+    if (order_plus_one == 2)
+    {
+      mapping_matrix_init(demixing_matrix, mapping_matrix_foa_demixing.rows,
+        mapping_matrix_foa_demixing.cols, mapping_matrix_foa_demixing.gain,
+        mapping_matrix_foa_demixing_data,
+        sizeof(mapping_matrix_foa_demixing_data));
+    }
+    else if (order_plus_one == 3)
+    {
+      mapping_matrix_init(demixing_matrix, mapping_matrix_soa_demixing.rows,
+        mapping_matrix_soa_demixing.cols, mapping_matrix_soa_demixing.gain,
+        mapping_matrix_soa_demixing_data,
+        sizeof(mapping_matrix_soa_demixing_data));
+    }
+    else if (order_plus_one == 4)
+    {
+      mapping_matrix_init(demixing_matrix, mapping_matrix_toa_demixing.rows,
+        mapping_matrix_toa_demixing.cols, mapping_matrix_toa_demixing.gain,
+        mapping_matrix_toa_demixing_data,
+        sizeof(mapping_matrix_toa_demixing_data));
+    }
+      else if (order_plus_one == 5)
+    {
+      mapping_matrix_init(demixing_matrix, mapping_matrix_fourthoa_demixing.rows,
+        mapping_matrix_fourthoa_demixing.cols, mapping_matrix_fourthoa_demixing.gain,
+        mapping_matrix_fourthoa_demixing_data,
+        sizeof(mapping_matrix_fourthoa_demixing_data));
+    }
+    else if (order_plus_one == 6)
+    {
+      mapping_matrix_init(demixing_matrix, mapping_matrix_fifthoa_demixing.rows,
+        mapping_matrix_fifthoa_demixing.cols, mapping_matrix_fifthoa_demixing.gain,
+        mapping_matrix_fifthoa_demixing_data,
+        sizeof(mapping_matrix_fifthoa_demixing_data));
+    }
+    else
+      return OPUS_BAD_ARG;
+
+    st->demixing_matrix_size_in_bytes = mapping_matrix_get_size(
+      demixing_matrix->rows, demixing_matrix->cols);
+    if (!st->demixing_matrix_size_in_bytes)
+      return OPUS_BAD_ARG;
+  }
+  else
+    return OPUS_UNIMPLEMENTED;
+
+  /* Ensure matrices are large enough for desired coding scheme. */
+  if (*streams + *coupled_streams > mixing_matrix->rows ||
+      channels > mixing_matrix->cols ||
+      channels > demixing_matrix->rows ||
+      *streams + *coupled_streams > demixing_matrix->cols)
+    return OPUS_BAD_ARG;
+
+  /* Set trivial mapping so each input channel pairs with a matrix column. */
+  for (i = 0; i < channels; i++)
+    mapping[i] = i;
+
+  /* Initialize multistream encoder with provided settings. */
+  ms_encoder = get_multistream_encoder(st);
+  ret = opus_multistream_encoder_init(ms_encoder, Fs, channels, *streams,
+                                      *coupled_streams, mapping, application);
+  return ret;
+}
+
+OpusProjectionEncoder *opus_projection_ambisonics_encoder_create(
+    opus_int32 Fs, int channels, int mapping_family, int *streams,
+    int *coupled_streams, int application, int *error)
+{
+  int size;
+  int ret;
+  OpusProjectionEncoder *st;
+
+  /* Allocate space for the projection encoder. */
+  size = opus_projection_ambisonics_encoder_get_size(channels, mapping_family);
+  if (!size) {
+    if (error)
+      *error = OPUS_ALLOC_FAIL;
+    return NULL;
+  }
+  st = (OpusProjectionEncoder *)opus_alloc(size);
+  if (!st)
+  {
+    if (error)
+      *error = OPUS_ALLOC_FAIL;
+    return NULL;
+  }
+
+  /* Initialize projection encoder with provided settings. */
+  ret = opus_projection_ambisonics_encoder_init(st, Fs, channels,
+     mapping_family, streams, coupled_streams, application);
+  if (ret != OPUS_OK)
+  {
+    opus_free(st);
+    st = NULL;
+  }
+  if (error)
+    *error = ret;
+  return st;
+}
+
+int opus_projection_encode(OpusProjectionEncoder *st, const opus_int16 *pcm,
+                           int frame_size, unsigned char *data,
+                           opus_int32 max_data_bytes)
+{
+  return opus_multistream_encode_native(get_multistream_encoder(st),
+    opus_projection_copy_channel_in_short, pcm, frame_size, data,
+    max_data_bytes, 16, downmix_int, 0, get_mixing_matrix(st));
+}
+
+int opus_projection_encode24(OpusProjectionEncoder *st, const opus_int32 *pcm,
+                           int frame_size, unsigned char *data,
+                           opus_int32 max_data_bytes)
+{
+  return opus_multistream_encode_native(get_multistream_encoder(st),
+    opus_projection_copy_channel_in_int24, pcm, frame_size, data,
+    max_data_bytes, MAX_ENCODING_DEPTH, downmix_int, 0, get_mixing_matrix(st));
+}
+
+#ifndef DISABLE_FLOAT_API
+int opus_projection_encode_float(OpusProjectionEncoder *st, const float *pcm,
+                                 int frame_size, unsigned char *data,
+                                 opus_int32 max_data_bytes)
+{
+  return opus_multistream_encode_native(get_multistream_encoder(st),
+    opus_projection_copy_channel_in_float, pcm, frame_size, data,
+    max_data_bytes, MAX_ENCODING_DEPTH, downmix_float, 1, get_mixing_matrix(st));
+}
+#endif
+
+void opus_projection_encoder_destroy(OpusProjectionEncoder *st)
+{
+  opus_free(st);
+}
+
+int opus_projection_encoder_ctl(OpusProjectionEncoder *st, int request, ...)
+{
+  va_list ap;
+  MappingMatrix *demixing_matrix;
+  OpusMSEncoder *ms_encoder;
+  int ret = OPUS_OK;
+
+  ms_encoder = get_multistream_encoder(st);
+  demixing_matrix = get_enc_demixing_matrix(st);
+
+  va_start(ap, request);
+  switch(request)
+  {
+  case OPUS_PROJECTION_GET_DEMIXING_MATRIX_SIZE_REQUEST:
+  {
+    opus_int32 *value = va_arg(ap, opus_int32*);
+    if (!value)
+    {
+      goto bad_arg;
+    }
+    *value =
+      ms_encoder->layout.nb_channels * (ms_encoder->layout.nb_streams
+      + ms_encoder->layout.nb_coupled_streams) * sizeof(opus_int16);
+  }
+  break;
+  case OPUS_PROJECTION_GET_DEMIXING_MATRIX_GAIN_REQUEST:
+  {
+    opus_int32 *value = va_arg(ap, opus_int32*);
+    if (!value)
+    {
+      goto bad_arg;
+    }
+    *value = demixing_matrix->gain;
+  }
+  break;
+  case OPUS_PROJECTION_GET_DEMIXING_MATRIX_REQUEST:
+  {
+    int i, j, k, l;
+    int nb_input_streams;
+    int nb_output_streams;
+    unsigned char *external_char;
+    opus_int16 *internal_short;
+    opus_int32 external_size;
+    opus_int32 internal_size;
+
+    /* (I/O is in relation to the decoder's perspective). */
+    nb_input_streams = ms_encoder->layout.nb_streams +
+      ms_encoder->layout.nb_coupled_streams;
+    nb_output_streams = ms_encoder->layout.nb_channels;
+
+    external_char = va_arg(ap, unsigned char *);
+    external_size = va_arg(ap, opus_int32);
+    if (!external_char)
+    {
+      goto bad_arg;
+    }
+    internal_short = mapping_matrix_get_data(demixing_matrix);
+    internal_size = nb_input_streams * nb_output_streams * sizeof(opus_int16);
+    if (external_size != internal_size)
+    {
+      goto bad_arg;
+    }
+
+    /* Copy demixing matrix subset to output destination. */
+    l = 0;
+    for (i = 0; i < nb_input_streams; i++) {
+      for (j = 0; j < nb_output_streams; j++) {
+        k = demixing_matrix->rows * i + j;
+        external_char[2*l] = (unsigned char)internal_short[k];
+        external_char[2*l+1] = (unsigned char)(internal_short[k] >> 8);
+        l++;
+      }
+    }
+  }
+  break;
+  default:
+  {
+    ret = opus_multistream_encoder_ctl_va_list(ms_encoder, request, ap);
+  }
+  break;
+  }
+  va_end(ap);
+  return ret;
+
+bad_arg:
+  va_end(ap);
+  return OPUS_BAD_ARG;
+}