What is KVR Audio? | Submit News | Advertise | Developer Account

Options (Affects News & Product results only):

OS:
Format:
Include:
Quick Search KVR

"Quick Search" KVR Audio's Product Database, News Items, Developer Listings, Forum Topics and videos here. For advanced Product Database searching please use the full product search. For the forum you can use the phpBB forum search.

To utilize the power of Google you can use the integrated Google Site Search.

Products 0

Developers 0

News 0

Forum 0

Videos 0

Search  

Envelope implementation, help

DSP, Plug-in and Host development discussion.

Moderator: Moderators (Main)

nastyfingers
KVRer
 
10 posts since 11 Jan, 2013, from Sheffield, UK

Postby nastyfingers; Fri Jan 11, 2013 3:17 pm Envelope implementation, help

Hi Guys,

I have a university assignment that requires me to make a basic additive synth with ADSR envelope.

I have a good start and it works, that is until I add my envelope into it.

I have read lots, tried lots and been stuck lots and now I have to admit that I am stuck.

Presently the code compiles, it just doesn't make any sound. If I remove the line that is supposed to output the sample at the correct level for the stage in the envelope it works again. So I know it's something with the ADSR class.

I have attached the relevant stuff, and left my comments etc in, in case it helps you see what I was thinking.

if anyone can help at all I would be over the moon.

Thanks in advance

Ben


ADSR.h
Code: Select all
#ifndef __ADSR
#define __ADSR

#include <math.h>

class ADSR
{
public:
   ADSR();
   ~ADSR();

   //functions
   int setvalues();
   //int stage(int identify);
   float process(float* pCurrentLevel, int attack, int decay, float sustain, int release, int* stage, long* pTime, int* pKeyState);
   int internalTimer;
   /*float attack(float currentLevel);
   int decay(float currentLevel);
   int sustain(float currentLevel);//is this stage needed as only keeping level same while note is on??? * by 1???
   int release(float currentLevel);*/

};
#endif



ADSR.cpp
Code: Select all
#include "ADSR.h"

ADSR::ADSR()
{
   
}
ADSR::~ADSR()
{

}

float ADSR::process(float* pCurrentLevel, int attack, int decay, float sustain, int release, int* pstage, long* pTime, int* pKeyState)
{
   //float target = 1.f;
      
   while(*pTime < attack)
   {
      *pCurrentLevel = *pCurrentLevel + (1 / attack);
      //*pTime++;
      //*pstage = 2;
      //target = 0.5;//sustain level
      return *pCurrentLevel;
   }
      
   while(*pTime < (attack + decay))
   {
      *pCurrentLevel = *pCurrentLevel - (sustain / decay);
      //*pTime;
      //*pstage = 3;
      //target = 0;
      return *pCurrentLevel;
   }

   while(*pTime > (attack + decay))
   {
      *pCurrentLevel = *pCurrentLevel * 1;
      internalTimer++;
      return *pCurrentLevel;
   }
   
   while(*pTime < (attack + decay + internalTimer + release) && *pKeyState == 2)
   {
      *pCurrentLevel = *pCurrentLevel - (sustain / release);
      return *pCurrentLevel;

   }

}



VST_Plugin.h
Code: Select all
//-------------------------------------------------------------------------------------------------------
// This is the most basic plug in code there is!
// All this plug in will do is apply some gain to the input
// This is done in process replacing
//-------------------------------------------------------------------------------------------------------

#ifndef __VST_Plug_in__
#define __VST_Plug_in__

#include "audioeffectx.h"

#include <math.h>
#include "ADSR.h"



const int NUMBER_OF_INPUTS = 0;
const int NUMBER_OF_OUTPUTS = 2;
const int NUMBER_OF_PROGRAMS = 0;
const int NUMBER_OF_PARAMETERS = 5;


// Base frequency (A4- 440Hz) for use in generating frequency table
const float BASE_A4  = 440.0;
const float PI =  3.141592;


//-------------------------------------------------------------------------------------------------------
class VST_Plug_in : public AudioEffectX
{
public:
   VST_Plug_in (audioMasterCallback audioMaster);
   ~VST_Plug_in ();
   virtual void processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames);

   // synth functions
   virtual VstInt32 processEvents (VstEvents* events);
   virtual VstInt32 canDo (char* text);

   float leftSample;
   float* pLeftSample;
   float rightSample;
   float frequency;
   float sampleRate;
   long time;
   long* pTime;

   // MIDI stuff
   // MIDI data : holds data about current state of MIDI (note on/off, frequency, velocity)
   int keyDown; // true : key is down, false : no key down
   int* pkeyDown;

   long currentNote; // the MIDI note number of the last note on (key down)
   float currentVelocity; // current MIDI note velocity (0 -> 1)
   float *m_pfFrequencyTable; // will store a list of frequency values (for note->frequency conversion)
   
   void noteOff ();
   void noteOn (long liNote, long liVelocity);
   int getAttack();
   int getDecay();
   float getSustain();
   int getRelease();

   float partial1;
   float partial2;
   float partial3;
   float partial4;
   float partial5;
   float partial6;
   float partial7;
   float partial8;

   int adsrStage;
   int* padsrStage;

   ADSR env;
   
};

#endif



VST_Plugin.cpp
Code: Select all
//-------------------------------------------------------------------------------------------------------
// VST Plug-Ins SDK
// Version 2.4      $Date: 2005/11/15 15:14:03 $
//
// Category     : VST 2.x SDK Samples
// Filename     : VST_Plug_in.cpp
// Created by   : Steinberg Media Technologies
// Description  : Stereo plugin which applies Gain [-oo, 0dB]
//
// © 2005, Steinberg Media Technologies, All Rights Reserved
//-------------------------------------------------------------------------------------------------------

#include "VST_Plug_in.h"

//-------------------------------------------------------------------------------------------------------
AudioEffect* createEffectInstance (audioMasterCallback audioMaster)
{
   return new VST_Plug_in (audioMaster);
}

