// --------------------------------------------------------------
// --------------------------------------------------------------
#include <stdlib.h>
#include <math.h>
#include <time.h>

#include "synth/synth_types.h"
#include "synth_defs.h"
#include "vector_utils.h"
#include "voice.h"

// --------------------------------------------------------------
// Exported functions
// --------------------------------------------------------------
void Voice_ModInit(voice_common_t *pCom)
{
	int i;
	synth_float_t k;
	synth_float_t a;

	// make frequency (Hz) table
	k = 1.0594630943593;	// 12th root of 2
	a = 440.f/64;	// a
	a *= k;	// b
	a *= k;	// bb
	a *= k;	// c, frequency of midi note 0
	for (i = 0; i < NUM_FREQS; i++)	// 128 midi notes
	{
		pCom->freqTab[i] = (synth_float_t)a;
		a *= k;
	}
	for (i = 0; i < SYNTH_MAX_BUFSIZE; i++)	// 128 midi notes
	{
		pCom->pBuf_zeros[i] = (synth_float_t)0;
		pCom->pBuf_ones[i] = (synth_float_t)1;
	}

}

void Voice_ModFree(voice_common_t *pCom)
{
	(void)pCom;
}

void VoiceInit(voice_t *pObj, voice_common_t *pCom, UINT32 id, synth_float_t fs)
{
	INT32 i;
	pObj->id = id;
	pObj->pCom = pCom;
	pObj->fs = fs;
	pObj->bufsize = 0;

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

	VCF_Init(&pObj->vcf, id, &pCom->vcf, fs);
	for (i = 0; i < VOICE_NUM_ENV; i++)
	{
		ENV_Init(&pObj->env[i], fs);
	}

	for (i = 0; i < VOICE_NUM_OSC; i++)
	{
		Smooth_Init(&pObj->smooth_vco_enable[i], fs, 0.05, 0.05, 0);
		Smooth_Init(&pObj->smooth_vco_portamento[i], fs, 0.001, 0.001, 440);
	}

//	memset(pObj->pParam_smoother, 0, VOICE_NUM_PARAMS*sizeof(smooth_t*));
	
	// Create parameter smoother
	// VOICE_PARAM_VCA_ENV_GAIN
//	pObj->pParam_smoother[VOICE_PARAM_VCA_ENV_GAIN] = (smooth_t*)malloc(sizeof(smooth_t));
//	Smooth_Init(pObj->pParam_smoother[VOICE_PARAM_VCA_ENV_GAIN], fs, 0.01, 0.01, 0);

	for (i = 0; i < VOICE_NUM_OSC; i++)
	{
		VCO_Init(&pObj->vco[i], pObj->id*VOICE_NUM_OSC+i, &pCom->vco, fs);
	}

	for (i = 0; i < VOICE_NUM_LFO; i++)
	{
		LFO_Init(&pObj->lfo[i], fs);
	}

	// First voice initializes global LFOs
	if (pObj->id == 0)
	{
		for (i = 0; i < VOICE_NUM_LFO; i++)
			LFO_Init(&pCom->glfo[i], fs);

		for (i = 0; i < VOICE_NUM_ENV; i++)
			ENV_Init(&pCom->genv[i], fs);

		VCF_Init(&pCom->gvcf[0], MAX_POLYPHONY, &pCom->vcf, fs);
		VCF_Init(&pCom->gvcf[1], MAX_POLYPHONY+1, &pCom->vcf, fs);
		Noise_Init(&pCom->gnoise, 1+(UINT32)clock() * (UINT32)clock());
	}

	pObj->state = note_state_idle;
	pObj->param[VOICE_PARAM_VCA_ENV_GAIN] = (synth_float_t)1;
	pObj->param[VOICE_PARAM_VCF_ENV_GAIN] = (synth_float_t)1;

	pObj->param[VOICE_PARAM_VCF_F] = (synth_float_t)1;

	pObj->param[VOICE_PARAM_VCF_Q] = (synth_float_t)0.7;
	pObj->param[VOICE_PARAM_VCF_Q_GAIN_0] = (synth_float_t)1;
	pObj->param[VOICE_PARAM_VCF_Q_CTRL_0] = (synth_float_t)0;

	pObj->param[VOICE_PARAM_OSC_0_PHASE] = 0;
	pObj->param[VOICE_PARAM_OSC_1_PHASE] = 0;
	pObj->param[VOICE_PARAM_OSC_0_TRANSPOSE] = 0;
	pObj->param[VOICE_PARAM_OSC_1_TRANSPOSE] = 0;
	pObj->param[VOICE_PARAM_KEY] = 128;
	pObj->param[VOICE_PARAM_OSC_0_SCALE] = 1.f;
	pObj->param[VOICE_PARAM_OSC_1_SCALE] = 1.f;
	pObj->param[VOICE_PARAM_MASTER_TUNE] = 1.f;
	pObj->param[VOICE_PARAM_MASTER_FREF] = 1.f;
	pObj->param[VOICE_PARAM_VOICE_PAN] = 0;
	
	pObj->pBuf_Q = NULL;

	VoiceSetBufsize(pObj, SYNTH_MAX_BUFSIZE);
}

void VoiceFree(voice_t *pObj)
{
	int i;

	VoiceSetBufsize(pObj, 0);

	VCF_Free(&pObj->vcf);

	for (i = 0; i < VOICE_NUM_ENV; i++)
		ENV_Free(&pObj->env[i]);
	
	for (i = 0; i < VOICE_NUM_OSC; i++)
		VCO_Free(&pObj->vco[i]);

	for (i = 0; i < VOICE_NUM_LFO; i++)
		LFO_Free(&pObj->lfo[i]);

	// First voice frees global LFOs
	if (pObj->id == 0)
	{
		for (i = 0; i < VOICE_NUM_LFO; i++)
			LFO_Free(&pObj->pCom->glfo[i]);

		for (i = 0; i < VOICE_NUM_ENV; i++)
			ENV_Free(&pObj->pCom->genv[i]);

		VCF_Free(&pObj->pCom->gvcf[0]);
		VCF_Free(&pObj->pCom->gvcf[1]);

		Noise_Free(&pObj->pCom->gnoise);
	}

	for (i = 0; i < VOICE_NUM_OSC; i++)
	{
		Smooth_Free(&pObj->smooth_vco_enable[i]);
		Smooth_Free(&pObj->smooth_vco_portamento[i]);
	}

	// Delete parameter smoother
	// VOICE_PARAM_VCA_ENV_GAIN
//	Smooth_Free(pObj->pParam_smoother[VOICE_PARAM_VCA_ENV_GAIN]);
//	free(pObj->pParam_smoother[VOICE_PARAM_VCA_ENV_GAIN]);

	if (pObj->id == 0)
		Voice_ModFree(pObj->pCom);

}

void VoiceSetFS(voice_t *pObj, synth_float_t fs)
{
	int i;

	pObj->fs = fs;

	for (i = 0; i < VOICE_NUM_ENV; i++)
		ENV_SetFS(&pObj->env[i], fs);

	VCF_SetFS(&pObj->vcf, fs);

	for (i = 0; i < VOICE_NUM_OSC; i++)
		Smooth_SetFS(&pObj->smooth_vco_enable[i], fs);

	for (i = 0; i < VOICE_NUM_OSC; i++)
		VCO_SetFS(&pObj->vco[i], fs);

	for (i = 0; i < VOICE_NUM_LFO; i++)
		LFO_SetFS(&pObj->lfo[i], fs);

	// First voice sets sample rate for global LFOs
	if (pObj->id == 0)
	{
		for (i = 0; i < VOICE_NUM_LFO; i++)
			LFO_SetFS(&pObj->pCom->glfo[i], fs);

		for (i = 0; i < VOICE_NUM_ENV; i++)
			ENV_SetFS(&pObj->pCom->genv[i], fs);

		VCF_SetFS(&pObj->pCom->gvcf[0], fs);
		VCF_SetFS(&pObj->pCom->gvcf[1], fs);
	}

}

