aboutsummaryrefslogtreecommitdiff
path: root/buffer_override/bufferoverrideProcess.cpp
blob: 5877020d6e22b50c17bc73bf43da0f1c30bc4312 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
/*------------------- by Marc Poirier  ][  March 2001 -------------------*/

#ifndef __bufferoverride
#include "bufferoverride.hpp"
#endif



//-----------------------------------------------------------------------------
void bufferoverride::updateBuffer(long samplePos)
{
  bool doSmoothing = true;	// but in some situations, we shouldn't
  float divisorLFOvalue, bufferLFOvalue;	// the current output values of the LFOs
  long prevForcedBufferSize;	// the previous forced buffer size


	readPos = 0;	// reset for starting a new minibuffer
	prevMinibufferSize = minibufferSize;
	prevForcedBufferSize = currentForcedBufferSize;

	//--------------------------PROCESS THE LFOs----------------------------
	// update the LFOs' positions to the current position
	divisorLFO->updatePosition(prevMinibufferSize);
	bufferLFO->updatePosition(prevMinibufferSize);
	// Then get the current output values of the LFOs, which also updates their positions once more.  
	// Scale the 0.0 - 1.0 LFO output values to 0.0 - 2.0 (oscillating around 1.0).
	divisorLFOvalue = processLFOzero2two(divisorLFO);
	bufferLFOvalue = 2.0f - processLFOzero2two(bufferLFO);	// inverting it makes more pitch sense
	// & then update the stepSize for each LFO, in case the LFO parameters have changed
	if (onOffTest(divisorLFO->fTempoSync))
		divisorLFO->stepSize = currentTempoBPS * (tempoRateTable->getScalar(divisorLFO->fRate)) * numLFOpointsDivSR;
	else
		divisorLFO->stepSize = LFOrateScaled(divisorLFO->fRate) * numLFOpointsDivSR;
	if (onOffTest(bufferLFO->fTempoSync))
		bufferLFO->stepSize = currentTempoBPS * (tempoRateTable->getScalar(bufferLFO->fRate)) * numLFOpointsDivSR;
	else
		bufferLFO->stepSize = LFOrateScaled(bufferLFO->fRate) * numLFOpointsDivSR;

	//---------------------------CALCULATE FORCED BUFFER SIZE----------------------------
	// check if it's the end of this forced buffer
	if (writePos >= currentForcedBufferSize)
	{
		writePos = 0;	// start up a new forced buffer

		// check on the previous forced & minibuffers; don't smooth if the last forced buffer wasn't divided
		if (prevMinibufferSize >= currentForcedBufferSize)
			doSmoothing = false;
		else
			doSmoothing = true;

		// now update the the size of the current force buffer
		if ( onOffTest(fBufferTempoSync) &&	// the user wants to do tempo sync / beat division rate
			 (currentTempoBPS > 0.0f) ) // avoid division by zero
		{
			currentForcedBufferSize = (long) ( SAMPLERATE / (currentTempoBPS * tempoRateTable->getScalar(fBuffer)) );
			// set this true so that we make sure to do the measure syncronisation later on
		}
		else
			currentForcedBufferSize = forcedBufferSizeSamples(fBuffer);
		// apply the buffer LFO to the forced buffer size
		currentForcedBufferSize = (long) ((float)currentForcedBufferSize * bufferLFOvalue);
		// really low tempos & tempo rate values can cause huge forced buffer sizes,
		// so prevent going outside of the allocated buffer space
		if (currentForcedBufferSize > SUPER_MAX_BUFFER)
			currentForcedBufferSize = SUPER_MAX_BUFFER;
		if (currentForcedBufferSize < 2)
			currentForcedBufferSize = 2;

		// untrue this so that we don't do the measure sync calculations again unnecessarily
		needResync = false;
	}

	//-----------------------CALCULATE THE DIVISOR-------------------------
	currentBufferDivisor = bufferDivisorScaled(fDivisor);
	// apply the divisor LFO to the divisor value if there's an "active" divisor (i.e. 2 or greater)
	if (currentBufferDivisor >= 2.0f)
	{
		currentBufferDivisor *= divisorLFOvalue;
		// now it's possible that the LFO could make the divisor less than 2, 
		// which will essentially turn the effect off, so we stop the modulation at 2
		if (currentBufferDivisor < 2.0f)
			currentBufferDivisor = 2.0f;
	}

	//-----------------------CALCULATE THE MINIBUFFER SIZE-------------------------
	// this is not a new forced buffer starting up
	if (writePos > 0)
	{
		// if it's allowed, update the minibuffer size midway through this forced buffer
		if (onOffTest(fBufferInterrupt))
			minibufferSize = (long) ( (float)currentForcedBufferSize / currentBufferDivisor );
		// if it's the last minibuffer, then fill up the forced buffer to the end 
		// by extending this last minibuffer to fill up the end of the forced buffer
		long remainingForcedBuffer = currentForcedBufferSize - writePos;
		if ( (minibufferSize*2) >= remainingForcedBuffer )
			minibufferSize = remainingForcedBuffer;
	}
	// this is a new forced buffer just beginning, act accordingly, do bar sync if necessary
	else {
		// because there isn't really any division (given my implementation) when the divisor is < 2
		if (currentBufferDivisor < 2.0f) minibufferSize = currentForcedBufferSize;
		else minibufferSize = (long) ( (float)currentForcedBufferSize / currentBufferDivisor );
	}

	//-----------------------CALCULATE SMOOTHING DURATION-------------------------
	// no smoothing if the previous forced buffer wasn't divided
	if (!doSmoothing)
		smoothcount = smoothDur = 0;
	else
	{
		smoothDur = (long) (fSmooth * (float)minibufferSize);
		long maxSmoothDur;
		// if we're just starting a new forced buffer, 
		// then the samples beyond the end of the previous one are not valid
		if (writePos <= 0)
			maxSmoothDur = prevForcedBufferSize - prevMinibufferSize;
		// otherwise just make sure that we don't go outside of the allocated arrays
		else
			maxSmoothDur = SUPER_MAX_BUFFER - prevMinibufferSize;
		if (smoothDur > maxSmoothDur)
			smoothDur = maxSmoothDur;
		smoothcount = smoothDur;
		smoothStep = 1.0f / (float)(smoothDur+1);	// the gain increment for each smoothing step

		fadeOutGain = cosf(PI/(float)(4*smoothDur));
		fadeInGain = sinf(PI/(float)(4*smoothDur));
		realFadePart = (fadeOutGain * fadeOutGain) - (fadeInGain * fadeInGain);	// cosf(3.141592/2/n)
		imaginaryFadePart = 2.0f * fadeOutGain * fadeInGain;	// sinf(3.141592/2/n)
	}
}

