/*-----------------------------------------------------------------------------© 1999, Steinberg Soft und Hardware GmbH, All Rights Reserved-----------------------------------------------------------------------------*/#include <stdio.h>#include <string.h>#include <math.h>#include "ADelay.hpp"#include "AEffEditor.hpp"//----------------------------------------------------------------------------- ADelayProgram::ADelayProgram (){	fDelay = 0.5;	fFeedBack = 0.5;	fOut = 0.75;	fLFOFreq = 0.25f;	fLFODepth = 0.0f;	strcpy (name, "Init");}//-----------------------------------------------------------------------------ADelay::ADelay (audioMasterCallback audioMaster)	: AudioEffectX (audioMaster, 16, kNumParams){	long i;	// zero out all pointers	table = 0;	buffer = 0;	programs = 0;	// initialize variables	Pi = 4.0 * atan(1.0);	size = 44100;	buffer = new float[size];	programs = new ADelayProgram[numPrograms];	fDelay = fFeedBack = fOut = vu = 0;	delay  = 0;	realDelay = 1.0f;	writeOffset = 0;		// LFO and table initialization	lfoInc = lfoPhase = lfoSamp = fLFOFreq = fLFODepth = 0.0f;	waveform = kWavetable;	// just picking an arbitrary tableSize	// probably should be bigger than 1024	tableSize = 8192;	table = new float[tableSize];	for(i = 0; i < tableSize; i++)		*(table+i) = sin(2.0f * Pi * ((float)i/tableSize));	if (programs)		setProgram (0);	setNumInputs (1);	setNumOutputs (2);	hasVu ();	canProcessReplacing ();	setUniqueID ('ADly');	suspend ();		// flush buffer}//------------------------------------------------------------------------ADelay::~ADelay (){	if (buffer)		delete[] buffer;	if (programs)		delete[] programs;	if (table)		delete[] table;}//------------------------------------------------------------------------void ADelay::setProgram (long program){	ADelayProgram * ap = &programs[program];	curProgram = program;	setParameter (kDelay, ap->fDelay);		setParameter (kFeedBack, ap->fFeedBack);	setParameter (kOut, ap->fOut);	setParameter (kLFOFreq, ap->fLFOFreq);	setParameter (kLFODepth, ap->fLFODepth);}//------------------------------------------------------------------------void ADelay::setProgramName (char *name){	strcpy (programs[curProgram].name, name);}//------------------------------------------------------------------------void ADelay::getProgramName (char *name){	if (!strcmp (programs[curProgram].name, "Init"))		sprintf (name, "%s %d", programs[curProgram].name, curProgram + 1);	else		strcpy (name, programs[curProgram].name);}//------------------------------------------------------------------------void ADelay::suspend (){	memset (buffer, 0, size * sizeof (float));}//------------------------------------------------------------------------float ADelay::getVu (){	float cvu = vu;		vu = 0;	return cvu;}//------------------------------------------------------------------------void ADelay::setParameter (long index, float value){	ADelayProgram * ap = &programs[curProgram];	switch (index)	{		case kDelay :    			fDelay = ap->fDelay = value;			delay = fDelay * ((size>>1) - 1);			break;		case kFeedBack : 			ap->fFeedBack = value; 			fFeedBack = (4.f * value) - 2.f;			break;		case kOut :      			fOut = ap->fOut = value; 			break;		case kLFOFreq:			ap->fLFOFreq = value;			fLFOFreq = value * 10.f;			lfoInc = fLFOFreq/sampleRate;			break;		case kLFODepth:			fLFODepth = ap->fLFODepth = value;			break;	}	if (editor)		editor->postUpdate ();}//------------------------------------------------------------------------float ADelay::getParameter (long index){	float v = 0;	switch (index)	{		case kDelay :    v = fDelay; break;		case kFeedBack : v = (fFeedBack + 2.f) * 0.25f; break;		case kOut :      v = fOut; break;		case kLFOFreq:	v = fLFOFreq * 0.03333333f; break;		case kLFODepth:	v = fLFODepth; break;	}	return v;}//------------------------------------------------------------------------void ADelay::getParameterName (long index, char *label){	switch (index)	{		case kDelay :    strcpy (label, " Delay  "); break;		case kFeedBack : strcpy (label, "FeedBack"); break;		case kOut :      strcpy (label, " Volume "); break;		case kLFOFreq:      strcpy (label, "LFO Frequency"); break;		case kLFODepth:      strcpy (label, "LFO Depth"); break;	}}//------------------------------------------------------------------------void ADelay::getParameterDisplay (long index, char *text){	switch (index)	{		case kDelay :    float2string (delay/sampleRate, text); break;		case kFeedBack : float2string (fFeedBack, text);	break;		case kOut :      dB2string (fOut, text); break;		case kLFOFreq:    float2string (fLFOFreq, text); break;		case kLFODepth:    float2string (fLFODepth, text); break;	}}//------------------------------------------------------------------------void ADelay::getParameterLabel (long index, char *label){	switch (index)	{		case kDelay :    strcpy (label, "seconds ");	break;		case kFeedBack : strcpy (label, " amount ");	break;		case kOut :      strcpy (label, "   dB   ");	break;		case kLFOFreq:      strcpy (label, "Hz"); break;		case kLFODepth:      strcpy (label, "amount"); break;	}}//------------------------------------------------------------------------void ADelay::process (float **inputs, float **outputs, long sampleframes){	processCombined(inputs, outputs, sampleframes, false);}//---------------------------------------------------------------------------void ADelay::processReplacing (float **inputs, float **outputs, long sampleframes){	processCombined(inputs, outputs, sampleframes, true);}void ADelay::processCombined(float **inputs, float **outputs, long sampleframes, bool replacing){	// values which reflect the actual delay time.	float delayFrac, lfoOffset;	long delayLong;		// copy the pointers to more easy to use pointers	float *in = *inputs;	float *out1 = outputs[0];	float *out2 = outputs[1];		// storage variables for internal values	float delayOut, inSum, absInSum;	float cvu = vu;	long sampleNumber;	// we will not adjust the delay immediatey, but adjust it by small increments	// by multiplying repeatedly against a constant	if(delay > realDelay)		realDelay *= 1.01f;	if(delay < realDelay)		realDelay *= 0.99f;		for(sampleNumber = 0; sampleNumber < sampleframes; sampleNumber++)	{		// first the LFO has to be calculated and allowed to update the 		// delay time.		// this is in a function purely for organisation...		calcLFOSamp();		// allow an offset up to the size of delay - 1		lfoOffset = lfoSamp * fLFODepth * (realDelay - 1.0);		// calculate new delay offset as integer and fractional part		delayLong = (long)(realDelay + lfoOffset);		delayFrac = (realDelay + lfoOffset) - (float)delayLong;				// calculate read and offset from delay value		readOffset = writeOffset - delayLong;		// wrap read offset around beginning of delay line		if(readOffset < 0)			readOffset += size;		// add the input sample to the summing node		inSum = *(in + sampleNumber);		// if the readOffset is 0, then we mix the readOffset sample (the 0 sample)		// with the one before it (the last sample)		if(readOffset == 0)			delayOut = (*(buffer) * (1.f - delayFrac)) 				+ (*(buffer + (size - 1)) * delayFrac);		// normally we mix the readOffset sample		// with the one before it (readOffset - 1)		// this is because readOffset - 1 represents a farther distance from		// the writeOffset		else			delayOut = (*(buffer + readOffset) * (1.f - delayFrac)) 				+ (*(buffer + (readOffset - 1)) * delayFrac);		inSum += delayOut * fFeedBack;		// now time to do some simple limiting with a waveshaping function				// btw - this isn't that much better than clipping!!!				// get absolute value of inSum		if (inSum < 0.0f)							absInSum = inSum * -1.f;		else			absInSum = inSum;		// we will limit whenever the number is bigger than 0.5		// by scaling by 1 when 0.5 and 0.5 when 2.0		if (absInSum > 0.5f)			inSum = inSum * (1.0f - ((absInSum - 0.5f) * 0.333333f));		// now inSum is scaled and can be put back in the delay line		// and also sent to the output		*(buffer + writeOffset) = inSum;		if(replacing)		{			*(out1 + sampleNumber) = inSum * fOut;	// replace buss			*(out2 + sampleNumber) = inSum * fOut;		}		else		{			*(out1 + sampleNumber) += inSum * fOut;	// add to buss			*(out2 + sampleNumber) += inSum * fOut;		}		if (inSum * fOut > cvu)		// for any vu meter				cvu = inSum * fOut;		writeOffset++;		readOffset++;		if(writeOffset >= size)			writeOffset -= size;		if(readOffset >= size)			readOffset -= size;	}	vu = cvu;}void ADelay::calcLFOSamp(){	// update the low frequency oscillator	switch(waveform)	{		case kTriangle:			// this is a triangle from 1.0 to -1.0			// since a range of 2, gets twice the increment			lfoSamp += (lfoInc + lfoInc);			if(lfoSamp > 1.0f)			{				// reverse the increment and wrap around				lfoInc = -1.0f * lfoInc;				lfoSamp = 2.0f - lfoSamp;			}			else if(lfoSamp < -1.0f)			{				// reverse the increment and wrap around				lfoInc = -1.0f * lfoInc;				lfoSamp = -2.0f - lfoSamp;			}			break;		case kSawtooth:			// this is a sawtooth from 1.0 to -1.0			// since a range of 2, gets twice the increment			lfoSamp += (lfoInc + lfoInc);			if(lfoSamp > 1.0f)			// go back to the bottom of the range by subtracting the full range				lfoSamp -= 2.0f;			break;		case kWavetable:			// phase is a sawtooth from 0.0 to 1.0			lfoPhase += lfoInc;			if(lfoPhase > 1.0f)			// go back to the bottom of the range by subtracting the full range				lfoPhase -= 1.0f;			lfoSamp = table[(long)(tableSize * lfoPhase)];			break;	}}
