/************************************************************************
 
	IMPORTANT NOTE : this file contains two clearly delimited sections :
	the ARCHITECTURE section (in two parts) and the USER section. Each section
	is governed by its own copyright and license. Please check individually
	each section for license and copyright information.
 *************************************************************************/

/*******************BEGIN ARCHITECTURE SECTION (part 1/2)****************/

/************************************************************************
 ************************************************************************
 FAUST VSTi Architecture File
 Copyright (C) 2013 by Yan Michalevsy
 All rights reserved.
 ----------------------------BSD License------------------------------
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions
 are met:
 
 * Redistributions of source code must retain the above copyright
 notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above
 copyright notice, this list of conditions and the following
 disclaimer in the documentation and/or other materials provided
 with the distribution.
 * Neither the name of Julius Smith nor the names of its
 contributors may be used to endorse or promote products derived
 from this software without specific prior written permission.
 
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 OF THE POSSIBILITY OF SUCH DAMAGE.
 
 ----------------------------VST SDK----------------------------------
 In order to compile a VST (TM) plugin with this architecture file
 you will need the proprietary VST SDK from Steinberg. Please check
 the corresponding license.
 
 ************************************************************************
 ************************************************************************/

/********************************************************************
 * vsti-poly.cpp - Polyphonic VSTi-2.4 wrapper for the FAUST language.
 *
 * Usage: faust -a vst.cpp myfaustprog.dsp
 *
 * By Yan Michalevsky (http://www.stanford.edu/~yanm2/)
 * based on vsti-mono.cpp by
 * Julius Smith (http://ccrma.stanford.edu/~jos/), based on vst.cpp
 * by Remy Muller <remy.muller at ircam.fr>
 * (http://www.smartelectronix.com/~mdsp/).  Essentially, vst.cpp was
 * first edited to look more like the "again" programming sample that
 * comes with the VST-2.4 SDK from Steinberg. Next, features from the
 * "vstxsynth" program sample were added to give simple MIDI synth
 * support analogous to that of faust2pd, except that only one voice
 * is supported.  (If the Faust patch has any input signals, this
 * architecture file should reduce to vst2p4.cpp --- i.e., basic VST
 * plugin support.)  As with faust2pd, to obtain MIDI control via
 * NoteOn/Off, Velocity, and KeyNumber, there must be a button named
 * "gate" and sliders (or numeric entries) named "gain" and "freq" in
 * the Faust patch specified in myfaustprog.dsp.
 *
 * NOTES:
 *  Relies on automatically generated slider GUI for VST plugins.
 *   - Horizontal and vertical sliders mapped to "vstSlider"
 *   - Numeric Entries similarly converted to "vstSlider"
 *   - No support for bar graphs or additional numeric and text displays
 *   - Tested on the Muse Receptor Pro 1.0, System Version 1.6.20070717,
 *     using Visual C++ 2008 Express Edition
 *     (part of the Microsoft Visual Studio 2008, Beta 2)
 *   - Reference:
 * http://ccrma.stanford.edu/realsimple/faust/Generating_VST_Plugin_Faust.html
 *
 * Initial work on the polyphonic VSTi architecture was done as part
 * of Music 420B class in Stanford University and in summarized in
 * http://stanford.edu/~yanm2/files/mus420b.pdf
 *
 * FAUST
 * Copyright (C) 2003-2007 GRAME, Centre National de Creation Musicale
 * http://www.grame.fr/
 *
 ********************************************************************/

// Suggestion: Faust could replace all leading comments in this file
// by the following shorter comment:

/********************************************************************
 * C++ source generated by the following command line:
 *
 *   faust -a vsti-poly.cpp name.dsp -o name-vsti.cpp
 *
 ********************************************************************/

// (where the filenames could be really right, and the path to vsti-poly.cpp
// could be included as well.)

#include <stdlib.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <math.h>
#include <errno.h>
#include <time.h>
#include <fcntl.h>
#include <assert.h>
#include <cstdio>
#include <string>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <list>

// #define DEBUG

#ifdef DEBUG
#define TRACE(x) x
#else
#define TRACE(x)
#endif

using namespace std;

