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

	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 Architecture File
	Copyright (C) 2007-2011 Julius Smith
	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-mono.cpp - Monaural VSTi-2.4 wrapper for the FAUST language. 
 *
 * Usage: faust -a vsti-mono.cpp myfaustprog.dsp
 *
 * 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
 *
 * 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.cpp name.dsp -o name-vsti.cpp
 *
 ********************************************************************/

// (where the filenames could be really right, and the path to vsti.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 <unistd.h>
#include <fcntl.h>
#include <assert.h>
#include <string>
#include <vector>
#include <math.h>

using namespace std ;

// There is a bug with powf() when cross compiling with mingw
// the following macro avoid the problem
#ifdef WIN32
#define powf(x,y) pow(x,y)
#define expf(x) exp(x)
#endif

// On Intel set FZ (Flush to Zero) and DAZ (Denormals Are Zero)
// flags to avoid costly denormals
#ifdef __SSE__
    #include <xmmintrin.h>
    #ifdef __SSE2__
        #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8040)
    #else
        #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8000)
    #endif
#else
    #define AVOIDDENORMALS 
#endif

struct Meta
{
    void declare (const char* key, const char* value) {  }
};
	

// abs is now predefined
//template<typename T> T abs (T a) { return (a<T(0)) ? -a : a; }

inline int lsr (int x, int n) { return int(((unsigned int)x) >> n); }

inline int int2pow2 (int x) { int r=0; while ((1<<r)<x) r++; return r; }

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

//inline void *aligned_calloc(size_t nmemb, size_t size) { return (void*)((unsigned)(calloc((nmemb*size)+15,sizeof(char)))+15 & 0xfffffff0); }
//inline void *aligned_calloc(size_t nmemb, size_t size) { return (void*)((size_t)(calloc((nmemb*size)+15,sizeof(char)))+15 & ~15); }

<<includeIntrinsic>>

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

class UI
{
  bool	fStopped;

public:
			
  UI() : fStopped(false) {}
  virtual ~UI() {}
		
  virtual void addButton(char* label, float* zone) = 0;
  virtual void addToggleButton(char* label, float* zone) = 0;
  virtual void addCheckButton(char* label, float* zone) = 0;
  virtual void addVerticalSlider(char* label, float* zone, float init, float min, float max, float step) = 0;
  virtual void addHorizontalSlider(char* label, float* zone, float init, float min, float max, float step) = 0;
  virtual void addNumEntry(char* label, float* zone, float init, float min, float max, float step) = 0;
	
  virtual void addNumDisplay(char* label, float* zone, int precision) = 0;
  virtual void addTextDisplay(char* label, float* zone, char* names[], float min, float max) = 0;
  virtual void addHorizontalBargraph(char* label, float* zone, float min, float max) = 0;
  virtual void addVerticalBargraph(char* label, float* zone, float min, float max) = 0;
		
  virtual void openFrameBox(char* label) = 0;
  virtual void openTabBox(char* label) = 0;
  virtual void openHorizontalBox(char* label) = 0;
  virtual void openVerticalBox(char* label) = 0;
  virtual void closeBox() = 0;
		
  virtual void run() {};
		
  void stop()		{ fStopped = true; }
  bool stopped()	{ return fStopped; }

  virtual void declare(float* zone, const char* key, const char* value) {}
};


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



//----------------------------------------------------------------
//  Base dsp class for this architecture
//----------------------------------------------------------------
			
class dsp {

protected:
	 
  int fSamplingFreq;
		
public:
	 
  dsp() {}
  virtual ~dsp() {}
  virtual int getNumInputs() = 0;
  virtual int getNumOutputs() = 0;
  virtual void buildUserInterface(UI* interface) = 0;
  virtual void init(int samplingRate) = 0;
  virtual void compute(int len, float** inputs, float** outputs) = 0;
};
		
/********************END ARCHITECTURE SECTION (part 1/2)****************/

/**************************BEGIN USER SECTION **************************/
		
<<includeclass>>

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

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

#include "audioeffectx.h" 

class vstUI;

