summaryrefslogtreecommitdiffstats
path: root/WaveClass3PolyPlusLPF_BETA/Wave.h
blob: a9afd727c75e8e6de7e9ba61554b0d59f0f89844 (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
/*
 * Wave.h
 *
 * Simon Green 2013.
 *
 * Based on Mozzi "Oscil.h"
 */

#ifndef WAVE_H_
#define WAVE_H_

#include "Arduino.h"
#include "mozzi_fixmath.h"
#include <util/atomic.h>

// fractional bits for oscillator index precision
#define OSCIL_F_BITS 16
#define OSCIL_F_BITS_AS_MULTIPLIER 65536
#define NUM_TABLE_CELLS 256
#define ADJUST_FOR_NUM_TABLE_CELLS 8
#define UPDATE_RATE AUDIO_RATE

/** Generate waveform
*/

enum WaveType { WAVE_SAW=0, WAVE_TRI, WAVE_RECT, WAVE_NOISE };

class Wave
{
public:
	/** Constructor. "Wave mywave;" makes a Wave oscillator
	*/
	Wave () {
          phase_fractional = 0;
          phase_increment_fractional = 0;
          wavetype = WAVE_SAW;
          noise = 0xACE1;
          pulse_width = 128;
	}

	/** Increments one step along the phase.
	@return the next value.
	 */
	inline
	char next()
	{
  		incrementPhase();

                char w;
  		//ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
		{
                        int n = (phase_fractional >> OSCIL_F_BITS) & (NUM_TABLE_CELLS-1);
                        switch(wavetype) {
                          case WAVE_SAW:
                            w = n - 128;
                            break;
                          case WAVE_TRI:
                            if (n & 0x80)  // >= 128
                              w = ((n^0xFF)<<1)-128;
                            else 
                              w = (n<<1)-128;
                            break;
                          case WAVE_RECT:
                            if (n > pulse_width)
                              w = 127;
                            else
                              w = -127;
                            break;
                          case WAVE_NOISE:
                            noise = (noise >> 1) ^ (-(noise & 1) & 0xB400u);
                            w = noise>>8;
                            break;
                        }
		}
                return w;
	}

	/** Set the wave type
	 */
	inline
	void setType(WaveType x)
	{
		wavetype = x;
	}

        WaveType getType() { return wavetype; }

	/** Set the pulse width for rectangle wave
	 */
	inline
	void setPulseWidth(unsigned char x)
	{
		pulse_width = x;
	}

	/** Set the oscillator frequency with an unsigned int. This is faster than using a
	float, so it's useful when processor time is tight, but it can be tricky with
	low and high frequencies, depending on the size of the wavetable being used. If
	you're not getting the results you expect, try explicitly using a float, or try
	setFreq_Q24n8() or or setFreq_Q16n16().
	@param frequency to play the wave table.
	*/
	inline
	void setFreq (unsigned int frequency) {
		ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
		{
			phase_increment_fractional = ((((unsigned long)NUM_TABLE_CELLS<<ADJUST_FOR_NUM_TABLE_CELLS)*frequency)/UPDATE_RATE) << (OSCIL_F_BITS - ADJUST_FOR_NUM_TABLE_CELLS);
		}
	}
	
	
	/** Set the oscillator frequency with a float. Using a float is the most reliable
	way to set frequencies, -Might- be slower than using an int but you need either
	this, setFreq_Q24n8() or setFreq_Q16n16() for fractional frequencies.
	@param frequency to play the wave table.
	 */
	inline
	void setFreq(float frequency)
	{ // 1 us - using float doesn't seem to incur measurable overhead with the oscilloscope
		ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
		{
			phase_increment_fractional = (unsigned long)((((float)NUM_TABLE_CELLS * frequency)/UPDATE_RATE) * OSCIL_F_BITS_AS_MULTIPLIER);
		}
	}
	
	
	/** Set the frequency using Q24n8 fixed-point number format.
	This might be faster than the float version for setting low frequencies such as
	1.5 Hz, or other values which may not work well with your table size. A Q24n8
	representation of 1.5 is 384 (ie. 1.5 * 256). Can't be used with UPDATE_RATE
	less than 64 Hz.
	@param frequency in Q24n8 fixed-point number format.
	*/
	inline
	void setFreq_Q24n8(Q24n8 frequency)
	{
		ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
		{
			//phase_increment_fractional = (frequency* (NUM_TABLE_CELLS>>3)/(UPDATE_RATE>>6)) << (F_BITS-(8-3+6));
			phase_increment_fractional = (((((unsigned long)NUM_TABLE_CELLS<<ADJUST_FOR_NUM_TABLE_CELLS)>>3)*frequency)/(UPDATE_RATE>>6)) 
				<< (OSCIL_F_BITS - ADJUST_FOR_NUM_TABLE_CELLS - (8-3+6));
		}
	}


	/** Set the frequency using Q16n16 fixed-point number format. This is useful in
	combination with Q16n16_mtof(), a fast alternative to mtof(), using Q16n16
	fixed-point format instead of floats.  Note: this should work OK with tables 2048 cells or smaller and
	frequencies up to 4096 Hz.  Can't be used with UPDATE_RATE less than 64 Hz.
	@param frequency in Q16n16 fixed-point number format.
	*/
	inline
	void setFreq_Q16n16(Q16n16 frequency)
	{
		ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
		{
			//phase_increment_fractional = ((frequency * (NUM_TABLE_CELLS>>7))/(UPDATE_RATE>>6)) << (F_BITS-16+1);
			phase_increment_fractional = (((((unsigned long)NUM_TABLE_CELLS<<ADJUST_FOR_NUM_TABLE_CELLS)>>7)*frequency)/(UPDATE_RATE>>6)) 
				<< (OSCIL_F_BITS - ADJUST_FOR_NUM_TABLE_CELLS - 16 + 1);

		}
	}

private:
	/** Increments the phase of the oscillator without returning a sample.
	 */
	inline
	void incrementPhase()
	{
		//phase_fractional += (phase_increment_fractional | 1); // odd phase incr, attempt to reduce frequency spurs in output
		phase_fractional += phase_increment_fractional;
	}

	unsigned long phase_fractional;
	volatile unsigned long phase_increment_fractional;

        unsigned char pulse_width;
        uint16_t noise;
        
        WaveType wavetype;
};

#endif /* WAVE_H_ */