struct Meta : std::map<const char*, const char*>
{
    void declare(const char* key, const char* value) { (*this)[key] = value; }
    
    const char* get(const char* key, const char* defaultString)
    {
        if (this->find(key) != this->end()) {
            return (*this)[key];
        } else {
            return defaultString;
        }
    }
};

/******************************************************************************
 *******************************************************************************
 *
 *							       VECTOR INTRINSICS
 *
 *******************************************************************************
 *******************************************************************************/

<<includeIntrinsic>>

/******************************************************************************
 *******************************************************************************
 *
 *								USER INTERFACE
 *
 *******************************************************************************
 *******************************************************************************/


/******************************************************************************
 *******************************************************************************
 *
 *								FAUST DSP
 *
 *******************************************************************************
 *****************************************************************************/

#include "faust/vst/vstui.h"
#include "faust/dsp/dsp.h"

/********************END ARCHITECTURE SECTION (part 1/2)****************/

/**************************BEGIN USER SECTION **************************/

<<includeclass>>

#include "faust/vst/voice.h"

/***************************END USER SECTION ***************************/

/*******************BEGIN ARCHITECTURE SECTION (part 2/2)***************/

/*****************************************************************************
 *
 * VST wrapper
 *
 ****************************************************************************/

#include "audioeffectx.h"

///////////////////////////////////////////////////////////////////
// Constants
///////////////////////////////////////////////////////////////////

#define MAX_NOUTS (2)

//----------------------------------------------------------------------------
// Faust class prototype
//----------------------------------------------------------------------------
#include "faust/vst/faust.h"
/*--------------------------------------------------------------------------*/

//----------------------------------------------------------------------------
// Class Implementations
//----------------------------------------------------------------------------

#define kNumPrograms (1)

AudioEffect* createEffectInstance(audioMasterCallback audioMaster)
{
    TRACE( fprintf(stderr, "Faust VSTi: Creating VST instance\n") );
    
    // The dsp and its UI need to be allocated now because
    // AudioEffectX wants the no. parameters available as an instance argument:
    mydsp* dspi = new mydsp();
    vstUI* dspUIi = new vstUI();
    dspi->buildUserInterface(dspUIi);
    
    TRACE( fprintf(stderr,"=== Faust vsti: created\n") ); // look for this in the system log
    
    return new Faust(audioMaster, dspi, dspUIi);
}

//-----------------------------------------------------------------------------
// Faust
//-----------------------------------------------------------------------------
Faust::Faust(audioMasterCallback audioMaster, dsp* dspi, vstUI* dspUIi)
: AudioEffectX(audioMaster, kNumPrograms, dspUIi->GetNumParams()),
m_dsp(dspi), m_dspUI(dspUIi),
m_voices(MAX_POLYPHONY, (Voice*)NULL),
m_playingVoices(),
m_freeVoices(),
m_prevVoice(-1),
m_tempOutputs(NULL),
m_tempOutputSize(INITIAL_TEMP_OUTPUT_SIZE),
m_currentNotes(),
m_currentVelocities(),
m_currentDeltas()
{
#ifdef DEBUG
    fprintf(stderr,"=== Faust vsti: classInit:\n");
#endif
    
    mydsp::classInit((int)getSampleRate()); // Ask AudioEffect for sample-rate
    
    setProgram(0);
    setProgramName("Default");
    
    if (audioMaster) {
        setNumInputs(m_dsp->getNumInputs());
        setNumOutputs(m_dsp->getNumOutputs());
        canProcessReplacing();
        if (m_dsp->getNumInputs() == 0) {
            isSynth(); // at least let's hope so!
            if (m_dsp->getNumOutputs() < 1) {
                fprintf(stderr,"*** faust: vsti: No signal inputs or outputs, "
                        "and Faust has no MIDI outputs!\n");
            }
        }
        const char* ID = getMetadata("id", "FAUST");
        if (ID[0] == 0) {
            setUniqueID(m_dspUI->makeID());
        } else {
            setUniqueID((ID[0]<<24)+(ID[1]<<16)+(ID[2]<<8)+ID[3]);
        }
    }
    
    // Initialize all members related to polyphonic performance
    TRACE( fprintf(stderr, "Faust VSTi: Initializing voices and temporary output buffers\n") );
    for (unsigned int i = 0; i < MAX_POLYPHONY; ++i) {
        m_voices[i] = new Voice((int)getSampleRate());
        m_freeVoices.push_back(i);
    }
    
    TRACE( fprintf(stderr, "Faust VSTi: Allocating %d temporary output "
                   "buffers\n", m_dsp->getNumOutputs()) );
    m_tempOutputs = (FAUSTFLOAT**) malloc(sizeof(FAUSTFLOAT*) * m_dsp->getNumOutputs());
    for (int i = 0; i < m_dsp->getNumOutputs(); ++i) {
        m_tempOutputs[i] = (FAUSTFLOAT*) malloc(sizeof(FAUSTFLOAT) * m_tempOutputSize);
    }
    
    initProcess();
    if (m_dsp->getNumInputs() == 0) {
        suspend(); //  Synths start out quiet
    }
}

