Explanation
Overview:
My final project is an 8-Step, 4-Channel Drum Sequencer that incorporates NeoPixels, and a potentiometer that sends CC messages to a DAW. In the video below, I chose to use Ableton to demo my project. There are three major components to this code which I will break down in the sections below.


Drum Sequencer
My first step in working on this project involved wiring and writing code for the actual drum sequencer itself. While the wiring was fairly simple and repetitious, the length of the sequencer required careful organization and conservative use of the real estate on the breadboard. In terms of writing the code, I primarily manipulated and expanded on the multi-channel drum sequencer code from Lab 9. I added variables and altered the code to account for four more steps, four more LED’s and four channels total to create an 8-step, 4-channel sequencer. I also included the same potentiometer that alters the tempo of the sequencer.

NeoPixels
For the next part of the code, I wanted to include a way to see the channel the user has selected using NeoPixels. Instead of different colored LED’s, I programmed two different NeoPixels to different colors that correspond to each of the four channels. As I pressed the button that changes the channel, I programmed both of the NeoPixels to change to a different color. This way, it is easy to tell which channel the drum sequencer is editing, and the colors are really pretty too. The first channel is white, the second channel is green, the third channel is red, and the fourth channel is blue.




Control Value (CC)
My last step in this project was to add a potentiometer that would manipulate one of the CC values in the DAW (in this case, Ableton). The particular parameter itself is variable based on the MIDI mapping in the DAW. In the demo below, I chose to alter the delay value with my potentiometer. I employed the code we wrote in the third lecture assignment, so that the potentiometer only sends messages when the knob is adjusted. One improvement I may consider making to the code in the future is adding a button that will activate the CC value’s ability to be changed by the DAW, as some minor noise occasionally occurs as a result of the analog nature of the potentiometer.
Video Demo:
Code:
#include <Adafruit_NeoPixel.h>
Adafruit_NeoPixel neopixel = Adafruit_NeoPixel(2, 35, NEO_RGB);
int buttonPin[8] = {32, 31, 30, 29, 28, 27, 26, 25};
int ledPin[8] = {22, 21, 20, 19, 18, 17, 16, 15};
int totalLedPins = 8;
int totalButtonPins = 8;
int tempoPot = A15;
int tempoValue = 0;
unsigned long lastStep = 0;
int currentStep = 0;
int totalSteps = 8;
int channelDisplayed = 0;
int totalChannels = 4;
int channelButtonPin = 33;
bool lastChannelButtonState = LOW;
bool channelButtonState = LOW;
bool lastButtonState[8] = {LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW};
bool buttonState[8] = {LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW};
bool switchedOn[4][8] = {
{ LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW },
{ LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW },
{ LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW },
{ LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW },
};
int midiNotes[4] = {36, 40, 42, 46};
int totalMidiNotes = 4;
int potCC = A17;
int ccVal = 0;
int lastCCVal = 0;
void setup() {
for (int i = 0; i < totalLedPins; i ++) {
pinMode(ledPin[i], OUTPUT);
}
for (int i = 0; i < totalButtonPins; i ++) {
pinMode(buttonPin[i], INPUT);
}
pinMode(channelButtonPin, INPUT);
neopixel.begin();
neopixel.clear();
neopixel.show();
}
void loop() {
checkButtons();
updateLeds();
stepForwards();
checkChannelButton();
updateChannelLeds();
sendCCVal();
}
void checkButtons() {
for (int i = 0; i < totalButtonPins; i++) {
lastButtonState[i] = buttonState[i];
buttonState[i] = digitalRead(buttonPin[i]);
if (lastButtonState[i] == LOW && buttonState[i] == HIGH) {
switchedOn[channelDisplayed][i] = !switchedOn[channelDisplayed][i];
delay(5);
} else if (lastButtonState[i] == HIGH && buttonState[i] == LOW) {
delay(5);
}
}
}
void updateLeds() {
for (int i = 0; i < totalLedPins; i++) {
if (switchedOn[channelDisplayed][i] == true) {
digitalWrite(ledPin[i], HIGH);
} else {
digitalWrite(ledPin[i], LOW);
}
}
digitalWrite(ledPin[currentStep], HIGH);
}
void stepForwards() {
tempoValue = analogRead(A15);
if (millis() > lastStep + tempoValue) {
lastStep = millis();
for (int i = 0; i < totalMidiNotes; i++) {
usbMIDI.sendNoteOff(midiNotes[i], 127, 1);
}
countUp();
for (int i = 0; i < totalMidiNotes; i++)
if (switchedOn[i][currentStep] == HIGH) {
usbMIDI.sendNoteOn(midiNotes[i], 127, 1);
}
}
}
void checkChannelButton() {
lastChannelButtonState = channelButtonState;
channelButtonState = digitalRead(channelButtonPin);
if (lastChannelButtonState == LOW and channelButtonState == HIGH) {
countUpChannel();
delay(5);
}
if (lastChannelButtonState == HIGH and channelButtonState == LOW) {
delay(5);
}
}
void updateChannelLeds() { //NeoPixel Code
if (channelDisplayed == 0) {
neopixel.setPixelColor(0, 225, 225, 225);
neopixel.setPixelColor(1, 225, 225, 225);
neopixel.show();
}
if (channelDisplayed == 1) {
neopixel.setPixelColor(0, 0, 225, 0);
neopixel.setPixelColor(1, 0, 225, 0);
neopixel.show();
}
if (channelDisplayed == 2) {
neopixel.setPixelColor(0, 225, 0, 0);
neopixel.setPixelColor(1, 225, 0, 0);
neopixel.show();
}
if (channelDisplayed == 3) {
neopixel.setPixelColor(0, 0, 125, 125);
neopixel.setPixelColor(1, 0, 125, 125);
neopixel.show();
}
neopixel.show();
}
void countUpChannel() {
channelDisplayed++;
if (channelDisplayed == totalChannels) {
channelDisplayed = 0;
}
}
void countUp() {
currentStep++;
if (currentStep == totalSteps) {
currentStep = 0;
}
}
void sendCCVal() { //CC Value Code
usbMIDI.read();
lastCCVal = ccVal;
ccVal = map(analogRead(potCC), 0, 1023, 0, 127);
if (ccVal != lastCCVal) {
Serial.println(ccVal);
usbMIDI.sendControlChange(1, ccVal, 1);
}
}