text: #include "Module_multiOscillator.hpp"
  #include "util/math.hpp"
  #include "Widget_multiOscillator.hpp"
  
  
  
  const char* multiOscillator::WaveFormAbbr[WaveFormType::NUM_WAVEFORMS] = { "SIN", "TRI", "SAW", "SQR" };
  
  //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  //::::::::::::: TS_Oscillator :::::::::::::::::::::::::::::
  //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  
  //--------------------------------------------------------
  // TS_Oscillator()
  // @numOutWaveForms: (IN) The number of output waveforms we will have from this oscillator.
  //--------------------------------------------------------
  TS_Oscillator::TS_Oscillator(int numOutWaveForms)
  {
  	numOutputWaveForms = numOutWaveForms;
  	if (numOutputWaveForms < 1)
  		numOutputWaveForms = 1;
  	for (int i = 0; i < numOutputWaveForms; i++)
  	{		
  		outputWaveforms.push_back(TS_OscillatorOutput());
  		outputWaveforms[i].outputChannelNumber = i + 1;
  	}
  	initialize();
  	return;
  }
  //--------------------------------------------------------
  // initialize()
  // Initialize (UI) values to default values.
  //--------------------------------------------------------
  void TS_Oscillator::initialize()
  {
  	ui_amplitude_V = MOSC_AMPLITUDE_DEFAULT_V;
  	ui_frequency_Hz = MOSC_FREQ_DEFAULT_HZ;
  	ui_phaseShift_deg = MOSC_PHASE_SHIFT_DEFAULT_DEG;
  	ui_offset_V = MOSC_OFFSET_DEFAULT_V;
  
  	for (int i = 0; i < static_cast<int>(outputWaveforms.size()); i++)
  	{
  		outputWaveforms[i].initialize();
  	}
  
  	return;
  } // end initialize()
  //--------------------------------------------------------
  // serialize()
  // @returns : The TS_Oscillator json node.
  //--------------------------------------------------------
  json_t* TS_Oscillator::serialize()
  {
  	json_t* rootJ = json_object();
  	json_object_set_new(rootJ, "amplitude_V", json_real(ui_amplitude_V));
  	json_object_set_new(rootJ, "frequency_Hz", json_real(ui_frequency_Hz));
  	json_object_set_new(rootJ, "phaseShift_deg", json_real(ui_phaseShift_deg));
  	json_object_set_new(rootJ, "offset_V", json_real(ui_offset_V));
  	json_object_set_new(rootJ, "numWaveforms", json_integer(outputWaveforms.size()));
  	json_t* waveformsJ = json_array();
  	for (int i = 0; i < static_cast<int>(outputWaveforms.size()); i++)
  	{
  		json_array_append_new(waveformsJ, outputWaveforms[i].serialize());
  	}
  	json_object_set_new(rootJ, "waveforms", waveformsJ);
  
  	return rootJ;	
  } // end serialize()
  //--------------------------------------------------------
  // deserialize()
  // @rootJ : (IN) The TS_Oscillator json node.
  //--------------------------------------------------------
  void TS_Oscillator::deserialize(json_t* rootJ)
  {
  	json_t* currJ = NULL;
  	if (rootJ) {
  		currJ = json_object_get(rootJ, "amplitude_V");
  		if (currJ)
  			ui_amplitude_V = json_real_value(currJ);
  		currJ = json_object_get(rootJ, "frequency_Hz");
  		if (currJ)
  			ui_frequency_Hz = json_real_value(currJ);
  		currJ = json_object_get(rootJ, "phaseShift_deg");
  		if (currJ)
  			ui_phaseShift_deg = json_real_value(currJ);
  		currJ = json_object_get(rootJ, "offset_V");
  		if (currJ)
  			ui_offset_V = json_real_value(currJ);
  		currJ = json_object_get(rootJ, "numWaveforms");
  		if (currJ)
  			numOutputWaveForms = json_integer_value(currJ);
  		if (numOutputWaveForms > static_cast<int>(outputWaveforms.size()))
  			numOutputWaveForms = static_cast<int>(outputWaveforms.size());
  		json_t* waveformsJ = json_object_get(rootJ, "waveforms");
  		for (int i = 0; i < numOutputWaveForms; i++)
  		{
  			currJ = json_array_get(waveformsJ, i);
  			if (currJ)
  			{
  				outputWaveforms[i].deserialize(currJ);
  			}
  		}
  	}
  } // end deserialize()
  
  //--------------------------------------------------------
  // setPhaseShift_deg()
  // @deg : (IN) The phase shift in degrees.
  //--------------------------------------------------------
  void TS_Oscillator::setPhaseShift_deg(float deg)
  {
  	phaseShift_deg = deg;
  	phaseShift_norm = deg / 360.0f;
  	return;
  }
  
  //--------------------------------------------------------
  // calculatePhase()
  // @dt : (IN) Time elapsed.
  // @doSync : (IN) If sync / reset requested.
  // @returns: True if shifted phase has reset (gone over 1)
  //--------------------------------------------------------
  bool TS_Oscillator::calculatePhase(float dt, bool doSync)
  {
  	bool waveReset = doSync;
  	if (doSync)
  	{
  		phase = 0;
  	}
  	else
  	{
  		// VCO clamps this, not sure if we need to
  		float dPhase = clamp(frequency_Hz * dt, 0.f, 0.5f);
  		phase = eucmod(phase + dPhase, 1.0f);
  	}
  	float prevSPhi = shiftedPhase;
  	shiftedPhase = eucmod(phase + phaseShift_norm, 1.0f);
  	if (!waveReset)
  		waveReset = prevSPhi > shiftedPhase;
  	return waveReset;
  }
  
  //--------------------------------------------------------
  // calcSin()
  // Sine wave.
  // Calculate with amplitude and offset.
  // @phaseShift_n : (IN) Normalized phase shift (-1 to 1).
  //--------------------------------------------------------
  float TS_Oscillator::calcSin(float phaseShift_n)
  {
  	return amplitude_V * sinf(eucmod(1.0f + shiftedPhase + phaseShift_n, 1.0f) * 2.0f * NVG_PI) + offset_V;
  } // end calcSin()
  //--------------------------------------------------------
  // calcRect()
  // Rectangle wave.
  // Calculate with amplitude and offset.
  // @phaseShift_n : (IN) Normalized phase shift (-1 to 1).
  // @pulseWidth_n : (IN) Normalized pulse width (0-1). Really should be like 0.01 to 0.99 or something.
  //--------------------------------------------------------
  float TS_Oscillator::calcRect(float phaseShift_n, float pulseWidth_n)
  {
  	float val = 0.0f;
  	if (eucmod(1.0f + shiftedPhase + phaseShift_n, 1.0f) < pulseWidth_n)
  		val = amplitude_V;
  	else
  		val = -amplitude_V;
  	return val + offset_V;
  } // end calcRect
  //--------------------------------------------------------
  // calcTri()
  // Triange wave.
  // Calculate with amplitude and offset.
  // @phaseShift_n : (IN) Normalized phase shift (-1 to 1).
  //--------------------------------------------------------
  float TS_Oscillator::calcTri(float phaseShift_n)
  {
  	float p_n = eucmod(1.0f + shiftedPhase + phaseShift_n, 1.0f);
  	float val = 0.0f;
  	if (p_n < 0.25f)
  		val = 4.0f * p_n; // 0 to 1 (positive slope)
  	else if (p_n < 0.75f)
  		val = 2.0f - 4.0f * p_n; // -1 to 0 (positive slope)
  	else
  		val = -4.0f + 4.f * p_n;
  	return amplitude_V * val + offset_V;
  } // end calcTri()
  //--------------------------------------------------------
  // calcSaw()
  // Sawtooth wave.
  // Calculate with amplitude and offset. (centered around 0)
  // @phaseShift_n : (IN) Normalized phase shift (-1 to 1).
  // @posRamp: (IN) True for positive ramp, false for negative ramp.
  //--------------------------------------------------------
  float TS_Oscillator::calcSaw(float phaseShift_n, bool posRamp)
  {
  	float p_n = eucmod(1.0f + shiftedPhase + phaseShift_n, 1.0f);
  	float val = 0.0f;
  	float a_v = 2 * amplitude_V;
  	if (posRamp)
  		val = -amplitude_V + a_v * p_n; // Going up /|/|/|
  	else
  		val = amplitude_V - a_v * p_n; // Going down \|\|\|
  	return val + offset_V;
  } // end calcSaw()
  
  //--------------------------------------------------------
  // calcSin()
  // Calculates A*sin(wt + phi) + C.
  // A = amplitude_V, w = frequency_radiansps, phi = phaseShift_radians, C = offset_V.
  //--------------------------------------------------------
  float TS_Oscillator::calcSin()
  {
  	return amplitude_V * sinf(shiftedPhase * 2.0f * NVG_PI) + offset_V;
  }
  //--------------------------------------------------------
  // calcSquare()
  // Pseudo calculates A*SIGN(sin(wt + phi)) + C.
  // A = amplitude_V, w = frequency_radiansps, phi = phaseShift_radians, C = offset_V.
  // SIGN() = +1 for positive, -1 for negative, 0 for 0.
  //--------------------------------------------------------
  float TS_Oscillator::calcSquare()
  {
  	float val = 0.0f;
  	if (shiftedPhase < 0.5f)
  		val = amplitude_V;
  	else
  		val = -amplitude_V;
  	return val + offset_V;
  }
  //--------------------------------------------------------
  // calcTri()
  // Pseudo calculates A*SIGN(sin(wt + phi)) + C.
  // A = amplitude_V, w = frequency_radiansps, phi = phaseShift_radians, C = offset_V.
  // SIGN() = +1 for positive, -1 for negative, 0 for 0.
  //--------------------------------------------------------
  float TS_Oscillator::calcTri()
  {
  	float val = 0.0f;
  	if (shiftedPhase < 0.25f)
  		val = 4.0f * shiftedPhase; // 0 to 1 (positive slope)
  	else if (shiftedPhase < 0.75f)
  		val = 2.0f - 4.0f * shiftedPhase; // -1 to 0 (positive slope)
  	else
  		val = -4.0f + 4.f * shiftedPhase;
  	return amplitude_V * val + offset_V;
  }
  //--------------------------------------------------------
  // calcSaw()
  // Sawtooth wave, positive ramp.
  //--------------------------------------------------------
  float TS_Oscillator::calcSaw()
  {
  	float val = 0.0f;
  	if (shiftedPhase < 0.5f)
  		val = 2.f * shiftedPhase;
  	else
  		val = -2.f + 2.f * shiftedPhase;
  	return amplitude_V * val + offset_V;
  }
  
  
  //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  //::::::::::::: multiOscillator :::::::::::::::::::::::::::
  //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  
  
  //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  // multiOscillator()
  // Create a module with numOscillators oscillators.
  // @numOscillators: (IN) Number of oscillators
  // @numOscillatorOutputs: (IN) Number of oscillators.
  //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  multiOscillator::multiOscillator(int numOscillators, int numOscillatorOutputs) :
  	Module(NUM_PARAMS + numOscillators * (TS_Oscillator::OSCWF_NUM_PARAMS + numOscillatorOutputs * TS_OscillatorOutput::OUT_NUM_PARAMS),
  		   NUM_INPUTS + numOscillators * (TS_Oscillator::OSCWF_NUM_INPUTS + numOscillatorOutputs * TS_OscillatorOutput::OUT_NUM_INPUTS),
  		   NUM_OUTPUTS + numOscillators * (TS_Oscillator::OSCWF_NUM_OUTPUTS + numOscillatorOutputs * TS_OscillatorOutput::OUT_NUM_OUTPUTS),
  		   NUM_LIGHTS + numOscillators * (TS_Oscillator::OSCWF_NUM_LIGHTS + numOscillatorOutputs * TS_OscillatorOutput::BaseLightIds::OUT_NUM_LIGHTS))
  {
  	this->numberOscillators = numOscillators;
  	this->oscillators = new TS_Oscillator[numberOscillators];
  	this->numOscillatorOutputs = numOscillatorOutputs;
  	for (int i = 0; i < numOscillators; i++)
  	{
  		oscillators[i] = TS_Oscillator(numOscillatorOutputs);
  	}
  	//this->theOscillator = new TS_Oscillator(numOscillatorOutputs);
  	initialOscillators();
  	return;
  }
  //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  // Clean up or ram.
  //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  multiOscillator::~multiOscillator()
  {
  	isInitialized = false;
  	if (oscillators != NULL)
  		delete[] oscillators;
  	oscillators = NULL;
  	return;
  }
  //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  // reset(void)
  // Initialize values.
  //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  void multiOscillator::reset()
  {
  	for (int i = 0; i < numberOscillators; i++)
  	{
  		oscillators[i].initialize();
  	}
  	return;
  }
  //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  // toJson(void)
  //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-	
  json_t *multiOscillator::toJson()
  {
  	json_t* rootJ = json_object();
  	// version
  	json_object_set_new(rootJ, "version", json_integer(TROWA_INTERNAL_VERSION_INT));
  	json_object_set_new(rootJ, "numOsc", json_integer(numberOscillators));
  	json_object_set_new(rootJ, "numOutputs", json_integer(numOscillatorOutputs));
  
  	json_t* oscillatorsJ = json_array();
  	for (int i = 0; i < numberOscillators; i++)
  	{
  		// Input
  		json_array_append_new(oscillatorsJ, oscillators[i].serialize());
  	}
  	json_object_set_new(rootJ, "oscillators", oscillatorsJ);
  	return rootJ;
  } // end toJson()
  //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  // fromJson(void)
  //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-	
  void multiOscillator::fromJson(json_t *rootJ)
  {
  	json_t* currJ = NULL;
  	int nOscillators = numberOscillators;
  	currJ = json_object_get(rootJ, "numOsc");
  	if (currJ)
  	{
  		nOscillators = json_integer_value(currJ);
  		if (nOscillators > numberOscillators)
  			nOscillators = numberOscillators;
  	}
  	currJ = json_object_get(rootJ, "numOutputs");
  	if (currJ)
  	{
  		numOscillatorOutputs = json_integer_value(currJ);
  	}
  
  	json_t* oscillatorsJ = json_object_get(rootJ, "oscillators");
  	for (int i = 0; i < nOscillators; i++)
  	{
  		currJ = json_array_get(oscillatorsJ, i);
  		if (currJ)
  		{
  			oscillators[i].deserialize(currJ);
  		}
  	} // end loop through osccilators
  	return;
  } // end fromJson()
  
  
  //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  // step(void)
  // Process
  //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  void multiOscillator::step()
  {
  	float dt = engineGetSampleTime();
  
  	// Get Oscillator CV and User Inputs
  	for (int osc = 0; osc < numberOscillators; osc++)
  	{
  		bool sync = false;
  		bool oscillatorReset = false; // If this oscillator is at 0 phase.
  		TS_Oscillator* theOscillator = &(oscillators[osc]);
  		int baseInputId = InputIds::OSC_INPUT_START + osc * (TS_Oscillator::BaseInputIds::OSCWF_NUM_INPUTS + numOscillatorOutputs * TS_OscillatorOutput::BaseInputIds::OUT_NUM_INPUTS);
  		int baseParamId = ParamIds::OSC_PARAM_START + osc * (TS_Oscillator::BaseParamIds::OSCWF_NUM_PARAMS + numOscillatorOutputs * TS_OscillatorOutput::BaseParamIds::OUT_NUM_PARAMS);
  		int baseOutputId = OutputIds::OSC_OUTPUT_START + osc * (TS_Oscillator::BaseOutputIds::OSCWF_NUM_OUTPUTS + numOscillatorOutputs * TS_OscillatorOutput::BaseOutputIds::OUT_NUM_OUTPUTS);
  		int baseLightId = LightIds::OSC_LIGHT_START + osc * (TS_Oscillator::BaseLightIds::OSCWF_NUM_LIGHTS + numOscillatorOutputs * TS_OscillatorOutput::BaseLightIds::OUT_NUM_LIGHTS);
  
  		//------------------------------
  		// Sync this oscillator
  		//------------------------------
  		if (lights[baseLightId + TS_Oscillator::BaseLightIds::OSCWF_SYNC_LED].value > 0)
  			lights[baseLightId + TS_Oscillator::BaseLightIds::OSCWF_SYNC_LED].value -= lights[baseLightId + TS_Oscillator::BaseLightIds::OSCWF_SYNC_LED].value / lightLambda * dt;
  		else if (lights[baseLightId + TS_Oscillator::BaseLightIds::OSCWF_SYNC_LED].value < 0)
  			lights[baseLightId + TS_Oscillator::BaseLightIds::OSCWF_SYNC_LED].value = 0;
  		if (theOscillator->synchTrigger.process(params[baseParamId + TS_Oscillator::BaseParamIds::OSCWF_SYNC_PARAM].value + inputs[baseInputId + TS_Oscillator::BaseInputIds::OSCWF_SYNC_INPUT].value))
  		{
  			lights[baseLightId + TS_Oscillator::BaseLightIds::OSCWF_SYNC_LED].value = 1.0f;
  			sync = true;
  		} // end if
  
  		//------------------------------
  		// Values In (Add Input + Knob)
  		//------------------------------
  		// *> Amplitude (V):
  		theOscillator->ui_amplitude_V = params[baseParamId + TS_Oscillator::BaseParamIds::OSCWF_AMPLITUDE_PARAM].value;
  		float a = theOscillator->ui_amplitude_V;
  		if (inputs[baseInputId + TS_Oscillator::BaseInputIds::OSCWF_AMPLITUDE_INPUT].active)
  			a += inputs[baseInputId + TS_Oscillator::BaseInputIds::OSCWF_AMPLITUDE_INPUT].value;
  		theOscillator->amplitude_V = a;
  
  		// *> Frequency (Hz):
  		// User Knob:
  #if TROWA_MOSC_FREQ_KNOB_NEEDS_CONVERSION
  		theOscillator->ui_frequency_Hz = rescale(params[baseParamId + TS_Oscillator::BaseParamIds::OSCWF_FREQUENCY_PARAM].value, TROWA_MOSC_F_KNOB_MIN_V, TROWA_MOSC_F_KNOB_MAX_V, MOSC_FREQ_MIN_HZ, MOSC_FREQ_MAX_HZ);
  #else
  		theOscillator->ui_frequency_Hz = params[baseParamId + TS_Oscillator::BaseParamIds::OSCWF_FREQUENCY_PARAM].value;
  #endif
  		float f = theOscillator->ui_frequency_Hz;
  		// Add Input:
  		if (inputs[baseInputId + TS_Oscillator::BaseInputIds::OSCWF_FREQUENCY_INPUT].active) {
  			// CV frequency
  			f = clamp(f + VoltageToFrequency(inputs[baseInputId + TS_Oscillator::BaseInputIds::OSCWF_FREQUENCY_INPUT].value), MOSC_FREQ_MIN_HZ, MOSC_FREQ_MAX_HZ);
  		}
  		theOscillator->frequency_Hz = f; // Actual Frequency
  
  		// *> Phase Shift (deg): 
  		theOscillator->ui_phaseShift_deg = rescale(params[baseParamId + TS_Oscillator::BaseParamIds::OSCWF_PHASE_SHIFT_PARAM].value, TROWA_MOSC_KNOB_MIN_V, TROWA_MOSC_KNOB_MAX_V, MOSC_PHASE_SHIFT_MIN_DEG, MOSC_PHASE_SHIFT_MAX_DEG);
  		float phi = theOscillator->ui_phaseShift_deg;
  		if (inputs[baseInputId + TS_Oscillator::BaseInputIds::OSCWF_PHASE_SHIFT_INPUT].active) {
  			phi += rescale(inputs[baseInputId + TS_Oscillator::BaseInputIds::OSCWF_PHASE_SHIFT_INPUT].value, TROWA_MOSC_INPUT_MIN_V, TROWA_MOSC_INPUT_MAX_V, MOSC_PHASE_SHIFT_MIN_DEG, MOSC_PHASE_SHIFT_MAX_DEG);
  		}
  		theOscillator->setPhaseShift_deg(phi);
  
  		// *> Offset (V):
  		theOscillator->ui_offset_V = params[baseParamId + TS_Oscillator::BaseParamIds::OSCWF_OFFSET_PARAM].value;
  		float c = theOscillator->ui_offset_V;
  		if (inputs[baseInputId + TS_Oscillator::BaseInputIds::OSCWF_OFFSET_INPUT].active)
  			c += inputs[baseInputId + TS_Oscillator::BaseInputIds::OSCWF_OFFSET_INPUT].value;
  		theOscillator->offset_V = c;
  
  		//------------------------------
  		// Calculate phase
  		//------------------------------
  		oscillatorReset = theOscillator->calculatePhase(dt, sync);
  
  		//------------------------------
  		// Sync Output
  		//------------------------------
  		if (oscillatorReset)
  		{
  			theOscillator->synchPulse.trigger(1e-4); // 1e-3
  		}
  		outputs[baseOutputId + TS_Oscillator::BaseOutputIds::OSCWF_SYNC_OUTPUT].value = (theOscillator->synchPulse.process(dt)) ? 10.0f : 0.0f;
  
  		//------------------------------
  		// Each output channel
  		//------------------------------
  		baseParamId += TS_Oscillator::BaseParamIds::OSCWF_NUM_PARAMS;
  		baseInputId += TS_Oscillator::BaseInputIds::OSCWF_NUM_INPUTS;
  		baseOutputId += TS_Oscillator::BaseOutputIds::OSCWF_NUM_OUTPUTS;
  		baseLightId += TS_Oscillator::BaseLightIds::OSCWF_NUM_LIGHTS;
  		for (int i = 0; i < theOscillator->numOutputWaveForms; i++)
  		{
  			float type = clamp((int)rescale(params[baseParamId + TS_OscillatorOutput::BaseParamIds::OUT_OSC_TYPE_PARAM].value, TROWA_MOSC_KNOB_MIN_V, TROWA_MOSC_KNOB_MAX_V, 0, WaveFormType::NUM_WAVEFORMS), 0, WaveFormType::NUM_WAVEFORMS - 1);
  			theOscillator->outputWaveforms[i].ui_waveFormType = static_cast<WaveFormType>(int(type));
  			if (inputs[baseInputId + TS_OscillatorOutput::BaseInputIds::OUT_OSC_TYPE_INPUT].active) {
  				type = clamp((int)rescale(inputs[baseInputId + TS_OscillatorOutput::BaseInputIds::OUT_OSC_TYPE_INPUT].value,
  					TROWA_MOSC_TYPE_INPUT_MIN_V, TROWA_MOSC_TYPE_INPUT_MAX_V, 0, WaveFormType::NUM_WAVEFORMS), 0, WaveFormType::NUM_WAVEFORMS - 1);
  			}
  			theOscillator->outputWaveforms[i].waveFormType = static_cast<WaveFormType>(int(type));
  
  			// *> Phase shift for this output
  			float phi = rescale(params[baseParamId + TS_OscillatorOutput::BaseParamIds::OUT_PHASE_SHIFT_PARAM].value,
  				TROWA_MOSC_KNOB_MIN_V, TROWA_MOSC_KNOB_MAX_V,
  				MOSC_PHASE_SHIFT_MIN_DEG, MOSC_PHASE_SHIFT_MAX_DEG);
  			theOscillator->outputWaveforms[i].ui_phaseShift_deg = phi;
  			if (inputs[baseInputId + TS_OscillatorOutput::BaseInputIds::OUT_PHASE_SHIFT_INPUT].active) {
  				phi += rescale(inputs[baseInputId + TS_OscillatorOutput::BaseInputIds::OUT_PHASE_SHIFT_INPUT].value,
  					TROWA_MOSC_INPUT_MIN_V, TROWA_MOSC_INPUT_MAX_V, MOSC_PHASE_SHIFT_MIN_DEG, MOSC_PHASE_SHIFT_MAX_DEG);
  			}
  			theOscillator->outputWaveforms[i].setPhaseShift_deg(phi);
  
  			// *> Aux parameter: (Currently only rect/square and saw/ramp)
  			float aux = 0.5f;
  			//switch (theOscillator->outputWaveforms[i].waveFormType)
  			//{
  			//case WaveFormType::WAVEFORM_SQR:
  			//case WaveFormType::WAVEFORM_SAW:
  			//	aux = rescale(params[baseParamId + TS_OscillatorOutput::BaseParamIds::OUT_AUX_PARAM].value, TROWA_MOSC_AUX_MIN_V, TROWA_MOSC_AUX_MAX_V, 0.0f, 1.0f);
  			//	break;
  			//default:
  			//	break;
  			//}
  			if (inputs[baseInputId + TS_OscillatorOutput::BaseInputIds::OUT_AUX_INPUT].active) {
  				aux = clamp(rescale(inputs[baseInputId + TS_OscillatorOutput::BaseInputIds::OUT_AUX_INPUT].value, TROWA_MOSC_AUX_MIN_V, TROWA_MOSC_AUX_MAX_V, 0.f, 1.f), 0.f, 1.f);
  			}
  			else
  			{
  				aux = rescale(params[baseParamId + TS_OscillatorOutput::BaseParamIds::OUT_AUX_PARAM].value, TROWA_MOSC_KNOB_AUX_MIN_V, TROWA_MOSC_KNOB_AUX_MAX_V, 0.0f, 1.0f);
  			}
  			theOscillator->outputWaveforms[i].auxParam_norm = aux;
  
  			// *> AM Type (digital = false, ring = true)
  			if (theOscillator->outputWaveforms[i].amRingModulationTrigger.process(params[baseParamId + TS_OscillatorOutput::BaseParamIds::OUT_AM_TYPE_PARAM].value))
  			{
  				theOscillator->outputWaveforms[i].amRingModulation = !theOscillator->outputWaveforms[i].amRingModulation;
  				//info("[Ch %d] AM Button Click id %d. Ring Mod = %d.", i + 1, baseParamId + TS_OscillatorOutput::BaseParamIds::OUT_AM_TYPE_PARAM, theOscillator->outputWaveforms[i].amRingModulation);
  			}
  			lights[baseLightId + TS_OscillatorOutput::BaseLightIds::OUT_AM_MODE_LED].value = (theOscillator->outputWaveforms[i].amRingModulation) ? 1.0f : 0.0f;
  
  			//------------------------------------------
  			// Calculate an output/outputs
  			//------------------------------------------
  			if (outputs[baseOutputId + TS_OscillatorOutput::BaseOutputIds::OUT_RAW_SIGNAL].active ||
  				(outputs[baseOutputId + TS_OscillatorOutput::BaseOutputIds::OUT_MULTIPLIED_SIGNAL].active && inputs[baseInputId + TS_OscillatorOutput::BaseInputIds::OUT_AM_INPUT].active))
  			{
  				// Calculate the RAW output:
  				float rawOutput = 0.0f;
  				float phi_n = theOscillator->outputWaveforms[i].phaseShift_norm;
  				switch (theOscillator->outputWaveforms[i].waveFormType)
  				{
  				case WaveFormType::WAVEFORM_SIN:
  					rawOutput = theOscillator->calcSin(phi_n);
  					break;
  				case WaveFormType::WAVEFORM_TRI:
  					rawOutput = theOscillator->calcTri(phi_n);
  					break;
  				case WaveFormType::WAVEFORM_SQR:
  					rawOutput = theOscillator->calcRect(phi_n, theOscillator->outputWaveforms[i].auxParam_norm);
  					break;
  				case WaveFormType::WAVEFORM_SAW:
  					rawOutput = theOscillator->calcSaw(phi_n, theOscillator->outputWaveforms[i].getRampSlope());
  					break;
  				default:
  					break;
  				}
  				outputs[baseOutputId + TS_OscillatorOutput::BaseOutputIds::OUT_RAW_SIGNAL].value = rawOutput;
  
  				// Calculate AM output:
  				if (outputs[baseOutputId + TS_OscillatorOutput::BaseOutputIds::OUT_MULTIPLIED_SIGNAL].active)
  				{
  					float modOutput = 0.f;
  					float modulator = inputs[baseInputId + TS_OscillatorOutput::BaseInputIds::OUT_AM_INPUT].value;
  					float modWeight = params[baseParamId + TS_OscillatorOutput::BaseParamIds::OUT_AM_MIX_PARAM].value; //rescale(params[baseParamId + TS_OscillatorOutput::BaseParamIds::OUT_AM_MIX_PARAM].value, TROWA_MOSC_KNOB_MIN_V, TROWA_MOSC_KNOB_MAX_V, 0.f, 1.f)
  					if (theOscillator->outputWaveforms[i].amRingModulation)
  					{
  						// Ring modulation
  						modOutput = theOscillator->ringModulator.ringMod(modulator, rawOutput);
  					}
  					else
  					{
  						// Digital modulation (just mulitply)
  						modOutput = modulator * rawOutput;
  					}
  					outputs[baseOutputId + TS_OscillatorOutput::BaseOutputIds::OUT_MULTIPLIED_SIGNAL].value = modWeight * modOutput + (1.0f - modWeight)*rawOutput;
  				} // end calculate the multiplied signal
  			} // end calculate the raw signal
  
  			baseParamId += TS_OscillatorOutput::BaseParamIds::OUT_NUM_PARAMS;
  			baseInputId += TS_OscillatorOutput::BaseInputIds::OUT_NUM_INPUTS;
  			baseOutputId += TS_OscillatorOutput::BaseOutputIds::OUT_NUM_OUTPUTS;
  			baseLightId += TS_OscillatorOutput::BaseLightIds::OUT_NUM_LIGHTS;
  		} // end loop through output signals/channels
  	} // end loop through oscillators
  	return;
  } // end step()
  
  
  //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  //::::::::::::: TS_OscillatorOutput :::::::::::::::::::::::::::::
  //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  
  //--------------------------------------------------------
  // initialize()
  //--------------------------------------------------------
  void TS_OscillatorOutput::initialize()
  {
  	phaseShift_deg = 0;
  	phaseShift_norm = 0;
  	amRingModulation = false;
  	auxParam_norm = 0.5f;
  	return;
  }
  //--------------------------------------------------------
  // setPhaseShift_deg()
  // @deg : (IN) The phase shift in degrees.
  //--------------------------------------------------------
  void TS_OscillatorOutput::setPhaseShift_deg(float deg)
  {
  	phaseShift_deg = deg;
  	phaseShift_norm = deg / 360.0f;
  	return;
  }
  //--------------------------------------------------------
  // serialize()
  // @returns : The TS_Oscillator json node.
  //--------------------------------------------------------
  json_t* TS_OscillatorOutput::serialize()
  {
  	json_t* rootJ = json_object();
  	json_object_set_new(rootJ, "wavetype", json_integer(waveFormType));
  	json_object_set_new(rootJ, "phaseShift_deg", json_real(ui_phaseShift_deg));
  	json_object_set_new(rootJ, "auxParam_norm", json_real(auxParam_norm));
  	json_object_set_new(rootJ, "amRingMod", json_integer(amRingModulation));
  	return rootJ;
  } // end serialize()
  //--------------------------------------------------------
  // deserialize()
  // @rootJ : (IN) The TS_Oscillator json node.
  //--------------------------------------------------------
  void TS_OscillatorOutput::deserialize(json_t* rootJ)
  {
  	json_t* currJ = NULL;
  	if (rootJ) {
  		currJ = json_object_get(rootJ, "wavetype");
  		if (currJ)
  			waveFormType = static_cast<WaveFormType>( json_integer_value(currJ) );
  		currJ = json_object_get(rootJ, "phaseShift_deg");
  		if (currJ)
  			ui_phaseShift_deg = json_real_value(currJ);
  		currJ = json_object_get(rootJ, "auxParam_norm");
  		if (currJ)
  			auxParam_norm = json_real_value(currJ);
  		currJ = json_object_get(rootJ, "amRingMod");
  		if (currJ)
  			amRingModulation = json_integer_value(currJ) > 0;
  	}
  } // end deserialize()
  
  
  // Model for trowa multiOscillator
  RACK_PLUGIN_MODEL_INIT(trowaSoft, MultiOscillator) {
     Model* modelMultiOscillator = Model::create<multiOscillator, multiOscillatorWidget>(/*manufacturer*/ TROWA_PLUGIN_NAME, /*slug*/ "multiWave", /*name*/ "multiWave", /*Tags*/ OSCILLATOR_TAG, RING_MODULATOR_TAG);
     return modelMultiOscillator;
  }
  