//----------------------------------------------------------------------------
Faust::~Faust()
{
    TRACE( fprintf(stderr, "Calling Faust VST destructor\n") );
    
    for (int i = 0; i < m_dsp->getNumOutputs(); ++i) {
        free(m_tempOutputs[i]);
    }
    free(m_tempOutputs);
    
    for (unsigned int i = 0; i < MAX_POLYPHONY; ++i) {
        delete m_voices[i];
        m_voices[i] = NULL;
    }
    
    delete m_dspUI;
    delete m_dsp;
}

//-----------------------------------------------------------------------------
void Faust::setProgram(VstInt32 program)
// Override this method of AudioEffect in order to set
// local instance variables corresponding to the current MIDI program.
// Here there is only one program.
{
    if (program < 0 || program >= kNumPrograms) {
        fprintf(stderr,
                "*** Faust vsti: setting program to %d is OUT OF RANGE\n",
                program);
        return;
    }
    
#ifdef DEBUG
    fprintf(stderr,"=== Faust vsti: setting program to %d\n",program);
#endif
    
    curProgram = program; // curProgram defined in audioeffect.h
}

//----------------------------------------------------------------------------
void Faust::setProgramName(const char* name)
{
    vst_strncpy(programName, name, kVstMaxProgNameLen);
}

//----------------------------------------------------------------------------
void Faust::getProgramName(char *name)
{
    vst_strncpy(name, programName, kVstMaxProgNameLen);
}

//----------------------------------------------------------------------------
void Faust::getParameterLabel(VstInt32 index, char *label)
{
    const char* unit = "";
    if (index < numParams) {
        unit = m_dspUI->getControlMetadata(index, "unit", "");
    }
    
    vst_strncpy(label, unit, kVstMaxParamStrLen); // parameter units in Name
    
    TRACE( fprintf(stderr, "Called getParameterLabel for parameter %d, returning %s\n",
                   index, label) );
}

//----------------------------------------------------------------------------
void Faust::getParameterDisplay(VstInt32 index, char *text)
{
    if (index < numParams) {
        m_dspUI->GetDisplay(index,text); // get displayed float value as text
    } else {
        vst_strncpy(text, "IndexOutOfRange", kVstMaxParamStrLen);
    }
}

//----------------------------------------------------------------------------
void Faust::getParameterName(VstInt32 index, char *label)
{
    if (index < numParams) {
        m_dspUI->GetName(index,label); // parameter name, including units
    } else {
        vst_strncpy(label, "IndexOutOfRange", kVstMaxParamStrLen);
    }
}

//----------------------------------------------------------------------------
bool Faust::getParameterProperties(VstInt32 index, VstParameterProperties* properties)
{
    if (index < 0 || index >= m_dspUI->GetNumParams()) {
        TRACE( fprintf(stderr, "Faust VSTi: Invalid parameter index %d\n",
                       index) );
        return false;
    }
    
    TRACE( fprintf(stderr, "Faust VSTi: getParameterProperties called with index %d\n",
                   index) );
    
    if (index == m_dspUI->gateIndex) {
        properties->flags |= kVstParameterIsSwitch;
    }
    
    getParameterName(index, properties->label);
    
    // TODO: set parameter range
    const vstSlider* slider = dynamic_cast<const vstSlider*>(m_dspUI);
    if (NULL != slider) {
        // has min-max range
        properties->minInteger = slider->getMinValue();
        properties->maxInteger = slider->getMaxValue();
        properties->stepInteger = slider->getStep();
    }
    
    return true;
}