void VoiceSetBufsize(voice_t *pObj, UINT32 size)
{
	int i;
	
	for (i = 0; i < VOICE_NUM_OSC; i++)
	{
		VCO_SetBufsize(&pObj->vco[i], size);
	}

	for (i = 0; i < VOICE_NUM_LFO; i++)
	{
		LFO_SetBufsize(&pObj->lfo[i], size);
	}

	// First voice sets bufsize for global LFOs
	if (pObj->id == 0)
	{
		for (i = 0; i < VOICE_NUM_LFO; i++)
			LFO_SetBufsize(&pObj->pCom->glfo[i], size);

		for (i = 0; i < VOICE_NUM_ENV; i++)
			ENV_SetBufsize(&pObj->pCom->genv[i], size);
	
		VCF_SetBufsize(&pObj->pCom->gvcf[0], size);
		VCF_SetBufsize(&pObj->pCom->gvcf[1], size);
	}

	VCF_SetBufsize(&pObj->vcf, size);

	for (i = 0; i < VOICE_NUM_ENV; i++)
		ENV_SetBufsize(&pObj->env[i], size);

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

	pObj->bufsize = size;

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

	pObj->pBuf_Q = NULL;

	if (!size)
		return;

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

}

void VoiceEvent(voice_t *pObj, UINT32 type)
{
	int i;

	switch(type)
	{
		case voice_event_trigger:
		case voice_event_retrigger:
		for (i = 0; i < VOICE_NUM_ENV; i++)
			ENV_Event(&pObj->env[i], env_event_retrigger);

		VCF_Coeff_update(&pObj->vcf);

		if (pObj->state == note_state_idle)
			VCF_clear_state(&pObj->vcf);

		pObj->state = note_state_triggered;

		for (i = 0; i < VOICE_NUM_OSC; i++)
		{
			if ((int)pObj->param[VOICE_PARAM_OSC_0_RESTART_ON_TRIGGER+i] > 0.5)
				if (type == voice_event_trigger)
					VCO_Reset(&pObj->vco[i], pObj->param[VOICE_PARAM_OSC_0_PHASE+i]);

			VCO_Start(&pObj->vco[i]);
			Smooth_SetValue(&pObj->smooth_vco_enable[i], pObj->param[VOICE_PARAM_OSC_0_ENABLE+i]*((synth_float_t)1E-6+pObj->param[VOICE_PARAM_OSC_0_LEVEL+i]));
		}

		for (i = 0; i < VOICE_NUM_LFO; i++)
		{
			if ((int)pObj->param[VOICE_PARAM_LFO_0_SYNC+i] == VOICE_LFO_SYNC_RESTART)
				LFO_Reset(&pObj->lfo[i], 0);
		}
		
		// Global events
		for (i = 0; i < VOICE_NUM_LFO; i++)
		{
			if ((int)pObj->param[VOICE_PARAM_LFO_0_SYNC+i] == VOICE_LFO_SYNC_RESTART)
				LFO_Reset(&pObj->pCom->glfo[i], 0);
		}
		for (i = 0; i < VOICE_NUM_ENV; i++)
			ENV_Event(&pObj->pCom->genv[i], env_event_retrigger);

		VCF_Coeff_update(&pObj->pCom->gvcf[0]);
		VCF_Coeff_update(&pObj->pCom->gvcf[1]);
		break;

		case voice_event_shutdown:
		case voice_event_release:
		for (i = 0; i < VOICE_NUM_ENV; i++)
			ENV_Event(&pObj->pCom->genv[i], env_event_release);

		if (pObj->state == note_state_idle)
			break;

		for (i = 0; i < VOICE_NUM_ENV; i++)
			ENV_Event(&pObj->env[i], env_event_release);

		pObj->state = note_state_released;
		break;

		default:
			break;
	}

};

synth_float_t getKeyFreq(voice_t *pObj, int key)
{
	int index = jsy_max(0, jsy_min(NUM_FREQS-1, key));
	return pObj->pCom->freqTab[index];
}

synth_float_t getPitch(voice_t *pObj, UINT32 osc_id)
{
	synth_float_t pitch;

	pitch = getKeyFreq(pObj, (int)pObj->param[VOICE_PARAM_KEY] + (int)pObj->param[VOICE_PARAM_OSC_0_TRANSPOSE+osc_id])
			* pObj->param[VOICE_PARAM_OSC_0_SCALE+osc_id]
			* pObj->param[VOICE_PARAM_OSC_0_DETUNE+osc_id]
			* pObj->param[VOICE_PARAM_MASTER_TUNE]
			* pObj->param[VOICE_PARAM_MASTER_FREF];

	return pitch;
}

