The Cairns Drum Sequencer V1
What is it?
This device is an 8 step drum machine that uses the LEDs to show which channel is selected, when midi is being read, and when the sequencer is stepping. This drum machine also includes a potentiometer that allows the user to adjust the velocity on each channel.
Video Demonstration:
How it Works…
Ableton Setup:
The Ableton setup isnt too difficult, but there are a few key things to get this working…
- Open up a project and upload the program to the Teensy.
- Open a drum kit/MIDI track that you would like to sequence, and make sure the input is “Teensy MIDI”.
- Arm the drum track and make sure the sounds that are part of drum kit are on these 5 notes: C2, G#1, E1, A#1, F#1.
The Sounds:
The sounds used in the demo are from an Ableton Live 10 standard pack which can be purchased on the Ableton website, but the best part about this drum sequencer is that you don’t have to use stock drum kits! Since the user can pick their own sounds and build their own drum kit that they can control with the sequencer.
Changing Channels/Scroll:
To change which channel is active, the user presses the scroll button on the bottom right of the drum sequencer. This feature was relatively easy to implement since we had done a lab previously that required us to make a 4 channel step sequencer. The code for this feature was unchanged from the previous 4 channel sequencer lab.
The Sequencer:
The sequencer is relatively easy to use, the LEDs will count up the steps and if you press a step button the corresponding LED will become lit and it will stay lit until you press the button again.
Changing Velocity:
To change the velocity of each channel, the user can turn the potentiometer located in the upper right portion of the sequencer. To accomplish this feature, I made it so that the velocity was standardized to 90 for each channel at the beginning of the program. The velocity of each channel would then be controlled by the potentiometer using the range 1-127 (the minimum and maximum velocity) through the analogRead function.
Future Features:
While making this project I had debated whether or not completing an Ableton synced tempo feature, where the drum sequencer would only sequence forward whenever it received a MIDI note from MIDI channel 1 in Ableton. This would allow the user to syncnronize the project tempo and the sequencer tempo, in any daw. However as I began experimenting with the feature it proved harder to implement than anticipated so I have decided to save this feature for future versions.
Here is some of the code from the in development sequencer:
If you hadn’t noticed, there is a small red LED on the right side of the drum sequencer which wasn’t blinking. In the future version that will include project synced tempo, this LED blinks whenever MIDI is being read by the daw, giving the user some insight into when the system is reading MIDI data.
My Arduino Code:
int ledPin[8] = {2, 3, 4, 5, 7, 9, 11, 24};
int numLeds = 8;
int channelLeds[5] = {22, 20, 18, 16, 14};
int numChannelLeds = 5;
int buttonPin[9] = {37, 39, 38, 32, 14, 33, 34, 35, 12};
int tempo = 0;
int mappedPotValTempo = 0;
int currentStep = 0;
int channelVelocity[5] = {90, 90, 90, 90, 90};
bool buttonState[9] = {LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW};
bool lastButtonState[9] = {LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW};
bool switchedOn[5][8] = { //this formatting was a challenge.
{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},
{LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW},
};
int activeChannel = 0;
int noteVals[5] = {40, 42, 44, 46, 48};
unsigned long lastStepTime = 0;
void setup() {
for (int i = 0; i < 9; i++) { //Sets I/O for all LEDs and button pins
pinMode(buttonPin[i], INPUT);
}
for (int i = 0; i < numLeds; i++) {
pinMode(ledPin[i], OUTPUT);
}
for (int i = 0; i < numChannelLeds; i++) {
pinMode(channelLeds[i], OUTPUT);
}
}
void loop() {
velocityPot();
changeChannels();
checkButtons();
updateLeds();
stepForwards();
}
void changeChannels() {
digitalWrite(channelLeds[activeChannel], LOW);
lastButtonState[8] = buttonState[8];
buttonState[8] = digitalRead(buttonPin[8]);
if(lastButtonState[8] == LOW && buttonState[8] == HIGH) {
activeChannel++;
if (activeChannel == 5) {
activeChannel = 0;
}
delay(5);
}
digitalWrite(channelLeds[activeChannel], HIGH);
}
void checkButtons() { //checks buttons and keeps them in sequence.
for (int i = 0; i < 8; i++) {
lastButtonState[i] = buttonState[i];
buttonState[i] = digitalRead(buttonPin[i]);
if(lastButtonState[i] == LOW && buttonState[i] == HIGH) {
switchedOn[activeChannel][i] = !switchedOn[activeChannel][i];
delay(5);
} else if(lastButtonState[i] == HIGH && buttonState[i] == LOW) {
delay(5);
}
}
}
void updateLeds() {
for (int i = 0; i < 8; i++) {
if(switchedOn[activeChannel][i] == HIGH or i == currentStep) { //so that it blinks if the button isn't pushed and stays lit if it is
digitalWrite(ledPin[i], HIGH);
} else {
digitalWrite(ledPin[i], LOW);
}
}}
void stepForwards() {
tempo = 500;
if (millis() >= lastStepTime + tempo) { //tell Teensy when to start running the function with millis()
lastStepTime = millis();
for (int i = 0; i < 5; i++) {
usbMIDI.sendNoteOff(noteVals[i], 0, 1);
}
currentStep++; //increase counter
if (currentStep == 8) {
currentStep = 0; //reset counter
}
for (int i = 0; i < 5; i++) {
if (switchedOn[i][currentStep] == true) {
usbMIDI.sendNoteOn(noteVals[i], channelVelocity[i], 1);
}
}
}
}
void velocityPot() {
channelVelocity[activeChannel] = map(analogRead(A21), 0, 1023, 0, 127);
// checks if a button is pressed
// if so (analog read pot) map the analog value to 0-127
// And set the channelVelocity[activeChannel] to that analog value
}