
#define VOCLIB_IMPLEMENTATION
#include "ma_vocoder_node.h"

static void ma_vocoder_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
{
    ma_vocoder_node* pVocoderNode = (ma_vocoder_node*)pNode;

    (void)pFrameCountIn;

    voclib_process(&pVocoderNode->voclib, ppFramesIn[0], ppFramesIn[1], ppFramesOut[0], *pFrameCountOut);
}

static ma_node_vtable g_ma_vocoder_node_vtable =
{
    ma_vocoder_node_process_pcm_frames,
    NULL,
    2,  /* 2 input channels. */
    1,  /* 1 output channel. */
    0
};

MA_API ma_vocoder_node_config ma_vocoder_node_config_init(ma_uint32 channels, ma_uint32 sampleRate)
{
    ma_vocoder_node_config config;

    MA_ZERO_OBJECT(&config);
    config.nodeConfig     = ma_node_config_init();   /* Input and output channels will be set in ma_vocoder_node_init(). */
    config.channels       = channels;
    config.sampleRate     = sampleRate;
    config.bands          = 16;
    config.filtersPerBand = 6;

    return config;
}



MA_API ma_result ma_vocoder_node_init(ma_node_graph* pNodeGraph, const ma_vocoder_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_vocoder_node* pVocoderNode)
{
    ma_result result;
    ma_node_config baseConfig;

    if (pVocoderNode == NULL) {
        return MA_INVALID_ARGS;
    }

    MA_ZERO_OBJECT(pVocoderNode);

    if (pConfig == NULL) {
        return MA_INVALID_ARGS;
    }

    if (voclib_initialize(&pVocoderNode->voclib, (unsigned char)pConfig->bands, (unsigned char)pConfig->filtersPerBand, (unsigned int)pConfig->sampleRate, (unsigned char)pConfig->channels) == 0) {
        return MA_INVALID_ARGS;
    }

    baseConfig = pConfig->nodeConfig;
    baseConfig.vtable = &g_ma_vocoder_node_vtable;
    baseConfig.inputChannels[0] = pConfig->channels;    /* Source/carrier. */
    baseConfig.inputChannels[1] = 1;                    /* Excite/modulator. Must always be single channel. */
    baseConfig.outputChannels[0] = pConfig->channels;   /* Output channels is always the same as the source/carrier. */
    baseConfig.outputChannels[1] = 0;                   /* Unused. */

    result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pVocoderNode->baseNode);
    if (result != MA_SUCCESS) {
        return result;
    }

    return MA_SUCCESS;
}

MA_API void ma_vocoder_node_uninit(ma_vocoder_node* pVocoderNode, const ma_allocation_callbacks* pAllocationCallbacks)
{
    /* The base node must always be initialized first. */
    ma_node_uninit(pVocoderNode, pAllocationCallbacks);
}