//------------------------------------------------------------------------------
// Faust class prototype
//------------------------------------------------------------------------------
class Faust : public AudioEffectX
{
public:
  Faust(audioMasterCallback audioMaster, mydsp* dspi, vstUI* dspUIi);
  virtual ~Faust();

  virtual void processReplacing (float **inputs, float **outputs, VstInt32 sampleFrames);
  virtual VstInt32 processEvents (VstEvents* events);

  virtual void setProgram (VstInt32 program);
  virtual void setProgramName (char *name);
  virtual void getProgramName (char *name);
  virtual bool getProgramNameIndexed (VstInt32 category, VstInt32 index, char *text);

  virtual void setParameter (VstInt32 index, float value);
  virtual float getParameter (VstInt32 index);
  virtual void getParameterLabel (VstInt32 index, char *label);
  virtual void getParameterDisplay (VstInt32 index, char *text);
  virtual void getParameterName (VstInt32 index, char *text);

  virtual void setSampleRate (float sampleRate);

  virtual bool getInputProperties (VstInt32 index, VstPinProperties *properties);
  virtual bool getOutputProperties (VstInt32 index, VstPinProperties *properties);

  virtual bool getEffectName (char *name);
  virtual bool getVendorString (char *text);
  virtual bool getProductString (char *text);
  virtual VstInt32 getVendorVersion ();
  virtual VstInt32 canDo (char *text);

  virtual VstInt32 getNumMidiInputChannels ();
  virtual VstInt32 getNumMidiOutputChannels ();

  virtual VstInt32 getMidiProgramName (VstInt32 channel, MidiProgramName *midiProgramName);
  virtual VstInt32 getCurrentMidiProgram (VstInt32 channel, MidiProgramName *currentProgram);
  virtual VstInt32 getMidiProgramCategory (VstInt32 channel, MidiProgramCategory *category);

private:
  mydsp*	dsp;
  vstUI*	dspUI;

  // For synths:
  bool noteIsOn;
  VstInt32 currentNote;
  VstInt32 currentVelocity;
  VstInt32 currentDelta;

  void initProcess ();
  void noteOn (VstInt32 note, VstInt32 velocity, VstInt32 delta);
  void noteOff ();
  void fillProgram (VstInt32 channel, VstInt32 prg, MidiProgramName* mpn);

  char programName[kVstMaxProgNameLen + 1];
};

/*--------------------------------------------------------------------------*/
class vstUIObject { /* superclass of all VST UI widgets */
protected:
  string fLabel;
  float* fZone;
		
  inline float clip(float min, float max, float val) 
  {
    return (val < min) ? min : (val > max) ? max : val;
  }
	 
  inline float normalize(float min, float max, float val) 
  { // VST parameters are normalized to the range [0;1] on the host
    val = min + val * (max - min);
    return (val < min) ? min : (val > max) ? max : val;
  }
	 
public:			
  vstUIObject(char* label, float* zone):fLabel(label),fZone(zone) {}
  virtual ~vstUIObject() {}

  virtual void  GetName(char *text){std::strcpy(text,fLabel.c_str());}
  virtual void  SetValue(double f) {*fZone = normalize(0.0f,1.0f,(float)f);}
  virtual void  SetValueNoNormalization(double f) {*fZone = clip(0.0f,1.0f,(float)f);}
  virtual float GetValue() {return *fZone;}
  virtual void  GetDisplay(char *text){std::sprintf(text,"%f",*fZone);}
  virtual long  GetID() 
  {	/* returns the sum of all the ASCII characters  contained in the parameter's label */
    unsigned int i;
    long acc;
    for(i=0,acc = 0;i<fLabel.length();i++) acc += (fLabel.c_str())[i];
    return acc;
  }
};

/*--------------------------------------------------------------------------*/
class vstToggleButton : public vstUIObject {
	
public:	
	
  vstToggleButton(char* label, float* zone):vstUIObject(label,zone) {}
  virtual ~vstToggleButton() {}
  virtual float GetValue() {return *fZone;}
  virtual void SetValue(double f) {*fZone = (f>0.5f)?1.0f:0.0f;}
  virtual void  GetDisplay(char *text){(*fZone>0.5f)? std::strcpy(text,"ON"): std::strcpy(text,"OFF");}
};