void VoiceParam2Set(voice_t *pObj, UINT32 type, synth_float_t value)
{
	pObj->param[type] = value;

	switch(type)
	{
		case VOICE_PARAM_VOICE_PAN:
		if (pObj->id % 2)
			pObj->param[type] = jsy_min(1, jsy_max(-1, -value/100));
		else
			pObj->param[type] = jsy_min(1, jsy_max(-1, value/100));
		break;

		case VOICE_PARAM_PORTAMENTO_TIME:
		Smooth_SetT(&pObj->smooth_vco_portamento[0], value, value);
		Smooth_SetT(&pObj->smooth_vco_portamento[1], value, value);
		break;

		case VOICE_PARAM_MASTER_FREF:
		pObj->param[type] = value/440;
		pObj->vco_pitch[0] = getPitch(pObj, 0);
		pObj->vco_pitch[1] = getPitch(pObj, 1);
		break;

		case VOICE_PARAM_MASTER_TUNE:
		pObj->param[type] = pow(2, (double)value/(double)(12*100));
		pObj->vco_pitch[0] = getPitch(pObj, 0);
		pObj->vco_pitch[1] = getPitch(pObj, 1);
		break;

		case VOICE_PARAM_OSC_0_WAVEFORM:
		case VOICE_PARAM_OSC_1_WAVEFORM:
		VCO_Param2Set(&pObj->vco[type-VOICE_PARAM_OSC_0_WAVEFORM], OSC_PARAM2_WAVEFORM, (int)pObj->param[type]);
		break;

		case VOICE_PARAM_OSC_0_SCALE:
		case VOICE_PARAM_OSC_1_SCALE:
		pObj->vco_pitch[type-VOICE_PARAM_OSC_0_SCALE] = getPitch(pObj, type-VOICE_PARAM_OSC_0_SCALE);
		break;

		case VOICE_PARAM_OSC_0_TRANSPOSE:
		case VOICE_PARAM_OSC_1_TRANSPOSE:
		pObj->param[type] = (int)value;
		pObj->vco_pitch[type-VOICE_PARAM_OSC_0_TRANSPOSE] = getPitch(pObj, type-VOICE_PARAM_OSC_0_TRANSPOSE);
		break;

		case VOICE_PARAM_OSC_0_DETUNE:
		case VOICE_PARAM_OSC_1_DETUNE:
		pObj->param[type] =	pow(2, (double)value/(double)(12*100));
		pObj->vco_pitch[type-VOICE_PARAM_OSC_0_DETUNE] = getPitch(pObj, type-VOICE_PARAM_OSC_0_DETUNE);
		break;

		case VOICE_PARAM_OSC_0_DUTYCYCLE:
		case VOICE_PARAM_OSC_1_DUTYCYCLE:
		pObj->param[type] = value / 100;
		VCO_Param2Set(&pObj->vco[type-VOICE_PARAM_OSC_0_DUTYCYCLE], OSC_PARAM2_DUTYCYCLE, pObj->param[type]);
		break;

		case VOICE_PARAM_KEY:
		pObj->vco_pitch[0] = getPitch(pObj, 0);
		pObj->vco_pitch[1] = getPitch(pObj, 1);
		break;

		case VOICE_PARAM_OSC_0_FM_AMT_0:
		case VOICE_PARAM_OSC_1_FM_AMT_0:
		pObj->param[type] = value / 12;
		break;

		case VOICE_PARAM_OSC_0_PWM_AMT_0:
		case VOICE_PARAM_OSC_1_PWM_AMT_0:
		case VOICE_PARAM_OSC_0_PWM_AMT_1:
		case VOICE_PARAM_OSC_1_PWM_AMT_1:
		case VOICE_PARAM_OSC_0_AM_AMT_0:
		case VOICE_PARAM_OSC_1_AM_AMT_0:
		case VOICE_PARAM_OSC_0_AM_AMT_1:
		case VOICE_PARAM_OSC_1_AM_AMT_1:
		case VOICE_PARAM_OSC_0_FM_AMT_1:
		case VOICE_PARAM_OSC_1_FM_AMT_1:
		case VOICE_PARAM_OSC_0_LEVEL:
		case VOICE_PARAM_OSC_1_LEVEL:
		case VOICE_PARAM_VCA_ENV_GAIN:
		case VOICE_PARAM_VCF_ENV_GAIN:
		case VOICE_PARAM_VCF_F_AMT_0:
		case VOICE_PARAM_VCF_Q_AMT_0:
		case VOICE_PARAM_VCF_F_AMT_1:
		case VOICE_PARAM_VCF_Q_AMT_1:
		case VOICE_PARAM_NOISE_LEVEL:
		case VOICE_PARAM_VCA_AM_AMT_0:
		case VOICE_PARAM_VCA_PAN_AMT_0:
		case VOICE_PARAM_VCF_Q_GAIN_0:
		case VOICE_PARAM_VCF_Q_CTRL_0:
		pObj->param[type] = value / 100;
		break;

		case VOICE_PARAM_OSC_0_FM_SRC_0:
		case VOICE_PARAM_OSC_1_FM_SRC_0:
		case VOICE_PARAM_OSC_0_AM_SRC_0:
		case VOICE_PARAM_OSC_1_AM_SRC_0:
		case VOICE_PARAM_OSC_0_PWM_SRC_0:
		case VOICE_PARAM_OSC_1_PWM_SRC_0:
		case VOICE_PARAM_VCF_F_SRC_0:
		case VOICE_PARAM_VCF_Q_SRC_0:
		case VOICE_PARAM_VCA_AM_SRC_0:
		case VOICE_PARAM_VCA_PAN_SRC_0:
		case VOICE_PARAM_OSC_0_FM_SRC_1:
		case VOICE_PARAM_OSC_1_FM_SRC_1:
		case VOICE_PARAM_OSC_0_AM_SRC_1:
		case VOICE_PARAM_OSC_1_AM_SRC_1:
		case VOICE_PARAM_OSC_0_PWM_SRC_1:
		case VOICE_PARAM_OSC_1_PWM_SRC_1:
		case VOICE_PARAM_VCF_F_SRC_1:
		case VOICE_PARAM_VCF_Q_SRC_1:
			pObj->param[type] = (int)value;
		break;

		case VOICE_PARAM_OSC_0_FM_SRC_OP:
		case VOICE_PARAM_OSC_1_FM_SRC_OP:
		case VOICE_PARAM_OSC_0_AM_SRC_OP:
		case VOICE_PARAM_OSC_1_AM_SRC_OP:
		case VOICE_PARAM_OSC_0_PWM_SRC_OP:
		case VOICE_PARAM_OSC_1_PWM_SRC_OP:
		case VOICE_PARAM_VCF_F_SRC_OP:
		case VOICE_PARAM_VCF_Q_SRC_OP:
			pObj->param[type] = (int)value;
		break;

		case VOICE_PARAM_VCF_TYPE:
			VCF_SetType(&pObj->vcf, (int)pObj->param[type]);
			if (pObj->id == 0)
			{
				VCF_SetType(&pObj->pCom->gvcf[0], (UINT32)pObj->param[type]);
				VCF_SetType(&pObj->pCom->gvcf[1], (UINT32)pObj->param[type]);
			}
		break;

		case VOICE_PARAM_VCF_ORDER:
			VCF_SetOrder(&pObj->vcf, (int)(pObj->param[type]+1)*2);
			if (pObj->id == 0)
			{
				VCF_SetOrder(&pObj->pCom->gvcf[0], (UINT32)(pObj->param[type]+1)*2);
				VCF_SetOrder(&pObj->pCom->gvcf[1], (UINT32)(pObj->param[type]+1)*2);
			}
		break;

		case VOICE_PARAM_VCF_F:
			VCF_SetF(&pObj->vcf, pObj->param[type]);
			if (pObj->id == 0)
			{
				VCF_SetF(&pObj->pCom->gvcf[0], pObj->param[type]);
				VCF_SetF(&pObj->pCom->gvcf[1], pObj->param[type]);
			}
		break;

		case VOICE_PARAM_VCF_Q:
			VCF_SetQ(&pObj->vcf, pObj->param[type]);
			if (pObj->id == 0)
			{
				VCF_SetQ(&pObj->pCom->gvcf[0], pObj->param[type]);
				VCF_SetQ(&pObj->pCom->gvcf[1], pObj->param[type]);
			}
		break;

		case VOICE_PARAM_ENV_0_A:
		case VOICE_PARAM_ENV_1_A:
		case VOICE_PARAM_ENV_2_A:
		case VOICE_PARAM_ENV_3_A:
		pObj->param[type] = value / 1000;
		ENV_Param2Set(&pObj->env[type-VOICE_PARAM_ENV_0_A], ENV_PARAM2_A, pObj->param[type]);
		if (pObj->id == 0)
		{
			ENV_Param2Set(&pObj->pCom->genv[type-VOICE_PARAM_ENV_0_A], ENV_PARAM2_A, pObj->param[type]);
		}
		break;

		case VOICE_PARAM_ENV_0_D:
		case VOICE_PARAM_ENV_1_D:
		case VOICE_PARAM_ENV_2_D:
		case VOICE_PARAM_ENV_3_D:
		pObj->param[type] = value / 1000;
		if (type == VOICE_PARAM_VCF_ENV_D)
		{
			pObj->param[type] = 3*value / 1000;
		}
		ENV_Param2Set(&pObj->env[type-VOICE_PARAM_ENV_0_D], ENV_PARAM2_D, pObj->param[type]);
		if (pObj->id == 0)
		{
			ENV_Param2Set(&pObj->pCom->genv[type-VOICE_PARAM_ENV_0_D], ENV_PARAM2_D, pObj->param[type]);
		}
		break;

		case VOICE_PARAM_ENV_0_S:
		case VOICE_PARAM_ENV_1_S:
		case VOICE_PARAM_ENV_2_S:
		case VOICE_PARAM_ENV_3_S:
		pObj->param[type] = value / 100;
		ENV_Param2Set(&pObj->env[type-VOICE_PARAM_ENV_0_S], ENV_PARAM2_S, pObj->param[type]);
		if (pObj->id == 0)
		{
			ENV_Param2Set(&pObj->pCom->genv[type-VOICE_PARAM_ENV_0_S], ENV_PARAM2_S, pObj->param[type]);
		}
		break;

		case VOICE_PARAM_ENV_0_R:
		case VOICE_PARAM_ENV_1_R:
		case VOICE_PARAM_ENV_2_R:
		case VOICE_PARAM_ENV_3_R:
		pObj->param[type] = value / 1000;
		if (type == VOICE_PARAM_VCF_ENV_R)
		{
			pObj->param[type] = 3*value / 1000;
		}
		ENV_Param2Set(&pObj->env[type-VOICE_PARAM_ENV_0_R], ENV_PARAM2_R, pObj->param[type]);
		if (pObj->id == 0)
		{
			ENV_Param2Set(&pObj->pCom->genv[type-VOICE_PARAM_ENV_0_R], ENV_PARAM2_R, pObj->param[type]);
		}
		break;

		case VOICE_PARAM_LFO_0_MODE:
		case VOICE_PARAM_LFO_1_MODE:
		case VOICE_PARAM_LFO_2_MODE:
		case VOICE_PARAM_LFO_3_MODE:
		case VOICE_PARAM_LFO_0_SYNC:
		case VOICE_PARAM_LFO_1_SYNC:
		case VOICE_PARAM_LFO_2_SYNC:
		case VOICE_PARAM_LFO_3_SYNC:
		break;

		case VOICE_PARAM_LFO_0_SPEED:
		case VOICE_PARAM_LFO_1_SPEED:
		case VOICE_PARAM_LFO_2_SPEED:
		case VOICE_PARAM_LFO_3_SPEED:
		LFO_Param2Set(&pObj->lfo[type-VOICE_PARAM_LFO_0_SPEED], LFO_PARAM2_FREQ, pObj->param[type]);

		// First voice sets params for global LFOs
		if (pObj->id == 0)
		{
			LFO_Param2Set(&pObj->pCom->glfo[type-VOICE_PARAM_LFO_0_SPEED], LFO_PARAM2_FREQ, pObj->param[type]);
		}
		break;

		case VOICE_PARAM_LFO_0_SMOOTH:
		case VOICE_PARAM_LFO_1_SMOOTH:
		case VOICE_PARAM_LFO_2_SMOOTH:
		case VOICE_PARAM_LFO_3_SMOOTH:
		pObj->param[type] = value / 1000;
		LFO_Param2Set(&pObj->lfo[type-VOICE_PARAM_LFO_0_SMOOTH], LFO_PARAM2_SMOOTH, pObj->param[type]);

		// First voice sets params for global LFOs
		if (pObj->id == 0)
		{
			LFO_Param2Set(&pObj->pCom->glfo[type-VOICE_PARAM_LFO_0_SMOOTH], LFO_PARAM2_SMOOTH, pObj->param[type]);
		}
		break;

		case VOICE_PARAM_LFO_0_DELAY:
		case VOICE_PARAM_LFO_1_DELAY:
		case VOICE_PARAM_LFO_2_DELAY:
		case VOICE_PARAM_LFO_3_DELAY:
		pObj->param[type] = value / 1000;
		LFO_Param2Set(&pObj->lfo[type-VOICE_PARAM_LFO_0_DELAY], LFO_PARAM2_DELAY, pObj->param[type]);

		// First voice sets params for global LFOs
		if (pObj->id == 0)
		{
			LFO_Param2Set(&pObj->pCom->glfo[type-VOICE_PARAM_LFO_0_DELAY], LFO_PARAM2_DELAY, pObj->param[type]);
		}
		break;

		case VOICE_PARAM_LFO_0_ATTACK:
		case VOICE_PARAM_LFO_1_ATTACK:
		case VOICE_PARAM_LFO_2_ATTACK:
		case VOICE_PARAM_LFO_3_ATTACK:
		pObj->param[type] = value / 1000;
		LFO_Param2Set(&pObj->lfo[type-VOICE_PARAM_LFO_0_ATTACK], LFO_PARAM2_ATTACK, pObj->param[type]);

		// First voice sets params for global LFOs
		if (pObj->id == 0)
		{
			LFO_Param2Set(&pObj->pCom->glfo[type-VOICE_PARAM_LFO_0_ATTACK], LFO_PARAM2_ATTACK, pObj->param[type]);
		}
		break;

		case VOICE_PARAM_LFO_0_SHAPE:
		case VOICE_PARAM_LFO_1_SHAPE:
		case VOICE_PARAM_LFO_2_SHAPE:
		case VOICE_PARAM_LFO_3_SHAPE:
		LFO_Param2Set(&pObj->lfo[type-VOICE_PARAM_LFO_0_SHAPE], LFO_PARAM2_WAVEFORM, (int)pObj->param[type]);

		// First voice sets params for global LFOs
		if (pObj->id == 0)
		{
			LFO_Param2Set(&pObj->pCom->glfo[type-VOICE_PARAM_LFO_0_SHAPE], LFO_PARAM2_WAVEFORM, pObj->param[type]);
		}
		break;

		case VOICE_PARAM_LFO_0_SYMMETRY:
		case VOICE_PARAM_LFO_1_SYMMETRY:
		case VOICE_PARAM_LFO_2_SYMMETRY:
		case VOICE_PARAM_LFO_3_SYMMETRY:
		pObj->param[type] = value / 100;
		LFO_Param2Set(&pObj->lfo[type-VOICE_PARAM_LFO_0_SYMMETRY], LFO_PARAM2_SYMMETRY, pObj->param[type]);

		// First voice sets params for global LFOs
		if (pObj->id == 0)
		{
			LFO_Param2Set(&pObj->pCom->glfo[type-VOICE_PARAM_LFO_0_SYMMETRY], LFO_PARAM2_SYMMETRY, pObj->param[type]);
		}
		break;

		case VOICE_PARAM_LFO_0_MIDISYNC_MODE:
		case VOICE_PARAM_LFO_1_MIDISYNC_MODE:
		case VOICE_PARAM_LFO_2_MIDISYNC_MODE:
		case VOICE_PARAM_LFO_3_MIDISYNC_MODE:
		LFO_Param2Set(&pObj->lfo[type-VOICE_PARAM_LFO_0_MIDISYNC_MODE], LFO_PARAM2_MIDISYNC_MODE, (int)pObj->param[type]);

		// First voice sets params for global LFOs
		if (pObj->id == 0)
		{
			LFO_Param2Set(&pObj->pCom->glfo[type-VOICE_PARAM_LFO_0_MIDISYNC_MODE], LFO_PARAM2_MIDISYNC_MODE, pObj->param[type]);
		}
		break;

		case VOICE_PARAM_LFO_0_MIDISYNC_BEATDIV:
		case VOICE_PARAM_LFO_1_MIDISYNC_BEATDIV:
		case VOICE_PARAM_LFO_2_MIDISYNC_BEATDIV:
		case VOICE_PARAM_LFO_3_MIDISYNC_BEATDIV:
		LFO_Param2Set(&pObj->lfo[type-VOICE_PARAM_LFO_0_MIDISYNC_BEATDIV], LFO_PARAM2_MIDISYNC_BEATDIV, (int)pObj->param[type]);

		// First voice sets params for global LFOs
		if (pObj->id == 0)
		{
			LFO_Param2Set(&pObj->pCom->glfo[type-VOICE_PARAM_LFO_0_MIDISYNC_BEATDIV], LFO_PARAM2_MIDISYNC_BEATDIV, pObj->param[type]);
		}
		break;

		default:
			break;
	}
}

