text: #include "midiDelay.hpp"
  
  //for fmod()
  #include <cmath>
  
  //for seeding rand()
  #include <time.h> 
  
  //-------------------------------------------------------------------------------------------------------
  AudioEffect* createEffectInstance (audioMasterCallback audioMaster) {
      return new MidiDelay (audioMaster);
  }
  
  MidiDelayProgram::MidiDelayProgram ()
  {
      // default Program Values
      for (int i=0;i<kNumParams;i++) {
          switch(i) {
              case kMode: param[kMode] = 0.1f; break;
              case kChannel: param[kChannel] = 0.0f; break;
              case kTime: param[kTime]=0.55f; break;
              case kDry: param[kDry] = 1.0f; break;
              case kSync: param[kSync] = 1.0f; break;
              default: param[i] = 0.f; break;
          }
      }
      // default program name
      strcpy (name, "Default");
  }
  
  //-----------------------------------------------------------------------------
  MidiDelay::MidiDelay(audioMasterCallback audioMaster)
  	: PizMidi(audioMaster, kNumPrograms, kNumParams), programs(0)
  { 
      programs = new MidiDelayProgram[numPrograms];
  	if (programs) {
  		CFxBank* defaultBank = new CFxBank(kNumPrograms,kNumParams);
  		if (readDefaultBank(PLUG_NAME,defaultBank)) {
  			if((VstInt32)defaultBank->GetFxID()==PLUG_IDENT) {
  				for(int i=0;i<kNumPrograms;i++){
  					for (int p=0;p<kNumParams;p++) {
  						programs[i].param[p] = defaultBank->GetProgParm(i,p);
  					}
  					strcpy(programs[i].name,defaultBank->GetProgramName(i));
  				}
  			}
  		}
  		else {
  			// built-in programs
  			for(int i=0;i<kNumPrograms;i++){
  		        sprintf(programs[i].name,"Program %d", i+1);
  			}
  		}
  	    setProgram (0);
  	}
  
      memset(expectingDelayedNoteOff,0,sizeof(expectingDelayedNoteOff));
      memset(expectingDelayedNoteOn,0,sizeof(expectingDelayedNoteOn));
      memset(notePlaying,0,sizeof(notePlaying));
      memset(noteKilled,0,sizeof(noteKilled));
  
      for (int n=0;n<128;n++) {
          for (int ch=0;ch<16;ch++) {
              lastout[n][ch]=-1;
          }
      }
      srand ( (unsigned int)time(NULL) );
  
      _ppq=0.0;
      _bpm=120.0f;
      _beatpos=0.0;
      samplesPerBeat=0;
  
      wasplaying=false;
      isplaying=false;
  
      init();
  }
  
  //-----------------------------------------------------------------------------------------
  MidiDelay::~MidiDelay()
  {
      cleanMidiDelayBuffer();
      if (programs) delete [] programs;
  }
  
  //------------------------------------------------------------------------
  void MidiDelay::setProgram (VstInt32 program)
  {
      MidiDelayProgram* ap = &programs[program];
  
      curProgram = program;
      for (int i=0;i<kNumParams;i++) {
          setParameter(i,ap->param[i]);
      }
  }
  
  //------------------------------------------------------------------------
  void MidiDelay::setProgramName (char *name)
  {
      vst_strncpy (programs[curProgram].name, name, kVstMaxProgNameLen);
  }
  
  //------------------------------------------------------------------------
  void MidiDelay::getProgramName (char *name)
  {
      strcpy (name, programs[curProgram].name);
  }
  
  //-----------------------------------------------------------------------------------------
  bool MidiDelay::getProgramNameIndexed (VstInt32 category, VstInt32 index, char* text)
  {
      if (index < kNumPrograms)
      {
          strcpy (text, programs[index].name);
          return true;
      }
      return false;
  }
  
  //-----------------------------------------------------------------------------------------
  void MidiDelay::resume ()
  {
      midiDelayBuffer.clear();
      PizMidi::resume();
  }
  
  //-----------------------------------------------------------------------------------------
  void MidiDelay::setParameter(VstInt32 index, float value)
  {
      if (index<kNumParams) {
          MidiDelayProgram* ap = &programs[curProgram];
          param[index] = ap->param[index] = value;
          if (index==kTime) {
  			const float inc = 1.f/21.f;
              if      (param[kTime]<1*inc)  stepsize = 12.0f; //0.3333333 (3 bars)
              else if (param[kTime]<2*inc)  stepsize = 8.0f; //0.5 (2 whole notes)
              else if (param[kTime]<3*inc)  stepsize = 6.0f; //0.75 (dotted whole)
              else if (param[kTime]<4*inc)  stepsize = 4.0f;  //1 (whole note)
              else if (param[kTime]<5*inc)  stepsize = 3.0f;  //1.5 dotted half
              else if (param[kTime]<6*inc)  stepsize = 2.0f; //2 (half note)
              else if (param[kTime]<7*inc)  stepsize = 1.5f; //4.
              else if (param[kTime]<8*inc)  stepsize = 1.333333333f; //2T
              else if (param[kTime]<9*inc)  stepsize = 1.0f; //4
              else if (param[kTime]<10*inc) stepsize = 0.75f; //8.
              else if (param[kTime]<11*inc) stepsize = 0.666666667f; //4T
              else if (param[kTime]<12*inc) stepsize = 0.5f; //8
              else if (param[kTime]<13*inc) stepsize = 0.375; //16.
              else if (param[kTime]<14*inc) stepsize = 0.333333333f; //8T
              else if (param[kTime]<15*inc) stepsize = 0.25f; //16
              else if (param[kTime]<16*inc) stepsize = 0.1875f; //32.
              else if (param[kTime]<17*inc) stepsize = 0.166666667f; //16T
              else if (param[kTime]<18*inc) stepsize = 0.125f; //32
              else if (param[kTime]<19*inc) stepsize = 0.09375f; //64.
              else if (param[kTime]<20*inc) stepsize = 0.083333333f; //32T
              else						  stepsize = 0.0625f; //64
          }
          else if (index==kSync) updateDisplay();
      }
  }
  
  //-----------------------------------------------------------------------------------------
  float MidiDelay::getParameter(VstInt32 index){
  
      return param[index];
  }
  
  //-----------------------------------------------------------------------------------------
  void MidiDelay::getParameterName(VstInt32 index, char *label){
      switch(index){
          case kSync:     strcpy(label, "TempoSync"); break;
          case kMode:     strcpy(label, "Mode"); break;
          case kTime:     strcpy(label, "DelayTime"); break;
          case kChannel:  strcpy(label, "Channel"); break;
          case kDry:      strcpy(label, "Dry"); break;
          case kWet:      strcpy(label, "Wet"); break;
          case kFeedback: strcpy(label, "Feedback"); break;
          case kLimit: strcpy(label, "FdbkLimit"); break;
          default: strcpy(label, " "); break;
      }
  }
  
  //-----------------------------------------------------------------------------------------
  void MidiDelay::getParameterDisplay(VstInt32 index, char *text){
      switch(index){
        case kFeedback:
        case kDry:
        case kWet: sprintf(text, "%d %%", roundToInt(param[index]*100.f)); break;
        
        case kMode:     
            if (param[index]==0.0) strcpy(text, "Off");
            else if (param[index]<0.5) strcpy(text, "Notes Only");
            else strcpy(text, "Notes & CCs");
            break;
        case kLimit:     
            if (param[index]==0.0) strcpy(text, "Off");
            else sprintf(text, "%d", roundToInt(param[index]*15.f)+1);
            break;
        case kSync:     
            if (param[index]<0.5f) strcpy(text, "Off");
            else strcpy(text, "On");
            break;
        case kTime:
            if (param[kSync]>=0.5f) {
  			  const float inc = 1.f/21.f;
                if      (param[index]<1*inc) strcpy(text, "3bars");
                else if (param[index]<2*inc) strcpy(text, "2bars");
                else if (param[index]<3*inc) strcpy(text, "dotted1");
                else if (param[index]<4*inc) strcpy(text, "1");
                else if (param[index]<5*inc)  strcpy(text, "dotted2");     
                else if (param[index]<6*inc) strcpy(text, "2");
                else if (param[index]<7*inc) strcpy(text, "dotted4");
                else if (param[index]<8*inc) strcpy(text, "2T");
                else if (param[index]<9*inc) strcpy(text, "4");
                else if (param[index]<10*inc)strcpy(text, "dotted8");
                else if (param[index]<11*inc)strcpy(text, "4T");
                else if (param[index]<12*inc)strcpy(text, "8");      
                else if (param[index]<13*inc)strcpy(text, "dotted16");
                else if (param[index]<14*inc)strcpy(text, "8T");  
                else if (param[index]<15*inc)strcpy(text, "16");
                else if (param[index]<16*inc)strcpy(text, "dotted32");  
                else if (param[index]<17*inc)strcpy(text, "16T");
                else if (param[index]<18*inc)strcpy(text, "32");
                else if (param[index]<19*inc)strcpy(text, "dotted64");
                else if (param[index]<20*inc)strcpy(text, "32T");
                else                         strcpy(text, "64");
            }
            else {
                sprintf(text, "%d ms", roundToInt((1.f-param[index])*2000.f));
            }
            break;
        case kChannel: 
            if (FLOAT_TO_CHANNEL(param[index])==-1) strcpy(text,"All"); 
            else sprintf(text, "%d",FLOAT_TO_CHANNEL(param[index])+1); 
            break;
        default: 
            sprintf(text, "%f", param[index]); 
            break;
      }
  }
  
  //-----------------------------------------------------------------------------------------
  void MidiDelay::cleanMidiDelayBuffer() {
      midiDelayBuffer.clear();
  }
  
  void MidiDelay::preProcess(void) {  
      // preparing Process
      VstTimeInfo * timeInfo = NULL;
      timeInfo = getTimeInfo(0xffff); //ALL
  
      if (timeInfo)
      {
          if (kVstTempoValid & timeInfo->flags) _bpm = (float)timeInfo->tempo;
          if (kVstPpqPosValid & timeInfo->flags) _ppq = timeInfo->ppqPos;
          if (kVstTransportPlaying & timeInfo->flags) 
              isplaying = true;
          else 
              isplaying = false;
          //dbg("tempo=" << _bpm);
  
          //        _ppq += (1.0f-2.0f*param[kOffset])/(stepsize*2.0f);
          _beatpos = stepsize * fmod(_ppq, 1.0/stepsize); 
      }
  
      samplesPerBeat = roundToInt(60.f * sampleRate / _bpm);
      samplesPerTick = roundToInt((float)samplesPerBeat/960.0f);
      totalSamples=roundToInt(_ppq*(double)samplesPerBeat);
  
      _cleanMidiOutBuffers();
  
      sortMidiEvents(midiDelayBuffer);
  }
  
  void MidiDelay::processMidiEvents(VstMidiEventVec *inputs, VstMidiEventVec *outputs, VstInt32 samples)
  {
      short listenchannel = FLOAT_TO_CHANNEL(param[kChannel]);
  
      VstInt32 delay = roundToInt((float)samplesPerBeat * stepsize);
      if (param[kSync]<0.5f) delay = roundToInt(((1.f-param[kTime])*1.98f+0.02f)*sampleRate);
  
      //process incoming events-------------------------------------------------------
      for (unsigned int i=0;i<inputs[0].size();i++) {
          //copying event "i" from input (with all its fields)
          VstMidiEvent tomod = inputs[0][i];
  
          int status    = tomod.midiData[0] & 0xf0;   // scraping  channel
          int channel   = tomod.midiData[0] & 0x0f;   // isolating channel (0-15)
          int data1     = tomod.midiData[1] & 0x7f;
          int data2	    = tomod.midiData[2] & 0x7f;
  
          dbg("incoming midi event, i=" << i << " status=" << status << " ch=" << channel);
  
          //make 0-velocity notes into "real" noteoffs for simplicity
          if (status==MIDI_NOTEON && data2==0) {
              status=MIDI_NOTEOFF;
              tomod.midiData[0] = MIDI_NOTEOFF | channel;
          }
  
          bool discard = false;
  
          //only look at the selected channel
          if (channel == listenchannel || listenchannel == -1 && param[kMode]>0.f)
          {
              //incoming NoteOff--------------------------------------------------------------
              if (status == MIDI_NOTEOFF) {
                  if (param[kWet]>0.f) {
                      VstMidiEvent delayed = tomod;
                      if (samples - delayed.deltaFrames - delay > 0)
                      {
                          // delayed note is within the current block, don't need counter
                          delayed.deltaFrames += delay;
                          dbg("delay noteoff within block, deltaFrames=" << delayed.deltaFrames);
                          delayed.midiData[2] = roundToInt((float)data2*param[kWet]);
                          if (!noteKilled[data1][channel]) {
                              outputs[0].push_back(delayed);
                              notePlaying[data1][channel] = false;
                          }
                          else noteKilled[data1][channel]=false;
                          delayed.deltaFrames = (delay - (samples-tomod.deltaFrames));
                          if (samples - delayed.deltaFrames - delay <= 0) {
                              delayed.midiData[3]=127;
                              delayed.flags=roundToInt(param[kLimit]*15.f)+1;
                              if (param[kLimit]==0.0f) delayed.flags=127;
                              midiDelayBuffer.push_back(delayed);
                          }
                      }
                      else {
                          expectingDelayedNoteOff[data1][channel]++;
                          // delayed note is in a future block, so start counter:
                          discard=true; //takes the event out of the current block
                          delayed.deltaFrames = (delay - (samples-delayed.deltaFrames));
                          delayed.midiData[3]=127;
                          delayed.flags=roundToInt(param[kLimit]*15.f)+1;
                          if (param[kLimit]==0.0f) delayed.flags=127;
                          midiDelayBuffer.push_back(delayed);
                          dbg("delay noteoff in later block, counter=" << delayed.deltaFrames);
                      }
                  }
                  if (param[kDry]>0.f) {
                      int dryvel = roundToInt((float)data2*param[kDry]);
                      dbg("dry note off");
                      tomod.midiData[2]=dryvel;
                      if (!noteKilled[data1][channel]) {
  						discard = true;
                          outputs[0].push_back(tomod);
                          notePlaying[data1][channel]=false;
                      }
                      else noteKilled[data1][channel]=false;
                  }
  				else discard = true;
              }
              //Incoming NoteOn---------------------------------------------------------------
              else if (status == MIDI_NOTEON)
              {
                  if (param[kWet]>0.f) {
                      VstMidiEvent delayed = tomod;
                      delayed.midiData[2] = roundToInt((float)data2*param[kWet]);
  					if (delayed.midiData[2]>0)
  					{
  						if (samples - delayed.deltaFrames - delay > 0)
  						{
  							// delayed note is within the current block, don't need counter
  							delayed.deltaFrames += delay;
  							if (notePlaying[data1][channel]) {
  								VstMidiEvent kill;
  								kill.midiData[0] = MIDI_NOTEOFF + channel;
  								kill.midiData[1] = data1;
  								kill.midiData[2] = 0;
  								kill.deltaFrames = delayed.deltaFrames;
  								kill.detune = 0;
  								outputs[0].push_back(kill);
  								noteKilled[data1][channel]=true;
  								dbg("killed note " << data1);
  							}
  							dbg("delay noteon within block, deltaFrames=" << tomod.deltaFrames);
  							notePlaying[data1][channel] = true;
  							outputs[0].push_back(delayed);
  							delayed.deltaFrames = (delay - (samples-tomod.deltaFrames));
  							//if (samples - delayed.deltaFrames - delay <= 0) {
  								delayed.midiData[3]=127;
  								delayed.flags=roundToInt(param[kLimit]*15.f)+1;
  								if (param[kLimit]==0.0f) delayed.flags=127;
  								midiDelayBuffer.push_back(delayed);
  							//}
  						}
  						else {
  							expectingDelayedNoteOn[data1][channel]++;
  							// delayed note is in a future block, so start counter:
  							discard=true; //takes the event out of the current block
  							delayed.deltaFrames = (delay - (samples-tomod.deltaFrames));
  							delayed.midiData[3]=127;
  							delayed.flags=roundToInt(param[kLimit]*15.f)+1;
  							if (param[kLimit]==0.0f) delayed.flags=127;
  							midiDelayBuffer.push_back(delayed);
  							dbg("delay noteoff in later block, counter=" << delayed.deltaFrames);
  						}
  					}
                  }
                  if (param[kDry]>0.f) {
                      int dryvel = roundToInt((float)data2*param[kDry]);
  					if (dryvel==0) dryvel = 1;
                      if (notePlaying[data1][channel]) {
                          VstMidiEvent kill;
                          kill.midiData[0] = MIDI_NOTEOFF + channel;
                          kill.midiData[1] = data1;
                          kill.midiData[2] = 0;
                          kill.deltaFrames = tomod.deltaFrames;
                          kill.detune = 0;
                          outputs[0].push_back(kill);
                          noteKilled[data1][channel]=true;
                          dbg("killed note " << data1);
                      }
                      dbg("dry note on");
                      tomod.midiData[2]=dryvel;
  					discard = true;
                      outputs[0].push_back(tomod);
                      notePlaying[data1][channel]=true;
                  }
  				else {
  					notePlaying[data1][channel]=false;
  					discard = true;
  				}
              }
              else if (status==MIDI_CONTROLCHANGE && param[kMode]>=0.5f) {
                  if (param[kDry]>0.f) {
                      tomod.midiData[2]=roundToInt(param[kDry]*(float)data2);
                      outputs[0].push_back(tomod);
                      lastout[data1][channel]=-1;
                  }
  				else discard = true;
                  if (param[kWet]>0.f) {
                      VstMidiEvent delayed = tomod;
                      if (samples - delayed.deltaFrames - delay > 0)
                      {
                          // delayed note is within the current block, don't need counter
                          delayed.deltaFrames += delay;
                          delayed.midiData[2] = roundToInt((float)data2*param[kWet]);
                          outputs[0].push_back(delayed);
                          lastout[data1][channel]=delayed.midiData[2];
                          delayed.deltaFrames = (delay - (samples-tomod.deltaFrames));
                          //if (samples - delayed.deltaFrames - delay <= 0) {
                              delayed.midiData[3]=127;
                              delayed.flags=roundToInt(param[kLimit]*15.f)+1;
                              if (param[kLimit]==0.0f) delayed.flags=127;
                              midiDelayBuffer.push_back(delayed);
                          //}
                      }
                      else {
                          discard=true; //takes the event out of the current block
                          delayed.deltaFrames = (delay - (samples-tomod.deltaFrames));
                          delayed.midiData[3]=127;
                          delayed.flags=roundToInt(param[kLimit]*15.f)+1;
                          if (param[kLimit]==0.0f) delayed.flags=127;
                          midiDelayBuffer.push_back(delayed);
                      }
                  }
              }
          } // if listenchannel==channel
          if (!discard) outputs[0].push_back(tomod);
      } //for() inputs loop
  
      //process delay buffer----------------------------------------------------------
      VstMidiEventVec newBuffer;
      newBuffer.clear();
      for (unsigned int i=0;i<midiDelayBuffer.size();i++) {
          dbg("processing delay buffer");
          VstMidiEvent event = midiDelayBuffer[i];
          int status     = event.midiData[0] & 0xf0;   // scraping  channel
          int channel    = event.midiData[0] & 0x0f;   // isolating channel (0-15)
          int data1      = event.midiData[1] & 0x7f;
          int data2	     = event.midiData[2] & 0x7f;
          bool discard=false;
          dbg("delta=" << event.deltaFrames);
          if (event.deltaFrames<samples && param[kWet]>0.f) {
              //event is due
  //            OutputDebugString("event is due");
              if (status==MIDI_NOTEON) {
                  expectingDelayedNoteOn[data1][channel]--;
                  if (data2>0) {
                      if (notePlaying[data1][channel]) {
                          VstMidiEvent kill;
                          kill.midiData[0] = MIDI_NOTEOFF + channel;
                          kill.midiData[1] = data1;
                          kill.midiData[2] = 0;
                          kill.deltaFrames = event.deltaFrames;
                          kill.detune = 0;
                          outputs[0].push_back(kill);
                          noteKilled[data1][channel]=true;
                          dbg("killed note " << data1);
                      }
                      notePlaying[data1][channel]=true;
                      dbg("delayed noteon");
                  }
                  else {
                      discard=true;
                  }
              }
              else if (status==MIDI_NOTEOFF) {
                  expectingDelayedNoteOff[data1][channel]--;
                  if (!noteKilled[data1][channel]) {
                      if (notePlaying[data1][channel]) {
                          notePlaying[data1][channel]=false;
                          dbg("delayed noteoff");
                      }
                      else {
                          //discard=true;
                      }
                  }
                  else {
                      noteKilled[data1][channel]=false;
                      discard=true;
                  }
              }
              else if (status==MIDI_CONTROLCHANGE && param[kMode]>=0.5f) {
                  VstMidiEvent out1 = event;
                  out1.midiData[2]=roundToInt(param[kWet]*(float)data2);
                  //if (lastout[data1][channel]!=out1.midiData[2]) 
                  outputs[0].push_back(out1);
                  lastout[data1][channel]=out1.midiData[2];
                  discard=true;
              }
              else discard=true;
              if (!discard) {
                  VstMidiEvent out = event;
                  out.midiData[2] = roundToInt((float)data2*param[kWet]);
                  outputs[0].push_back(out);
              }
              event.flags--;
  
              if (param[kFeedback]>0.f) {
                  if (param[kFeedback]<1.f) {
                      event.midiData[2] = roundToInt((float)data2*param[kFeedback])-1;
                      if (status==MIDI_NOTEON && event.midiData[2]<1) 
  						event.midiData[2]=1;
  					else if (event.midiData[2]<0)
  						event.midiData[2]=0;
  				}
                  if (samples - event.deltaFrames - delay > 0)
                  {
                      event.deltaFrames += delay;
                      if (status==MIDI_NOTEOFF) {
                          if (!noteKilled[data1][channel]) {
                              notePlaying[data1][channel] = false;
                              VstMidiEvent out1 = event;
                              out1.midiData[2] = roundToInt((float)event.midiData[2]*param[kWet]);
                              outputs[0].push_back(out1);
                              event.flags--;
                          }
                          else noteKilled[data1][channel]=false;
                      }
                      else if (status==MIDI_NOTEON) {
                          VstMidiEvent out1 = event;
                          out1.midiData[2] = roundToInt((float)event.midiData[2]*param[kWet]);
  						if (out1.midiData[2]>0) {
  							if (notePlaying[data1][channel]) {
  								VstMidiEvent kill;
  								kill.midiData[0] = MIDI_NOTEOFF + channel;
  								kill.midiData[1] = data1;
  								kill.midiData[2] = 0;
  								kill.deltaFrames = event.deltaFrames;
  								kill.detune = 0;
  								outputs[0].push_back(kill);
  								noteKilled[data1][channel]=true;
  								dbg("killed note " << data1);
  							}
  							notePlaying[data1][channel]=true;
  							outputs[0].push_back(out1);
  						}
  						else
  							notePlaying[data1][channel]=false;
                          event.flags--;
                      }
                      else if (status==MIDI_CONTROLCHANGE && param[kMode]>=0.5f) {
                          VstMidiEvent out1 = event;
                          out1.midiData[2]=roundToInt(param[kWet]*(float)data2);
                          //if (lastout[data1][channel]!=out1.midiData[2]) 
                          outputs[0].push_back(out1);
                          lastout[data1][channel]=out1.midiData[2];
                          event.flags--;
                      }
                      if (param[kFeedback]<1.f) {
                          event.midiData[3]=roundToInt((float)event.midiData[3]*param[kFeedback])-1;
                          if (event.midiData[3]<0) event.midiData[3]=0;
                      }
                      if (event.midiData[3]>0 && event.flags>0) {
                          if (status==MIDI_NOTEOFF) expectingDelayedNoteOff[data1][channel]++;
                          else if (status==MIDI_NOTEON) expectingDelayedNoteOn[data1][channel]++;
                          event.deltaFrames = (delay - (samples - event.deltaFrames));
                          if (param[kFeedback]<1.f) {
                              event.midiData[3] = roundToInt((float)event.midiData[3]*param[kFeedback])-1;
                              if (event.midiData[3]<0) event.midiData[3]=0;
                          }
                          midiDelayBuffer.push_back(event);
                      }
                  }
                  else {
                      if (event.midiData[3]>0 && event.flags>0) {
                          if (status==MIDI_NOTEOFF) expectingDelayedNoteOff[data1][channel]++;
                          else if (status==MIDI_NOTEON) expectingDelayedNoteOn[data1][channel]++;
                          event.deltaFrames = (delay - (samples - event.deltaFrames));
                          if (param[kFeedback]<1.f) {
                              event.midiData[3] = roundToInt((float)event.midiData[3]*param[kFeedback])-1;
                              if (event.midiData[3]<0) event.midiData[3]=0;
                          }
                          newBuffer.push_back(event);
                      }
                  }
              }
          }
          else {
              event.deltaFrames -= samples;
              newBuffer.push_back(event);
          }
      }
      midiDelayBuffer.clear();
      midiDelayBuffer=newBuffer;
  
      sortMidiEvents(outputs[0]);
  
      if (param[kMode]>=0.5f) {
          std::reverse( outputs[0].begin(), outputs[0].end() );
          VstMidiEventVec temp=outputs[0];
          outputs[0].clear();
          for (unsigned int i=0;i<temp.size();i++) {
              if (i==0) outputs[0].push_back(temp[i]);
              else {
                  int what = modulate(temp[i],temp[i-1]);
                  if (what==0) {
                      outputs[0].push_back(temp[i]);
  //                    OutputDebugString("yes");
                  }
  //                else OutputDebugString("no");
              }
          }
      }
  
      if (wasplaying && !isplaying) { //just stopped
          dbg("stopped");
      }
      wasplaying=isplaying;
  }                                                                                                               
  
  int MidiDelay::modulate(VstMidiEvent input, VstMidiEvent mod) {
      if (input.midiData[0]!=mod.midiData[0]) return 0;
      if (input.midiData[1]!=mod.midiData[1]) return 0;
      if (abs(input.deltaFrames - mod.deltaFrames)<=20) {
          if (input.midiData[2]>=mod.midiData[2]) return 1;
          return 2;
      }
      return 0;
  }
  