/*--------------------------------------------------------------------------*/
class vstCheckButton : public vstUIObject {
	
public:
	
  vstCheckButton(char* label, float* zone):vstUIObject(label,zone) {}	
  virtual ~vstCheckButton() {}
  virtual float GetValue() {return *fZone;}
  virtual void SetValue(double f) {*fZone = (f>0.5f)?1.0f:0.0f;}
  virtual void  GetDisplay(char *text){(*fZone>0.5f)? std::strcpy(text,"ON"): std::strcpy(text,"OFF");}		
};

/*--------------------------------------------------------------------------*/
class vstButton : public vstUIObject {
	
public:
	
  vstButton(char* label, float* zone):vstUIObject(label,zone) {}
  virtual ~vstButton() {}		
  virtual float GetValue() {return *fZone;}
  virtual void SetValue(double f) {*fZone = (f>0.5f)?1.0f:0.0f;}		
  virtual void  GetDisplay(char *text){(*fZone>0.5f)? std::strcpy(text,"ON"): std::strcpy(text,"OFF");}
};

/*--------------------------------------------------------------------------*/
class vstSlider : public vstUIObject{

private:
	
  float fInit;
  float fMin;
  float fMax;
  float fStep;
	
public:	
	
  vstSlider(char* label, float* zone, float init, float min, float max, float step)
    :vstUIObject(label,zone), fInit(init), fMin(min), fMax(max),fStep(step) {}
  virtual ~vstSlider() {}	

  // The VST host calls GetValue() and expects a result in [0,1].
  // The VST host calls SetValue(f) with f in [0,1]. We convert to real units.
  // When we process MIDI controls, we call SetValueNoNormalization(f) with f in real units.
  virtual float GetValue() {return (*fZone-fMin)/(fMax-fMin);}	// normalize
  virtual void SetValue(double f) {*fZone = normalize(fMin,fMax,(float)f);} // denormalize
  virtual void SetValueNoNormalization(double f) {*fZone = clip(fMin,fMax,(float)f);} // raw
};

/*--------------------------------------------------------------------------*/
class vstUI : public UI
{
private:
	
  vector<vstUIObject*> fUITable;

public:
			
  int freqIndex;
  int gainIndex;
  int gateIndex;

  vstUI(){   
    freqIndex = gainIndex = gateIndex = -1;
  }
  virtual ~vstUI() 
  {
    for (vector<vstUIObject*>::iterator iter = fUITable.begin(); iter != fUITable.end(); iter++) delete *iter;
  }

  void setAny(int anyIndex, float val, char *str) {
    if (anyIndex<0) {
#ifdef DEBUG
      // On the Receptor, and perhaps other hosts, output to stderr is logged in a file.
      fprintf(stderr,"*** Faust vsti: %sIndex = %d never set!\n",str,anyIndex);
#endif
      return;
    }
    if (anyIndex >= fUITable.size()) {
#ifdef DEBUG
        fprintf(stderr,"*** Faust vsti: %sIndex = %d too large!\n",str,anyIndex);
#endif
	return;
    }
#ifdef DEBUG
    fprintf(stderr,"*** Faust vsti: Setting %sIndex = %d to %f\n",str,anyIndex,val);
#endif
    fUITable[anyIndex]->SetValueNoNormalization(val);
  }
		
  void setFreq(float val) {
    setAny(freqIndex, val, "freq");
  }
		
  void setGate(float val) {
    setAny(gateIndex, val, "gate");
  }
		
  void setGain(float val) {
    setAny(gainIndex, val, "gain");
  }

  bool ckAnyMatch(char* label, char* indexName, int *index) {
    if (strcmp(label,indexName)==0) { 
#ifdef DEBUG
      fprintf(stderr,"=== Faust vsti: label '%s' matches '%s'\n",label,indexName);
#endif
      *index = fUITable.size() - 1; 
      return true;
    }
    return false;
  }

