-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtonearm.ino
205 lines (154 loc) · 4.83 KB
/
tonearm.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
194
195
196
197
198
199
200
201
202
203
204
/*
* Project Adventures of Cem on the Wheels of Steel
* Description: Inspired by Per Holmquist's 'Beat Blox' project, I'm trying to develop my own MIDI controller that uses 4 Adafruit APDS9960 proximity sensors that allows users to create their own beats.
* The sequence created physically on a rotating disk; a wheel of steel, will be converted to MIDI information and fed to a Digital Audio Workstation such as Ableton Live through USB to be turned and be converted into music.
* Components: Adafruit APDS9960, SparkFun PID 13906
* Author: Cem Ergin
* Date: May 10th 2019
*/
// MIDI USB Dependencies
#include <pitchToFrequency.h>
#include <pitchToNote.h>
#include <frequencyToNote.h>
#include <MIDIUSB_Defs.h>
#include <MIDIUSB.h>
// Proximity Sensor Dependencies
#include <Adafruit_APDS9960.h>
// DEBUG
#define SHOULD_DEBUG 1
// NUMBER OF CHANNELS
#define NUM_CHANNELS 4
// VARIABLES FOR SELECTOR OUTPUT
#define SELECTOR_ONE 4
#define SELECTOR_TWO 5
// FIRST NOTE IN SERIES
const int NOTE_ONE = 60;
// APDS9960 Instant
Adafruit_APDS9960 apds;
int proximityThreshold = 9; // Optimize to taste
// Note On Off Variables
int colorOffset = 0;
// Sensor Index to switch between sensors
int sensorIndex = 0;
bool noteOn[NUM_CHANNELS];
int midiNotes[NUM_CHANNELS * 2];
int lastNote[NUM_CHANNELS];
// Variables for Color Sensing
uint16_t red, green, blue, clear_light;
void setup() {
// Digital Pin Setup
pinMode(SELECTOR_ONE, OUTPUT);
pinMode(SELECTOR_TWO, OUTPUT);
// Start Serial Output
Serial.begin(57600);
// Welcome Message
if(SHOULD_DEBUG) {
Serial.println("Adventures on the Wheels of Steel");
Serial.println("Have fun and don't forget to make some noise!");
}
// Setup Sensors
setupSensors();
// Initialize Note On Off Arrays
setupNotes();
}
void loop() {
// Reset colorOffset for each loop
colorOffset = 0;
// Check proximity reading
int prox = apds.readProximity();
if ( prox > proximityThreshold ) {
if(!noteOn[sensorIndex]) {
// Object got close, start note
noteOn[sensorIndex] = true;
// Check color of object
apds.getColorData(&red, &green, &blue, &clear_light);
// If more blue than red play variation
if (blue > red) {
colorOffset = NUM_CHANNELS;
}
// Record last note played for note off
lastNote[sensorIndex] = midiNotes[sensorIndex + colorOffset];
// Send note on MIDI message
midiEventPacket_t noteOn = {0x09, 0x90, lastNote[sensorIndex], 100};
MidiUSB.sendMIDI(noteOn);
MidiUSB.flush();
}
} else {
if (noteOn[sensorIndex]) {
// Object got away, end note
noteOn[sensorIndex] = false;
// Send note off MIDI message
midiEventPacket_t noteOff = {0x09, 0x80, lastNote[sensorIndex], 0};
MidiUSB.sendMIDI(noteOff);
MidiUSB.flush();
}
}
incrementSensorIndex();
}
// Sets up the noteOn, midiNote and lastNote arrays depending on NUM_CHANNELS
void setupNotes() {
if(SHOULD_DEBUG) {
Serial.println("Initializing MIDI Channels");
}
for ( int i = 0; i < NUM_CHANNELS; i++) {
noteOn[i] = false;
midiNotes[i] = NOTE_ONE + i;
lastNote[i] = NOTE_ONE + i;
midiNotes[i + NUM_CHANNELS] = NOTE_ONE + i + NUM_CHANNELS;
if(SHOULD_DEBUG) {
Serial.println("Channel #" + String(i) + " Notes: " + String(midiNotes[i]) + " & " + String(midiNotes[i+NUM_CHANNELS]));
}
}
return;
}
void setupSensors() {
if(SHOULD_DEBUG) {
Serial.println("Setting up APDS9960 Sensors");
}
// Reset sensorIndex just in case
resetSensorIndex();
selectSensor(sensorIndex);
delay(10);
for ( int i = 0; i < NUM_CHANNELS; i++) {
if(!apds.begin()){
if(SHOULD_DEBUG){
Serial.println("Failed to initialize Sensor # " + String(sensorIndex) + " !");
}
} else {
Serial.println("Sensor #" + String(sensorIndex) + " initialized!");
}
apds.enableProximity(true); // Enable proximity mode
apds.enableColor(true); // Enable color mode
incrementSensorIndex(); // Increement sensor index to setup next sensor
}
return;
}
void resetSensorIndex() {
sensorIndex = 0;
}
// Selects sensor to read using SparkFun PID 13906
// Additional code is needed to allow more than 4 sensors to work
void selectSensor(int i) {
int modulus = i % 2;
int division = i / 2;
if (modulus >= 1) {
digitalWrite(SELECTOR_ONE, HIGH);
} else {
digitalWrite(SELECTOR_ONE, LOW);
}
if (division > 0) {
digitalWrite(SELECTOR_TWO, HIGH);
} else {
digitalWrite(SELECTOR_TWO, LOW);
}
}
void incrementSensorIndex() {
// Increment Sensor Index
sensorIndex++;
// If sensorIndex greater than number of channels reset
if ( sensorIndex >= NUM_CHANNELS ) {
resetSensorIndex();
}
// Break the news to the selector chip
selectSensor(sensorIndex);
}