|
|||
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 #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 #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 //---------------------------------------------------------- --------------------------------------------- // 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 //---------------------------------------------------------- --------------------------------------------- // 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; } |
|||
| ^ | Joined: 11 Jan 2013 Member: #296245 Location: Sheffield, UK | ||
|
|||
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. |
|||
| ^ | Joined: 15 Apr 2012 Member: #278696 Location: Toronto, ON | ||
|
|||
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. 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. 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 |
|||
| ^ | Joined: 11 Jan 2013 Member: #296245 Location: Sheffield, UK | ||
|
|||
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: 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. |
|||
| ^ | Joined: 15 Apr 2012 Member: #278696 Location: Toronto, ON | ||
|
|||
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 |
|||
| ^ | Joined: 11 Jan 2013 Member: #296245 Location: Sheffield, UK | ||
|
|||
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 #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 #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 //---------------------------------------------------------- --------------------------------------------- // 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 //---------------------------------------------------------- --------------------------------------------- // 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; } |
|||
| ^ | Joined: 11 Jan 2013 Member: #296245 Location: Sheffield, UK | ||
|
|||
int maxAttack, minAttack, attack, maxDecay, minDecay, ...
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": int VST_Plug_in::sendAttack() { int retAttVal; ... Here's another case: CurrentLevel = CurrentLevel * ((1 / attack) + 1); |
|||
| ^ | Joined: 18 Mar 2012 Member: #277127 | ||
|
|||
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. 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. |
|||
| ^ | Joined: 11 Jan 2013 Member: #296245 Location: Sheffield, UK | ||
|
|||
try "stage == 1" instead of "stage = 1" |
|||
| ^ | Joined: 28 Oct 2004 Member: #46041 | ||
|
|||
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 #include <math.h> #include "timer.h" timer::timer() { time = 0; pTime = &time; } timer::~timer() { } long* timer::timeCount() { return pTime; } ADSR.cpp #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 //---------------------------------------------------------- --------------------------------------------- // 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; } |
|||
| ^ | Joined: 11 Jan 2013 Member: #296245 Location: Sheffield, UK | ||
|
|||
Try another change:
http://pw1.netcom.com/~tjensen/ptr/ch3x.htm 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. |
|||
| ^ | Joined: 28 Oct 2004 Member: #46041 | ||
|
|||
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: Quote: 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 |
|||
| ^ | Joined: 11 Jan 2013 Member: #296245 Location: Sheffield, UK | ||
|
|||
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 int attack, int decay, float sustain, int release to float attack, float decay, float sustain, float release And you can add "f" to signal that arithmetics should be made in floating point. ((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 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. |
|||
| ^ | Joined: 28 Oct 2004 Member: #46041 | ||
|
|||
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. 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. |
|||
| ^ | Joined: 11 Jan 2013 Member: #296245 Location: Sheffield, UK | ||
|
|||
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 |
|||
| ^ | Joined: 28 Feb 2011 Member: #251491 |
| KVR Forum Index » DSP and Plug-in Development | All times are GMT - 8 Hours |
|
Printable version |
Disclaimer: All communications made available as part of this forum and any opinions, advice, statements, views or other information expressed in this forum are solely provided by, and the responsibility of, the person posting such communication and not of kvraudio.com (unless kvraudio.com is specifically identified as the author of the communication).
Powered by phpBB © phpBB Group