  void ckAllMatches(char* label) {
    ckAnyMatch(label,"gain",&gainIndex);
    ckAnyMatch(label,"gate",&gateIndex);
    ckAnyMatch(label,"freq",&freqIndex);
  }

  void addButton(char* label, float* zone) {
    vstButton* theButton = new vstButton(label, zone);
    fUITable.push_back(theButton);
#ifdef DEBUG
    fprintf(stderr,"=== Faust vsti: Adding Button with label '%s'\n",label);
#endif
    ckAnyMatch(label,"gate",&gateIndex);
  }
		
  void addToggleButton(char* label, float* zone) {fUITable.push_back(new vstToggleButton(label, zone));}
		
  void addCheckButton(char* label, float* zone) {fUITable.push_back(new vstCheckButton(label, zone));}
		
  void addVerticalSlider(char* label, float* zone, float init, float min, float max, float step) 
  { 	
    vstSlider* theSlider = new vstSlider(label, zone, init, min, max, step);
    fUITable.push_back(theSlider);
#ifdef DEBUG
    fprintf(stderr,"=== Faust vsti: Adding VSlider (HSlider) with label '%s'\n",label);
#endif
    ckAllMatches(label);
  }
		
  void addHorizontalSlider(char* label, float* zone, float init, float min, float max, float step) 
  {
    vstSlider* theSlider = new vstSlider(label, zone, init, min, max, step);
    fUITable.push_back(theSlider);
#ifdef DEBUG
    fprintf(stderr,"=== Faust vsti: Adding HSlider with label '%s'\n",label);
#endif
    ckAllMatches(label);
  }
		
  void addNumEntry(char* label, float* zone, float init, float min, float max, float step)
  { /* Number entries converted to horizontal sliders */
    vstSlider* theSlider = new vstSlider(label, zone, init, min, max, step);
    fUITable.push_back(theSlider);
#ifdef DEBUG
    fprintf(stderr,"=== Faust vsti: Adding NumEntry (HSlider) with label '%s'\n",label);
#endif
    ckAllMatches(label);
  }
		
  void openFrameBox(char* label) {}
  void openTabBox(char* label) {}
  void openHorizontalBox(char* label) {}
  void openVerticalBox(char* label) {}
  void closeBox() {}
		
  void  SetValue(VstInt32 index, double f) {assert(index<fUITable.size()); fUITable[index]->SetValue(f);}
  float GetValue(VstInt32 index) {assert(index<fUITable.size()); return fUITable[index]->GetValue();}
  void  GetDisplay(VstInt32 index, char *text) {assert(index<fUITable.size()); fUITable[index]->GetDisplay(text);}
  void  GetName(VstInt32 index, char *text) {assert(index<fUITable.size()); fUITable[index]->GetName(text);}
  long  GetNumParams() {return fUITable.size();}

  long  makeID()
    /* Creates a (unique?)id by summing all the parameter's labels, 
     * then wrapping it in the range [0;maxNumberOfId] and adding 
     * this number to the offset made by the Four Character ID: 'FAUS'
     */
  {   
    const long maxNumberOfId = 128;
    long baseid = 'FAUS';
    long id=0;
    for(int i=0;i<fUITable.size();i++) id += fUITable[i]->GetID();
    return baseid + id % maxNumberOfId;
  }
		
  // To be implemented
  void addNumDisplay(char* label, float* zone, int precision){}
  void addTextDisplay(char* label, float* zone, char* names[], float min, float max){}
  void addHorizontalBargraph(char* label, float* zone, float min, float max){}
  void addVerticalBargraph(char* label, float* zone, float min, float max){}
};

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

#define kNumPrograms 1

AudioEffect* createEffectInstance (audioMasterCallback audioMaster)
{
  // 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);
#ifdef DEBUG
  fprintf(stderr,"=== Faust vsti: created\n"); // look for this in the system log
#endif
  return new Faust(audioMaster,dspi,dspUIi);
}

