-
Notifications
You must be signed in to change notification settings - Fork 0
/
pbmw.ino
159 lines (129 loc) · 4.56 KB
/
pbmw.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
#include <Arduino.h>
#include <Adafruit_ADS1015.h>
#include <math.h>
#include <usbmidi.h>
Adafruit_ADS1115 adc;
const unsigned char MIDI_OPCODE_PITCH_BEND = 0B1110;
const unsigned char MIDI_OPCODE_CONTROL_CHANGE = 0B1011;
const unsigned char MIDI_CONTROL_CHANGE_MOD_WHEEL = 1;
const unsigned char MIDI_CHANNEL = 0; // "channel 1"
const unsigned char pb_header = (MIDI_OPCODE_PITCH_BEND << 4) + MIDI_CHANNEL; // opcode | channel
const unsigned char mw_header = (MIDI_OPCODE_CONTROL_CHANGE << 4) + MIDI_CHANNEL; // opcode | channel
const uint16_t jitter_thresh = 5;
const float alpha = 0.95; // {0..1} falloff factor for exponential moving average (higher means older values count less)
const uint16_t ring_buffer_size = 64;
unsigned char last_sent_mw = 0;
long t = 0;
struct ring_buffer {
size_t max_size;
size_t size;
size_t head_index;
size_t tail_index;
uint16_t *array;
};
typedef struct ring_buffer ring_buffer;
ring_buffer *ring_buffer_initialize(size_t max_size) {
ring_buffer *rb = (ring_buffer*)malloc(sizeof(ring_buffer));
rb->max_size = max_size;
rb->size = 0;
rb->head_index = 0;
rb->tail_index = 0;
rb->array = (uint16_t*)malloc(max_size * sizeof(uint16_t));
return rb;
}
void ring_buffer_free(ring_buffer *rb) {
free(rb->array);
free(rb);
}
uint16_t ring_buffer_get(ring_buffer *rb, size_t index) {
return rb->array[(rb->head_index + index) % rb->max_size];
}
void ring_buffer_push(ring_buffer *rb, uint16_t value) {
size_t new_head_index =
rb->head_index == 0
? rb->max_size - 1
: rb->head_index - 1;
size_t new_tail_index =
rb->size == rb->max_size
? (rb->tail_index == 0 ? rb->max_size - 1 : rb->tail_index - 1)
: rb->tail_index - 1;
rb->array[new_head_index] = value;
rb->head_index = new_head_index;
rb->tail_index = new_tail_index;
if (rb->size != rb->max_size) { rb->size++; }
}
ring_buffer *pb_samples;
ring_buffer *pb_moving_averages;
ring_buffer *mw_samples;
ring_buffer *mw_moving_averages;
void setup() {
// Serial.begin(9000);
pb_samples = ring_buffer_initialize(64);
mw_samples = ring_buffer_initialize(64);
pb_moving_averages = ring_buffer_initialize(64);
mw_moving_averages = ring_buffer_initialize(64);
adc.setGain(GAIN_TWO);
adc.begin();
}
// f(xₜ):
// { when t = 1 }: x₁
// { when t > 1 }: α * xₜ + (1 - α) * f(x₍ₜ₋₁₎)
//
// where α is a coefficient in {0..1} representing the coefficient of falloff,
// i.e. number of previous samples used, i.e. the cutoff frequency in our
// low-pass filter
//
unsigned int exponential_moving_average(unsigned int x) {
return(alpha * x + (1 - alpha) * ring_buffer_get(pb_moving_averages, 1));
}
bool is_outside_range(unsigned int x, unsigned int lower, unsigned int upper) {
return((x < lower) || (x > upper));
}
bool is_within_range(unsigned int x, unsigned int lower, unsigned int upper) {
return((x >= lower) && (x <= upper));
}
void loop() {
USBMIDI.poll();
uint16_t pb_curr = adc.readADC_SingleEnded(0);
uint16_t mw_curr = adc.readADC_SingleEnded(1);
// for some reason, the ADS1115 reading has an effective resolution of 15 bits, not 16.
// https://github.com/adafruit/Adafruit_ADS1X15/pull/9
//
// if ADC returns a value over (2**15)-1, we know it's an overflow and should "mean" 0.
if (pb_curr > 32767) { pb_curr = 0; }
if (mw_curr > 32767) { mw_curr = 0; }
pb_curr >>= 1; // 15 bit -> 14 bit
mw_curr >>= 1; // 15 bit -> 14 bit
if (t < ring_buffer_size) {
memset(mw_samples->array, mw_curr, mw_samples->max_size);
memset(mw_moving_averages->array, mw_curr, mw_moving_averages->max_size);
t++;
}
ring_buffer_push(mw_samples, mw_curr);
uint16_t mw_ave = exponential_moving_average(mw_curr);
ring_buffer_push(mw_moving_averages, mw_ave);
unsigned char mw_lsb = (mw_curr & 0B0000000001111111);
unsigned char mw_msb = (mw_curr & 0B0011111110000000) >> 7;
if (mw_msb != last_sent_mw) {
USBMIDI.write(mw_header);
USBMIDI.write(MIDI_CONTROL_CHANGE_MOD_WHEEL);
USBMIDI.write(mw_msb);
USBMIDI.flush();
last_sent_mw = mw_msb;
}
// implement a pitch-bend deadzone of +/- 100
if (pb_curr < (8192 + 100) && pb_curr > (8192 - 100)) {
pb_curr = 8192;
}
ring_buffer_push(pb_samples, pb_curr);
uint16_t pb_ave = exponential_moving_average(pb_curr);
ring_buffer_push(pb_moving_averages, pb_ave);
if (pb_curr != ring_buffer_get(pb_samples, 1)) {
unsigned char pb_lsb = (pb_curr & 0B0000000001111111);
unsigned char pb_msb = (pb_curr & 0B0011111110000000) >> 7;
USBMIDI.write(pb_header);
USBMIDI.write(pb_lsb);
USBMIDI.write(pb_msb);
USBMIDI.flush();
}
}