-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathuno-synth.ino
193 lines (159 loc) · 5.92 KB
/
uno-synth.ino
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
/*
Auduino Lo-Fi granular synthesiser
by Peter Knight, https://create.arduino.cc/projecthub/GroupGets/5potsynth-b3506d
Adapted by Anderson Costa for the Wokwi simulator
https://wokwi.com/arduino/projects/299483632784900621
Early stage (experimental)
Analog in 0: Grain 1 pitch
Analog in 1: Grain 1 decay
Analog in 2: Grain 2 pitch
Analog in 3: Grain 2 decay
Analog in 4: Grain repetition frequency
Digital in 4: Switch stepped mapping (LOW = chromatic / HIGH = pentatonic)
Digital in 7: Switch smooth mapping (LOW = logarithmic)
Digital out 3: Audio signal
Digital out 5: LED indication of repetition frequency
*/
#include <avr/io.h>
#include <avr/interrupt.h>
// Map Analogue channels
#define GRAIN_FREQ_CONTROL A0
#define GRAIN_DECAY_CONTROL A1
#define GRAIN2_FREQ_CONTROL A2
#define GRAIN2_DECAY_CONTROL A3
#define SYNC_CONTROL A4
// Map Digital pins
#define PWM_PIN 3
#define LED_BIT 5
#define SW1_PIN 4
#define SW2_PIN 7
#define PWM_VALUE OCR2B
#define PWM_INTERRUPT TIMER2_OVF_vect
// Variables
uint16_t syncPhaseAcc;
uint16_t syncPhaseInc;
uint16_t grainPhaseAcc;
uint16_t grainPhaseInc;
uint16_t grainAmp;
uint8_t grainDecay;
uint16_t grain2PhaseAcc;
uint16_t grain2PhaseInc;
uint16_t grain2Amp;
uint8_t grain2Decay;
// Smooth logarithmic mapping
//
uint16_t antilogTable[] = {
64830, 64132, 63441, 62757, 62081, 61413, 60751, 60097, 59449, 58809, 58176, 57549, 56929, 56316, 55709, 55109,
54515, 53928, 53347, 52773, 52204, 51642, 51085, 50535, 49991, 49452, 48920, 48393, 47871, 47356, 46846, 46341,
45842, 45348, 44859, 44376, 43898, 43425, 42958, 42495, 42037, 41584, 41136, 40693, 40255, 39821, 39392, 38968,
38548, 38133, 37722, 37316, 36914, 36516, 36123, 35734, 35349, 34968, 34591, 34219, 33850, 33486, 33125, 32768
};
uint16_t mapPhaseInc(uint16_t input) {
return (antilogTable[input & 0x3f]) >> (input >> 6);
}
// Stepped chromatic mapping
//
uint16_t midiTable[] = {
17, 18, 19, 20, 22, 23, 24, 26, 27, 29, 31, 32, 34, 36, 38, 41, 43, 46, 48, 51, 54, 58, 61, 65, 69, 73,
77, 82, 86, 92, 97, 103, 109, 115, 122, 129, 137, 145, 154, 163, 173, 183, 194, 206, 218, 231,
244, 259, 274, 291, 308, 326, 346, 366, 388, 411, 435, 461, 489, 518, 549, 581, 616, 652, 691,
732, 776, 822, 871, 923, 978, 1036, 1097, 1163, 1232, 1305, 1383, 1465, 1552, 1644, 1742,
1845, 1955, 2071, 2195, 2325, 2463, 2610, 2765, 2930, 3104, 3288, 3484, 3691, 3910, 4143,
4389, 4650, 4927, 5220, 5530, 5859, 6207, 6577, 6968, 7382, 7821, 8286, 8779, 9301, 9854,
10440, 11060, 11718, 12415, 13153, 13935, 14764, 15642, 16572, 17557, 18601, 19708, 20879,
22121, 23436, 24830, 26306
};
uint16_t mapMidi(uint16_t input) {
return (midiTable[(1023 - input) >> 3]);
}
// Stepped Pentatonic mapping
//
uint16_t pentatonicTable[54] = {
0, 19, 22, 26, 29, 32, 38, 43, 51, 58, 65, 77, 86, 103, 115, 129, 154, 173, 206, 231, 259, 308, 346,
411, 461, 518, 616, 691, 822, 923, 1036, 1232, 1383, 1644, 1845, 2071, 2463, 2765, 3288,
3691, 4143, 4927, 5530, 6577, 7382, 8286, 9854, 11060, 13153, 14764, 16572, 19708, 22121, 26306
};
uint16_t mapPentatonic(uint16_t input) {
uint8_t value = (1023 - input) / (1024 / 53);
return (pentatonicTable[value]);
}
void audioOn() {
// Set up PWM to 31.25kHz, phase accurate
TCCR2A = _BV(COM2B1) | _BV(WGM20);
TCCR2B = _BV(CS20);
TIMSK2 = _BV(TOIE2);
}
void setup() {
Serial.begin(9600);
pinMode(GRAIN_FREQ_CONTROL, INPUT);
pinMode(GRAIN2_DECAY_CONTROL, INPUT);
pinMode(GRAIN_DECAY_CONTROL, INPUT);
pinMode(GRAIN2_FREQ_CONTROL, INPUT);
pinMode(SYNC_CONTROL, INPUT);
pinMode(SW1_PIN, INPUT_PULLUP);
pinMode(SW2_PIN, INPUT_PULLUP);
pinMode(PWM_PIN, OUTPUT);
pinMode(LED_BIT, OUTPUT);
// audioOn();
}
// The loop is pretty simple
// it just updates the parameters for the oscillators
void loop() {
// Switch stepped mapping (LOW = chromatic / HIGH = pentatonic)
if (digitalRead(SW1_PIN) == LOW) {
// Stepped mapping to MIDI notes: C, Db, D, Eb, E, F...
syncPhaseInc = mapMidi(analogRead(SYNC_CONTROL));
} else {
// Stepped pentatonic mapping: D, E, G, A, B
syncPhaseInc = mapPentatonic(analogRead(SYNC_CONTROL));
}
// Switch stepped mapping (LOW = Smooth logarithmic)
if (digitalRead(SW2_PIN) == LOW) {
// Smooth frequency mapping
syncPhaseInc = mapPhaseInc(analogRead(SYNC_CONTROL)) / 4;
}
// Updates the phase & decay for the oscillators
grainPhaseInc = mapPhaseInc(analogRead(GRAIN_FREQ_CONTROL)) / 2;
grainDecay = analogRead(GRAIN_DECAY_CONTROL) / 8;
grain2PhaseInc = mapPhaseInc(analogRead(GRAIN2_FREQ_CONTROL)) / 2;
grain2Decay = analogRead(GRAIN2_DECAY_CONTROL) / 4;
// Send audio signal to output
tone(PWM_PIN, signal());
}
// SIGNAL(PWM_INTERRUPT)
uint16_t signal()
{
uint8_t value;
uint16_t output;
syncPhaseAcc += syncPhaseInc;
if (syncPhaseAcc < syncPhaseInc) {
// Time to start the next grain
grainPhaseAcc = 0;
grainAmp = 0x7fff;
grain2PhaseAcc = 0;
grain2Amp = 0x7fff;
// Faster than using digitalWrite
PORTD ^= 1 << LED_BIT;
}
// Increment the phase of the grain oscillators
grainPhaseAcc += grainPhaseInc;
grain2PhaseAcc += grain2PhaseInc;
// Convert phase into a triangle wave
value = (grainPhaseAcc >> 7) & 0xff;
if (grainPhaseAcc & 0x8000) value = ~value;
// Multiply by current grain amplitude to get sample
output = value * (grainAmp >> 8);
// Repeat for second grain
value = (grain2PhaseAcc >> 7) & 0xff;
if (grain2PhaseAcc & 0x8000) value = ~value;
output += value * (grain2Amp >> 8);
// Make the grain amplitudes decay by a factor every sample (exponential decay)
grainAmp -= (grainAmp >> 8) * grainDecay;
grain2Amp -= (grain2Amp >> 8) * grain2Decay;
// Scale output to the available range, clipping if necessary
output >>= 9;
if (output > 255) output = 255;
// Output to PWM (this is faster than using analogWrite)
// PWM_VALUE = output;
return output;
}