//-----------------------------------------------------------------------------
// Faust
//-----------------------------------------------------------------------------
Faust::Faust(audioMasterCallback audioMaster, mydsp* dspi, vstUI* dspUIi)
  :AudioEffectX(audioMaster, kNumPrograms,dspUIi->GetNumParams())
{
  // Copy the pointers to dsp and dspUI instances and take them over 
  // (we'll also deallocate):
  dsp = dspi;
  dspUI = dspUIi;

#ifdef DEBUG
  fprintf(stderr,"=== Faust vsti: classInit:\n");
#endif

  dsp->classInit((int)getSampleRate()); // Ask AudioEffect for sample-rate

  setProgram(0);
  setProgramName("Default");

  if (audioMaster) {
    setNumInputs(dsp->getNumInputs());
    setNumOutputs(dsp->getNumOutputs());		
    canProcessReplacing();
    if (dsp->getNumInputs() == 0) {
      isSynth(); // at least let's hope so!
      if (dsp->getNumOutputs() < 1) {
	fprintf(stderr,"*** faust: vsti: No signal inputs or outputs, and Faust has no MIDI outputs!\n");
      }
    }
    setUniqueID(dspUI->makeID());					
  }
  initProcess();
  if (dsp->getNumInputs() == 0) {
    suspend(); //  Synths start out quiet
  }
}

//----------------------------------------------------------------------------
Faust::~Faust()
{
  if (dsp) delete dsp;
  if (dspUI) delete dspUI;
}

//-----------------------------------------------------------------------------
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 (char* name)
{
  vst_strncpy (programName, name, kVstMaxProgNameLen);
}

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

//-----------------------------------------------------------------------------
void Faust::getParameterLabel(VstInt32 index, char *label)
{
  // We are not using parameter "units" display:
  vst_strncpy (label, "", kVstMaxParamStrLen); // parameter units in Name
}

//-----------------------------------------------------------------------------
void Faust::getParameterDisplay(VstInt32 index, char *text)
{
  if(index<numParams)
    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)
    dspUI->GetName(index,label); // parameter name, including units
  else
    vst_strncpy (label, "IndexOutOfRange", kVstMaxParamStrLen);
}

//-----------------------------------------------------------------------------
void Faust::setParameter(VstInt32 index, float value)
{
  if(index<numParams)
    dspUI->SetValue(index,value);
}

//-----------------------------------------------------------------------------
float Faust::getParameter(VstInt32 index)
{
  if(index<numParams)
    return dspUI->GetValue(index);
  else
    return 0.0f;
}

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

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

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

//-----------------------------------------------------------------------------
bool Faust::getEffectName (char* name)
{
  // Get from Faust-supplied metadata?
  vst_strncpy (name, "Effect Name goes here", kVstMaxEffectNameLen);
  return true;
}

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

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

//-----------------------------------------------------------------------------
VstInt32 Faust::getVendorVersion ()
{ 
  return 1000; 
}

//-----------------------------------------------------------------------------
VstInt32 Faust::canDo (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);
  dsp->instanceInit((int)getSampleRate()); // in case AudioEffect altered it
}

//-----------------------------------------------------------------------------
void Faust::initProcess ()
{
  noteIsOn = false;
  currentDelta = currentNote = currentDelta = 0;
  dsp->instanceInit((int)getSampleRate());
}

