Skip to content

File ns_audio.c

File List > neuralSPOT > neuralspot > ns-audio > src > ns_audio.c

Go to the documentation of this file

//*****************************************************************************
//
// ${copyright}
//
// This is part of revision ${version} of the AmbiqSuite Development Package.
//
//*****************************************************************************

#include "ns_audio.h"
#include "am_bsp.h"
#include "am_mcu_apollo.h"
#include "am_util.h"
#include "ns_audadc.h"
#include "ns_ipc_ring_buffer.h"
#include "ns_pdm.h"

const ns_core_api_t ns_audio_V0_0_1 = {.apiId = NS_AUDIO_API_ID, .version = NS_AUDIO_V0_0_1};
const ns_core_api_t ns_audio_V1_0_0 = {.apiId = NS_AUDIO_API_ID, .version = NS_AUDIO_V1_0_0};
const ns_core_api_t ns_audio_V2_0_0 = {.apiId = NS_AUDIO_API_ID, .version = NS_AUDIO_V2_0_0};
const ns_core_api_t ns_audio_V2_1_0 = {.apiId = NS_AUDIO_API_ID, .version = NS_AUDIO_V2_1_0};
const ns_core_api_t ns_audio_oldest_supported_version = {
    .apiId = NS_AUDIO_API_ID, .version = NS_AUDIO_OLDEST_SUPPORTED_VERSION};
const ns_core_api_t ns_audio_current_version = {
    .apiId = NS_AUDIO_API_ID, .version = NS_AUDIO_CURRENT_VERSION};

#ifdef NS_RTT_AUDIODEBUG
    #include "SEGGER_RTT.h"
    #define TIMEOUT 400000 // RTT streaming timeout loop count
    #define AUDIO_SAMPLE_TO_RTT (64 * 512)
    #define RTT_BUFFER_LENGTH (AUDIO_SAMPLE_TO_RTT * 2)
uint8_t g_rttRecorderBuffer[RTT_BUFFER_LENGTH];
AM_SHARED_RW int16_t g_in16SampleToRTT[AUDIO_SAMPLE_TO_RTT];
uint32_t g_ui32SampleToRTT = 0;
#endif

ns_audio_config_t *g_ns_audio_config = NULL;

uint32_t ns_audio_init(ns_audio_config_t *cfg) {
#ifndef NS_DISABLE_API_VALIDATION
    // Check the handle.
    if (cfg == NULL) {
        return NS_STATUS_INVALID_HANDLE;
    }

    // check API version
    if (ns_core_check_api(
            cfg->api, &ns_audio_oldest_supported_version, &ns_audio_current_version)) {
        return NS_STATUS_INVALID_VERSION;
    }

    if ((cfg->callback == NULL) || (cfg->audioBuffer == NULL) || (cfg->sampleBuffer == NULL) || ((uintptr_t)cfg->audioBuffer % 2 != 0)) {
        return NS_STATUS_INVALID_CONFIG;
    }

    #ifdef AM_PART_APOLLO4L
    if (cfg->eAudioSource == NS_AUDIO_SOURCE_AUDADC) {
        return NS_STATUS_INVALID_CONFIG;
    }

    if (cfg->numChannels != 1) {
        return NS_STATUS_INVALID_CONFIG; // APOLLO4L only supports 1 channel
    }

    #endif

    if ((cfg->api->version.major != 1) && (cfg->eAudioSource == NS_AUDIO_SOURCE_AUDADC) &&
        (cfg->workingBuffer == NULL)) {
        return NS_STATUS_INVALID_CONFIG;
    }
    if (sizeof(*(cfg->sampleBuffer)) > cfg->numSamples * 2) {
        return NS_STATUS_INVALID_CONFIG;
    }

    if ((cfg->numChannels > 2) || ((cfg->sampleRate  != 8000) && cfg->sampleRate != 16000)) {
        return NS_STATUS_INVALID_CONFIG;
    }

#endif

    // cfg->api->initialized = true;

    g_ns_audio_config = cfg;

    if (g_ns_audio_config->eAudioApiMode == NS_AUDIO_API_RINGBUFFER) {
        // init a ringbuffer
        ns_ipc_ringbuff_setup_t setup = {
            .indx = 0,
            .pData = g_ns_audio_config->audioBuffer,
            .ui32ByteSize = g_ns_audio_config->numSamples * 2};

        ns_ipc_ring_buffer_init(g_ns_audio_config->bufferHandle, setup);
        g_ns_audio_config->bufferHandle = g_ns_audio_config->bufferHandle;
    }

    if (g_ns_audio_config->eAudioSource == NS_AUDIO_SOURCE_AUDADC) {
#ifndef NS_AUDADC_PRESENT
        am_util_stdio_printf("Error - Trying to init non-existent AUDADC\n");
        return NS_STATUS_INIT_FAILED;
#endif
        // If api doesn't support dynamic audio source, initialize audadc here
        if (ns_core_check_api(g_ns_audio_config->api, &ns_audio_oldest_supported_version, &ns_audio_V2_0_0) == NS_STATUS_SUCCESS) {
            // initialize audadc
            if(ns_start_audio(g_ns_audio_config) != NS_STATUS_SUCCESS) {
                return NS_STATUS_INIT_FAILED;
            }
        }
    }
    else if (g_ns_audio_config->eAudioSource == NS_AUDIO_SOURCE_PDM) {
        // If api doesn't support dynamic audio source, initialize pdm here
        if (ns_core_check_api(g_ns_audio_config->api, &ns_audio_oldest_supported_version, &ns_audio_V2_0_0) == NS_STATUS_SUCCESS) {
            // initialize pdm
            if(ns_start_audio(g_ns_audio_config) != NS_STATUS_SUCCESS) {
                return NS_STATUS_INIT_FAILED;
            }
        }
    }
    else {
        return NS_STATUS_INVALID_CONFIG;
    }

#ifdef NS_RTT_AUDIODEBUG
    SEGGER_RTT_Init();
    SEGGER_RTT_ConfigUpBuffer(
        1, "DataLogger", g_rttRecorderBuffer, RTT_BUFFER_LENGTH, SEGGER_RTT_MODE_NO_BLOCK_SKIP);
    am_util_stdio_printf("RTT Control Block Address:  0x%08X\n", (uint32_t)&_SEGGER_RTT);
#endif

    return AM_HAL_STATUS_SUCCESS;
}