//-------------------------------------------------------------------------------------------------------
VST_Plug_in::VST_Plug_in (audioMasterCallback audioMaster)
: AudioEffectX (audioMaster, NUMBER_OF_PROGRAMS, NUMBER_OF_PARAMETERS)   
{
   setNumInputs (NUMBER_OF_INPUTS);      // stereo in
   setNumOutputs (NUMBER_OF_OUTPUTS);      // stereo out
   setUniqueID ('Add1');   // identify
   canProcessReplacing ();   // supports replacing output
   leftSample  = 0.0;
   rightSample = 0.0;
   frequency = 0.0;

   currentVelocity = 0.0;
   currentNote = 0;
   keyDown = 2;
   pkeyDown = &keyDown;
   adsrStage = 4;
   padsrStage = &adsrStage;

   isSynth ();                  // Informs host that this is a VSTi
   
   // initialise frequency table
   m_pfFrequencyTable = new float [128] ; // 128 Midi notes

   if (m_pfFrequencyTable)
   {
      for (int i = 0; i<  128; i++)
      {
         m_pfFrequencyTable[i] = BASE_A4 *powf(2.f,(i-57)/12.f) ;
      }
   }
   time = 0;

   partial1 = 0.f;
   partial2 = 0.f;
   partial3 = 0.f;
   partial4 = 0.f;
   partial5 = 0.f;
   partial6 = 0.f;
   partial7 = 0.f;
   partial8 = 0.f;

   //get sample rate from host
   sampleRate = getSampleRate();

   ADSR env;
      
}

//-------------------------------------------------------------------------------------------------------
VST_Plug_in::~VST_Plug_in ()
{
   // nothing to do here
}


//-----------------------------------------------------------------------------------------
// this is where the intresting stuff happens :0

void VST_Plug_in::processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames)
{
    float* out1 = outputs[0];
    float* out2 = outputs[1];
   //ADSR env;

   for(int i = 0; i < sampleFrames; i++)
    {
      // NEW : only send out audio if there is a note on currently
      frequency = m_pfFrequencyTable[currentNote];
      
      partial1 = (float)sin(2.0*PI*time++*(frequency/sampleRate))* 0.125;
      partial2 = (float)sin(2.0*PI*time++*((2*frequency)/sampleRate))* 0.125;
      partial3 = (float)sin(2.0*PI*time++*((3*frequency)/sampleRate))* 0.125;
      partial4 = (float)sin(2.0*PI*time++*((4*frequency)/sampleRate))* 0.125;
      partial5 = (float)sin(2.0*PI*time++*((5*frequency)/sampleRate))* 0.125;
      partial6 = (float)sin(2.0*PI*time++*((6*frequency)/sampleRate))* 0.125;
      partial7 = (float)sin(2.0*PI*time++*((7*frequency)/sampleRate))* 0.125;
      partial8 = (float)sin(2.0*PI*time++*((8*frequency)/sampleRate))* 0.125;
      
      leftSample = partial1 + partial2 + partial3 + partial4 + partial5 + partial6 + partial7 + partial8;

      leftSample = (env.process(pLeftSample, getAttack(), getDecay(), getSustain(), getRelease(), padsrStage, pTime, pkeyDown)) * currentVelocity;
      
      //leftSample = leftSample * currentVelocity;
      rightSample = leftSample;

      // write samples to output buffer
        (*out1++) = leftSample;
        (*out2++) = rightSample;
    }
}


// NEW : overriden function, tells host what the plugin can do (see notes)

VstInt32 VST_Plug_in::canDo(char *text)
{
   if (!strcmp (text, "receiveVstEvents"))      // SimpleSynth can receive VST events
      return 1;
   if (!strcmp (text, "receiveVstMidiEvent"))   // SimpleSynth can receive VST MIDI events
      return 1;
   return -1;   // explicitly can't do; 0 => don't know   
}

// NEW : this process function is called to collect incoming VST events

VstInt32 VST_Plug_in::processEvents (VstEvents* events)
{
   // parse event list
   for (long i = 0; i < events->numEvents; i++)
   {
      if ((events->events[i])->type == kVstMidiType)
      {
         VstMidiEvent* event = (VstMidiEvent*)events->events[i];
         char* midiData = event->midiData;
         long status = midiData[0] & 0xf0;      // ignoring channel

         if (status == 0x90 || status == 0x80)   // we only look at notes
         {
            long note = midiData[1];
            long velocity = midiData[2];

            if (status == 0x80)
            {
               velocity = 0;   
               // set velocity to zero if it is a note off message
            }
            if (!velocity && (note == currentNote))
            {
               noteOff ();
            }
            else
            {
               noteOn (note, velocity);
            }
         }
      }
   }
   return 1; // indicate that we wish to receive more events
}

void VST_Plug_in::noteOn(long liNote, long liVelocity)
{
   keyDown = 1;
   time = 0;
   adsrStage = 1;
   currentNote = liNote;
   
   // velocity is liVelocity /127
   currentVelocity = liVelocity / 127.f;
   
}

void VST_Plug_in::noteOff()
{
   keyDown = 2;
   adsrStage = 4;
   currentVelocity = 0;
}

int VST_Plug_in::getAttack()
{
   int attack = 44100;
   //capture attack time in samples from slider
   return attack;
}

int VST_Plug_in::getDecay()
{
   int decay = 22050;
   //decay from slider
   return decay;
}

float VST_Plug_in::getSustain()
{
   float sustain = 0.75;
   //sustain from slider;
   return sustain;
}

int VST_Plug_in::getRelease()
{
   int release = 88200;
   //release from slider
   return release;
}
LemonLime
KVRist
 
228 posts since 15 Apr, 2012, from Toronto, ON

Postby LemonLime; Fri Jan 11, 2013 6:25 pm

In your processReplacing function, you've assigned the sum of the partials to "leftSample", then immediately overwrite that with the envelope process. Is the "pLeftSample" pointer argument to env.process supposed to be "leftSample" or at least point to it? It's unassigned as far as I can see. "pTime" hasn't been assigned to anything either, nor is it being incremented (commented out) so the envelope isn't moving anywhere.