//-----------------------------------------------------------------------------
void Faust::processReplacing(float **inputs, float **outputs, VstInt32 sampleFrames)
{
    AVOIDDENORMALS;
#ifdef DEBUG
    fprintf(stderr,"=== Faust vsti: processReplacing . . .\n");
#endif

  if (dsp->getNumInputs() > 0) { // We're an effect . . . keep going:

    dsp->compute(sampleFrames, inputs, outputs);

  } else { // We're a synth . . . 
    int i, nouts = dsp->getNumOutputs();

    if (noteIsOn) { // we're synthesizing . . .

      if (currentDelta > 0) {  // but waiting out a timestamp delay . . .
	if (currentDelta >= sampleFrames) { // start time is after this chunk
	  currentDelta -= sampleFrames;
	  // According to the VST programming sample, we DON'T clear the output buffers yet.
	  // Could this be a bug in the sample program?  I would like to add the following:
	  // for (i=0; i<nouts; i++) { memset (outptr[i], 0, sampleFrames * sizeof (float)); }
	  return;
	} else {
	  // float* outptr[nouts];
	  float** outptr = (float **)malloc(nouts * sizeof(float*));

#ifdef DEBUG
	  fprintf(stderr,"*** Faust vsti: currentDelta = %d\n",currentDelta);
#endif

	  for (i=0; i<nouts; i++) {
	    outptr[i] = outputs[i]; // leaving caller's pointers alone
	    // According to the VST programming sample, we DO clear the output buffers now
	    // (since the start-time for the note is somewhere within the current chunk buf).
	    memset (outptr[i], 0, currentDelta * sizeof (float));
	    outptr[i] += currentDelta;
	  }
	  sampleFrames -= currentDelta;
	  currentDelta = 0;
	  dsp->compute(sampleFrames, inputs, outptr);
	  free(outptr);
	}
      } else {
	dsp->compute(sampleFrames, inputs, outputs);
      }

    } else { // silence until NoteOn . . .
      for (i=0; i<nouts; i++) {	memset (outputs[i], 0, sampleFrames * sizeof (float)); }
    }
  }
}

//-----------------------------------------------------------------------------
VstInt32 Faust::processEvents (VstEvents* ev)
{
  if (ev->numEvents > 0) {
#ifdef DEBUG
    fprintf(stderr,"=== Faust vsti: processEvents processing %d events\n",
	    ev->numEvents);
#endif
  }

  for (VstInt32 i = 0; i < ev->numEvents; i++)
    {
#ifdef DEBUG
      fprintf(stderr,"=== Faust vsti: event type = %d\n", 
	      (ev->events[i])->type);
#endif
      if ((ev->events[i])->type != kVstMidiType) {
#ifdef DEBUG
	fprintf(stderr,"=== Faust vsti: EVENT IGNORED!\n");
#endif
	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

      if (status == 0x90) { // note on
	VstInt32 note = midiData[1] & 0x7f;
	VstInt32 velocity = midiData[2] & 0x7f;
#ifdef DEBUG
	fprintf(stderr,
		"=== Faust vsti: note = %d, velocity = %d, delay = %d\n",
		note,velocity,event->deltaFrames);
#endif
	if (velocity>0) {
	  noteOn(note, velocity, event->deltaFrames);
	} else {
	  noteOff();
	}
      } else if (status == 0x80) { // note off
	noteOff();
	//      } 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");
	  noteOff (); // why is all-notes-off inside a "control change" event?
	}
	//      } else if (status == 0xC0) { // program change
	//      } else if (status == 0xD0) { // mono aftertouch
	//      } else if (status == 0xE0) { // pitch change
	//      } else if (status == 0xF0) { // SYSX ...
      }
      // For a list, see 
      // http://www.alfred-j-faust.de/rft/midi%20status%20types.html

#ifdef DEBUG
      fprintf(stderr,"=== Faust vsti: Going to next event\n", event->midiData[2]);
#endif

      event++;
    }
  return 1;
}

//-----------------------------------------------------------------------------
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
  currentNote = note;
  currentVelocity = velocity;
  currentDelta = delta;
  noteIsOn = true;
  float freq = 440.0f * powf(2.0f,(((float)note)-69.0f)/12.0f);
  float gain = velocity/127.0f;
  dspUI->setFreq(freq); // Hz - requires Faust control-signal "freq"
  dspUI->setGain(gain); // 0-1 - requires Faust control-signal "gain"
  dspUI->setGate(1.0f); // 0 or 1 - requires Faust button-signal "gate"
}

//-----------------------------------------------------------------------------
void Faust::noteOff ()
{
  if (noteIsOn) {
#ifdef DEBUG
    fprintf(stderr,"=== Faust vsti: noteOff\n");
#endif
    dspUI->setGate(0);
  } else {
#ifdef DEBUG
    fprintf(stderr,"=== Faust vsti: noteOff IGNORED (note was not on)\n");
#endif
  }
}


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