synth_float_t VoiceParamGet(voice_t *pObj, UINT32 type)
{
	return pObj->param[type];
}

UINT32 VoiceStateGet(voice_t *pObj)
{
	return pObj->state;
}

void VoiceProcessDataV_Common(voice_t *pObj, float *pOut1, float *pOut2, UINT32 len)
{
	UINT32 i;

	(void)pOut1;
	(void)pOut2;

	voice_common_t *pCom = pObj->pCom;

	// First voice only
	if (pObj->id == 0)
	{
		//  Processes global LFOs
		for (i=0; i < VOICE_NUM_LFO; i++)
		{
			if ((int)pObj->param[VOICE_PARAM_LFO_0_MODE+i] == VOICE_LFO_MODE_GLOBAL)
			{
				pCom->pBuf_LFO_gbl[i] = LFO_ProcessDataV(&pCom->glfo[i], len);
			}
		}

		// Zero noise output
		for (i=0; i< len; i++)
		{
			pCom->noise_out[i] = 0;
		}

#if 0
		// Process global envelope generators
		for (i=0; i < (INT32)VOICE_NUM_ENV; i++)
		{
			pCom->pBuf_ENV_gbl[i] = ENV_ProcessDataV(&pCom->genv[i], len);
		}

		// Assign modulation sources
		i = 0;
		pModSrc[i++] = pCom->pBuf_zeros;	// Off
		pModSrc[i++] = pCom->pBuf_LFO_gbl[0];
		pModSrc[i++] = pCom->pBuf_LFO_gbl[1];
		pModSrc[i++] = pCom->pBuf_LFO_gbl[2];
		pModSrc[i++] = pCom->pBuf_LFO_gbl[3];
		pModSrc[i++] = pCom->pBuf_ENV_gbl[0];			// Env. 0
		pModSrc[i++] = pCom->pBuf_ENV_gbl[1];			// Env. 1
		pModSrc[i++] = pCom->pBuf_ENV_gbl[2];			// Env. 2
		pModSrc[i++] = pCom->pBuf_ENV_gbl[3];			// Env. 3
		pModSrc[i++] = pCom->pBuf_zeros;	// VCO 1 AM/FM
		pModSrc[i++] = pCom->pBuf_zeros;	// VCO 2 AM/FM

		// Mix Cutoff modulator
		env_gain = pObj->param[VOICE_PARAM_VCF_ENV_GAIN];

		if ((int)pObj->param[VOICE_PARAM_VCF_F_SRC_1] == VOICE_MOD_SRC_1_DISABLED)
		{
			Add_VS(vcf_fm, pModSrc[(int)pObj->param[VOICE_PARAM_VCF_F_SRC_0]], 1.0, 0, len);
		}
		else
		{
			switch((int)pObj->param[VOICE_PARAM_VCF_F_SRC_OP])
			{
				default:
				case VOICE_MOD_SRC_OP_ADD:
				Add_VV(vcf_fm, pModSrc[(int)pObj->param[VOICE_PARAM_VCF_F_SRC_0]], 1.0, pModSrc[(int)pObj->param[VOICE_PARAM_VCF_F_SRC_1]], pObj->param[VOICE_PARAM_VCF_F_AMT_1], len);
				break;

				case VOICE_MOD_SRC_OP_MUL:
				Mul_VV(vcf_fm, pModSrc[(int)pObj->param[VOICE_PARAM_VCF_F_SRC_0]], pModSrc[(int)pObj->param[VOICE_PARAM_VCF_F_SRC_1]], len);
				if (pObj->param[VOICE_PARAM_VCF_F_AMT_1] <= 0)
					Add_inplace_VV(vcf_fm, pObj->param[VOICE_PARAM_VCF_F_AMT_1], pModSrc[(int)pObj->param[VOICE_PARAM_VCF_F_SRC_0]], 1, len);
				else
					Add_inplace_VV(vcf_fm, pObj->param[VOICE_PARAM_VCF_F_AMT_1], pModSrc[(int)pObj->param[VOICE_PARAM_VCF_F_SRC_0]], 0, len);

				break;
			}
		}
		Add_inplace_VV(vcf_fm, pObj->param[VOICE_PARAM_VCF_F_AMT_0], pCom->pBuf_ENV_gbl[VOICE_ENV_VCF], env_gain, len);
		Clamp_inplace_V(vcf_fm, -1, 1, len);

		// Mix resonance modulator
		if ((int)pObj->param[VOICE_PARAM_VCF_Q_SRC_1] == VOICE_MOD_SRC_1_DISABLED)
		{
			Add_VS(vcf_qm, pModSrc[(int)pObj->param[VOICE_PARAM_VCF_Q_SRC_0]], 1.0, 0, len);
		}
		else
		{
			switch((int)pObj->param[VOICE_PARAM_VCF_Q_SRC_OP])
			{
				default:
				case VOICE_MOD_SRC_OP_ADD:
				Add_VV(vcf_qm, pModSrc[(int)pObj->param[VOICE_PARAM_VCF_Q_SRC_0]], 1.0, pModSrc[(int)pObj->param[VOICE_PARAM_VCF_Q_SRC_1]], pObj->param[VOICE_PARAM_VCF_Q_AMT_1], len);
				break;

				case VOICE_MOD_SRC_OP_MUL:
				Mul_VV(vcf_qm, pModSrc[(int)pObj->param[VOICE_PARAM_VCF_Q_SRC_0]], pModSrc[(int)pObj->param[VOICE_PARAM_VCF_Q_SRC_1]], len);
				if (pObj->param[VOICE_PARAM_VCF_Q_AMT_1] <= 0)
					Add_inplace_VV(vcf_qm, pObj->param[VOICE_PARAM_VCF_Q_AMT_1], pModSrc[(int)pObj->param[VOICE_PARAM_VCF_Q_SRC_0]], 1, len);
				else
					Add_inplace_VV(vcf_qm, pObj->param[VOICE_PARAM_VCF_Q_AMT_1], pModSrc[(int)pObj->param[VOICE_PARAM_VCF_Q_SRC_0]], 0, len);
				break;
			}
		}
		Mul_inplace_VS(vcf_qm, pObj->param[VOICE_PARAM_VCF_Q_AMT_0], len);
		Clamp_inplace_V(vcf_qm, -1, 1, len);

		for (i=0; i< len; i++)
			temp[i] = pOut1[i];

		pCom->pBuf_VCF_gbl_out = VCF_ProcessDataV(&pObj->pCom->gvcf[0], vcf_fm, vcf_qm, temp, len);
		for (i=0; i< len; i++)
			pOut1[i] = pCom->pBuf_VCF_gbl_out[i];

		for (i=0; i< len; i++)
			temp[i] = pOut2[i];

		pCom->pBuf_VCF_gbl_out = VCF_ProcessDataV(&pObj->pCom->gvcf[1], vcf_fm, vcf_qm, temp, len);
		for (i=0; i< len; i++)
			pOut2[i] = pCom->pBuf_VCF_gbl_out[i];

#endif
		if (pObj->param[VOICE_PARAM_NOISE_ENABLE] > (synth_float_t)0.5)
		{
			if ((int)pObj->param[VOICE_PARAM_NOISE_SHAPE] == VOICE_NOISE_SHAPE_WHITE)
			{
				for (i=0; i< len; i++)
				{
					pCom->noise_out[i] = Noise_Uniform(&pCom->gnoise, 2.0f, 0.0);
				}
			}

			if ((int)pObj->param[VOICE_PARAM_NOISE_SHAPE] == VOICE_NOISE_SHAPE_PINK)
			{
				for (i=0; i< len; i++)
				{
					pCom->noise_out[i] = Noise_Gaussian(&pCom->gnoise, 2*0.28867513459481f, 0.0);
				}
			}
		}
	}

}