//----------------------------------------------------------------------------
void Faust::setParameter(VstInt32 index, float value)
{
    if (index >= numParams || index < 0) {
        TRACE( fprintf(stderr, "Faust VSTi: Invalid parameter index %d\n",
                       index) );
        return;
    }
    
    m_dspUI->SetValue(index, value);
    
    for (unsigned int i = 0; i < MAX_POLYPHONY; ++i) {
        TRACE( fprintf(stderr, "Setting parameter %d for voice %d to value %f\n",
                       index, i, value) );
        m_voices[i]->SetValue(index, value);
    }
}

//----------------------------------------------------------------------------
float Faust::getParameter(VstInt32 index)
{
    return (index >= 0 && index < numParams) ? m_dspUI->GetValue(index) : 0.0f;
}

//-----------------------------------------------------------------------------
bool Faust::getInputProperties(VstInt32 index, VstPinProperties* properties)
{
    if(index < 0 || index >= m_dsp->getNumInputs()) {
        return false;
    }
    
    sprintf (properties->label, "Grame Faust DSP input: %d",index);
    sprintf (properties->shortLabel, "In %d",index);
    properties->flags = kVstPinIsActive;
    if (m_dsp->getNumInputs() == 2) {
        properties->flags |= kVstPinIsStereo;
    }
    
    return true;
}

//-----------------------------------------------------------------------------
bool Faust::getOutputProperties(VstInt32 index, VstPinProperties* properties)
{
    if(index < 0 || m_dsp->getNumOutputs() < 1) {
        return false;
    }
    
    sprintf (properties->label, "Grame Faust DSP output: %d",index);
    sprintf (properties->shortLabel, "Out %d",index);
    properties->flags = kVstPinIsActive;
    if (m_dsp->getNumOutputs() == 2) {
        properties->flags |= kVstPinIsStereo;
    }
    return true;
}

//----------------------------------------------------------------------------
bool Faust::getProgramNameIndexed(VstInt32 category, VstInt32 index,
                                   char* text)
{
    if (index < kNumPrograms) {
        vst_strncpy(text, programName, kVstMaxProgNameLen);
        return true;
    }
    return false;
}

//-----------------------------------------------------------------------------
const char* Faust::getMetadata(const char* key, const char* defaultString)
{
    Meta meta;
    mydsp* tmp = new mydsp;
    tmp->metadata(&meta);
    const char* res = meta.get(key, defaultString);
    delete tmp;
    return res;
}

//-----------------------------------------------------------------------------
bool Faust::getEffectName(char* name)
{
    const char* effectName = getMetadata("name", "Effect Name goes here");
    vst_strncpy(name, effectName, kVstMaxEffectNameLen);
    return true;
}

//-----------------------------------------------------------------------------
bool Faust::getVendorString(char* text)
{
    const char* vendorString = getMetadata("author", "Vendor String goes here");
    vst_strncpy(text, vendorString, kVstMaxVendorStrLen);
    return true;
}

//-----------------------------------------------------------------------------
bool Faust::getProductString(char* text)
{
    const char* productString = getMetadata("name", "Product String goes here");
    vst_strncpy (text, productString, kVstMaxProductStrLen);
    return true;
}

//-----------------------------------------------------------------------------
VstInt32 Faust::getVendorVersion()
{
    const char* versionString = getMetadata("version", "0.0");
    return (VstInt32)atof(versionString);
}

//-----------------------------------------------------------------------------
VstInt32 Faust::canDo(const char* text)
{
    if (!strcmp (text, "receiveVstEvents")) {
        return 1;
    }
    
    if (!strcmp (text, "receiveVstMidiEvent")) {
        return 1;
    }
    
    if (!strcmp (text, "midiProgramNames")) {
        return 1;
    }
    
    return -1;	// explicitly can't do; 0 => don't know
}