And you should delete the allocation of m_pfFrequencyTable in your destructor. :)
nastyfingers
KVRer
 
10 posts since 11 Jan, 2013, from Sheffield, UK

Postby nastyfingers; Sat Jan 12, 2013 4:11 am

Thank you for your help, You are correct, it was supposed to send leftSample (or a pointer to it). The idea was that the partials are summed, and the envelope sets a level according to the current stage in the envelope. It just seems to be overwriting that with nothing.

Time is supposed to be incremented with the sine wave generation and had thought that would progress the envelope using the pointers. Is that wrong? I have since noticed that each partial was set to increment time, which essentially should have just sped up the envelope. I removed time incrimination from all but one partial, and still the same issue.
Code: Select all
partial1 = (float)sin(2.0*PI*time++*(frequency/sampleRate))* 0.125;

I then tried uncommenting the time increment lines in the adsr.cpp and still the same.

Obviously my thinking is wrong somewhere I just cannot see what is iffy. :cry:

In case you hadn't guessed I am far from an advanced programmer and are only part way through a year 2 module, and currently surrounded by 2 poorly kids, so you help is very much appreciated.

Cheers

Ben
LemonLime
KVRist
 
228 posts since 15 Apr, 2012, from Toronto, ON

Postby LemonLime; Sat Jan 12, 2013 7:50 am

The 'time' element in calculating your partials should not be the same time element used in tracking where you are in the envelope/signal. It should just be an index, always incrementing from 0 to 7 (since you have 7 partials and one fundamental).

There's still an issue with your envelope process function. If the first argument is supposed to be the sample value, you just need to multiply that by the current envelope value -- don't add or subtract anything to it, that's mixing. Applying an envelope is just multiplication:
Code: Select all
sample = sample * envelopeValue;


Also, I would advise never to go straight into coding the plug-in. Especially if you're inexperienced as a programmer, always start off with a basic command-line program. They're much easier to debug, and once you have it working you can place the code within the plug-in framework.
nastyfingers
KVRer
 
10 posts since 11 Jan, 2013, from Sheffield, UK

Postby nastyfingers; Sat Jan 12, 2013 2:29 pm

Dude thank you very much.

I have now had my envelope functioning perfectly.

Can see easily where I was going wrong now. duh * not + or -

Unfortunately the faders are giving me a new headache. I tweaked the same code as a different plugin I wrote for a previous assignment, just this time the faders will not update...? The envelope was working until I added user adjustment of the values of ADSR.

unsure now wether this is a continuing issue or requires a new post??

Once again thank you for your help.

Ben
nastyfingers
KVRer
 
10 posts since 11 Jan, 2013, from Sheffield, UK

Postby nastyfingers; Sat Jan 12, 2013 3:39 pm

I think the envelope works but am not sure as I broke it making it pull its values based on the fader/slider position, and they just don't seem to want to play ball.

I am sure it is something to do with my value/range scaling as the sustain slider appears to work.

I have spent probably too long re-checking my code and just cannot what is awry.

Not sure exactly what bits of code will be useful now, so I have just attached the lot, updated.

Thanks again

Ben.

ADSR.h
Code: Select all
   
    #ifndef __ADSR
    #define __ADSR
   
    #include <math.h>
   
    class ADSR
    {
    public:
       ADSR();
       ~ADSR();
   
       int time, internalTimer, stage;
       float process(float CurrentLevel, int attack, int decay, float sustain, int release, int* pKeyState);
    };
    #endif


ADSR.cpp
Code: Select all
    #include "ADSR.h"
   
    ADSR::ADSR()
    {
       time = 0;
       stage = 4;
       internalTimer = 0;
    }
    ADSR::~ADSR()
    {
   
    }
   
    float ADSR::process(float CurrentLevel, int attack, int decay, float sustain, int release, int* pKeyState)
    {
       if(stage = 1 && time >= (attack + decay + internalTimer + release))
       {
          time = 0;
       }
       while(time < attack)
       {
          CurrentLevel = CurrentLevel * ((1 / attack) + 1);
          time++;
          return CurrentLevel;
       }
          
       while(time < (attack + decay))
       {
          CurrentLevel = CurrentLevel * (sustain / decay);
          time++;
          return CurrentLevel;
       }
   
       while(time > (attack + decay))
       {
          CurrentLevel = CurrentLevel * 1;
          internalTimer++;
          return CurrentLevel;
       }
       
       while(time < (attack + decay + internalTimer + release) && *pKeyState == 2)
       {
          CurrentLevel = CurrentLevel * (sustain / release);
          time++;
          return CurrentLevel;
   
       }
       return CurrentLevel;
    }