void VoiceProcessDataV(voice_t *pObj, float *pOut1, float *pOut2, UINT32 len)
{
	int i, j;
	synth_float_t out, env_gain;
	synth_float_t *pBuf_ENV[VOICE_NUM_ENV], *pBuf_Postvcf;
	synth_float_t *pBuf_OSC[VOICE_NUM_OSC];
	synth_float_t *pBuf_LFO[VOICE_NUM_LFO];
	UINT32 vco_is_disabled[VOICE_NUM_OSC];
	synth_float_t *pModSrc[VOICE_NUM_MOD_SOURCES];
	voice_common_t *pCom = pObj->pCom;

	// Temp Variables
	synth_float_t VCO_fmout[VOICE_NUM_OSC][SYNTH_MAX_BUFSIZE];
	synth_float_t vco_pwm[SYNTH_MAX_BUFSIZE];
	synth_float_t vco_fm[SYNTH_MAX_BUFSIZE];
	synth_float_t vco_am[SYNTH_MAX_BUFSIZE];
	synth_float_t vcf_fm[SYNTH_MAX_BUFSIZE];
	synth_float_t vcf_qm[SYNTH_MAX_BUFSIZE];
	synth_float_t vca_am[SYNTH_MAX_BUFSIZE];
	synth_float_t vca_pan[SYNTH_MAX_BUFSIZE];
	synth_float_t vco_pitch[VOICE_NUM_OSC][SYNTH_MAX_BUFSIZE];
	synth_float_t osc_out[SYNTH_MAX_BUFSIZE];
	synth_float_t osc_out_smoothed[VOICE_NUM_OSC][SYNTH_MAX_BUFSIZE];

#if 0
	// Process parameter smoothing
	for (i=0; i < (INT32)VOICE_NUM_PARAMS; i++)
	{
		if (pObj->pParam_smoother[i])
		{
			if (!Smooth_is_settled(pObj->pParam_smoother[i], pObj->param[i]))
			{
				Smooth_Process_V(pObj->pParam_smoother[i], pObj->param[i], pObj->param_smoothed[i], len);
			}
		}
	}
#endif

	// Process envelope generators
	for (i=0; i < (INT32)VOICE_NUM_ENV; i++)
	{
		pBuf_ENV[i] = ENV_ProcessDataV(&pObj->env[i], len);
	}

	// Process LFOs
	for (i=0; i < (INT32)VOICE_NUM_LFO; i++)
	{
		if ((int)pObj->param[VOICE_PARAM_LFO_0_MODE+i] == VOICE_LFO_MODE_INDIVIDUAL)
		{
			pBuf_LFO[i] = LFO_ProcessDataV(&pObj->lfo[i], len);
		}
	}

	// Use global LFO
	for (i=0; i < (INT32)VOICE_NUM_LFO; i++)
	{
		if ((int)pObj->param[VOICE_PARAM_LFO_0_MODE+i] == VOICE_LFO_MODE_GLOBAL)
		{
			pBuf_LFO[i] = pCom->pBuf_LFO_gbl[i];
		}
	}

	// Assign modulation sources
	i = 0;
	pModSrc[i++] = pCom->pBuf_zeros;	// Off
	pModSrc[i++] = pBuf_LFO[0];
	pModSrc[i++] = pBuf_LFO[1];
	pModSrc[i++] = pBuf_LFO[2];
	pModSrc[i++] = pBuf_LFO[3];
	pModSrc[i++] = pBuf_ENV[0];			// Env. 0
	pModSrc[i++] = pBuf_ENV[1];			// Env. 1
	pModSrc[i++] = pBuf_ENV[2];			// Env. 2
	pModSrc[i++] = pBuf_ENV[3];			// Env. 3
	pModSrc[i++] = pCom->pBuf_zeros;	// VCO 1 AM/FM
	pModSrc[i++] = pCom->pBuf_zeros;	// VCO 2 AM/FM

	// Process smoothing for VCO switching
	for (i=0; i < (INT32)VOICE_NUM_OSC; i++)
	{
		vco_is_disabled[i] = Smooth_is_settled(&pObj->smooth_vco_enable[i], 0) && (UINT32)(pObj->param[VOICE_PARAM_OSC_0_ENABLE+i] < (synth_float_t)0.5);
	}

	// Process pitche for VCOs
	for (i=0; i < (INT32)VOICE_NUM_OSC; i++)
	{
		if (pObj->param[VOICE_PARAM_PORTAMENTO_TIME] <= (synth_float_t)0.001)
		{
			Add_inplace_VS(vco_pitch[i], 0, pObj->vco_pitch[i], len);
		}
		else
		{
			Smooth_Process_V(&pObj->smooth_vco_portamento[i], pObj->vco_pitch[i], vco_pitch[i], len);
		}
	}

	// Process Oscillators
	pBuf_OSC[0] = VCO_GetProcessBuffer(&pObj->vco[0]);
	if (!vco_is_disabled[0])
	{
		// FM 0
		if ((int)pObj->param[VOICE_PARAM_OSC_0_FM_SRC_1] == VOICE_MOD_SRC_1_DISABLED)
		{
			Add_VS(vco_fm, pModSrc[(int)pObj->param[VOICE_PARAM_OSC_0_FM_SRC_0]], 1.0, 0, len);
		}
		else
		{
			switch((int)pObj->param[VOICE_PARAM_OSC_0_FM_SRC_OP])
			{
				default:
				case VOICE_MOD_SRC_OP_ADD:
				Add_VV(vco_fm, pModSrc[(int)pObj->param[VOICE_PARAM_OSC_0_FM_SRC_0]], 1.0, pModSrc[(int)pObj->param[VOICE_PARAM_OSC_0_FM_SRC_1]], pObj->param[VOICE_PARAM_OSC_0_FM_AMT_1], len);
					break;

				case VOICE_MOD_SRC_OP_MUL:
				Mul_VV(vco_fm, pModSrc[(int)pObj->param[VOICE_PARAM_OSC_0_FM_SRC_0]], pModSrc[(int)pObj->param[VOICE_PARAM_OSC_0_FM_SRC_1]], len);
				if (pObj->param[VOICE_PARAM_OSC_0_FM_AMT_1] <= 0)
					Add_inplace_VV(vco_fm, pObj->param[VOICE_PARAM_OSC_0_FM_AMT_1], pModSrc[(int)pObj->param[VOICE_PARAM_OSC_0_FM_SRC_0]], 1, len);
				else
					Add_inplace_VV(vco_fm, pObj->param[VOICE_PARAM_OSC_0_FM_AMT_1], pModSrc[(int)pObj->param[VOICE_PARAM_OSC_0_FM_SRC_0]], 0, len);
				break;
			}
		}
		Clamp_inplace_V(vco_fm, -1, 1, len);
		Mul_inplace_VS(vco_fm, pObj->param[VOICE_PARAM_OSC_0_FM_AMT_0], len);

		// PWM 0
		if ((int)pObj->param[VOICE_PARAM_OSC_0_PWM_SRC_1] == VOICE_MOD_SRC_1_DISABLED)
		{
			Add_VS(vco_pwm, pModSrc[(int)pObj->param[VOICE_PARAM_OSC_0_PWM_SRC_0]], 1.0, 0, len);
		}
		else
		{
			switch((int)pObj->param[VOICE_PARAM_OSC_0_PWM_SRC_OP])
			{
				default:
				case VOICE_MOD_SRC_OP_ADD:
				Add_VV(vco_pwm, pModSrc[(int)pObj->param[VOICE_PARAM_OSC_0_PWM_SRC_0]], 1.0, pModSrc[(int)pObj->param[VOICE_PARAM_OSC_0_PWM_SRC_1]], pObj->param[VOICE_PARAM_OSC_0_PWM_AMT_1], len);
					break;

				case VOICE_MOD_SRC_OP_MUL:
				Mul_VV(vco_pwm, pModSrc[(int)pObj->param[VOICE_PARAM_OSC_0_PWM_SRC_0]], pModSrc[(int)pObj->param[VOICE_PARAM_OSC_0_PWM_SRC_1]], len);
				if (pObj->param[VOICE_PARAM_OSC_0_PWM_AMT_1] <= 0)
					Add_inplace_VV(vco_pwm, pObj->param[VOICE_PARAM_OSC_0_PWM_AMT_1], pModSrc[(int)pObj->param[VOICE_PARAM_OSC_0_PWM_SRC_0]], 1, len);
				else
					Add_inplace_VV(vco_pwm, pObj->param[VOICE_PARAM_OSC_0_PWM_AMT_1], pModSrc[(int)pObj->param[VOICE_PARAM_OSC_0_PWM_SRC_0]], 0, len);
				break;
			}
		}
		Mul_inplace_VS(vco_pwm, pObj->param[VOICE_PARAM_OSC_0_PWM_AMT_0], len);

		// Process VCO 0
		VCO_ProcessDataV(&pObj->vco[0], vco_pitch[0], vco_fm, vco_pwm, NULL, NULL, len);

		// AM 0
		if ((int)pObj->param[VOICE_PARAM_OSC_0_AM_SRC_1] == VOICE_MOD_SRC_1_DISABLED)
		{
			Add_VS(vco_am, pModSrc[(int)pObj->param[VOICE_PARAM_OSC_0_AM_SRC_0]], 1.0, 0, len);
		}
		else
		{
			switch((int)pObj->param[VOICE_PARAM_OSC_0_AM_SRC_OP])
			{
				default:
				case VOICE_MOD_SRC_OP_ADD:
				Add_VV(vco_am, pModSrc[(int)pObj->param[VOICE_PARAM_OSC_0_AM_SRC_0]], 1.0, pModSrc[(int)pObj->param[VOICE_PARAM_OSC_0_AM_SRC_1]], pObj->param[VOICE_PARAM_OSC_0_AM_AMT_1], len);
					break;

				case VOICE_MOD_SRC_OP_MUL:
				Mul_VV(vco_am, pModSrc[(int)pObj->param[VOICE_PARAM_OSC_0_AM_SRC_0]], pModSrc[(int)pObj->param[VOICE_PARAM_OSC_0_AM_SRC_1]], len);
				if (pObj->param[VOICE_PARAM_OSC_0_AM_AMT_1] <= 0)
					Add_inplace_VV(vco_am, pObj->param[VOICE_PARAM_OSC_0_AM_AMT_1], pModSrc[(int)pObj->param[VOICE_PARAM_OSC_0_AM_SRC_0]], 1, len);
				else
					Add_inplace_VV(vco_am, pObj->param[VOICE_PARAM_OSC_0_AM_AMT_1], pModSrc[(int)pObj->param[VOICE_PARAM_OSC_0_AM_SRC_0]], 0, len);
				break;
			}
		}
		if (((int)pObj->param[VOICE_PARAM_OSC_0_AM_SRC_0] != VOICE_MOD_SRC_0_DISABLED) || ((int)pObj->param[VOICE_PARAM_OSC_0_AM_SRC_1] != VOICE_MOD_SRC_1_DISABLED))
		{
			if (pObj->param[VOICE_PARAM_OSC_0_AM_AMT_0] <= 0)
			{
				Add_inplace_VS(vco_am, pObj->param[VOICE_PARAM_OSC_0_AM_AMT_0], 1, len);
			}
			else
			{
				Add_inplace_VS(vco_am, pObj->param[VOICE_PARAM_OSC_0_AM_AMT_0], 0, len);
			}

			Clamp_inplace_V(vco_am, 0, 1, len);

			// Modulate
			Mul_inplace_VV(pBuf_OSC[0], vco_am, len);
		}
	}

	if (!vco_is_disabled[0])
	{
		Add_VS(VCO_fmout[0], pBuf_OSC[0], 0.5, 0.5, len);
		pModSrc[VOICE_MOD_SRC_VCO_0] = VCO_fmout[0];
//		pModSrc[VOICE_MOD_SRC_VCO_0] = pBuf_OSC[0];
	}

	pBuf_OSC[1] = VCO_GetProcessBuffer(&pObj->vco[1]);
	if (!vco_is_disabled[1])
	{
		// FM 1
		if ((int)pObj->param[VOICE_PARAM_OSC_1_FM_SRC_1] == VOICE_MOD_SRC_1_DISABLED)
		{
			Add_VS(vco_fm, pModSrc[(int)pObj->param[VOICE_PARAM_OSC_1_FM_SRC_0]], 1.0, 0, len);
		}
		else
		{
			switch((int)pObj->param[VOICE_PARAM_OSC_1_FM_SRC_OP])
			{
				default:
				case VOICE_MOD_SRC_OP_ADD:
				Add_VV(vco_fm, pModSrc[(int)pObj->param[VOICE_PARAM_OSC_1_FM_SRC_0]], 1.0, pModSrc[(int)pObj->param[VOICE_PARAM_OSC_1_FM_SRC_1]], pObj->param[VOICE_PARAM_OSC_1_FM_AMT_1], len);
					break;

				case VOICE_MOD_SRC_OP_MUL:
				Mul_VV(vco_fm, pModSrc[(int)pObj->param[VOICE_PARAM_OSC_1_FM_SRC_0]], pModSrc[(int)pObj->param[VOICE_PARAM_OSC_1_FM_SRC_1]], len);
				if (pObj->param[VOICE_PARAM_OSC_1_FM_AMT_1] <= 0)
					Add_inplace_VV(vco_fm, pObj->param[VOICE_PARAM_OSC_1_FM_AMT_1], pModSrc[(int)pObj->param[VOICE_PARAM_OSC_1_FM_SRC_0]], 1, len);
				else
					Add_inplace_VV(vco_fm, pObj->param[VOICE_PARAM_OSC_1_FM_AMT_1], pModSrc[(int)pObj->param[VOICE_PARAM_OSC_1_FM_SRC_0]], 0, len);
				break;
			}
		}
		Clamp_inplace_V(vco_fm, -1, 1, len);
		Mul_inplace_VS(vco_fm, pObj->param[VOICE_PARAM_OSC_1_FM_AMT_0], len);

		// PWM 1
		if ((int)pObj->param[VOICE_PARAM_OSC_1_PWM_SRC_1] == VOICE_MOD_SRC_1_DISABLED)
		{
			Add_VS(vco_pwm, pModSrc[(int)pObj->param[VOICE_PARAM_OSC_1_PWM_SRC_0]], 1.0, 0, len);
		}
		else
		{
			switch((int)pObj->param[VOICE_PARAM_OSC_1_PWM_SRC_OP])
			{
				default:
				case VOICE_MOD_SRC_OP_ADD:
				Add_VV(vco_pwm, pModSrc[(int)pObj->param[VOICE_PARAM_OSC_1_PWM_SRC_0]], 1.0, pModSrc[(int)pObj->param[VOICE_PARAM_OSC_1_PWM_SRC_1]], pObj->param[VOICE_PARAM_OSC_1_PWM_AMT_1], len);
					break;

				case VOICE_MOD_SRC_OP_MUL:
				Mul_VV(vco_pwm, pModSrc[(int)pObj->param[VOICE_PARAM_OSC_1_PWM_SRC_0]], pModSrc[(int)pObj->param[VOICE_PARAM_OSC_1_PWM_SRC_1]], len);
				if (pObj->param[VOICE_PARAM_OSC_1_PWM_AMT_1] <= 0)
					Add_inplace_VV(vco_pwm, pObj->param[VOICE_PARAM_OSC_1_PWM_AMT_1], pModSrc[(int)pObj->param[VOICE_PARAM_OSC_1_PWM_SRC_0]], 1, len);
				else
					Add_inplace_VV(vco_pwm, pObj->param[VOICE_PARAM_OSC_1_PWM_AMT_1], pModSrc[(int)pObj->param[VOICE_PARAM_OSC_1_PWM_SRC_0]], 0, len);
				break;
			}
		}
		Mul_inplace_VS(vco_pwm, pObj->param[VOICE_PARAM_OSC_1_PWM_AMT_0], len);

		// Process VCO 1
		VCO_ProcessDataV(&pObj->vco[1], vco_pitch[1], vco_fm, vco_pwm, NULL, NULL, len);

		// AM 1
		if ((int)pObj->param[VOICE_PARAM_OSC_1_AM_SRC_1] == VOICE_MOD_SRC_1_DISABLED)
		{
			Add_VS(vco_am, pModSrc[(int)pObj->param[VOICE_PARAM_OSC_1_AM_SRC_0]], 1.0, 0, len);
		}
		else
		{
			switch((int)pObj->param[VOICE_PARAM_OSC_1_AM_SRC_OP])
			{
				default:
				case VOICE_MOD_SRC_OP_ADD:
				Add_VV(vco_am, pModSrc[(int)pObj->param[VOICE_PARAM_OSC_1_AM_SRC_0]], 1.0, pModSrc[(int)pObj->param[VOICE_PARAM_OSC_1_AM_SRC_1]], pObj->param[VOICE_PARAM_OSC_1_AM_AMT_1], len);
					break;

				case VOICE_MOD_SRC_OP_MUL:
				Mul_VV(vco_am, pModSrc[(int)pObj->param[VOICE_PARAM_OSC_1_AM_SRC_0]], pModSrc[(int)pObj->param[VOICE_PARAM_OSC_1_AM_SRC_1]], len);
				if (pObj->param[VOICE_PARAM_OSC_1_AM_AMT_1] <= 0)
					Add_inplace_VV(vco_am, pObj->param[VOICE_PARAM_OSC_1_AM_AMT_1], pModSrc[(int)pObj->param[VOICE_PARAM_OSC_1_AM_SRC_0]], 1, len);
				else
					Add_inplace_VV(vco_am, pObj->param[VOICE_PARAM_OSC_1_AM_AMT_1], pModSrc[(int)pObj->param[VOICE_PARAM_OSC_1_AM_SRC_0]], 0, len);
				break;
			}
		}
		if (((int)pObj->param[VOICE_PARAM_OSC_1_AM_SRC_0] != VOICE_MOD_SRC_0_DISABLED) || ((int)pObj->param[VOICE_PARAM_OSC_1_AM_SRC_1] != VOICE_MOD_SRC_1_DISABLED))
		{
			if (pObj->param[VOICE_PARAM_OSC_1_AM_AMT_0] <= 0)
			{
				Add_inplace_VS(vco_am, pObj->param[VOICE_PARAM_OSC_1_AM_AMT_0], 1, len);
			}
			else
			{
				Add_inplace_VS(vco_am, pObj->param[VOICE_PARAM_OSC_1_AM_AMT_0], 0, len);
			}

			Clamp_inplace_V(vco_am, 0, 1, len);

			// Modulate
			Mul_inplace_VV(pBuf_OSC[1], vco_am, len);
		}
	}

	if (!vco_is_disabled[1])
	{
		Add_VS(VCO_fmout[1], pBuf_OSC[1], 0.5, 0.5, len);
		pModSrc[VOICE_MOD_SRC_VCO_1] = VCO_fmout[1];
//		pModSrc[VOICE_MOD_SRC_VCO_1] = pBuf_OSC[1];
	}

	// Smooth Oscillator output during switching on/off and do level adjust here
	for (i=0; i < (INT32)VOICE_NUM_OSC; i++)
	{
		Smooth_Process_VV(&pObj->smooth_vco_enable[i], pObj->param[VOICE_PARAM_OSC_0_ENABLE+i], pBuf_OSC[i], osc_out_smoothed[i], len);
	}

	// Mix Oscillators
	Add_VV(osc_out, osc_out_smoothed[0], pObj->param[VOICE_PARAM_OSC_0_LEVEL], pCom->noise_out, pObj->param[VOICE_PARAM_NOISE_LEVEL], len);
	Add_inplace_VV(osc_out, 1, osc_out_smoothed[1], pObj->param[VOICE_PARAM_OSC_1_LEVEL], len);

	// Mix Cutoff modulator
	env_gain = pObj->param[VOICE_PARAM_VCF_ENV_GAIN];

	if ((int)pObj->param[VOICE_PARAM_VCF_F_SRC_1] == VOICE_MOD_SRC_1_DISABLED)
	{
		Add_VS(vcf_fm, pModSrc[(int)pObj->param[VOICE_PARAM_VCF_F_SRC_0]], 1.0, 0, len);
	}
	else
	{
		switch((int)pObj->param[VOICE_PARAM_VCF_F_SRC_OP])
		{
			default:
			case VOICE_MOD_SRC_OP_ADD:
			Add_VV(vcf_fm, pModSrc[(int)pObj->param[VOICE_PARAM_VCF_F_SRC_0]], 1.0, pModSrc[(int)pObj->param[VOICE_PARAM_VCF_F_SRC_1]], pObj->param[VOICE_PARAM_VCF_F_AMT_1], len);
			break;

			case VOICE_MOD_SRC_OP_MUL:
			Mul_VV(vcf_fm, pModSrc[(int)pObj->param[VOICE_PARAM_VCF_F_SRC_0]], pModSrc[(int)pObj->param[VOICE_PARAM_VCF_F_SRC_1]], len);
			if (pObj->param[VOICE_PARAM_VCF_F_AMT_1] <= 0)
				Add_inplace_VV(vcf_fm, pObj->param[VOICE_PARAM_VCF_F_AMT_1], pModSrc[(int)pObj->param[VOICE_PARAM_VCF_F_SRC_0]], 1, len);
			else
				Add_inplace_VV(vcf_fm, pObj->param[VOICE_PARAM_VCF_F_AMT_1], pModSrc[(int)pObj->param[VOICE_PARAM_VCF_F_SRC_0]], 0, len);

			break;
		}
	}
	Add_inplace_VV(vcf_fm, pObj->param[VOICE_PARAM_VCF_F_AMT_0], pBuf_ENV[VOICE_ENV_VCF], env_gain, len);
	Clamp_inplace_V(vcf_fm, -1, 1, len);

	// Mix resonance modulator
	if ((int)pObj->param[VOICE_PARAM_VCF_Q_SRC_1] == VOICE_MOD_SRC_1_DISABLED)
	{
		Add_VS(vcf_qm, pModSrc[(int)pObj->param[VOICE_PARAM_VCF_Q_SRC_0]], 1.0, 0, len);
	}
	else
	{
		switch((int)pObj->param[VOICE_PARAM_VCF_Q_SRC_OP])
		{
			default:
			case VOICE_MOD_SRC_OP_ADD:
			Add_VV(vcf_qm, pModSrc[(int)pObj->param[VOICE_PARAM_VCF_Q_SRC_0]], 1.0, pModSrc[(int)pObj->param[VOICE_PARAM_VCF_Q_SRC_1]], pObj->param[VOICE_PARAM_VCF_Q_AMT_1], len);
			break;

			case VOICE_MOD_SRC_OP_MUL:
			Mul_VV(vcf_qm, pModSrc[(int)pObj->param[VOICE_PARAM_VCF_Q_SRC_0]], pModSrc[(int)pObj->param[VOICE_PARAM_VCF_Q_SRC_1]], len);
			if (pObj->param[VOICE_PARAM_VCF_Q_AMT_1] <= 0)
				Add_inplace_VV(vcf_qm, pObj->param[VOICE_PARAM_VCF_Q_AMT_1], pModSrc[(int)pObj->param[VOICE_PARAM_VCF_Q_SRC_0]], 1, len);
			else
				Add_inplace_VV(vcf_qm, pObj->param[VOICE_PARAM_VCF_Q_AMT_1], pModSrc[(int)pObj->param[VOICE_PARAM_VCF_Q_SRC_0]], 0, len);
			break;
		}
	}
	Mul_inplace_VS(vcf_qm, pObj->param[VOICE_PARAM_VCF_Q_AMT_0], len);
	Clamp_inplace_V(vcf_qm, -1, 1, len);

	// Filter envelope and filtering
	for (i=0; i < (INT32)VOICE_NUM_OSC; i++)
	{
		vco_is_disabled[i] = Smooth_is_settled(&pObj->smooth_vco_enable[i], 0) && (pObj->param[VOICE_PARAM_OSC_0_ENABLE+i] < (synth_float_t)0.5);
	}
	if (vco_is_disabled[0] && vco_is_disabled[1] && (pObj->param[VOICE_PARAM_NOISE_ENABLE] < (synth_float_t)0.5))
	{
		pBuf_Postvcf = osc_out;
	}
	else
	{
		pBuf_Postvcf = VCF_ProcessDataV(&pObj->vcf, vcf_fm, vcf_qm, osc_out, len);
	}

	// Amplifier envelope and voice panning
	env_gain = 0.25f*jsy_max(0, jsy_min(1, pObj->param[VOICE_PARAM_VCA_ENV_GAIN]));

	// VCA AM
	Mul_VS(vca_am, pModSrc[(int)pObj->param[VOICE_PARAM_VCA_AM_SRC_0]], pObj->param[VOICE_PARAM_VCA_AM_AMT_0], len);
	Add_inplace_VS(vca_am, -1.0, 1.0, len);

	// VCA PAN
	if ((int)pObj->param[VOICE_PARAM_VCA_PAN_SRC_0] != VOICE_MOD_SRC_0_DISABLED)
	{
		Add_VS(vca_pan, pModSrc[(int)pObj->param[VOICE_PARAM_VCA_PAN_SRC_0]], 2.0, -1.0, len);
		Mul_inplace_VS(vca_pan, pObj->param[VOICE_PARAM_VCA_PAN_AMT_0], len);
	}
	else
	{
		Add_VS(vca_pan, pModSrc[(int)pObj->param[VOICE_PARAM_VCA_PAN_SRC_0]], 0, pObj->param[VOICE_PARAM_VCA_PAN_AMT_0], len);
	}

	for (j=0; j < (INT32)len; j++)
	{
//		env_gain = 0.25f*jsy_max(0, jsy_min(1, pObj->param_smoothed[VOICE_PARAM_VCA_ENV_GAIN][j]));
		out = pBuf_Postvcf[j] * pBuf_ENV[VOICE_ENV_VCA][j] * env_gain * vca_am[j];
		(*pOut1++) += (float)((1+pObj->param[VOICE_PARAM_VOICE_PAN])*(1+vca_pan[j])*out);
		(*pOut2++) += (float)((1-pObj->param[VOICE_PARAM_VOICE_PAN])*(1-vca_pan[j])*out);
	}

	if (ENV_IsIdle(&pObj->env[VOICE_ENV_VCA]))
	{
		pObj->state = note_state_idle;
		for (i = 0; i < VOICE_NUM_ENV; i++)
			ENV_Reset(&pObj->env[i]);

	}
}