//----------------------------------------------------------------------------
VstInt32 Faust::getNumMidiInputChannels()
{
    return 1; // one MIDI-in channel
}

//----------------------------------------------------------------------------
VstInt32 Faust::getNumMidiOutputChannels()
{
    return 0; // no MIDI-outs
}

//----------------------------------------------------------------------------
VstInt32 Faust::getMidiProgramName(VstInt32 channel, MidiProgramName* mpn)
{
    VstInt32 prg = mpn->thisProgramIndex;
    if (prg < 0 || prg > 0) return 0;
    fillProgram (channel, prg, mpn);
    return 1; // we have only 1 "MIDI program"
}

//------------------------------------------------------------------------
VstInt32 Faust::getCurrentMidiProgram(VstInt32 channel, MidiProgramName* mpn)
{
    // There is only one MIDI program here, so return it regardless of MIDI channel:
    if (channel < 0 || channel >= 16 || !mpn) return -1;
    VstInt32 prg = 0;
    mpn->thisProgramIndex = prg;
    fillProgram (channel, prg, mpn);
    return prg;
}

//------------------------------------------------------------------------
void Faust::fillProgram(VstInt32 channel, VstInt32 prg, MidiProgramName* mpn)
// Fill mpn struct for given channel.  Here there should be only one.
{
    mpn->midiBankMsb = mpn->midiBankLsb = -1;
    mpn->reserved = 0;
    mpn->flags = 0;
    vst_strncpy (mpn->name, programName, kVstMaxProgNameLen);
    mpn->midiProgram = (char)prg; // prg should only be 0
    mpn->parentCategoryIndex = -1;
}

//------------------------------------------------------------------------
VstInt32 Faust::getMidiProgramCategory(VstInt32 channel, MidiProgramCategory* cat)
// VST host wants to fill cat struct for given channel.  We have only one category.
{
    cat->parentCategoryIndex = -1;	// -1:no parent category
    cat->flags = 0;                 // reserved, none defined yet, zero.
    VstInt32 category = cat->thisCategoryIndex;
    vst_strncpy (cat->name, "Faust Patch", kVstMaxProgNameLen);
    return 1; // one category
}

//***********************************************************************

//----------------------------------------------------------------------------
void Faust::setSampleRate(float sampleRate)
{
    AudioEffect::setSampleRate(sampleRate);
    m_dsp->init((int)getSampleRate()); // in case AudioEffect altered it
    
    for (unsigned int i = 0; i < MAX_POLYPHONY; ++i) {
        m_voices[i]->m_dsp.init((int)getSampleRate());
    }
}

//----------------------------------------------------------------------------
void Faust::initProcess()
{
    currentVelocity = currentNote = currentDelta = 0;
    m_dsp->init((int)getSampleRate());
    
    for (unsigned int i = 0; i < MAX_POLYPHONY; ++i) {
        m_voices[i]->m_dsp.init((int)getSampleRate());
    }
}

//----------------------------------------------------------------------------
void Faust::processReplacing(FAUSTFLOAT** inputs, FAUSTFLOAT** outputs, VstInt32 sampleFrames)
{
    AVOIDDENORMALS;
#ifdef DEBUG
    // fprintf(stderr,"=== Faust vsti: processReplacing . . .\n");
#endif
    
    if (m_dsp->getNumInputs() > 0) {
        // We're an effect . . . keep going:
        m_dsp->compute(sampleFrames, inputs, outputs);
    }
    else {
        // We're a synth . . .
        synthProcessReplacing(inputs, outputs, sampleFrames);
    }
}

//----------------------------------------------------------------------------
inline float midiToFreq(int note)
{
    return 440.0f*powf(2.0f,(((float)note)-69.0f)/12.0f);
}

