Adafruit Trellis Geome Midi Sequencer Code
Moderators: adafruit_support_bill, adafruit

Adafruit Trellis Geome Midi Sequencer Code

by gkuetemeyer on Tue Mar 25, 2014 11:02 am

Here is the code for the Geome sequencer:

Code: Select all | TOGGLE FULL SIZE
/***************************************************
  Geome Midi Sequencer
  George Kuetemeyer
  gk@pobox.com
  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();
  }
}


This works pretty well. Not too glitchy. Would really like to be able to handle Trellis within an interrupt handler. That way it would be easier to implement midi clock signal, etc., as I am doing with some other projects.
gkuetemeyer
 
Posts: 19
Joined: Sat Jan 11, 2014 1:15 pm

Re: Adafruit Trellis Geome Midi Sequencer Code

by brianlight on Thu Mar 27, 2014 5:22 pm

This is awesome loaded this up on my Teensy 3.1 board and utilized usb over Midi. Thanks for sharing.

brianlight
 
Posts: 27
Joined: Mon Dec 23, 2013 9:52 pm

Re: Adafruit Trellis Geome Midi Sequencer Code

by gkuetemeyer on Sat Mar 29, 2014 11:07 pm

Just updated the Geome Sequencer code a bit. The original code I posted was short and sweet, but I really should have been more thorough with turning off notes. You won't notice the issue if you assign a percussive sound like piano, xylo, etc., but might notice that organ and violin voicings might overwhelm your midi device.

I fixed this by making sure to turn off *all* notes from a particular step. The best way is to pass to sendNoteOff the exact same info (pitch/volume) that you sent to sendNoteON. This type of situation calls out for some kind of push/pop stack mechanism. Fortunately there is a nice library out there (StackArray) that does a great job.

Here are the changes:

1. Install StackArray library

2. Add #include <StackArray> to the top of the sequencer code.

3. Globally enter this variable: StackArray <uint8_t> playedNotes;

4. Substitute new play_note function:

Code: Select all | TOGGLE FULL SIZE
//-------------------------------------------------------------------------------
void play_note(byte channel, byte pitch, byte volume) {
  playedNotes.push(volume);
  playedNotes.push(pitch);
  MIDI.sendNoteOn(pitch, volume, channel);
}


5. Substitute new turn_off_note function:

Code: Select all | TOGGLE FULL SIZE
//-------------------------------------------------------------------------------
void turn_off_note(byte channel) {
  uint8_t pitch;
  uint8_t volume;
  while(!playedNotes.isEmpty()) {
    pitch   = playedNotes.pop();
    volume = playedNotes.pop();
    MIDI.sendNoteOff(pitch, volume, channel);
  }
}


Note that when you call turn_off_note now you should only pass the channel info.

GK
gkuetemeyer
 
Posts: 19
Joined: Sat Jan 11, 2014 1:15 pm

Re: Adafruit Trellis Geome Midi Sequencer Code

by brianlight on Sun May 04, 2014 4:19 pm

Just a suggestion but I think it would be really awesome if you could scroll left & right with a potentiometer to insert more notes 8 x 8; 8 x 16; or 8 x 32 would be awesome

brianlight
 
Posts: 27
Joined: Mon Dec 23, 2013 9:52 pm