VST_Plug_in.h
Code: Select all
    //-------------------------------------------------------------------------------------------------------
    // This is the most basic plug in code there is!
    // All this plug in will do is apply some gain to the input
    // This is done in process replacing
    //-------------------------------------------------------------------------------------------------------
   
    #ifndef __VST_Plug_in__
    #define __VST_Plug_in__
   
    #include "audioeffectx.h"
   
    #include <math.h>
    #include "ADSR.h"
   
   
   
    const int NUMBER_OF_INPUTS = 2;
    const int NUMBER_OF_OUTPUTS = 2;
    const int NUMBER_OF_PROGRAMS = 1;
    const int NUMBER_OF_PARAMETERS = 5;
   
    enum
       {
       kGain,
       kAttack,
       kDecay,
       kSustain,
       kRelease
       };
   
    // Base frequency (A4- 440Hz) for use in generating frequency table
       const float BASE_A4  = 440.0;
       const double PI = 3.14159265358979323846;
   
    //-------------------------------------------------------------------------------------------------------
    class VST_Plug_in : public AudioEffectX
    {
    public:
       VST_Plug_in (audioMasterCallback audioMaster);
       ~VST_Plug_in ();
       
       virtual void processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames);
   
       virtual VstInt32 processEvents (VstEvents* events);
       virtual VstInt32 canDo (char* text);
       
       double leftSample;
       double rightSample;
       float frequency;
       float sampleRate;
       long time;
       long* pTime;
       float gain;
   
       // MIDI stuff
       // MIDI data : holds data about current state of MIDI (note on/off, frequency, velocity)
       int keyDown; // true : key is down, false : no key down
       int* pkeyDown;
   
       long currentNote; // the MIDI note number of the last note on (key down)
       float currentVelocity; // current MIDI note velocity (0 -> 1)
       float *m_pfFrequencyTable; // will store a list of frequency values (for note->frequency conversion)
       
       void noteOff ();
       void noteOn (long liNote, long liVelocity);
       int getAttack(float value);
       int sendAttack();
       int getDecay(float value);
       int sendDecay();
       float getSustain(float value);
       float sendSustain();
       int getRelease(float value);
       int sendRelease();
   
       int maxAttack, minAttack, attack, maxDecay, minDecay, decay, minRelease, maxRelease, release;
       float sustain;
   
       double partial1, partial2, partial3, partial4, partial5, partial6, partial7, partial8;
   
       ADSR env;
   
       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);   
    };
   
    #endif