//----------------------------------------------------------------------------
void Faust::synthProcessReplacing(FAUSTFLOAT** inputs, FAUSTFLOAT** outputs,
                                  VstInt32 sampleFrames)
{
    float* outptr[MAX_NOUTS] = {NULL,};
    int i;
    int nouts = m_dsp->getNumOutputs();
    
    if (nouts > MAX_NOUTS) {
        TRACE( fprintf(stderr, "VSTi: nouts > MAX_NOUTS. Number of outputs (%d) exceeds limit. "
                       "Truncated to maximum.\n", nouts) );
        nouts = MAX_NOUTS;
    }
    
    // we're synthesizing . . .
    if (m_currentNotes.size() > 0) {
        int previousDelta = 0;
        while (m_currentNotes.size() > 0) { // a new note
            currentDelta = m_currentDeltas.front();
            // but waiting out a timestamp delay . . .
            if (currentDelta >= sampleFrames) {
                // start time is after this chunk
                for(std::list<VstInt32>::iterator it = m_currentDeltas.begin(); it != m_currentDeltas.end(); ++it) {
                    *it -= sampleFrames;
                }
                compute(inputs, outputs, sampleFrames);
                return;
            }
            else {
                m_currentDeltas.pop_front();
                currentNote = m_currentNotes.front();
                m_currentNotes.pop_front();
                currentVelocity = m_currentVelocities.front();
                m_currentVelocities.pop_front();
                if (currentVelocity > 0) { // Note on
                    // Reserve a free voice for the played
                    int currentVoice;
                    if (m_freeVoices.size() > 0) {
                        currentVoice = m_freeVoices.front();
                        m_freeVoices.pop_front();
                        struct voice_node *n = new voice_node;
                        n->note = currentNote;
                        n->voice = currentVoice;
                        m_playingVoices.push_back(n);
                    }
                    else {
                        voice_node *front = m_playingVoices.front();
                        currentVoice = front->voice;
                        float freq = midiToFreq(front->note);
                        m_voices[currentVoice]->setPrevFreq(freq);
                        front->note = currentNote;
                        m_playingVoices.pop_front();
                        m_playingVoices.push_back(front);
                    }
                    memset(outptr, 0, sizeof(outptr));
                    // Before the note starts
                    assert(nouts <= MAX_NOUTS);
                    for (i = 0; i < nouts; i++) {
                        outptr[i] = outputs[i] + previousDelta; // leaving caller's pointers alone
                    }
                    compute(inputs, outptr, currentDelta - previousDelta);
                    // Note start
                    float freq = midiToFreq(currentNote);
                    m_voices[currentVoice]->setFreq(freq); // Hz - requires Faust control-signal "freq"
                    float gain = currentVelocity/127.0f;
                    m_voices[currentVoice]->setGain(gain); // 0-1 - requires Faust control-signal "gain"
                    m_voices[currentVoice]->setGate(1.0f); // 0 or 1 - requires Faust control-signal "gate"
                }
                else { // Note off
                    // Find the voice to be turned off
                    int currentVoice;
                    bool voiceFound = false;
                    std::list<voice_node*>::iterator voice_iter = m_playingVoices.begin();
                    for (; voice_iter != m_playingVoices.end(); voice_iter++) {
                        if (currentNote == (*voice_iter)->note) {
                            currentVoice = (*voice_iter)->voice;
                            TRACE( fprintf(stderr, "=== Faust VSTi: Found matching voice for note %d: %d\n", currentNote, currentVoice) );
                            if (std::find(m_releasedVoices.begin(), m_releasedVoices.end(), currentVoice) == m_releasedVoices.end()) {
                                m_releasedVoices.push_back(currentVoice);
                                voiceFound = true;
                            }
                        }
                    }
                    memset(outptr, 0, sizeof(outptr));
                    // Before the note ends
                    for (i = 0; i < nouts; i++) {
                        outptr[i] = outputs[i] + previousDelta; // leaving caller's pointers alone
                    }
                    compute(inputs, outptr, currentDelta - previousDelta);
                    // Note end
                    if (voiceFound) {
                        m_voices[currentVoice]->setGate(0);
                    }
                }
                previousDelta = currentDelta;
            }
        }
        memset(outptr, 0, sizeof(outptr));
        // Compute the left over time
        int count = sampleFrames - currentDelta;
        // Left over time in chunk
        for (i = 0; i < nouts; i++) {
            outptr[i] = outputs[i] + currentDelta; // leaving caller's pointers alone
        }
        compute(inputs, outptr, count);
    }
    else {
        compute(inputs, outputs, sampleFrames);
    }
    
}

