Skip to content

Instantly share code, notes, and snippets.

@medicalwei
Created August 5, 2013 01:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save medicalwei/6152900 to your computer and use it in GitHub Desktop.
Save medicalwei/6152900 to your computer and use it in GitHub Desktop.
Arduino Esplora Sequencer
#include <Esplora.h>
#include <TFT.h>
#include <SPI.h>
// these are the frequencies for the notes from middle C
// to one octave above middle C:
const int note[] = {
247,
262, // C
277, // C#
294, // D
311, // D#
330, // E
349, // F
370, // F#
392, // G
415, // G#
440, // A
466, // A#
494, // B
523, // C next octave
554
};
const int notes = 14;
char seqString[][7] = {
"Seq 1",
"Seq 2",
"Seq 3",
"Seq 4",
"Seq 5",
"Seq 6",
"Seq 7",
"Seq 8",
"Seq 9",
"Seq 10",
"Seq 11",
"Seq 12",
"Seq 13",
"Seq 14",
"Seq 15",
"Seq 16"
};
char noteString[][6] = {
"pause",
"C", // C
"C#", // C#
"D", // D
"D#", // D#
"E", // E
"F", // F
"F#", // F#
"G", // G
"G#", // G#
"A", // A
"A#", // A#
"B", // B
"C+" // C next octave
};
// time
unsigned long prevTime;
// sequencer properties
int seqPos = 15;
int seqMax = 16;
int seqArray[16] = {0};
// BPM and delay time
int bpm; unsigned long delayTime;
int writtenBpm = 0;
char bpmPrintout[4];
// Attack time
unsigned long attack = 100000;
// cursor
int curPos = 0;
// button statuses
int btnPrevStat[4] = {HIGH, HIGH, HIGH, HIGH};
// button events
void btnDownClick(){
setNote(curPos, -1);
printStatus(seqString[curPos], noteString[seqArray[curPos]]);
}
void btnUpClick(){
setNote(curPos, 1);
printStatus(seqString[curPos], noteString[seqArray[curPos]]);
}
void btnLeftClick(){
setCursor_(-1);
printStatus(seqString[curPos], noteString[seqArray[curPos]]);
}
void btnRightClick(){
setCursor_(1);
printStatus(seqString[curPos], noteString[seqArray[curPos]]);
}
// handle buttons
void buttonHandlers(){
int btnStat[4];
btnStat[0] = Esplora.readButton(SWITCH_DOWN);
btnStat[1] = Esplora.readButton(SWITCH_LEFT);
btnStat[2] = Esplora.readButton(SWITCH_UP);
btnStat[3] = Esplora.readButton(SWITCH_RIGHT);
if (btnStat[0] != btnPrevStat[0] && btnStat[0] == LOW){
btnDownClick();
}
if (btnStat[1] != btnPrevStat[1] && btnStat[1] == LOW){
btnLeftClick();
}
if (btnStat[2] != btnPrevStat[2] && btnStat[2] == LOW){
btnUpClick();
}
if (btnStat[3] != btnPrevStat[3] && btnStat[3] == LOW){
btnRightClick();
}
btnPrevStat[0] = btnStat[0];
btnPrevStat[1] = btnStat[1];
btnPrevStat[2] = btnStat[2];
btnPrevStat[3] = btnStat[3];
}
void readSliderAndSetDelayTime(){
bpm = map(Esplora.readSlider(), 0, 1023, 255, 0);
if (bpm != 0) {
delayTime = 60000000 / bpm / 4;
if (abs(bpm - writtenBpm) > 1) {
String(bpm).toCharArray(bpmPrintout, 4);
printStatus("BPM", bpmPrintout);
writtenBpm = bpm;
}
} else if (bpm != writtenBpm) {
printStatus("Music", "STOP");
writtenBpm = bpm;
}
}
inline void drawNote(unsigned int target){
int maxHeight = EsploraTFT.height() - 10;
if (seqArray[target]) {
EsploraTFT.rect(target*10, maxHeight - seqArray[target]*5, 10, 5);
}
}
void drawSequence(){
for(int i=0; i<16; i++){
setNote(i, 0);
}
}
void setNote(int target, int delta){
EsploraTFT.fill(0, 0, 0);
drawNote(curPos);
seqArray[curPos] = (seqArray[target] + delta) % notes;
if (seqArray[target] < 0) {
seqArray[target] += notes;
}
EsploraTFT.fill(0, 255, 255);
drawNote(target);
}
inline void drawCursor(){
int maxHeight = EsploraTFT.height();
EsploraTFT.rect(curPos*10, maxHeight - 10, 10, 10);
}
void setCursor_(int delta){
EsploraTFT.fill(0, 0, 0);
drawCursor();
curPos = (curPos + delta) % 16;
if (curPos < 0) {
curPos += 16;
}
EsploraTFT.fill(255, 0, 255);
drawCursor();
}
void sequenceChanged(){
// Clean sequencer position
EsploraTFT.fill(0, 0, 0);
EsploraTFT.rect(seqPos*10, 0, 10, 10);
seqPos = seqPos + 1;
if (seqPos >= seqMax){
seqPos = 0;
}
EsploraTFT.fill(255, 127, 0);
EsploraTFT.rect(seqPos*10, 0, 10, 10);
if (seqArray[seqPos]) {
int pitch = Esplora.readJoystickY();
int diff;
if (pitch>0){
diff = abs(note[seqArray[seqPos]-1] - note[seqArray[seqPos]]);
} else {
diff = abs(note[seqArray[seqPos]+1] - note[seqArray[seqPos]]);
}
pitch = map(pitch, -512, 512, diff, -diff);
Esplora.tone(note[seqArray[seqPos]]+pitch);
} else {
Esplora.noTone();
}
}
void printStatus(char* string1, char* string2){
EsploraTFT.fill(0, 0, 0);
EsploraTFT.stroke(0, 0, 0);
EsploraTFT.rect(0, 11, EsploraTFT.width(), 25);
EsploraTFT.stroke(255, 255, 255);
EsploraTFT.setTextSize(1);
EsploraTFT.text(string1, 0, 11);
EsploraTFT.setTextSize(2);
EsploraTFT.text(string2, 0, 20);
EsploraTFT.stroke(63, 63, 63);
}
void loop() {
readSliderAndSetDelayTime();
buttonHandlers();
unsigned long time = micros();
if (time-prevTime > attack){
Esplora.noTone();
}
if (bpm != 0 && time-prevTime >= delayTime){
sequenceChanged();
prevTime = time;
}
}
void drawGrid(){
EsploraTFT.stroke(63, 63, 63);
EsploraTFT.fill(0, 0, 0);
for(int i=0; i<16; i++){
EsploraTFT.rect(i*10, 0, 10, 10);
EsploraTFT.rect(i*10, EsploraTFT.height() - 10, 10, 10);
for (int j=0; j < notes - 1; j++) {
EsploraTFT.rect(i*10, EsploraTFT.height() - 15 - j*5, 10, 5);
}
}
}
void setup() {
// Draw black screen
EsploraTFT.begin();
EsploraTFT.background(0, 0, 0);
EsploraTFT.stroke(63, 63, 63);
// set time
prevTime = micros();
// paint initialize things
drawGrid();
drawSequence();
// first trigger
setCursor_(0);
}
@medicalwei
Copy link
Author

Copyright (c) 2013, Yao Wei
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment