//*****************************************************************************
//
//! @file am_vos_init.c
//!
//! @brief VoS system initialize
//
//*****************************************************************************

//*****************************************************************************
//
// Copyright (c) 2024, Ambiq Micro, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. 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.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// Third party software included in this distribution is subject to the
// additional license terms as defined in the /docs/licenses directory.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// This is part of revision ambiqvos_r4.5-fdfa8cf6a4 of the AmbiqSuite Development Package.
//
//*****************************************************************************

#include "am_vos_sys_config.h"
#include "am_vos_board_setup.h"

#include "am_vos_utils.h"

#include "am_vos_task.h"
#include "am_vos_init.h"
#include "am_vos_audio.h"
#include "am_vos_spp.h"
#include "am_vos_codec.h"

#if configUSE_BLE
#include "am_vos_ble.h"
#endif // configUSE_BLE

#if configUSE_DSPC_TalkTo
#include "am_vos_awe.h"
#endif // configUSE_DSPC_TalkTo

#if configUSE_AMVOS_AMA
#include "am_vos_ama.h"
#endif // configUSE_AMVOS_AMA

#if configUSE_SYSVIEWER
#include "SEGGER_SYSVIEW.h"
#include "SEGGER_RTT.h"
#endif // configUSE_SYSVIEWER

#if configUSE_STDIO_PRINTF
#include "am_vos_rtt_recorder.h"
#endif // configUSE_STDIO_PRINTF

#if configUSE_RTT_RECORDER
#include "am_vos_rtt_recorder.h"
#endif // configUSE_RTT_RECORDER

#if configUSE_SPP_AGC
#include "am_vos_agc.h"
#endif // configUSE_SPP_AGC

#if configUSE_SPP_P2A
#include "am_vos_p2a.h"
#endif // configUSE_SPP_P2A

#if configUSE_SPP_EXP
#include "am_vos_exp.h"
#endif // configUSE_SPP_EXP

#if configUSE_SPP_DRC
#include "am_vos_comp.h"
#include "am_vos_agc.h"
#include "am_vos_exp.h"
#endif // configUSE_SPP_DRC

#if configUSE_AMBIQ_VADv3
#include "webrtc_vad.h"
#endif // configUSE_AMBIQ_VADv3
//*****************************************************************************
//
// KWD application tasks list. User needs to keep this lists name unchaged.
//
//*****************************************************************************
am_vos_task_t am_KWD_tasks[AM_VOS_MAX_TASK];

VosSysInfo g_sVosSys =
{
    .ui8KwdDetectedFlag = 0,             // 1: the key word is detected; 0: no key word detected.
    .ui8ButtonDownFlag = 0,              // 1: the button is pushed down; 0: no button is pushed.
    .ui8PushTalkFlag = 0,                // trig for AMA push-to-talk.
    .ui8CmdDetectedFlag = 0,

    .ui32StreamingTimeOutCount = 0,      // Voice streaming timeout counter
    .bWwdEnabled = 0,

#if configUSE_AMVOS_AMA
    .ui8ProvideSpeechFlag = 0,           // trig for AMA conversation talk.

    .i32DelayedSample = 0,
    .i32EndingSampleCnt = 0,
    .i32PreRollDelayMs = AUDIO_PREROLL_TIME_MS,
    .ui8AlexaAppPlatform = 0,
#elif configUSE_AMVOS_ATVV
    .ui32MicOpenTimeOutCount = 0,
    .ui8FragmentCount = 0,
#else // configUSE_AMVOS_AMA
    .ui8SeqNumber = 0,
#endif // configUSE_AMVOS_AMA

#if configUSE_RTT_RECORDER
    .ui8RecordStartFlag = 0,
//    .ui32RTTSendBytes = 0,
#endif // configUSE_RTT_RECORDER

#if configUSE_AAD
    .ui8AadEnabled = 0,
    .ui8AadDebounceFlag = 1,
    .i32TimeoutToAadEnable = AAD_TIMEOUT_COUNT,     // Start pdm > AAD_TIMEOUT_COUNT * 5 ms when power-up
    .ui8AadSkipFrameFlag = 0,
    .ui32AadDiscardTimeUS = AAD_DISCARD_TIME_US,
#endif // configUSE_AAD

#if configUSE_AMBIQ_VADv3
    .ui8FullProcessedFlag = 0,
    .ui8RequireFullProcess = 0,
    .ui16VadTimeoutCnt = 0,
    .i16VadDebounceCnt = AM_VAD_DEBOUNCE_SAMPLES / PCM_FRAME_SIZE_SAMPLES,
#endif // configUSE_AMBIQ_VADv3

#if configUSE_SPP_P2A
    .u8PgaFlag = 0,
#endif // configUSE_SPP_P2A

#if VOS_MEASURE_SPP_MIPS
    .ui32SppTimeTick = 0,
    .ui32SppCallNum = 0,
#endif // VOS_MEASURE_SPP_MIPS

#if VOS_MEASURE_WWE_MIPS
    .ui32WweTimeTick = 0,
    .ui32WweCallNum = 0,
#endif // VOS_MEASURE_WWE_MIPS

#if VOS_MEASURE_CODEC_MIPS
    .ui32CodecTimeTick = 0,
    .ui32CodecCallNum = 0,
#endif // VOS_MEASURE_CODEC_MIPS

#if VOS_MEASURE_SYS_MIPS
    .ui32VosSleepTimeTick = 0,
    .ui32VosSleepCallNum = 0,
#endif // VOS_MEASURE_SYS_MIPS

#if VOS_MEASURE_VAD_WAKETIME
    .ui32VosVadWakeTimeTick = 0,
    .ui32VosVadWakeTimeTickTotal = 0,
#endif // VOS_MEASURE_VAD_WAKETIME

#if VOS_MEASURE_ISR_PCM_DATA
    .ui32VosIsrCount = 0,
    .ui32VosPcmSize = 0,
#endif // VOS_MEASURE_ISR_PCM_DATA

#if VOS_MEASUREMENT_ENABLE
    .ui32VosBenchTimeSec = 0,
#endif

    .ui32SysHeapSize = VOS_HEAP_TOTAL_SIZE
};

//*****************************************************************************
//
// KWD application tasks configuration
//
//*****************************************************************************
static const am_vos_task_setup_t g_KWD_TaskSetup[] =
{
    // TASK ID,                     Task function pointer,              Task name string,           Stack,  Cback,  Priority,   Queue size
    {AM_VOS_TASK_LED,               &am_vos_led_task,               "key&led",                  256,    NULL,   2,          4},

#if configUSE_LOG_UART0 || configUSE_PRINTF_UART0
    {AM_VOS_TASK_UART0,             &am_vos_uart0_gatekeeper_task,  "uart0_data_print",         256,    NULL,   2,          16},
#endif // configUSE_LOG_UART0 || configUSE_PRINTF_UART0

#if configUSE_PRINTF_RTT || configUSE_PRINTF_SWO
    {AM_VOS_TASK_STDIO,             &am_vos_stdio_gatekeeper_task,  "print_on_rtt_swo",         768,    NULL,   3,          16},  // a high priority task to print
#endif // configUSE_PRINTF_RTT || configUSE_PRINTF_SWO

#if configUSE_AMBIQ_SPP
    {AM_VOS_TASK_VAD,               &am_vos_vad_task,               "voice_activity_detection", 1024,   NULL,   5,          16},
#endif // configUSE_AMBIQ_SPP

#if configUSE_Fluent
    {AM_VOS_TASK_AUD_PROCESSING,    &am_vos_audio_processing_task,  "audio_process_task",       3072,   NULL,   5,          16},
#else
//    {AM_VOS_TASK_AUD_PROCESSING,    &am_vos_audio_processing_task,  "audio_process_task",       1024,   NULL,   4,          16},
    {AM_VOS_TASK_AUD_PROCESSING,    &am_vos_audio_processing_task,  "audio_process_task",       1024,   NULL,   5,          16},
#endif

#if configUSE_AUDIO_CODEC
    {AM_VOS_TASK_CODEC,             &am_vos_codec_task,             "audio_stream_compress",    2048,   NULL,   4,          16},
#endif // configUSE_AUDIO_CODEC

#if configUSE_BLE
    {AM_VOS_BLE,                    &am_vos_ble_task,               "ble_task",                 768,    NULL,   6,          16},
#endif // configUSE_BLE
};

#define KWD_TASK_INIT_COUNT     (sizeof(g_KWD_TaskSetup)/sizeof(am_vos_task_setup_t))

//*****************************************************************************
//
// KWD application tasks list. User needs to keep this lists name unchaged.
//
//*****************************************************************************

TimerHandle_t am_KWD_timers[AM_VOS_MAX_TIMER];
//*****************************************************************************
//
// KWD application timers configuration
//
//*****************************************************************************
static const am_vos_timer_setup_t g_KWD_TimerSetup[] =
{
    {AM_VOS_TIMER_HEART_BEAT, "HeartBeat", HEART_BEAT_PERIOD,  pdTRUE, &am_vos_timer_heart_beat_callback},
    #if 0 //configUSE_MUTE_MIC
        {AM_VOS_TIMER_LONG_PRESS, "ButtonLongPress", LONG_PRESS_TIMER_PERIOD_MS, pdFALSE, &am_vos_timer_longpress_callback},
    #endif // configUSE_MUTE_MIC
};

#define KWD_TIMER_INIT_COUNT     (sizeof(g_KWD_TimerSetup)/sizeof(am_vos_timer_setup_t))

//******************************************************************************
//KWD system init function
//*****************************************************************************

void am_vos_sys_init(void)
{
    am_vos_board_init();

#if configUSE_STDIO_PRINTF
    am_vos_stdio_printf_init((am_vos_stdio_printf_t)am_vos_printf);
    //am_vos_stdio_printf_init((am_vos_stdio_printf_t)am_hal_itm_print);
#endif // configUSE_STDIO_PRINTF

#if configUSE_DSPC_TalkTo
    // QSD isn't working for now
    am_spp_init(&g_sVosAud.sSppInputBuf, &g_sVosAud.sSppOutputBuf);
    
#if (!configUSE_TalkTo_SCNR)
    am_vos_awe_scnr_off();
#endif // !configUSE_TalkTo_SCNR
#endif // configUSE_DSPC_TalkTo

    //
    // audio codec initialization
    //
#if configUSE_AUDIO_CODEC
    am_vos_codec_init();
#endif // configUSE_AUDIO_CODEC

#if configUSE_RTT_RECORDER
    am_vos_rtt_init(g_sVosSys.pui8RttRecorderBuffer, RTT_BUFFER_LENGTH);
#endif // configUSE_RTT_RECORDER

    //
    // init audio buffers
    //
    am_audio_buffer_init();

#if configUSE_SPP_AGC
    int agc_target_level = 2068;	// -24 dBm
    int agc_attack_time = 10;
    int agc_decay_time = 200;

    g_sVosAud.pvSppAgcHandle = am_vos_agc_init(16000, PCM_FRAME_SIZE_SAMPLES, agc_target_level, agc_attack_time, agc_decay_time);
#endif // configUSE_SPP_AGC

#if configUSE_SPP_P2A
    am_vos_p2a_init(16000, 80);
#endif // configUSE_SPP_P2A

#if configUSE_SPP_EXP
    int attack_time = 200;
    int decay_time = 20;
    int noise_gate = -50;       // -80;
    float exp_slope = 4;
    float exp_knee_width = 4;
    am_vos_exp_init(16000, PCM_FRAME_SIZE_SAMPLES, noise_gate, exp_slope, exp_knee_width, attack_time, decay_time );
#endif

#if configUSE_SPP_DRC
    int agc_exp_attack_time = 10;
    int agc_exp_decay_time  = 200;
    int comp_exp_noise_gate = -80;

    int comp_attack_time = 1;
    int comp_decay_time  = 100;
    int comp_knee_thrd  = -15;

    float comp_slope = 0.1;
    g_sVosAud.pvSppCompHandle = (void *)pvPortMalloc(sizeof(objComp));

    am_vos_comp_init(USE_PCM_SAMPLE_RATE, PCM_FRAME_SIZE_SAMPLES, comp_knee_thrd, comp_exp_noise_gate,
                               comp_slope, comp_attack_time, comp_decay_time, g_sVosAud.pvSppCompHandle);

    int agc_target_level = 8231;	// -12 dBm
    g_sVosAud.pvSppAgcHandle = am_vos_agc_init(USE_PCM_SAMPLE_RATE, PCM_FRAME_SIZE_SAMPLES, agc_target_level,
                              agc_exp_attack_time, agc_exp_decay_time);

    //int exp_knee_thrd = -46;
    float exp_slope = 4;
    float exp_knee_width = 4;
    am_vos_exp_init(USE_PCM_SAMPLE_RATE, PCM_FRAME_SIZE_SAMPLES, comp_exp_noise_gate, exp_slope,
                    exp_knee_width, agc_exp_attack_time, agc_exp_decay_time );
#endif // configUSE_SPP_DRC

#if configUSE_AMBIQ_VADv3
    g_sVosAud.psVadHandle = WebRtcVad_Create();
    WebRtcVad_Init(g_sVosAud.psVadHandle);
    WebRtcVad_set_mode(g_sVosAud.psVadHandle, 1);
#endif // configUSE_AMBIQ_VADv3
    //
    // create application tasks
    //
    am_vos_task_init();
    am_vos_task_create_all_tasks(g_KWD_TaskSetup, KWD_TASK_INIT_COUNT);

    am_vos_timer_create_all_timers(g_KWD_TimerSetup, KWD_TIMER_INIT_COUNT);

    // Enable system heart beat LED
    xTimerStart(am_KWD_timers[AM_VOS_TIMER_HEART_BEAT], 0);

#if (!configUSE_BLE)
#if (configUSE_AAD && T5838_AAD_D1)
   am_device_t5838_aad_init();
#endif
   am_vos_mic_enable();
#endif

#if configUSE_SYSVIEWER
    //
    // Configure the SEGGER SystemView Interface.
    //
    SEGGER_RTT_Init();      //explicitly call this function to init RTT block
    SEGGER_SYSVIEW_Conf();
#endif // configUSE_SYSVIEWER
}