//----------------------------------------------------------------------------
void Faust::compute(FAUSTFLOAT** inputs, FAUSTFLOAT** outputs,
                      VstInt32 sampleFrames)
{
    const unsigned int output_buffer_size = sizeof(FAUSTFLOAT) * sampleFrames;
    
    // Clear the buffers
    for (int i = 0; i < m_dsp->getNumOutputs(); ++i) {
        for (int frame = 0; frame < sampleFrames; ++frame) {
            outputs[i][frame] = 0;
        }
    }
    
    if (sampleFrames > (VstInt32)m_tempOutputSize) {
        // if requested number of samples to synthesize exceeds current temporary buffer
        TRACE( fprintf(stderr, "Faust VSTi: Increasing temporary buffer to %d frames\n",
                       sampleFrames) );
        for (unsigned int i = 0; i < MAX_POLYPHONY; ++i) {
            m_tempOutputs[i] = (FAUSTFLOAT*) realloc(m_tempOutputs[i], output_buffer_size * m_dsp->getNumOutputs());
            m_tempOutputSize = sampleFrames;
        }
    }
    
    std::list<voice_node*> removed;
    std::list<voice_node*>::iterator voice_iter = m_playingVoices.begin();
    for(;voice_iter != m_playingVoices.end(); voice_iter++) {
        int voice = (*voice_iter)->voice;
        m_voices[voice]->m_dsp.compute(sampleFrames, inputs, m_tempOutputs);
        
        bool silent = true;
        // mix current voice into output
        for (int i = 0; i < m_dsp->getNumOutputs(); ++i) {
            for (int frame = 0; frame < sampleFrames; ++frame) {
                outputs[i][frame] += m_tempOutputs[i][frame];
                if (fabs(m_tempOutputs[i][frame]) > 1e-6) {
                    silent = false;
                }
            }
        }
        if (silent) {
            std::vector<VstInt32>::iterator it;
            it = std::find(m_releasedVoices.begin(), m_releasedVoices.end(), voice);
            if (it != m_releasedVoices.end()) {
                m_freeVoices.push_back(voice);
                m_releasedVoices.erase(it);
                removed.push_back(*voice_iter);
            }
        }
    } // end of signal computation and mixdown
    while (removed.size() > 0) {
        std::list<voice_node*>::iterator it;
        it = std::find(m_playingVoices.begin(), m_playingVoices.end(), removed.front());
        float freq = midiToFreq((*it)->note);
        m_voices[(*it)->voice]->setPrevFreq(freq);
        free(*it);
        m_playingVoices.erase(it);
        removed.pop_front();
    }
    
    // normalize sample by the max polyphony
    for (int i = 0; i < m_dsp->getNumOutputs(); ++i) {
        for (int frame = 0; frame < sampleFrames; ++frame) {
            outputs[i][frame] /= (FAUSTFLOAT)sqrt(MAX_POLYPHONY);
        }
    }
}