VST_Plug_in.cpp
Code: Select all
    //-------------------------------------------------------------------------------------------------------
    // VST Plug-Ins SDK
    // Version 2.4      $Date: 2005/11/15 15:14:03 $
    //
    // Category     : VST 2.x SDK Samples
    // Filename     : VST_Plug_in.cpp
    // Created by   : Steinberg Media Technologies
    // Description  : a crap additive synth
    //
    // © 2005, Steinberg Media Technologies, All Rights Reserved
    //-------------------------------------------------------------------------------------------------------
   
    #include "VST_Plug_in.h"
   
    //-------------------------------------------------------------------------------------------------------
    AudioEffect* createEffectInstance (audioMasterCallback audioMaster)
    {
       return new VST_Plug_in (audioMaster);
    }
   
    //-------------------------------------------------------------------------------------------------------
    VST_Plug_in::VST_Plug_in (audioMasterCallback audioMaster)
    : AudioEffectX (audioMaster, NUMBER_OF_PROGRAMS, NUMBER_OF_PARAMETERS)   
    {
       setNumInputs (NUMBER_OF_INPUTS);      // stereo in
       setNumOutputs (NUMBER_OF_OUTPUTS);      // stereo out
       setUniqueID ('Add1');   // identify
       canProcessReplacing ();   // supports replacing output
       leftSample  = 0.0;
       rightSample = 0.0;
       frequency = 0.0;
       gain = 1.f;
       currentVelocity = 0.0;
       currentNote = 0;
       keyDown = 2;
       pkeyDown = &keyDown;
       time = 0;
       pTime = &time;
       partial1 = partial2 = partial3 = partial4 = partial5 = partial6 = partial7 = partial8 = 0.f;
       maxAttack = 2 * 192000;
       minAttack = 1;
       attack = 44100;
       maxDecay = 192000;
       minDecay = 1;
       decay = 22050;
       maxRelease = 4 * 192000;
       minRelease = 1;
       release = 96000;
       sustain = 1.f;
       sampleRate = getSampleRate();//get sample rate from host
       
       ADSR env;
   
       isSynth ();   // Informs host that this is a VSTi
       
       // initialise frequency table
       m_pfFrequencyTable = new float [128] ; // 128 Midi notes
       if (m_pfFrequencyTable)
       {
          for (int i = 0; i<  128; i++)
          {
             m_pfFrequencyTable[i] = BASE_A4 *powf(2.f,(i-57)/12.f) ;
          }
       }   
    }
   
    //-------------------------------------------------------------------------------------------------------
    VST_Plug_in::~VST_Plug_in ()
    {
       // nothing to do here
    }
   
   
    //-----------------------------------------------------------------------------------------
    // this is where the intresting stuff happens :0
   
    void VST_Plug_in::processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames)
    {
        float* out1 = outputs[0];
        float* out2 = outputs[1];
       //ADSR env;
   
       for(int i = 0; i < sampleFrames; i++)
        {
          // NEW : only send out audio if there is a note on currently
          frequency = m_pfFrequencyTable[currentNote];
          
          partial1 = (double)sin(2.0*PI*time++*(frequency/sampleRate))* 0.125;
          partial2 = (double)sin(2.0*PI*time++*((2*frequency)/sampleRate))* 0.125;
          partial3 = (double)sin(2.0*PI*time++*((3*frequency)/sampleRate))* 0.125;
          partial4 = (double)sin(2.0*PI*time++*((4*frequency)/sampleRate))* 0.125;
          partial5 = (double)sin(2.0*PI*time++*((5*frequency)/sampleRate))* 0.125;
          partial6 = (double)sin(2.0*PI*time++*((6*frequency)/sampleRate))* 0.125;
          partial7 = (double)sin(2.0*PI*time++*((7*frequency)/sampleRate))* 0.125;
          partial8 = (double)sin(2.0*PI*time++*((8*frequency)/sampleRate))* 0.125;
          
          leftSample = partial1 + partial2 + partial3 + partial4 + partial5 + partial6 + partial7 + partial8;
          
          leftSample = leftSample * env.process(leftSample, attack, decay, sustain, release, pkeyDown);
          
          leftSample = leftSample * currentVelocity;
          leftSample = leftSample * gain;
          
          rightSample = leftSample;
   
          // write samples to output buffer
            (*out1++) = leftSample;
            (*out2++) = rightSample;
        }
    }
   
   
    // NEW : overriden function, tells host what the plugin can do (see notes)
   
    VstInt32 VST_Plug_in::canDo(char *text)
    {
       if (!strcmp (text, "receiveVstEvents"))      // SimpleSynth can receive VST events
          return 1;
       if (!strcmp (text, "receiveVstMidiEvent"))   // SimpleSynth can receive VST MIDI events
          return 1;
       return -1;   // explicitly can't do; 0 => don't know   
    }
   
    // NEW : this process function is called to collect incoming VST events
   
    VstInt32 VST_Plug_in::processEvents (VstEvents* events)
    {
       // parse event list
       for (long i = 0; i < events->numEvents; i++)
       {
          if ((events->events[i])->type == kVstMidiType)
          {
             VstMidiEvent* event = (VstMidiEvent*)events->events[i];
             char* midiData = event->midiData;
             long status = midiData[0] & 0xf0;      // ignoring channel
   
             if (status == 0x90 || status == 0x80)   // we only look at notes
             {
                long note = midiData[1];
                long velocity = midiData[2];
   
                if (status == 0x80)
                {
                   velocity = 0;   
                   // set velocity to zero if it is a note off message
                }
                if (!velocity && (note == currentNote))
                {
                   noteOff ();
                }
                else
                {
                   noteOn (note, velocity);
                }
             }
          }
       }
       return 1; // indicate that we wish to receive more events
    }
   
    void VST_Plug_in::noteOn(long liNote, long liVelocity)
    {
       keyDown = 1;
       time = 0;
       currentNote = liNote;
       
       // velocity is liVelocity /127
       currentVelocity = liVelocity / 127.f;
       
    }
   
    void VST_Plug_in::noteOff()
    {
       keyDown = 2;
       currentVelocity = 0;
    }
   
    // this function is called whenever the fader is moved
   
    void VST_Plug_in::setParameter (VstInt32 index, float value)
    {
       switch (index)
       {
          case kGain : gain = value;break; 
          case kAttack : attack = getAttack(value); break; //need vale in samples for envelope here
          case kDecay : decay = getDecay(value); break;
          case kSustain : sustain = getSustain(value); break;
          case kRelease : release = getRelease(value); break;
       }
    }
   
   
   
    //-----------------------------------------------------------------------------------------
   
    // this function is called whenever the gui requires data
   
    // eg when it is displayed
   
    float VST_Plug_in::getParameter (VstInt32 index)
   
    {
       switch (index)
       {
          case kGain : return gain;break;
          case kAttack : return sendAttack(); break; //need 0 - 1 val here
          case kDecay : return sendDecay();break;
          case kSustain : return sendSustain();break;
          case kRelease : return sendRelease();break;
       }
    }
   
   
   
    //-----------------------------------------------------------------------------------------
   
    // getParameterName places the parameter name on the plug in
   
    void VST_Plug_in::getParameterName (VstInt32 index, char* label)
   
    {
       switch (index)
       {
          case kGain : vst_strncpy (label, "Gain", kVstMaxParamStrLen);break;
          case kAttack : vst_strncpy (label, "Attack", kVstMaxParamStrLen); break;
          case kDecay : vst_strncpy (label, "Decay", kVstMaxParamStrLen); break;
          case kSustain : vst_strncpy (label, "Sustain", kVstMaxParamStrLen); break;
          case kRelease : vst_strncpy (label, "Release", kVstMaxParamStrLen); break;
       }
    }
   
   
   
    //----------------------------------------------------------------------------------------
   
    // getParameterDisplay displays the parameter value on the plug-in
   
    void VST_Plug_in::getParameterDisplay (VstInt32 index, char* text)
   
    {
       switch (index)
       {
          case kGain : dB2string(gain, text, kVstMaxParamStrLen);break;
          case kAttack : float2string(attack, text, kVstMaxParamStrLen); break;
          case kDecay : float2string(decay, text, kVstMaxParamStrLen); break;
          case kSustain: dB2string(sustain, text, kVstMaxParamStrLen); break;
          case kRelease : float2string(release, text, kVstMaxParamStrLen); break;
       }
       
       //linear display
       //float2string(gain, text, kVstMaxParamStrLen);
   
       //dB display
       //dB2string (gain, text, kVstMaxParamStrLen);
    }
   
   
   
    //-----------------------------------------------------------------------------------------
   
    void VST_Plug_in::getParameterLabel (VstInt32 index, char* label)
   
    {
       switch (index)
       {
          case kGain : vst_strncpy (label, "dB", kVstMaxParamStrLen);break;
          case kAttack : vst_strncpy (label, "Samples", kVstMaxParamStrLen); break;
          case kDecay : vst_strncpy (label, "Samples", kVstMaxParamStrLen); break;
          case kSustain : vst_strncpy (label, "dB", kVstMaxParamStrLen); break;
          case kRelease : vst_strncpy (label, "Samples", kVstMaxParamStrLen); break;
       }
    }
   
    int VST_Plug_in::getAttack(float value)
    {   
       attack = minAttack + ((maxAttack-minAttack) * value);
       return attack;
    }
    int VST_Plug_in::sendAttack()
    {   
       int retAttVal;
       retAttVal = (attack - minAttack) / (maxAttack-minAttack);
       return retAttVal;
    }
   
    int VST_Plug_in::getDecay(float value)
    {
       decay = minDecay + ((maxDecay-minDecay) * value);
       return decay;
    }
    int VST_Plug_in::sendDecay()
    {
       int retDecVal;
       retDecVal = (decay - minDecay) / (maxDecay-minDecay);
       return retDecVal;
    }
   
   
    float VST_Plug_in::getSustain(float value)
    {
       sustain = value;
       return sustain;
    }
    float VST_Plug_in::sendSustain()
    {
       return sustain;
    }
   
    int VST_Plug_in::getRelease(float value)
    {
       release = minRelease + ((maxRelease-minRelease) * value);
       return release;
    }
    int VST_Plug_in::sendRelease()
    {
       int retRelVal;
       retRelVal = (release - minRelease) / (maxRelease-minRelease);
       return retRelVal;
    }


:help:
bitwise
KVRist
 
90 posts since 18 Mar, 2012

Postby bitwise; Sun Jan 13, 2013 7:34 am

Code: Select all
int maxAttack, minAttack, attack, maxDecay, minDecay, ...


