-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdigix0x.ino
More file actions
481 lines (451 loc) · 15.4 KB
/
Copy pathdigix0x.ino
File metadata and controls
481 lines (451 loc) · 15.4 KB
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
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
#include <Arduino.h>
#include "SAMD51_InterruptTimer.h"
// vco variables
float input = 0;
uint32_t saw = 0;
uint16_t saw_last_value = 0;
uint16_t threshold = 0;
uint16_t freq_offset = 0;
uint16_t freq_inv = 0;
uint16_t sq_note = 1;
// filter caps
float cap1 = 0; // lpf c1
float cap2 = 0; // lpf c2
float cap3 = 0; // lpf c3
float cap4 = 0; // lpf c4
float cap_out = 0; // hpf c into diffamp
float cap_reso = 0; // hpf c into reso
float cap_vca1 = 0; // hpf c into vca from diffamp
float cap_vca2 = 0; // hpf c into vca from reso
int16_t output = 0;
float cap_reso2 = 0; // second hpf into reso
// filter resistors C10, 0.00063 .021, T=5us
float k0 = 1.47;
float k1 = 0.2876302;
float k2 = 0.0797963;
float k3 = 0.0654076;
float k4 = 0.0072146;
float k5 = 0.0013524;
// filter parameters
#define HPF_Q 0.4
#define HPF_W 8.0 // in Hz
#define Ts 0.000005 // sample period in seconds
float k6 = Ts*6.283*HPF_Q*HPF_W; // hpf r into reso
float k10 = Ts*6.283*HPF_W/HPF_Q; // hpf r into reso2
float k7 = 0.00785; // hpf r into vca from diffamp
float k8 = 0.0204; // hpf r into vca from reso
// vca env variables
float vca_env = 0.0; // main envelope output
float vca_env_decay = 0.99991; // stock is 0.99991
uint8_t vca_env_timer1 = 0; // timers to set sample rates
uint8_t vca_env_timer2 = 0;
uint8_t vca_env_state = 0; // 0 = off, 1 = attack, 2 = decay, 3 = release (volatile?)
float last_vca_env = 0.0; // storage of last vca_env before release phase
float vca_delay_cap = 0.0; // timing for delay gate->vca
// vcf env variables
float vcf_env = 0.0; // main envelope out
float vcf_env_decay = 0.9972; // 0.9972 is shortest, 0.99978 is longest
uint16_t vcf_env_timer1 = 0; // timer to set sample rate
uint8_t vcf_env_state = 0; // 0 = off, 1 = attack, 2 = decay (volatile?)
// accent variables (used in vcf section)
float accent = 0; // accent knob setting: 0 -> 1
float accent_cap1 = 0; // cap for vcf accent
float accent_cap2 = 0; // cap for vca accent
float accent_vca = 0; // signal to vca
float accent_vcf = 0; // signal to vcf
float k11 = 0.0018; // R from env to accent cap: 0.002 R10, 0.00095 R0, 0.0013 R5, 0.0018 R8, 0.0011 R2
float k12 = 0.00143; // R from accent cap to ground: 0.0018 R10, 0.0011 R0, 0.0014 R5, 0.00165 R8, 0.0012 R2
float k13 = k11/10.0; // R from env to accent cap during attack: 0.0002 for R10,
float k14 = k12/10.0; // R from accent cap to ground durring attack: 0.00018 for R10,
float k15 = 0.0; // mix: 0 R10, 0.39 R0, 0.21 R5, 0 R8, 0.36 R2
// slide variables
float slide_cap = 0;
float slide_resistor = 0.00909; // Ts/22ms for stock - Ts = 10*20us
uint8_t slide_timer = 0xff; // 0xff = off, other values for clocking while on
// controls
uint16_t reso = 10;
uint32_t timer = 0;
uint16_t cv = 120;
float resonance = 0;
float average = 0;
float last_average = 0;
byte timer2 = 0;
int16_t tune = 0;
float accent_pot = 0;
float env_mod_pot = 0;
float cutoff_pot = 0;
uint16_t decay_pot = 0;
uint8_t waveform = 0; // sq(1) or saw(0)
uint16_t overdrive = 0; // filter overdrive
// lookup tables
uint16_t const tanh_table[256] = {
#include "303filt_tanh.h"
};
//int16_t const sine_table[1024] = {
// #include "sinetable.h"
//};
uint16_t const note_table[4096] = {
#include "midi_note_table_48k_invert3.h"
};
int16_t const saw_edge[2048] = {
#include "saw_edge2.h"
};
float const cv_table[6*1024] = {
#include "303filt_coeff_5us.h"
};
float decay_table[1024] = {
#include "303_vcf_decay_table.h"
};
int16_t const sq_wavs[64][1024] = {
#include "sqwavs.h"
};
uint8_t sequence[16] = {
23 | 1<<6 | 0 << 7, // midi note -12 | accent | slide
23 | 0<<6 | 0 << 7,
23 | 1<<6 | 0 << 7,
23 | 1<<6 | 0 << 7,
23 | 1<<6 | 1 << 7,
23 | 0<<6 | 0 << 7,
21 | 1<<6 | 1 << 7,
21 | 0<<6 | 0 << 7,
21 | 1<<6 | 1 << 7,
21 | 0<<6 | 0 << 7,
21 | 0<<6 | 0 << 7,
21 | 0<<6 | 0 << 7,
30 | 1<<6 | 1 << 7,
30 | 0<<6 | 0 << 7,
21 | 0<<6 | 0 << 7,
21 | 0<<6 | 0 << 7
};
//uint8_t sequence[16] = {
// 11 | 1<<6 | 1 << 7, // midi note -23 | accent | slide
// 11 | 0<<6 | 1 << 7,
// 11 | 1<<6 | 1 << 7,
// 11 | 1<<6 | 1 << 7,
// 11 | 1<<6 | 1 << 7,
// 11 | 0<<6 | 1 << 7,
// 11 | 1<<6 | 1 << 7,
// 11 | 0<<6 | 1 << 7,
// 11 | 1<<6 | 1 << 7,
// 11 | 0<<6 | 1 << 7,
// 11 | 0<<6 | 1 << 7,
// 11 | 0<<6 | 1 << 7,
// 11 | 1<<6 | 1 << 7,
// 11 | 0<<6 | 1 << 7,
// 11 | 0<<6 | 1 << 7,
// 11 | 0<<6 | 1 << 7
//};
// sequencer
uint32_t tempo = 500; // step clock, T_step/(12*T_interrupt), T_step = 60s/BPM
uint32_t tempo_counter = 0;
uint8_t step_counter = 0;
uint8_t current_step = 0;
uint8_t sequence_length = 16;
uint8_t current_note = 0;
int16_t current_note_value = 0;
uint32_t freq = 100;
int max_value = 0;
float a = 2.7939677E-10;
float b = 6.07153216E-20;
float c = 5.573775612E-30;
void setup() {
// Serial.begin(1000000);
// delay(4000);
// Serial.println("begin");
// // put your setup code here, to run once:
pinMode(13, OUTPUT);
pinMode(12, INPUT_PULLUP);
TC.startTimer(21, myISR); // 20 usec
}
int i = 0;
void loop() {
// read control interface
uint16_t temp7 = analogRead(A2); // reso
resonance = 0.005078125*temp7; // reso 0.005078125 is stock
// i++;
// if (i = 1000) {
// Serial.println(max_value);
// i = 0;
// }
temp7 = 0x03ff - temp7; // accent half of reso pot is reversed
// accent variables set by resonance
k15 = 0.000385*temp7;
k14 = 0.000168 - 0.000000045*temp7;
k13 = 0.000165 - 0.0000000675*temp7;
k12 = 10.0*k14;
k11 = 10.0*k13;
temp7 = analogRead(A3); // cutoff
cutoff_pot = 0.332*temp7 + 328;
env_mod_pot = 0.16667 + 0.0008138*analogRead(A5); // env mod
// float temp9 = 615*(vcf_env - 0.326)*(0.16667 + 0.0008138*temp8);
// float temp9 = 615*(0 - 0.326)*(0.16667 + 0.0008138*temp8);
// temp7 += (int16_t)temp9;
// if (temp7 > 1023) temp7 = 1023;
// if (temp7 < 0) temp7 = 0;
// cv = 6*temp7;
decay_pot = analogRead(A4); // decay
// vcf_env_decay = decay_table[temp7];
// temp7 = analogRead(A1); // accent
// accent_pot = 0.0009775*temp7;
waveform = digitalRead(12); // check for saw or sq
}
void myISR() {
PORT->Group[0].OUTSET.reg = 1<<23;
analogWrite(A0,output - 0x0500);
// PORT->Group[0].OUTCLR.reg = 1<<23;
saw += freq;
int32_t wave;
if (waveform == 1) { // make square
uint16_t temp3 = saw >> 22; // set phase to 1024 steps
uint16_t temp4 = saw >> 6; // get fractional step
wave = ((sq_wavs[sq_note][temp3]*(0x10000-temp4) + sq_wavs[sq_note][((temp3+1)&0x03ff)]*(temp4))>>16);
}
else { // make saw
wave = (saw >> 16);
uint16_t phase = wave;
phase += freq_offset;
if (phase < threshold) {
int16_t temp1 = wave;
wave -= saw_edge[(((temp1*freq_inv) >> 13) + 1024)];
}
wave = 0x7fff - wave;
}
analogWrite(A1,(wave >> 5) + 0x0800);
// input is +/-2.5V = 0.333*+/-32k
// int input = sine_table[(saw>>22)]>>4; // was 0.333*, and gave good resonance results
// int16_t temp5 = saw >> 18;
// input += (float)(temp4 - saw_last_value);
// saw_last_value = temp4;
// input -= k9*input;
int temp3 = (int)((wave>>1) - cap_reso2);
// if (temp3 > max_value) max_value = temp3;
if (temp3 > 0xfe00) temp3 = 0xfe00;
else if (temp3 < -65000) temp3 = -65000;
uint16_t temp4 = (abs(temp3) >> 8)&0x00ff;
if (temp3 < 0) {
temp3 &= 0x000000ff;
temp3 = 0 - ((tanh_table[temp4]*(256-temp3) + tanh_table[temp4+1]*(temp3))>>8);
}
else {
temp3 &= 0x000000ff;
temp3 = ((tanh_table[temp4]*(256-temp3) + tanh_table[temp4+1]*(temp3))>>8);
}
// float temp3 = (float)((saw>>17) - cap_reso2);
// if (temp3 > 64300) temp3 = 64300;
// else if (temp3 < -64300) temp3 = -64300;
// float temp12 = temp3*temp3;
// temp3 = 2*(temp3*(1-temp12*(a - temp12*(b - c*temp12))));
for (int i = 0; i < 4; i++) {
// main filter section - 4x lowpass followed by 3x highpass
cap1 += k1*(k0*temp3 - (cap1 + cap2));
cap2 += k2*cap1;
cap3 += k3*(cap2 - (cap3 + cap4));
float temp1 = k4*cap3;
cap4 += temp1;
temp1 -= k5*cap_out;
cap_out += temp1;
float temp2 = resonance*temp1 - k6*(cap_reso - cap_reso2); // int is the reso amt
cap_reso += temp2;
cap_reso2 += temp2 - k10*cap_reso2;
// average += cap_out;
}
average = 4*cap_out;
// PORT->Group[0].OUTCLR.reg = 1<<23;
float temp1 = (average - last_average);
last_average = average;
average = 0;
cap_vca1 += temp1 - k7*cap_vca1;
cap_vca2 += temp1 - k8*cap_vca2;
// output = (int16_t)((last_average/4) + 0x8000) >> 4;
// output = (int16_t)((vca_env*(cap_vca1 + 0.42*resonance*cap_vca2)/4)+0x8000)>>4;// 0, 0.51, 1.65, 2.2
// output = (int16_t)(vcf_env*65536)>>4;
output = (int16_t)(((vca_env + 2.855f*accent_vca)*(cap_vca1 + 0.42f*resonance*cap_vca2)*0.04f)+0x8000)>>4;
// update filter parameters
timer2++;
if (timer2 == 3) {
int16_t temp5 = (int16_t)(cutoff_pot + 615*(vcf_env - 0.326f)*env_mod_pot + 1353*accent_vcf);
if (temp5 < 0) temp5 = 0;
if (temp5 > 1023) temp5 = 1023;
temp5 *= 6;
k1 = cv_table[temp5];
temp5++;
k2 = cv_table[temp5];
temp5++;
k3 = cv_table[temp5];
temp5++;
k4 = cv_table[temp5];
temp5++;
k5 = cv_table[temp5];
temp5++;
k0 = cv_table[temp5];
timer2 = 0;
}
// vcf envelope generation
if (vcf_env_state == 0) vcf_env = 0; // off phase, do nothing
else if (vcf_env_state == 1) { // attack phase
vcf_env += 0.15f*(1- vcf_env); // attack time constant of 0.15
if (vcf_env > .99998f) { // check if at max
vcf_env = 0.99998f;
vcf_env_state = 2;
vcf_env_timer1 = 0;
}
// accent: reso->vcf
float temp2 = accent*vcf_env - 0.362f - accent_cap1; // signal into cap
if (temp2 > 0) {
accent_cap1 += k13*temp2;
accent_cap1 -= k14*accent_cap1;
accent_vcf = k15*temp2 + accent_cap1;
}
else {
accent_cap1 -= k14*accent_cap1;
accent_vcf = accent_cap1;
}
// accent: vcf->vca
accent_cap2 += 0.0404f*(accent*vcf_env - accent_cap2); // slight low pass
if (accent_cap2 > 0.09f) accent_vca = accent_cap2 - 0.09f; // diode threshold
else accent_vca = 0;
}
else if (vcf_env_state == 2) { // decay phase
vcf_env_timer1++;
if (vcf_env_timer1 == 10) { // 1/10 sample rate
vcf_env_timer1 = 0;
vcf_env *= vcf_env_decay; // decay
if (vcf_env < 0.000015f) { // check if at minimum
vcf_env = 0;
vcf_env_state = 3; // release phase for attacks
}
// accent: reso->vcf
float temp2 = accent*vcf_env - 0.362f - accent_cap1; // signal into cap
if (temp2 > 0) {
accent_cap1 += k11*temp2;
accent_cap1 -= k12*accent_cap1;
accent_vcf = k15*temp2 + accent_cap1;
}
else {
accent_cap1 -= k12*accent_cap1;
accent_vcf = accent_cap1;
}
// accent: vcf->vca
// not sure what to do here for the moment
// there is a slow decay to 0.9 that is a very slight difference from whats below
// not sure if its worth the extra effort to figure it out
// if (accent_cap2 > 0.2) accent_cap2 += 0.404*(vcf_env - accent_cap2);
// else accent_cap2 += 0.004*(0.08 - accent_cap2);
accent_cap2 += 0.404f*(accent*vcf_env - accent_cap2); // slight low pass
if (accent_cap2 > 0.09f) accent_vca = accent_cap2 - 0.09f; // diode threshold
else accent_vca = 0;
}
}
else if (vcf_env_state == 3) { // release phase for attack, if needed
vcf_env_timer1++;
if (vcf_env_timer1 == 10) {
vcf_env_timer1 = 0;
// accent: reso->vcf
accent_cap1 -= k12*accent_cap1;
accent_vcf = accent_cap1;
if (accent_vcf < 0.000015f) {
accent_vcf = 0;
vcf_env_state = 0;
}
}
}
// vca envelope generation
if (vca_env_state == 0) { // off phase
vca_env = 0; // set env to zero
vca_delay_cap *= 0.9998f; // keep decaying delay cap
}
else if (vca_env_state == 1) { // attack phase
vca_delay_cap += 0.002f; // charage decay cap
if (vca_delay_cap > 0.4828f) { // if its hit vbe threshold, turn on attack
vca_delay_cap = 0.565f; // reset to max vbe
vca_env *= 1.076f; // do attack phase
vca_env += 0.0076f;
if (vca_env > .99998f) { // check if done attacking
vca_env = 0.99998f; // prep for decay
vca_env_state = 2;
vca_env_timer1 = 0;
vca_env_timer2 = 0;
vca_env_decay = 0.99991f;
}
}
}
else if (vca_env_state == 2) { // decay phase
vca_env_timer1++;
if (vca_env_timer1 == 10) { // 1/10 sample rate for decay
vca_env_timer1 = 0;
vca_env_timer2++;
vca_env *= vca_env_decay;
if (vca_env_timer2 == 100) { // very small addition needed here
vca_env_timer2 = 0; // so 1/1000 sample rate needed
vca_env_decay -= 0.000001f; // limited by float32 resolution
}
if (vca_env < 0.000015f) { // envelope off
vca_env = 0;
vca_env_state = 0;
}
}
}
else if (vca_env_state == 3) { // release phase
vca_delay_cap *= 0.9998f; // decay delay cap
vca_env += 0.004f*(last_vca_env - vca_env); // release is ~ a truncated expo decay
if (vca_env <= 0.000015f) { // envelope off
vca_env = 0;
vca_env_state = 0;
}
}
// process slides
if (slide_timer != 0xff) { // dont bother sliding if off
slide_timer++;
if (slide_timer == 10) {
slide_timer = 0;
slide_cap += slide_resistor*(current_note_value - slide_cap);
uint16_t temp_note = ((uint16_t)slide_cap + tune);
sq_note = (temp_note >> 6) & 0x003f;
uint16_t interim = note_table[temp_note];
freq = interim << 10;
freq_offset = interim >> 3;
threshold = interim >> 2;
freq_inv = note_table[(temp_note + 1)];
}
}
// play pattern sequence
tempo_counter++;
if (tempo_counter >= tempo) {
tempo_counter = 0;
step_counter++;
if (step_counter >= 12) {
step_counter = 0;
current_step++;
if (current_step >= sequence_length) current_step = 0;
uint8_t temp1 = current_note;
current_note = sequence[current_step];
if ((current_note & 0x3f) != 0) current_note_value = (current_note & 0x3f)<<6; // if not a rest
if ((temp1 & 0x80) == 0) { // only attack if prev note had no slide
vca_env_state = 1;
vcf_env_state = 1;
slide_timer = 0xff; // turn off slides
uint16_t temp_note = (current_note_value + tune);
sq_note = (temp_note >> 6) & 0x003f;
uint16_t interim = note_table[temp_note];
freq = interim << 10;
freq_offset = interim >> 3;
threshold = interim >> 2;
freq_inv = note_table[(temp_note + 1)];
slide_cap = current_note_value; // prep in case next note needs slide
}
else slide_timer = 0; // enable slides
if ((current_note & 0x40) == 0) { // check if not accents
accent = 0; // turn off accents
vcf_env_decay = decay_table[decay_pot]; // set decay to pot setting
}
else {
accent = accent_pot; // turn on accents
vcf_env_decay = 0.9972f; // shorten vcf env decay to minimum
}
}
else if (step_counter == 7) {
if ((current_note & 0x80) == 0) vca_env_state = 3; // only turn off notes with no slide
}
}
PORT->Group[0].OUTCLR.reg = 1<<23;
}