aboutsummaryrefslogtreecommitdiff
path: root/dfx-library/lfo.h
blob: 0c743c7f61b99e0918e85c47427919f4ebaf3dce (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
/*------------------- by Marc Poirier  ][  January 2002 ------------------*/

#ifndef __lfo
#define __lfo

#include <math.h>
#include <stdlib.h>

#include "dfxmisc.h"
#include "TempoRateTable.h"


//-------------------------------------------------------------------------------------
// these are the 8 LFO waveforms:
enum
{
	kSineLFO,
	kTriangleLFO,
	kSquareLFO,
	kSawLFO,
	kReverseSawLFO,
	kThornLFO,
	kRandomLFO,
	kRandomInterpolatingLFO,

	numLFOshapes
};

//-------------------------------------------------------------------------------------
// constants & macros

#define LFOshapeScaled(A)   (paramSteppedScaled((A), numLFOshapes))
#define LFOshapeUnscaled(A)   (paramSteppedUnscaled((A), numLFOshapes))

#define NUM_LFO_POINTS 512
const float NUM_LFO_POINTS_FLOAT = (float)NUM_LFO_POINTS;	// to reduce casting later on
const float LFO_TABLE_STEP = 1.0f / (float)NUM_LFO_POINTS;	// to reduce division & encourage multiplication
const long SQUARE_HALF_POINT = NUM_LFO_POINTS / 2;	// the point in the table when the square waveform drops to zero

#define LFO_SMOOTH_DUR 48
const float LFO_SMOOTH_STEP = 1.0f / (float)LFO_SMOOTH_DUR;

// this scales the return of processLFO() from 0.0 - 1.0 output to 0.0 - 2.0 (oscillating around 1.0)
#define processLFOzero2two(A)   ( ((A)->processLFO() * 2.0f) - (A)->fDepth + 1.0f );


//----------------------------------------------------------------------------- 
class LFO
{
public:
	LFO();
	~LFO();

	void reset();
	void fillLFOtables();

	void pickTheLFOwaveform();
	void getShapeName(char *nameString);

	void syncToTheBeat(long samplesToBar);

	// the LFO waveform tables
	float *sineTable, *triangleTable, *squareTable, *sawTable, *reverseSawTable, *thornTable;

	// the following are intended to be used as 0.0 - 1.0 VST parameter values:
	float fOnOff;	// parameter value for turning the LFO on or off
	float fTempoSync;	// parameter value for toggling tempo sync
	float fRate;	// parameter value for LFO rate (in Hz)
	float fTempoRate;	// parameter value for LFO rate (in cycles per beat)
	float fDepth;	// parameter value LFO depth
	float fShape;	// parameter value for LFO shape

	bool onOff;	// in case it's easier to have a bool version of fOnOff
	float position;	// this tracks the position in the LFO table
	float stepSize;	// size of the steps through the LFO table
	float *table;	// pointer to the LFO table
	float randomNumber;	// this stores random values for the random LFO waveforms
	float oldRandomNumber;	// this stores previous random values for the random interpolating LFO waveform
	float cycleRate;	// the rate in Hz of the LFO (only used for first layer LFOs)
	long smoothSamples;	// a counter for the position during a smoothing fade
	long granularityCounter;	// a counter for implementing LFO processing on a block basis
	long granularity;	// the number of samples to wait before processing


	//--------------------------------------------------------------------------------------
	// This function wraps around the LFO table position when it passes the cycle end.
	// It also sets up the smoothing counter if a discontiguous LFO waveform is being used.
	void updatePosition(long numSteps = 1)
	{
		// increment the LFO position tracker
		position += (stepSize * (float)numSteps);

		if (position >= NUM_LFO_POINTS_FLOAT)
		{
			// wrap around the position tracker if it has made it past the end of the LFO table
			position = fmodf(position, NUM_LFO_POINTS_FLOAT);
			// get new random LFO values, too
			oldRandomNumber = randomNumber;
			randomNumber = (float)rand() / (float)RAND_MAX;
			// set up the sample smoothing if a discontiguous waveform's cycle just ended
			switch (LFOshapeScaled(fShape))
			{
				case kSquareLFO     :
				case kSawLFO        :
				case kReverseSawLFO :
				case kRandomLFO     :
					smoothSamples = LFO_SMOOTH_DUR;
				default:
					break;
			}
		}

		// special check for the square waveform - it also needs smoothing at the half point
		else if (LFOshapeScaled(fShape) == kSquareLFO)
		{
			// check to see if it has just passed the halfway point
			if ( ((long)position >= SQUARE_HALF_POINT) && 
				 ((long)(position - stepSize) < SQUARE_HALF_POINT) )
				smoothSamples = LFO_SMOOTH_DUR;
		}
	}

	//--------------------------------------------------------------------------------------
	// this function gets the current 0.0 - 1.0 output value of the LFO & increments its position
	float processLFO()
	{
	  float randiScalar, outValue;
	  int shape = LFOshapeScaled(fShape);

		if (shape == kRandomInterpolatingLFO)
		{
			// calculate how far into this LFO cycle we are so far, scaled from 0.0 to 1.0
			randiScalar = position * LFO_TABLE_STEP;
			// interpolate between the previous random number & the new one
			outValue = (randomNumber * randiScalar) + (oldRandomNumber * (1.0f-randiScalar));
		}
		//
		else if (shape == kRandomLFO)
			outValue = randomNumber;
		//
		else
			outValue = table[(long)position];

		return (outValue * fDepth);
	}

};


#endif