void bufferoverride::m_signal(int n, float *const *in, float *const *out) {
	// directly move everything to the vst part	
	doTheProcess((float **)in, (float **)out, (long)n, true);
}  // end m_signal


void bufferoverride::m_help() {
}

//---------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
void bufferoverride::doTheProcess(float **inputs, float **outputs, long sampleFrames, bool replacing)
{

//-------------------------SAFETY CHECK----------------------

	// there must have not been available memory or something (like WaveLab goofing up), 
	// so try to allocate buffers now
	if ((buffer1 == NULL)) createAudioBuffers();

	// if the creation failed, then abort audio processing
	if (buffer1 == NULL) return;


//-------------------------INITIALIZATIONS----------------------
	// this is a handy value to have during LFO calculations & wasteful to recalculate at every sample
	numLFOpointsDivSR = NUM_LFO_POINTS_FLOAT / SAMPLERATE;
	divisorLFO->pickTheLFOwaveform();
	bufferLFO->pickTheLFOwaveform();

	// calculate this scaler value to minimize calculations later during processOutput()
//	float inputGain = 1.0f - fDryWetMix;
//	float outputGain = fDryWetMix;
	float inputGain = sqrtf(1.0f - fDryWetMix);
	float outputGain = sqrtf(fDryWetMix);


//-----------------------TEMPO STUFF---------------------------
	// figure out the current tempo if we're doing tempo sync
	if ( onOffTest(fBufferTempoSync) || 
			(onOffTest(divisorLFO->fTempoSync) || onOffTest(bufferLFO->fTempoSync)) )
	{
		// calculate the tempo at the current processing buffer
		if ( (fTempo > 0.0f) || (hostCanDoTempo != 1) )	// get the tempo from the user parameter
		{
			currentTempoBPS = tempoScaled(fTempo) / 60.0f;
			needResync = false;	// we don't want it true if we're not syncing to host tempo
		}
		else	// get the tempo from the host
		{
/*			timeInfo = getTimeInfo(kBeatSyncTimeInfoFlags);
			if (timeInfo)
			{
				if (kVstTempoValid & timeInfo->flags)
					currentTempoBPS = (float)timeInfo->tempo / 60.0f;
				else
					currentTempoBPS = tempoScaled(fTempo) / 60.0f;
//				currentTempoBPS = ((float)tempoAt(reportCurrentPosition())) / 600000.0f;
				// but zero & negative tempos are bad, so get the user tempo value instead if that happens
				if (currentTempoBPS <= 0.0f)
					currentTempoBPS = tempoScaled(fTempo) / 60.0f;
				//
				// check if audio playback has just restarted & reset buffer stuff if it has (for measure sync)
				if (timeInfo->flags & kVstTransportChanged)
				{
					needResync = true;
					currentForcedBufferSize = 1;
					writePos = 1;
					minibufferSize = 1;
					prevMinibufferSize = 0;
					smoothcount = smoothDur = 0;
				}
			}
			else	// do the same stuff as above if the timeInfo gets a null pointer
			{
*/				currentTempoBPS = tempoScaled(fTempo) / 60.0f;
				needResync = false;	// we don't want it true if we're not syncing to host tempo
//			}
		}
	}


//-----------------------AUDIO STUFF---------------------------
	// here we begin the audio output loop, which has two checkpoints at the beginning
	for (long samplecount = 0; (samplecount < sampleFrames); samplecount++)
	{
		// check if it's the end of this minibuffer
		if (readPos >= minibufferSize)
			updateBuffer(samplecount);

		// store the latest input samples into the buffers
		buffer1[writePos] = inputs[0][samplecount];

		// get the current output without any smoothing
		float out1 = buffer1[readPos];

		// and if smoothing is taking place, get the smoothed audio output
		if (smoothcount > 0)
		{
			out1 = (out1 * fadeInGain) + (buffer1[readPos+prevMinibufferSize] * fadeOutGain);

			smoothcount--;

			fadeInGain = (fadeOutGain * imaginaryFadePart) + (fadeInGain * realFadePart);
			fadeOutGain = (realFadePart * fadeOutGain) - (imaginaryFadePart * fadeInGain);
		}

		// write the output samples into the output stream
		if (replacing)
		{
			outputs[0][samplecount] = (out1 * outputGain) + (inputs[0][samplecount] * inputGain);
		}
		else
		{
			outputs[0][samplecount] += (out1 * outputGain) + (inputs[0][samplecount] * inputGain);
		}

		// increment the position trackers
		readPos++;
		writePos++;
	}
}