Code: Select all
retAttVal = (attack - minAttack) / (maxAttack-minAttack);


(attack - minAttack) is never greater than (maxAttack-minAttack)
and since all the variables are "int", retAttVal will be zero most
of the times.

retAttVal would be zero anyway, because it's "int":

Code: Select all
   
int VST_Plug_in::sendAttack()
{   
    int retAttVal;
    ...



Here's another case:

Code: Select all
CurrentLevel = CurrentLevel * ((1 / attack) + 1);
nastyfingers
KVRer
 
10 posts since 11 Jan, 2013, from Sheffield, UK

Postby nastyfingers; Sun Jan 13, 2013 7:57 am

Hi thank you,

Yes since posting I realised what was wrong with the sliders. Turning all relevant functions and variables to float has sliders function as expected. I should have realised earlier having had a similar issue with Max MSP. :oops:

Amazing what some sleep and no kids can do for you!

Still struggling with the envelope progression though. Just cannot see why it won't step through the relevant stages. My logic must be wrong but where???

Trying a timer class using pointers... doubt it'll make any difference. :(
larm
KVRian
 
751 posts since 28 Oct, 2004

Postby larm; Sun Jan 13, 2013 8:02 am

try "stage == 1" instead of "stage = 1"
nastyfingers
KVRer
 
10 posts since 11 Jan, 2013, from Sheffield, UK

Postby nastyfingers; Sun Jan 13, 2013 9:32 am

larm wrote:try "stage == 1" instead of "stage = 1"


Nicely spotted thank you, unfortunately correcting it hasn't made the envelope function.

Having implemented a timer class that has 1 variable tracking time that is access via pointers, I have a new issue on top the fact that it isn't doing what I want.

If the time variable for the envelope is incremented within the processReplacing() loop the code complies but crashes the host.

When incrementing time within each adsr multiplication, it compiles and runs in a host, just doesn't work as wanted.

I would have thought that incrementing time in the processReplacing() loop was the was to go, apparently not.

I am very appreciative of everyones help here, I know its cheeky but have attached my current code to see if anyone can see whats actually wrong with my logic. I wish I were Vulcan sometimes.

Don't think the .h's are needed so just the 3 .cpp's, and I tidied it all up so it's much easier to read now.

Thank you again,

Ben


timer.cpp
Code: Select all
#include <math.h>
#include "timer.h"


timer::timer()
{
   time = 0;
   pTime = &time;
}
timer::~timer()
{
}

long* timer::timeCount()
{
   return pTime;
}


ADSR.cpp
Code: Select all
#include <math.h>
#include "ADSR.h"


ADSR::ADSR()
{
   stage = 1;
   susTimer = 0;
}
ADSR::~ADSR()
{
}

double ADSR::process(float currentSamp, int attack, int decay, float sustain, int release, int* pKeyState, long* pTime)
{
   if(stage == 1 && *pTime >= (attack + decay + susTimer + release))
   {
      *pTime = 0;
   }

   while(*pTime < attack)
   {
      currentSamp = currentSamp * ((1 / attack) + 1);
      *pTime++;//doesn't crash but doesnt do anything either
      stage = 1;
      return currentSamp;
   }
      
   while(*pTime < (attack + decay))
   {
      currentSamp = currentSamp * (sustain / decay);
      *pTime++;//doesn't crash but doesnt do anything either
      stage = 2;
      return currentSamp;
   }

   while(*pTime > (attack + decay))
   {
      currentSamp = currentSamp * 1;
      susTimer++;
      stage = 3;
      return currentSamp;
   }
   
   while(*pTime < (attack + decay + susTimer + release))
   {
      currentSamp = currentSamp * (sustain / release);
      *pTime++;//doesn't crash but doesnt do anything either
      stage = 4;
      return currentSamp;
   }
}


VST_Plug_in.cpp
Code: Select all
//-------------------------------------------------------------------------------------------------------
// VST Plug-Ins SDK
// Version 2.4      $Date: 2005/11/15 15:14:03 $
//
// Category     : VST 2.x SDK Samples
// Filename     : VST_Plug_in.cpp
// Created by   : Steinberg Media Technologies
// Description  : a crap additive synth
//
// © 2005, Steinberg Media Technologies, All Rights Reserved
//-------------------------------------------------------------------------------------------------------

#include "VST_Plug_in.h"
#include "audioeffectx.h"
#include <math.h>
#include "ADSR.h"
#include "timer.h"

AudioEffect* createEffectInstance (audioMasterCallback audioMaster)
{
   return new VST_Plug_in (audioMaster);
}

VST_Plug_in::VST_Plug_in (audioMasterCallback audioMaster)
: AudioEffectX (audioMaster, NUMBER_OF_PROGRAMS, NUMBER_OF_PARAMETERS)   
{
   setNumInputs (NUMBER_OF_INPUTS);      // stereo in
   setNumOutputs (NUMBER_OF_OUTPUTS);      // stereo out
   setUniqueID ('Add1');   // identify
   canProcessReplacing ();   // supports replacing output
   isSynth ();   // Informs host that this is a VSTi
   sampleRate = getSampleRate();//get sample rate from host

   leftSample  = 0.0;
   rightSample = 0.0;
   frequency = 0.0;
   gain = 1.f;
   currentVelocity = 0.f;
   currentNote = 0;
   keyDown = 2;
   pkeyDown = &keyDown;
   partialTime = 0;
   partial1 = partial2 = partial3 = partial4 = partial5 = partial6 = partial7 = partial8 = 0.0;
   maxAttack = 384000;
   minAttack = 1;
   attack = 44100;
   maxDecay = 192000;
   minDecay = 1;
   decay = 22050;
   maxRelease = 768000;
   minRelease = 1;
   release = 96000;
   sustain = 1.f;

   ADSR env;
   timer universalTime;
   pTime = universalTime.timeCount();

   // initialise frequency table
   m_pfFrequencyTable = new float [128] ; // 128 Midi notes
   if (m_pfFrequencyTable)
   {
      for (int i = 0; i<  128; i++)
      {
         m_pfFrequencyTable[i] = BASE_A4 *powf(2.f,(i-57)/12.f) ;
      }
   }   
}

VST_Plug_in::~VST_Plug_in ()
{
   // nothing to do here
}

// this is where the intresting stuff happens :0
void VST_Plug_in::processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames)
{
    float* out1 = outputs[0];
    float* out2 = outputs[1];

   for(int i = 0; i < sampleFrames; i++)
    {
      // NEW : only send out audio if there is a note on currently
      frequency = m_pfFrequencyTable[currentNote];

      partial1 = (double)sin(2.0*PI*partialTime++*(frequency/sampleRate))* 0.125;//Nyquist frequency issues. 
      partial2 = (double)sin(2.0*PI*partialTime*((2*frequency)/sampleRate))* 0.125;//Obvious looping/foldback of frequencies with higher notes.
      partial3 = (double)sin(2.0*PI*partialTime*((3*frequency)/sampleRate))* 0.125;//Appears mostly fixed when time is only
      partial4 = (double)sin(2.0*PI*partialTime*((4*frequency)/sampleRate))* 0.125;//incremented with the fundamental.
      partial5 = (double)sin(2.0*PI*partialTime*((5*frequency)/sampleRate))* 0.125;
      partial6 = (double)sin(2.0*PI*partialTime*((6*frequency)/sampleRate))* 0.125;
      partial7 = (double)sin(2.0*PI*partialTime*((7*frequency)/sampleRate))* 0.125;
      partial8 = (double)sin(2.0*PI*partialTime*((8*frequency)/sampleRate))* 0.125;
      leftSample = partial1 + partial2 + partial3 + partial4 + partial5 + partial6 + partial7 + partial8;
      
      leftSample = (env.process(leftSample, attack, decay, sustain, release, pkeyDown, pTime) * currentVelocity); //needs to be pointer as data is duplicated when sent.
      //*pTime++; //crashes host if uncommented
      leftSample = leftSample * gain;
      rightSample = leftSample;
      
      // write samples to output buffer
        (*out1++) = leftSample;
        (*out2++) = rightSample;
    }
}

