The DroneSynth is a digital synthesizer and MIDI control interface. The project started as coursework for the class Digital Electronics Lab, and developed further into a second iteration. The second prototype, was similar to the first in terms of the software, but featured a more durable control interface. I used this prototype during my participation at the Bang on a Can Musicians' Intensive at the NYU Abu Dhabi Arts Center in February 2017.
The video below shows the first prototype and demonstrates the capabilities of the synth.
This video shows the second prototype being used in an improvised performance/composition.
You can basically think of this project in 2 parts: the software synthesizer in Max and the controller, which consists of the Sensors and Arduino Sketch. Let me talk about the Synthesizer first. Within the patch there are 3 sound waves produced. At the foundation is a sine wave whose pitch is determined by the potentiometer value on the main pot. This base frequency is summed with any harmonics which are added in by turning the corresponding pots (A1, A2, B1, B2, C1, C2). The 6 pots allow you to add up to six harmonics to the base frequency. The harmonics are multiplications of the order 2, 3, 4, 6, 8, 9, and 14x the base frequency.
This summed signal goes through 2 separate processings: through the p littleProcessor and p angelHaze subpatchers.
In p littleProcessor, the signal is processed so that you get the following outputs:
// include the library for the Sharp IR sensor
#include <SharpIR.h>
// ~~~ declaration of variables ~~~ //
// buttons
int buttonPin = 10;
bool buttonStateSetduration = LOW;
int buttonPin2 = 9;
int buttonPin3 = 8;
int buttonPin4 = 7;
int buttonPin5 = 6;
int buttonPin6 = 5;
int buttonPin7 = 4;
int range = 0;
// potentiometer values
int potValBaseFreq;
int potValHarm1;
int potValHarm2;
int potValHarm3;
int potValHarm4;
int potValHarm5;
int potValHarm6;
// Flex resistor variables
const int flexpin = A8;
int finalFlexDifference;
int lastFlex = 0;
int smoothRatioFlex = 5;
//smoothing of flexVal
const int numReadings = 10;
int readingsFlex[numReadings]; // the readings from the analog Flex input
int readIndexFlex = 0; // the index of the current Flex reading
int totalFlex = 0; // the running total of Flex values
int averageFlex = 0; // the average for Flex
// smoothing of IR values
int readingsIR[numReadings]; // the readings from the infrared sensor
int readIndexIR = 0; // the index of the current IR readings
int totalIR = 0; // the running total of IR values
int averageIR = 0; // the average for IR
void setup() {
Serial.begin (9600);
// declare buttonPins as inputs
pinMode(buttonPin, INPUT);
pinMode(buttonPin2, INPUT);
pinMode(buttonPin3, INPUT);
pinMode(buttonPin4, INPUT);
pinMode(buttonPin5, INPUT);
pinMode(buttonPin6, INPUT);
pinMode(buttonPin7, INPUT);
// this setup code is part of the setup for averageing
// initialize all the readings to 0:
for (int thisReading = 0; thisReading < numReadings; thisReading++) {
readingsFlex[thisReading] = 0;
}
for (int thisReading = 0; thisReading < numReadings; thisReading++) {
readingsIR[thisReading] = 0;
}
}
void loop() {
/* buttonStateSetduration is a variable that corresponds
to the first button. Functions differnetly than other buttons in that it just reports
its state, doesn't set an output number like buttons 2 to 7 */
buttonStateSetduration = digitalRead(buttonPin);
// send MidiNoteOn with the arguments (note number, velocity, midi channel).
// velocity is irrelvent since it's not used for anything
usbMIDI.sendNoteOn(buttonStateSetduration, 127, 7);
// if any of the buttons 2 through 7 are pressed send corresponding usbMIDI
if (digitalRead(buttonPin2) == HIGH) {
usbMIDI.sendNoteOn(1, 127, 1);
}
else if (digitalRead(buttonPin3) == HIGH) {
usbMIDI.sendNoteOn(2, 127, 2);
}
else if (digitalRead(buttonPin4) == HIGH)
{
usbMIDI.sendNoteOn(3, 127, 3);
}
else if (digitalRead(buttonPin5) == HIGH) {
usbMIDI.sendNoteOn(4, 127, 4);
}
else if (digitalRead(buttonPin6) == HIGH) {
usbMIDI.sendNoteOn(5, 127, 5);
}
else if (digitalRead(buttonPin7) == HIGH) {
usbMIDI.sendNoteOn(6, 127, 6);
}
// redundant code
/*int controlRange = 1;
usbMIDI.sendMIDInote(controlRange, range, 1);
usbMIDI.sendNoteOn(range, 127, 1);*/
/* ~~ IR sensor section + smoothing ~~ */
SharpIR sharp(A9, 25, 93, 1080);
/*in the form ir, x, y,
ir: the pin where your sensor is attached.
25: the number of readings the library will make before calculating an average distance.
93: the difference between two consecutive measurements to be taken as valid (in %)
model: is an int that determines your sensor: 1080 for GP2Y0A21Y, 20150 for GP2Y0A02Y
NOTE: the first level of averaging occurs within this SharpIR function itself, where it
takes 25 readings and gives a first avergae.
*/
// defines the range over which the IR will respond
int dis = sharp.distance();
int constDis = constrain(dis, 4, 44);
// ~~ second round of avergaing for IR values ~~
totalIR = totalIR - readingsIR[readIndexIR];
// read from the sensor:
readingsIR[readIndexIR] = constDis;
// add the reading to the total:
totalIR = totalIR + readingsIR[readIndexIR];
// advance to the next position in the array:
readIndexIR = readIndexIR + 1;
// if we're at the end of the array...
if (readIndexIR >= numReadings) {
// ...wrap around to the beginning:
readIndexIR = 0;
}
// calculate the average:
averageIR = totalIR / numReadings;
// if the reading is between the acceptable range 4<averageIR<44
if (averageIR > 4 && averageIR < 44) {
int controlIR = 2;
usbMIDI.sendControlChange(controlIR, averageIR, 2);
// send a MIDI control message (control, value (the average), channel
}
/* Flex Sensor and FlexVal smoothing */
int flexposition;
int flexVal;
// subtract the last reading:
totalFlex = totalFlex - readingsFlex[readIndexFlex];
// read from the sensor:
flexposition = analogRead(A8);
//constrain flexposition between 650 and 1010:
flexVal = constrain(flexposition, 650, 1010);
readingsFlex[readIndexFlex] = flexposition;
// add the reading to the total:
totalFlex = totalFlex + readingsFlex[readIndexFlex];
// advance to the next position in the array:
readIndexFlex = readIndexFlex + 1;
// if we're at the end of the array...
if (readIndexFlex >= numReadings) {
// ...wrap around to the beginning:
readIndexFlex = 0;
}
// calculate the average:
averageFlex = totalFlex / numReadings;
// map the average to a narrower range
averageFlex = map(averageFlex, 600, 1000, 0, 20);
// constrain the range to provide a fixed maximum and minimum value
averageFlex = constrain(averageFlex, 0, 20);
int controlFlex = 3;
usbMIDI.sendControlChange(controlFlex, averageFlex, 3);
// send a MIDI control message (control, value (the average), channel
/* Output potentiometer values */
// read potValBaseFreq from analog pin A1:
potValBaseFreq = analogRead(A1);
// map the Potentiometer value (0-1023) to a MIDI number (0-127):
potValBaseFreq = map(potValBaseFreq, 0, 1023, 0, 127);
// constrain the MIDI val to its domain (0-127):
potValBaseFreq = constrain(potValBaseFreq, 0, 127);
// output the value through MIDI control change (control, number, channel):
// the control argument isn't really important, since the value is routed via channel number in Max
usbMIDI.sendControlChange(5, potValBaseFreq, 5);
potValHarm1 = analogRead(A2);
potValHarm1 = map(potValHarm1, 0, 1023, 0, 127);
potValHarm1 = constrain(potValHarm1, 0, 127);
usbMIDI.sendControlChange(6, potValHarm1, 6);
potValHarm2 = analogRead(A3);
potValHarm2 = map(potValHarm2, 0, 1023, 0, 127);
potValHarm2 = constrain(potValHarm2, 0, 127);
usbMIDI.sendControlChange(7, potValHarm2, 7);
potValHarm3 = analogRead(A4);
potValHarm3 = map(potValHarm3, 0, 1023, 0, 127);
potValHarm3 = constrain(potValHarm3, 0, 127);
usbMIDI.sendControlChange(8, potValHarm3, 8);
potValHarm4 = analogRead(A5);
potValHarm4 = map(potValHarm4, 0, 1023, 0, 127);
potValHarm4 = constrain(potValHarm4, 0, 127);
usbMIDI.sendControlChange(9, potValHarm4, 9);
potValHarm5 = analogRead(A6);
potValHarm5 = map(potValHarm5, 0, 1023, 0, 127);
potValHarm5 = constrain(potValHarm5, 0, 127);
usbMIDI.sendControlChange(10, potValHarm5, 10);
potValHarm6 = analogRead(A7);
potValHarm6 = map(potValHarm6, 0, 1023, 0, 127);
potValHarm6 = constrain(potValHarm6, 0, 127);
usbMIDI.sendControlChange(11, potValHarm6, 11);
// 50 ms delay
delay(50);
}