//-----------------------------------------------------------------------------
VstInt32 Faust::processEvents(VstEvents* ev)
{
    if (ev->numEvents > 0) {
        TRACE( fprintf(stderr,"=== Faust vsti: processEvents processing %d "
                       "events\n", ev->numEvents) );
    }
    
    for (VstInt32 i = 0; i < ev->numEvents; i++) {
        TRACE( fprintf(stderr,"=== Faust vsti: event type = %d\n",
                       (ev->events[i])->type) );
        if ((ev->events[i])->type != kVstMidiType) {
            TRACE( fprintf(stderr,"=== Faust vsti: EVENT IGNORED!\n") );
            continue;
        }
        
        VstMidiEvent* event = (VstMidiEvent*)ev->events[i];
        char* midiData = event->midiData;
        VstInt32 chan = midiData[0] & 0xf;
        VstInt32 status = midiData[0] & 0xf0;
        
#ifdef DEBUG
        fprintf(stderr,"\n=== Faust vsti: event->midiData[0] = 0x%x\n",
                event->midiData[0]);
        fprintf(stderr,"=== Faust vsti: midi channel = 0x%x\n", chan);
        fprintf(stderr,"=== Faust vsti: midi status = 0x%x\n", status);
        fprintf(stderr,"=== Faust vsti: event->midiData[1] = 0x%x\n",
                event->midiData[1]);
        fprintf(stderr,"=== Faust vsti: event->midiData[2] = 0x%x\n",
                event->midiData[2]);
#endif
        
        VstInt32 note = midiData[1] & 0x7f;
        
        if (status == 0x90) { // note on
            VstInt32 velocity = midiData[2] & 0x7f;
            TRACE( fprintf(stderr,
                           "=== Faust vsti: note = %d, velocity = %d, delay = %d\n",
                           note,velocity,event->deltaFrames) );
            if (velocity > 0) {
                noteOn(note, velocity, event->deltaFrames);
            } 
            else {
                noteOff(note, event->deltaFrames);
            }
        } 
        else if (status == 0x80) { // note off
            noteOff(note, event->deltaFrames);
            //      } else if (status == 0xA0) { // poly aftertouch
        } else if (status == 0xB0) { // control change
            /* DO SOMETHING WITH THE CONTROLLER DATA */
            fprintf(stderr,"=== Faust vsti: CONTROL CHANGE (status 0xB0)!\n");
            if (midiData[1] == 0x7e || midiData[1] == 0x7b) { // all notes off
                fprintf(stderr,"=== Faust vsti: ALL NOTES OFF!\n");
                
                //TODO: figure out how to signal all notes off
                // why is all-notes-off inside a "control change" event?
                allNotesOff(); 
            }
        }
        else if (status == 0xE0) { // pitch change
            int val = midiData[1] | (midiData[2] << 7);
            float bend = (val - 0x2000) / 8192.0f;
            bendPitch(bend);
        }
        //      } else if (status == 0xF0) { // SYSX ...
        //      } else if (status == 0xC0) { // program change
        //      } else if (status == 0xD0) { // mono aftertouch
        // For a list, see 
        // http://www.alfred-j-faust.de/rft/midi%20status%20types.html
        
        TRACE( fprintf(stderr,"=== Faust vsti: Going to next event\n") );
        
        event++;
    }
    
    return 1;
}

//----------------------------------------------------------------------------
void Faust::bendPitch(float bend)
{
    TRACE( fprintf(stderr, "Bending pitch by %f\n", bend) );
    for (unsigned int i = 0; i < MAX_POLYPHONY; ++i) {
        m_voices[i]->setPitchBend(bend);
    }
}

//----------------------------------------------------------------------------
void Faust::noteOn (VstInt32 note, VstInt32 velocity, VstInt32 delta)
{
#ifdef DEBUG
    fprintf(stderr,"=== Faust vsti: noteOn: note = %d, vel = %d, del = %d\n",note,velocity,delta);
#endif
    m_currentNotes.push_back(note);
    m_currentVelocities.push_back(velocity);
    m_currentDeltas.push_back(delta);
}

//-----------------------------------------------------------------------------
void Faust::noteOff(VstInt32 note, VstInt32 delta)
{
    TRACE( fprintf(stderr,"=== Faust vsti: noteOff\n") );
    m_currentNotes.push_back(note);
    m_currentVelocities.push_back(0);
    m_currentDeltas.push_back(delta);
} // end of nToteOff

void Faust::allNotesOff(void)
{
    TRACE( fprintf(stderr, "All notes off\n") );
    
    for (unsigned int i = 0; i < MAX_POLYPHONY; ++i) {
        m_voices[i]->setGate(0);
    }
    
    std::list<voice_node*>::iterator voice_iter = m_playingVoices.begin();
    for (; voice_iter != m_playingVoices.end(); voice_iter++) {
        int voice = (*voice_iter)->voice;
        if (std::find(m_releasedVoices.begin(), m_releasedVoices.end(), voice) == m_releasedVoices.end()) {
            m_releasedVoices.push_back(voice);
        }
    }
}

/********************END ARCHITECTURE SECTION (part 2/2)****************/