// NEW : overriden function, tells host what the plugin can do (see notes)
VstInt32 VST_Plug_in::canDo(char *text)
{
   if (!strcmp (text, "receiveVstEvents"))      // SimpleSynth can receive VST events
      return 1;
   if (!strcmp (text, "receiveVstMidiEvent"))   // SimpleSynth can receive VST MIDI events
      return 1;
   return -1;   // explicitly can't do; 0 => don't know   
}

// NEW : this process function is called to collect incoming VST events
VstInt32 VST_Plug_in::processEvents (VstEvents* events)
{
   // parse event list
   for (long i = 0; i < events->numEvents; i++)
   {
      if ((events->events[i])->type == kVstMidiType)
      {
         VstMidiEvent* event = (VstMidiEvent*)events->events[i];
         char* midiData = event->midiData;
         long status = midiData[0] & 0xf0;      // ignoring channel

         if (status == 0x90 || status == 0x80)   // we only look at notes
         {
            long note = midiData[1];
            long velocity = midiData[2];

            if (status == 0x80)
            {
               velocity = 0;   
               // set velocity to zero if it is a note off message
            }
            if (!velocity && (note == currentNote))
            {
               noteOff ();
            }
            else
            {
               noteOn (note, velocity);
            }
         }
      }
   }
   return 1; // indicate that we wish to receive more events
}

void VST_Plug_in::noteOn(long liNote, long liVelocity)
{
   keyDown = 1;
   partialTime = 0;
   currentNote = liNote;
   currentVelocity = liVelocity / 127.f;
}

void VST_Plug_in::noteOff()
{
   keyDown = 2;
   currentVelocity = 0;
}

// this function is called whenever the fader is moved
void VST_Plug_in::setParameter (VstInt32 index, float value)
{
   switch (index)
   {
      case kGain : gain = value;break; 
      case kAttack : attack = getAttack(value); break; //need vale in samples for envelope here
      case kDecay : decay = getDecay(value); break;
      case kSustain : sustain = getSustain(value); break;
      case kRelease : release = getRelease(value); break;
   }
}

// this function is called whenever the gui requires data
float VST_Plug_in::getParameter (VstInt32 index)

{
   switch (index)
   {
      case kGain : return gain;break;
      case kAttack : return sendAttack(); break; //need 0 - 1 val here
      case kDecay : return sendDecay();break;
      case kSustain : return sendSustain();break;
      case kRelease : return sendRelease();break;
   }
}

// getParameterName places the parameter name on the plug in
void VST_Plug_in::getParameterName (VstInt32 index, char* label)

{
   switch (index)
   {
      case kGain : vst_strncpy (label, "Gain", kVstMaxParamStrLen);break;
      case kAttack : vst_strncpy (label, "Attack", kVstMaxParamStrLen); break;
      case kDecay : vst_strncpy (label, "Decay", kVstMaxParamStrLen); break;
      case kSustain : vst_strncpy (label, "Sustain", kVstMaxParamStrLen); break;
      case kRelease : vst_strncpy (label, "Release", kVstMaxParamStrLen); break;
   }
}

// getParameterDisplay displays the parameter value on the plug-in
void VST_Plug_in::getParameterDisplay (VstInt32 index, char* text)

{
   switch (index)
   {
      case kGain : dB2string(gain, text, kVstMaxParamStrLen);break;
      case kAttack : float2string(attack, text, kVstMaxParamStrLen); break;
      case kDecay : float2string(decay, text, kVstMaxParamStrLen); break;
      case kSustain: dB2string(sustain, text, kVstMaxParamStrLen); break;
      case kRelease : float2string(release, text, kVstMaxParamStrLen); break;
   }
   /*linear display
   float2string(gain, text, kVstMaxParamStrLen);
   dB display
   dB2string (gain, text, kVstMaxParamStrLen);*/
}

