// --------------------------------------------------------------
// --------------------------------------------------------------
#include <string.h>
#include <stdlib.h>

#include "synth_defs.h"
#include "blit.h"
#include "vco.h"
#include "wavetable.h"

// --------------------------------------------------------------
// internal funcs
// --------------------------------------------------------------
// --------------------------------------------------------------
// Exported functions
// --------------------------------------------------------------
void VCO_ModInit(vco_common_t *pCom)
{
	BLIT_ModInit(&pCom->blit);
}

void VCO_ModFree(vco_common_t *pCom)
{
	BLIT_ModFree(&pCom->blit);
}

void VCO_Init(osc_t *pObj, UINT32 id, vco_common_t *pCom, synth_float_t fs)
{
	pObj->pCom = pCom;
	pObj->id = id;
	pObj->fs = fs;

	if (pObj->id == 0)
	{
		VCO_ModInit(pCom);
	}

	pObj->pOut = NULL;
	pObj->bufsize = 0;
	VCO_SetBufsize(pObj, SYNTH_MAX_BUFSIZE);

	pObj->param[OSC_PARAM2_WAVEFORM] = OSC_WAVEFORM_SAWTOOTH;

	Sine_Init(&pObj->sine, fs);
	BLIT_Init(&pObj->blep, &pCom->blit, fs);
	WT_Init(&pObj->wt, id, &pCom->wt, fs);
	pObj->impulse = OSC_IMPULS_HEIGHT;
}

void VCO_Free(osc_t *pObj)
{
	Sine_Free(&pObj->sine);
	BLIT_Free(&pObj->blep);
	WT_Free(&pObj->wt);

	VCO_SetBufsize(pObj, 0);

	if (pObj->id == 0)
	{
		VCO_ModFree(pObj->pCom);
	}
}

void VCO_SetFS(osc_t *pObj, synth_float_t fs)
{
	pObj->fs = fs;
	Sine_SetFS(&pObj->sine, fs);
	BLIT_SetFS(&pObj->blep, fs);
	WT_SetFS(&pObj->wt, fs);

}

void VCO_SetBufsize(osc_t *pObj, UINT32 size)
{
	BLIT_SetBufsize(&pObj->blep, size);

	if (pObj->bufsize == size)
		return;

	pObj->bufsize = size;

	if (pObj->pOut)
		free(pObj->pOut);

	pObj->pOut = NULL;

	if (!size)
		return;

	pObj->pOut = (synth_float_t*)malloc(pObj->bufsize*sizeof(synth_float_t));

}

void VCO_Reset(osc_t *pObj, synth_float_t phase)
{
	Sine_Reset(&pObj->sine, phase);
	BLIT_Reset(&pObj->blep, phase);
	WT_Reset(&pObj->wt, phase);
	pObj->impulse = OSC_IMPULS_HEIGHT;
}

void VCO_Start(osc_t *pObj)
{
	Sine_Start(&pObj->sine);
	BLIT_Start(&pObj->blep);
	WT_Start(&pObj->wt);
}

void VCO_Param2Set(osc_t *pObj, UINT32 type, synth_float_t value)
{
	UINT32 wt_table;

	switch(type)
	{
		case OSC_PARAM2_WAVEFORM:
			if (pObj->param[type] == (UINT32)value)
				break;
			pObj->param[type] = (UINT32)value;
			if ((pObj->param[OSC_PARAM2_WAVEFORM] >= OSC_WAVEFORM_WAVETABLE) && (pObj->param[OSC_PARAM2_WAVEFORM] < (OSC_WAVEFORM_WAVETABLE+WT_NUM_WAVETABLES)))
			{
				wt_table = (UINT32)(pObj->param[OSC_PARAM2_WAVEFORM]-OSC_WAVEFORM_WAVETABLE);
//				WT_Param2Set(&pObj->wt, WT_PARAM2_WAVEFORM, WT_WAVEFORM_REGULAR);
				WT_Param2Set(&pObj->wt, WT_PARAM2_WAVEFORM, WT_WAVEFORM_INTERPOLATED);
				WT_Param2Set(&pObj->wt, WT_PARAM2_WAVETABLE_ID, wt_table);
			}
			Sine_Prepare(&pObj->sine);
			BLIT_Prepare(&pObj->blep, 1024);
			WT_Prepare(&pObj->wt);
			break;

		case OSC_PARAM2_DUTYCYCLE:
			pObj->param[type] = value;
			BLIT_SetDutyCycle(&pObj->blep, value);
			WT_Param2Set(&pObj->wt, WT_PARAM2_WAVETABLE_ENTRY, value);
			break;

		default:
			break;
	}
}

void VCO_ProcessDataV(osc_t *pObj, synth_float_t *pPitch, synth_float_t *pCV_fm, synth_float_t *pCV_pwm, UINT32 *pSyncIn, UINT32 *pSyncOut, UINT32 len)
{
	UINT32 i, is_slave;
	synth_float_t *pOut;
	synth_float_t out;

	pOut = pObj->pOut;

	is_slave = (pSyncIn != NULL) && (pSyncOut == NULL);

	if (pSyncOut)
		memset(pSyncOut, 0, len*sizeof(synth_float_t));

	// Create output
	if (pObj->param[OSC_PARAM2_WAVEFORM] == OSC_WAVEFORM_SAWTOOTH)
	{
		BLIT_Process_SAW_Vector(&pObj->blep, pPitch, pCV_fm, pSyncIn, pSyncOut, pObj->pOut, len);
	}

	if (pObj->param[OSC_PARAM2_WAVEFORM] == OSC_WAVEFORM_SQUARE)
	{
		BLIT_Process_SQR_Vector(&pObj->blep, pPitch, pCV_fm, pCV_pwm, pSyncIn, pSyncOut, pObj->pOut, len);
	}

	if (pObj->param[OSC_PARAM2_WAVEFORM] == OSC_WAVEFORM_TRIANGLE)
	{
		BLIT_Process_TRI_Vector(&pObj->blep, pPitch, pCV_fm, pSyncIn, pSyncOut, pObj->pOut, len);
	}

	if (pObj->param[OSC_PARAM2_WAVEFORM] == OSC_WAVEFORM_SINE)
	{
		for (i=0; i< len; i++)
		{
			out = Sine_Process_Scalar(&pObj->sine, pPitch[i], pCV_fm[i]);
			if(is_slave && pSyncIn[i])
			{
				Sine_Reset(&pObj->sine, 0);
			}
			*(pOut++) = out;
		}
	}

	if (pObj->param[OSC_PARAM2_WAVEFORM] == OSC_WAVEFORM_IMPULSE)
	{
		for (i=0; i< len; i++)
		{
			out = pObj->impulse;
			pObj->impulse = 0;
			*(pOut++) = out;
		}
	}

	if ((pObj->param[OSC_PARAM2_WAVEFORM] >= OSC_WAVEFORM_WAVETABLE) && (pObj->param[OSC_PARAM2_WAVEFORM] < (OSC_WAVEFORM_WAVETABLE+WT_NUM_WAVETABLES)))
	{
		WT_ProcessDataV(&pObj->wt, pPitch, pCV_fm, pCV_pwm, pObj->pOut, len);
	}
}

synth_float_t* VCO_GetProcessBuffer(osc_t *pObj)
{
	return pObj->pOut;
}

