Code: Select all
/***************************************************
Geome Midi Sequencer
George Kuetemeyer
[email protected]
March 22, 2014
Geome was inspired by a variety of monome-like devices.
Check out: monome.org for more info.
Geome is laid out as an eight by eight matrix. The cells in the
top six rows provide six note options for an eight step sequencer.
The first row buttons turn on random octave shifts for each step.
The second row buttons turn on a kind of echo - currently set at -3 steps.
Designed specifically to work with the Adafruit Trellis w/HT16K33
----> https://www.adafruit.com/products/1616
----> https://www.adafruit.com/products/1611
These displays use I2C to communicate, 2 pins are required to
interface
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Trellis library Written by Limor Fried/Ladyada for Adafruit Industries.
MIT license, all text above must be included in any redistribution
****************************************************/
#include <Wire.h>
#include <Adafruit_Trellis.h>
#include <Adafruit_Trellis_XY.h>
#include <MIDI.h>
#include <Button.h>
/***************************************************/
Adafruit_Trellis matrix0 = Adafruit_Trellis();
Adafruit_Trellis matrix1 = Adafruit_Trellis();
Adafruit_Trellis matrix2 = Adafruit_Trellis();
Adafruit_Trellis matrix3 = Adafruit_Trellis();
Adafruit_TrellisSet trellis = Adafruit_TrellisSet(&matrix0, &matrix1, &matrix2, &matrix3);
// set to however many you're working with here, up to 8
#define NUMTRELLIS 4
#define numCells (NUMTRELLIS * 16)
#define INTPIN 2
//-----------------------------------------------------------
struct TileOffsets TileOffsets = {
{ 0, 4, 0, 4, 0, 0, 0, 0 },
{ 0, 0, 4, 4, 0 ,0, 0, 0 }
};
uint8_t xVal;
uint8_t yVal;
uint8_t xyTrellisID;
// store x/y array states -- eight bytes
uint8_t EightByEight[8];
// sequencer step
uint8_t seqStep = 0;
// C Pentatonic Scale
// 5 notes plus octave fits trellis size pretty well.
// Obviously there are lots of options here!!
uint8_t Scale[6] = { 48, 50, 53, 55, 57, 60 };
// non-Trellis button on Midi Shield
// using this button to start/stop sequencer
Button start_stop = Button(2, PULLUP);
uint8_t start_stop_state = 0;
// midi channel
uint8_t midi_channel = 1;
//-----------------------------------------------------------
void setup() {
MIDI.begin();
// INT pin requires a pullup
pinMode(INTPIN, INPUT);
digitalWrite(INTPIN, HIGH);
// initialize trellis.
// NB: Your value order may differ, depending on how you've arranged
// your tiles
trellis.begin(0x72, 0x73, 0x70, 0x71);
// turn off Trellis LEDs
for (uint8_t i = 0; i < numCells; i++) {
trellis.clrLED(i);
}
// you can adjust brightness according to your needs/taste
trellis.setBrightness(1);
trellis.writeDisplay();
}
//-----------------------------------------------------------
void loop() {
if (trellis.readSwitches()) {
// go through every button
for (uint8_t i=0; i < numCells; i++) {
if (trellis.justPressed(i)) {
// button debounce
delay(10);
// get x/y coordinates
TrellisToXY(i, TileOffsets, &xVal, &yVal);
// Alternate LED
if (trellis.isLED(i)) {
trellis.clrLED(i);
// update X/Y matrix
bitWrite(EightByEight[yVal], xVal, 0);
}
else {
trellis.setLED(i);
// update X/Y matrix
bitWrite(EightByEight[yVal], xVal, 1);
}
}
}
// display LED matrix
trellis.writeDisplay();
}
else {
// adjust tempo
delay(map(analogRead(0), 0, 1023, 50, 1000));
// check start/stop
if(start_stop.isPressed() == 1) {
if(start_stop_state == 0) {
start_stop_state = 1;
}
else {
start_stop_state = 0;
}
}
// play next notes
if(start_stop_state == 1) {
play_notes();
}
}
}
//-------------------------------------------------------------------------------
// play notes
void play_notes() {
uint8_t octave = 0;
uint8_t echoStep = 0;
// compute next step in sequence
seqStep = (seqStep + 1) % 8; // modulo operator rolls over variable
// echo for last step
echoStep = (seqStep - 3) % 8;
// turn off previous notes in channel
turn_off_note(midi_channel, 64, 64);
// get random octave for step note
if(bitRead(EightByEight[0], seqStep) == 1) {
octave = 12 * random(0, 4);
}
for(uint8_t y = 2; y < 8; y++) {
if(bitRead(EightByEight[y], seqStep) == 1) {
play_note(midi_channel, Scale[y - 2] + octave, 64);
}
if(bitRead(EightByEight[1], seqStep) == 1) {
if(bitRead(EightByEight[y], echoStep) == 1) {
play_note(midi_channel, Scale[y - 2] + octave, 64);
}
}
}
// display cursor for current step column
display_cursor();
}
//-------------------------------------------------------------------------------
void play_note(byte channel, byte pitch, byte volume) {
MIDI.sendNoteOn(pitch, volume, channel);
}
//-------------------------------------------------------------------------------
void turn_off_note(byte channel, byte pitch, byte volume) {
MIDI.sendNoteOff(pitch, volume, channel);
}
//-------------------------------------------------------------------------------
void display_cursor() {
// we do this twice so we can invert EightByEight column and then restore original values
for(uint8_t c = 0; c < 2; c++) {
for(uint8_t y = 0; y < 8; y++) {
if(bitRead(EightByEight[y], seqStep) == 0) {
trellis.setLED(XYToTrellis(numCells, TileOffsets, seqStep, y));
bitWrite(EightByEight[y], seqStep, 1);
}
else {
trellis.clrLED(XYToTrellis(numCells, TileOffsets, seqStep, y));
bitWrite(EightByEight[y], seqStep, 0);
}
}
trellis.writeDisplay();
}
}