/** \file
*   Simple Monophonic Sine Wave Synthesizer.
*   Probably the simplest possible virtual instrument: a single voice that generates a sine wave, with no envelope control.
*/

                           
                                

// internal processing properties and functions
double amplitude=0;
double omega=0;

double currentAmplitude=0;
double currentPhase=0;
double currentPitchOffset=0;
uint8  currentNote=0;
const double period=2*PI;

void handleMidiEvent(const MidiEvent& evt)
{
    switch(MidiEventUtils::getType(evt))
    {
    case kMidiNoteOn:
        {
            amplitude=double(MidiEventUtils::getNoteVelocity(evt))/127.0;
            currentNote=MidiEventUtils::getNote(evt);
            omega=2*PI*pow(2,((double(currentNote-69.0)+currentPitchOffset)/12.0))*440.0/sampleRate;
            break;
        }
    case kMidiNoteOff:
        {
            if(currentNote==MidiEventUtils::getNote(evt))
                amplitude=0;
            break;
        }
    case kMidiPitchWheel:
        {
            currentPitchOffset=2*double(MidiEventUtils::getPitchWheelValue(evt))/8192.0;
            omega=2*PI*pow(2,((double(currentNote-69.0)+currentPitchOffset)/12.0))*440.0/sampleRate;
            break;
        }
    }
}

string name="Key Tracking Ring Modulation";
string description = "Key Tracking Ring Modulation";

MidiEvent tempEvent;
array<string> inputParametersNames = { "Main", "Ring", "Freq Ratio", "Volume" };
array<double> inputParameters(inputParametersNames.length);
array<string> inputParametersUnits = { "%", "%", "", "%" };
array<double> inputParamatersMin = { 0, 0, 0, 0 };
array<double> inputParametersMax = { 100, 100, 8, 200 };
array<double> inputParametersDefault = { 0, 100, 1, 100 };
array<int>    inputParametersSteps = { 101,101,81,201 };
array<string> inputParametersFormats = { ".0", ".0", ".1", ".0" };

double dMainVolume;
double dRingVolume;
double dFreqRatio;
double dMasterVolume;

/** per-block processing function: called for every sample with updated parameters values.
*
*/
void processBlock(BlockData& data)
{
    uint nextEventIndex=0;

    MidiEventUtils::setType(tempEvent,kMidiPitchWheel);
    for(uint i=0;i<data.samplesToProcess;i++)
    {
        // manage MIDI events
        while(nextEventIndex!=data.inputMidiEvents.length && data.inputMidiEvents[nextEventIndex].timeStamp<=double(i))
        {
            handleMidiEvent(data.inputMidiEvents[nextEventIndex]);
            nextEventIndex++;
        }

        // update amplitude smoothly (to avoid clicks)
        currentAmplitude=.99*currentAmplitude+.01*amplitude;

        // compute sample value
        double sampleValue=currentAmplitude*sin(currentPhase);

        // update phase
		currentPhase += (omega * dFreqRatio);

        // copy to all audio outputs
        for(uint ch=0;ch<audioOutputsCount;ch++)
        {
            data.samples[ch][i] *= (dMainVolume + dRingVolume * sampleValue);
			data.samples[ch][i] *= dMasterVolume;
        }
    }

    // to avoid overflow, reduce phase
    while(currentPhase>period)
        currentPhase-=period;
}

int getTailSize()
{
    return -1;
}

void updateInputParameters()
{
	dMainVolume = inputParameters[0] / 100;
	dRingVolume = inputParameters[1] / 100;
	dFreqRatio = inputParameters[2];
	dMasterVolume = inputParameters[3] / 100;
}


/** 
*  \file Midi.hxx
*  Midi utilities library for angelscript
* 
*  Created by Blue Cat Audio <services@bluecataudio.com>
*  Copyright 2011-2014 Blue Cat Audio. All rights reserved.
*
*/

/** Enumeration of Midi Events types currently supported by this library.
*
*/
enum MidiEventType
{
    kMidiNoteOff, ///< Note Off Event
    kMidiNoteOn, ///< Note On Event
    kMidiControlChange, ///< Control Change Event (CC)
    kMidiProgramChange, ///< Program Change Event
    kMidiPitchWheel,  ///< Picth Wheel Event
    kUnknown ///< Other Events
};