void VST_Plug_in::getParameterLabel (VstInt32 index, char* label)

{
   switch (index)
   {
      case kGain : vst_strncpy (label, "dB", kVstMaxParamStrLen);break;
      case kAttack : vst_strncpy (label, "Samples", kVstMaxParamStrLen); break;
      case kDecay : vst_strncpy (label, "Samples", kVstMaxParamStrLen); break;
      case kSustain : vst_strncpy (label, "dB", kVstMaxParamStrLen); break;
      case kRelease : vst_strncpy (label, "Samples", kVstMaxParamStrLen); break;
   }
}

float VST_Plug_in::getAttack(float value)
{   
   attack = minAttack + ((maxAttack-minAttack) * value);
   return attack;
}
float VST_Plug_in::sendAttack()
{   
   float retAttVal;
   retAttVal = (attack - minAttack) / (maxAttack-minAttack);
   return retAttVal;
}

float VST_Plug_in::getDecay(float value)
{
   decay = minDecay + ((maxDecay-minDecay) * value);
   return decay;
}
float VST_Plug_in::sendDecay()
{
   float retDecVal;
   retDecVal = (decay - minDecay) / (maxDecay-minDecay);
   return retDecVal;
}

float VST_Plug_in::getSustain(float value)
{
   sustain = value;
   return sustain;
}
float VST_Plug_in::sendSustain()
{
   return sustain;
}

float VST_Plug_in::getRelease(float value)
{
   release = minRelease + ((maxRelease-minRelease) * value);
   return release;
}
float VST_Plug_in::sendRelease()
{
   float retRelVal;
   retRelVal = (release - minRelease) / (maxRelease-minRelease);
   return retRelVal;
}
larm
KVRian
 
751 posts since 28 Oct, 2004

Postby larm; Sun Jan 13, 2013 10:49 am

Try another change:

http://pw1.netcom.com/~tjensen/ptr/ch3x.htm

Code: Select all
Now, let's consider some of the things the above examples have shown us. First off, consider the fact that *ptr++ is to be interpreted as returning the value pointed to by ptr and then incrementing the pointer value. This has to do with the precedence of the operators. Were we to write (*ptr)++ we would increment, not the pointer, but that which the pointer points to! i.e. if used on the first character of the above example string the 'T' would be incremented to a 'U'. You can write some simple example code to illustrate this.
nastyfingers
KVRer
 
10 posts since 11 Jan, 2013, from Sheffield, UK

Postby nastyfingers; Sun Jan 13, 2013 12:05 pm

larm thank you.

So I wasn't incrementing the contents but the address. I wasn't sure until reading that rather handy link wether pointer contents where incremented the same way as assigned. Binky never covered that in "Fun with Pointers" So thank you for the extra learning. Alas the envelope still isn't working.

I don't get which bit of the decisions on adsr stage is iffy, and what it is returning, as without going through one of the while loops there is nothing to return. I even get a compiler warning to that effect:
e:\library\mobile documents\blah ... blah\adsr.cpp(52): warning C4715: 'ADSR::process' : not all control paths return a value

I have tried being more, less and non specific with the conditions of the if and while bits of ADSR::process() and still nothing actually happens, although something is returned??

I am sure that had I spent more time doing c++ and less time messing about with Max MSP I'd be able to see what's awry with the logic there.

any further input as ever is greatly appreciated.

Cheers
larm
KVRian
 
751 posts since 28 Oct, 2004

Postby larm; Sun Jan 13, 2013 12:39 pm

Next thing to learn about is the difference between integer and floating point division. Since your parameters to ADSR process are integers (no decimals) your envelope multipliers/factors are either 0 or 1.

Rewrite

Code: Select all
int attack, int decay, float sustain, int release



to

Code: Select all
float attack, float decay, float sustain, float release


And you can add "f" to signal that arithmetics should be made in floating point.

Code: Select all
((1.0f / attack) + 1.0f)


The warning is because the compiler sees the potentiality of all of the while loops being skipped over. Add a final return and the warning disappears.

Try
Code: Select all
return -1.0;


for debugging, that way you would probably get a nasty click and a full on negative DC-offset, signalling that the ADSR probably didn't do as you thought..

Also I'm not sure about your time step delta, time++ will add "1" but are your ADSR values in seconds or samples? Could be good to have them run in a sample-rate independent format (seconds, milliseconds) and increase the time with 1.0/sample rate.
nastyfingers
KVRer
 
10 posts since 11 Jan, 2013, from Sheffield, UK

Postby nastyfingers; Sun Jan 13, 2013 4:02 pm

Larm your help has got me far. Thank you very much.

I cannot believe that for the 2nd time today I have been got by the int/float thing. Ah well that's that bit sorted.

With your next bit of useful advice, return -1; I have managed to get the envelope to progress as far sustain. Capturing the release stage is proving tricky.

As I had expected now that A and D are actually doing something I have found that the are only holding the level constant, not ramping it.

I tried storing a previous sample like with filters and mixing the 2 however this just causes signal overload.

Code: Select all
while(*pTime <= attack)
   {
      currentSamp = prevSamp + (currentSamp * (1.0f / attack));
      (*pTime)++;//doesn't crash but doesnt do anything either
      stage = 1;
      prevSamp = currentSamp;
      return currentSamp;
   }


And yes, should I ever get the envelope working with time in samples, I was then going to make it sample rate independent with sensible time values. So again thank you for advice there, you've defiantly got my brain thinking in the right direction for that. For now at least I am finding debugging easier thinking in samples.

:)
davidguda
KVRist
 
204 posts since 28 Feb, 2011

Postby davidguda; Mon Jan 14, 2013 4:35 am

Why do you use while and then a return inside every while?
like this

while(*pTime < attack)
{......

In practice it will work like if you wrote "if" instead of "while" which is OK but not great for readability as you expect som kind of looping when reading a while.
David Guda gudaaudio.com
Next

Moderator: Moderators (Main)

Return to DSP and Plug-in Development