Skip to content

File ns_melspec.c

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

Go to the documentation of this file

//*****************************************************************************
//
// Copyright (c) 2022, 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.
//
//
//*****************************************************************************

#include "am_bsp.h"
#include "am_mcu_apollo.h"
#include "am_util.h"

#include "float.h"
#include "ns_ambiqsuite_harness.h"
#include "ns_audio_melspec.h"
#include <string.h>

// Globals
// float32_t g_melspecBuffer[2 * MELSPEC_FRAME_LEN]; // interleaved real + imaginary parts
// int32_t g_melspecFbankFirst[MELSPEC_NUM_FBANK_BINS];
// int32_t g_melspecFbankLast[MELSPEC_NUM_FBANK_BINS];
// float g_melspecMelFBank[MELSPEC_NUM_FBANK_BINS][50];

arm_cfft_instance_f32 g_melspecRfft;

void
ns_melspec_init(ns_melspec_cfg_t *cfg) {
    arm_status status = arm_cfft_init_f32(&g_melspecRfft, cfg->frame_len);
    if (status != ARM_MATH_SUCCESS) {
        ns_printf("problem initializing melspec: status enum %d\n", status);
    }
    cfg->melspecBuffer = (float *)(cfg->arena);

    cfg->fbc.arena_fbanks = (uint8_t *)(cfg->melspecBuffer) + 2 * cfg->frame_len * sizeof(float);
    cfg->fbc.sample_frequency = cfg->sample_frequency;
    cfg->fbc.num_fbank_bins = cfg->num_fbank_bins;
    cfg->fbc.low_freq = cfg->low_freq;
    cfg->fbc.high_freq = cfg->high_freq;
    cfg->fbc.frame_len_pow2 = cfg->frame_len_pow2;
    ns_fbanks_init(&(cfg->fbc));
}

void
ns_melspec_audio_to_stft(ns_melspec_cfg_t *cfg, const int16_t *audio_in, float32_t *stft_out) {
    int16_t i;

    // copy the integer audio buffer into complex valued stft input buffer
    for (i = 0; i < cfg->frame_len; i++) {
        stft_out[2 * i] = (float32_t)audio_in[i]; // real part
        stft_out[2 * i + 1] = (float32_t)0.0;     // imaginary part
    }

    // compute short-time fourier transform in-place.
    arm_cfft_f32(&g_melspecRfft, stft_out, 0, 0);
}

void
ns_melspec_stft_to_audio(ns_melspec_cfg_t *cfg, float32_t *stft_in, int16_t *audio_out) {
    int16_t i;

    // compute the inverse stft in-place (destroys original input!!)
    arm_cfft_f32(&g_melspecRfft, stft_in, 1, 0);
    for (i = 0; i < cfg->frame_len; i++) {
        // TODO: check correctness of this calculation
        audio_out[i] = (int16_t)stft_in[2 * i];
    }
}

void
ns_melspec_stft_to_compressed_melspec(ns_melspec_cfg_t *cfg, const float32_t *stft_in,
                                      float32_t *melspec_out) {
    int16_t i, j, k;

    for (i = 0; i < cfg->num_fbank_bins; i++) {
        k = 0;
        float curr_val = 0.0;
        for (j = cfg->fbc.mfccFbankFirst[i]; j < cfg->fbc.mfccFbankLast[i]; j++) {
            curr_val += stft_in[2 * j] * (*(cfg->fbc.melFBank))[i][k++];
        }
        melspec_out[i] = (float)pow(curr_val, cfg->compression_exponent);
    }
}

void
ns_melspec_melspec_to_stft(ns_melspec_cfg_t *cfg, const float32_t *melspec_in,
                           float32_t *stft_out) {
    int16_t i, j, k;

    // TODO: check correctness of this calculation with python implementation
    // assumed melspec_in shape: [num_frames, num_mel_bins]  (num_frames likely 1)
    // g_melspecMelFBank shape: [num_mel_bins, num_spec_bins[first_nonzero,last_nonzero]]
    // stft_out shape: [num_frames, num_spec_bins]
    // stft_out <- MatMul(melspec_in, g_melspecMelFBank)
    for (i = 0; i < cfg->num_fbank_bins; i++) {
        k = 0;
        float curr_val = 0.0;
        for (j = cfg->fbc.mfccFbankFirst[i]; j < cfg->fbc.mfccFbankLast[i]; j++) {
            curr_val += (*(cfg->fbc.melFBank))[i][k++] * stft_out[j * 2];
        }
        stft_out[i] = curr_val;
    }
}