uint32_t ns_start_audio(ns_audio_config_t *cfg) {
    if (g_ns_audio_config->eAudioSource == NS_AUDIO_SOURCE_AUDADC) {
#ifdef NS_AUDADC_PRESENT
        if (g_ns_audio_config->audadc_config == NULL) {
            g_ns_audio_config->audadc_config = &ns_audadc_default;
        }
        if (audadc_init(cfg)) {
            return NS_STATUS_INIT_FAILED;
        }
        // Trigger the AUDADC sampling for the first time manually.
        if (AM_HAL_STATUS_SUCCESS !=
            am_hal_audadc_sw_trigger(g_ns_audio_config->audioSystemHandle)) {
            am_util_stdio_printf("Error - triggering the AUDADC failed.\n");
            return NS_STATUS_INIT_FAILED;
        }
#else
        return NS_STATUS_INIT_FAILED;
#endif
    } 
    else {
        if (g_ns_audio_config->pdm_config == NULL) {
            g_ns_audio_config->pdm_config = &ns_pdm_default;
        }
        if (pdm_init(g_ns_audio_config)) {
            return NS_STATUS_INIT_FAILED;
        }
    }
    return NS_STATUS_SUCCESS;
}

uint32_t ns_end_audio(ns_audio_config_t *cfg) {
    if (g_ns_audio_config->eAudioSource == NS_AUDIO_SOURCE_AUDADC) {
       audadc_deinit(cfg);
       return NS_STATUS_SUCCESS;
    } else if (g_ns_audio_config->eAudioSource == NS_AUDIO_SOURCE_PDM) {
        pdm_deinit(cfg);
        return NS_STATUS_SUCCESS;
    }
    return NS_STATUS_INVALID_CONFIG;
}





// static uint32_t synthData = 0;
// static void
// gen_synthetic_audadc(ns_audio_config_t *config, uint32_t cnt) {
//     uint8_t channel = 0;
//     for (int i = 0; i < cnt; i++) {
//         // Generate synthetic ADC data
//         config->sampleBuffer[i] = (synthData & 0xFFF) << 4;
//         config->sampleBuffer[i] |= channel << 19;
//         synthData++;
//         channel = (channel + 1) % 2;
//     }
// }

void ns_audio_getPCM(int16_t *pcm, uint32_t *raw, int16_t len) {
    if (g_ns_audio_config->api->version.major < 2) {
        for (int i = 0; i < len; i++) {
            pcm[i] = (int16_t)(raw[i] & 0x0000FFF0);
            if (i == 4) {
                // Workaround for AUDADC sample glitch, here while it is root caused
                pcm[3] = (pcm[2] + pcm[4]) / 2;
            }
        }
    } else {
        ns_audio_getPCM_v2(g_ns_audio_config, pcm);
    }
}

void ns_audio_getPCM_v2(ns_audio_config_t *config, void *pcm) {
#ifndef NS_AUDADC_PRESENT
    // Platform only supports PDM, and there is nothing to do for PDM here
    return;
#else
    uint32_t ui32PcmSampleCnt = config->numSamples * config->numChannels;

    if (config->eAudioSource == NS_AUDIO_SOURCE_AUDADC) {
        uint32_t LeftChCount = 0;
        uint32_t RightChCount = 0;

        uint32_t *pcm32 = (uint32_t *)pcm;
        uint16_t *pcm16 = (uint16_t *)pcm;

        // gen_synthetic_audadc(config, ui32PcmSampleCnt);
    #ifdef NS_AMBIQSUITE_VERSION_R4_1_0
        am_hal_audadc_samples_read(
            config->audioSystemHandle, config->sampleBuffer, &ui32PcmSampleCnt, true,
            config->workingBuffer, false, NULL);
    #else
        am_hal_audadc_samples_read(
            config->audioSystemHandle, config->sampleBuffer, &ui32PcmSampleCnt, true,
            config->workingBuffer, false, NULL, config->sOffsetCalib);
    #endif
        for (int i = 0; i < ui32PcmSampleCnt; i++) {
            if (config->numChannels == 1) {
                pcm16[i] = config->workingBuffer[i].int16Sample;
            } else {
                if (config->workingBuffer[i].ui16AudChannel == 0) {
                    // Low gain samples (MIC0) data to left channel.
                    pcm32[LeftChCount++] = (config->workingBuffer[i].int16Sample & 0x0000FFFF);
                } else {
                    // right channel (MIC2, MIC3) data
                    pcm32[RightChCount++] |= ((config->workingBuffer[i].int16Sample) << 16);
                }
            }
        }
    } else if (config->eAudioSource == NS_AUDIO_SOURCE_PDM) {
        // ISR current does the work, do nothing here
        // uint8_t *pcm8 = (uint8_t *)pcm;

        // for ( uint32_t i = 0; i < ui32PcmSampleCnt; i++ ){
        //     pcm8[2 * i] = (config->sampleBuffer[i] & 0xFF00) >> 8U;
        //     pcm8[2 * i + 1] = (config->sampleBuffer[i] & 0xFF0000) >> 16U;
        // }
    }
#endif
}