/** Utility functions to handle Midi events.
*
*/
namespace MidiEventUtils
{
    /** Retrieves the type of the Midi event.
    *
    */
    MidiEventType getType(const MidiEvent& evt)
    {
        MidiEventType type=kUnknown;
        switch(evt.byte0 & 0xF0)
        {
        case 0x80:
            type=kMidiNoteOff;
            break;
        case 0x90:
            type=kMidiNoteOn;
            break;
        case 0xB0:
            type=kMidiControlChange;
            break;
        case 0xC0:
            type=kMidiProgramChange;
            break;
        case 0xE0:
            type=kMidiPitchWheel;
            break; 
        }
        return type;
    }

    /** Set the type of the Midi event.
    *  @see MidiEventType enum for supported Midi events.
    */
    void setType(MidiEvent& evt,MidiEventType type)
    {
        switch(type)
        {
        case kMidiNoteOff:
            evt.byte0=0x80|(evt.byte0 & 0x0F);
            break;
        case kMidiNoteOn:
            evt.byte0=0x90|(evt.byte0 & 0x0F);
            break;
        case kMidiControlChange:
            evt.byte0=0xB0|(evt.byte0 & 0x0F);
            break;
        case kMidiProgramChange:
            evt.byte0=0xC0|(evt.byte0 & 0x0F);
            break;
        case kMidiPitchWheel:
            evt.byte0=0xE0|(evt.byte0 & 0x0F);
            break;
        }
    }

    /** Get the channel number of the event (1-16).
    *
    */
    uint8 getChannel(const MidiEvent& evt)
    {
        return (evt.byte0 & 0x0F)+1;
    }

    /** Set the channel number for the event (1-16).
    *
    */
    void setChannel(MidiEvent& evt, uint8 ch)
    {
        if(ch!=0)
            evt.byte0=(evt.byte0&0xF0)|((ch-1)&0x0F);
    }

    /** For a note event, retrieves the Midi note for the event (0-127).
    *
    */
    uint8 getNote(const MidiEvent& evt)
    {
        return evt.byte1&0x7F;
    }

    /** For a note event, sets the Midi note for the event (0-127).
    *
    */
    void setNote(MidiEvent& evt, uint8 note)
    {
        evt.byte1=(note&0x7F);
    }

    /** For a note event, retrieves the velocity for the event (0-127).
    *
    */
    uint8 getNoteVelocity(const MidiEvent& evt)
    {
        return evt.byte2 & 0x7F;
    }

    /** For a note event, sets the velocity for the event (0-127).
    *
    */
    void setNoteVelocity(MidiEvent& evt, uint8 velocity)
    {
        evt.byte2=velocity&0x7F;
    }

    /** For a CC (Control Change) event, gets the control number (0-127).
    *
    */
    uint8 getCCNumber(const MidiEvent& evt)
    {
        return evt.byte1 & 0x7F;
    }

    /** For a CC (Control Change) event, sets the control number (0-127).
    *
    */
    void setCCNumber(MidiEvent& evt,uint8 nb)
    {
        evt.byte1=nb&0x7F;
    }

    /** For a CC (Control Change) event, gets the control value (0-127).
    *
    */
    uint8 getCCValue(const MidiEvent& evt)
    {
        return evt.byte2 & 0x7F;	
    }

    /** For a CC (Control Change) event, sets the control value (0-127).
    *
    */
    void setCCValue(MidiEvent& evt,uint8 value)
    {
        evt.byte2=value & 0x7F;
    }

    /** For a Program Change event, gets the program number (0-127).
    *
    */
    uint8 getProgram(const MidiEvent& evt)
    {
        return evt.byte1&0x7F;
    }

    /** For a Program Change event, sets the program number (0-127).
    *
    */
    void setProgram(MidiEvent& evt, uint8 prog)
    {
        evt.byte2=prog&0x7F;
    }    

    /** For a pitch Wheel event, gets the pitch value (-8192 to +8192).
    *
    */
    int getPitchWheelValue(const MidiEvent& evt)
    {
        return (evt.byte2 & 0x7F)*128+(evt.byte1 & 0x7F)-64*128;	
    }

    /** For a pitch Wheel event, sets the pitch value (-8192 to +8192).
    *
    */
    void setPitchWheelValue(MidiEvent& evt, int value)
    {
        int midiValue=value+64*128;
        evt.byte1=midiValue&0x7F;
        evt.byte2=(midiValue/128)&0x7F;
    }
}
/** 
 *  \file Constants.hxx
 *  Common math constants for dsp scripting.
 * 
 *  Created by Blue Cat Audio <services@bluecataudio.com>
 *  Copyright 2014 Blue Cat Audio. All rights reserved.
 *
 */

/// Pi value
const double PI=3.141592653589793238462;