void
am_vos_print_system_info(void)
{
    am_util_id_t sIdDevice;

    am_util_stdio_printf("\n========= Ambiq VoS SDK =========\n");

    am_util_id_device(&sIdDevice);
    am_util_stdio_printf("Device type: %s\n", sIdDevice.pui8DeviceName);
    am_util_stdio_printf("Device info:\n\tPart number: 0x%08X\n"
                         "\tRevision: 0x%X (Rev%c%c)\n",
                         sIdDevice.sMcuCtrlDevice.ui32ChipPN,
                         sIdDevice.sMcuCtrlDevice.ui32ChipRev,
                         sIdDevice.ui8ChipRevMaj, sIdDevice.ui8ChipRevMin );
    am_util_stdio_printf("\tChip ID 0: 0x%X\n", sIdDevice.sMcuCtrlDevice.ui32ChipID0);
    am_util_stdio_printf("\tChip ID 1: 0x%X\n", sIdDevice.sMcuCtrlDevice.ui32ChipID1);

    am_util_stdio_printf("\nFirmware version: ");
    am_util_stdio_printf(VOS_FW_VER_STRING);
    am_util_stdio_printf("\nMIC input:");

#if USE_DMIC_PDM
    am_util_stdio_printf(" PDM (DMIC)");
#endif // USE_DMIC_PDM

#if USE_AMIC_AUDADC
    am_util_stdio_printf(" AUDADC (AMIC)");
#endif // USE_AMIC_AUDADC

#if (USE_DMIC_PDM || USE_AMIC_AUDADC)
#if USE_MIC_SINGLE
    am_util_stdio_printf(": Single MIC");
#else
    am_util_stdio_printf(": Dual MICs");
#endif
#endif // (USE_DMIC_PDM || USE_AMIC_AUDADC)

    am_util_stdio_printf("\nLoad modules:");

#if configUSE_DSPC_TalkTo
    am_util_stdio_printf(" TalkTo");
#endif // configUSE_DSPC_TalkTo

#if configUSE_Sensory_THF
    am_util_stdio_printf(" THF");
#endif // configUSE_Sensory_THF

#if configUSE_Fluent
    am_util_stdio_printf(" Fluent");
#endif // configUSE_Fluent

#if configUSE_AMVOS_AMA
    am_util_stdio_printf(" AMA");
#endif // configUSE_AMVOS_AMA

#if configUSE_AMVOS_ATVV
    am_util_stdio_printf(" ATVV");
#endif // configUSE_AMVOS_AMA

#if configUSE_MSBC_BLUEZ
    am_util_stdio_printf(" mSBC");
#endif // configUSE_MSBC_BLUEZ

#if configUSE_OPTIM_OPUS
    am_util_stdio_printf(" OPUS");
#endif // configUSE_OPTIM_OPUS

#if configUSE_ADPCM
    am_util_stdio_printf(" ADPCM");
#endif // configUSE_ADPCM

    am_util_stdio_printf("\nFeatures:");

#if configUSE_RTT_RECORDER
    am_util_stdio_printf("\n\tBTN0: Audio Recording");
#endif // configUSE_RTT_RECORDER

#if configUSE_PAIRING_MODE_BTN && configUSE_BLE
    am_util_stdio_printf("\n\tBTN0: Pairing Mode");
#endif // configUSE_PAIRING_MODE_BTN && configUSE_BLE

#if configUSE_PUSH_TO_TALK
    am_util_stdio_printf("\n\tBTN1: Push To Talk");
#endif // configUSE_PUSH_TO_TALK

#if configUSE_MUTE_MIC
    am_util_stdio_printf("\n\tBTN2: Mute MIC");
#endif // configUSE_MUTE_MIC

#if configUSE_AAD
    am_util_stdio_printf("\n\tAAD");
#endif // configUSE_AAD

    am_util_stdio_printf("\nSystem Heap: %d KB (%d bytes)", g_sVosSys.ui32SysHeapSize / 1024, g_sVosSys.ui32SysHeapSize);
    am_util_stdio_printf("\n=================================\n\n");
    am_util_stdio_printf("System reset reason: 0x4FFFF000 = 0x%08x \n", (*(volatile uint32_t*)0x4FFFF000));
}
