Arduino step sequencer

   Oo    .oOOOo.  .oOOOo.
  o  O   o     o  o     o
 O    o  O.       O.
oOooOoOo  `OOoo.   `OOoo.
o      O       `O       `O
O      o        o        o
o      O O.    .O O.    .O
O.     O  `oooO'   `oooO'
Arduino Step Sequencer

Introduction

A stepsequencer is by wikipedia a “special case” of musical sequencer. But
I figure you know what a stepsequencer is if you’re reading this page, about
me making a stepsequencer with an Arduino. The main reason is probably that
I burned my Gorf when assembling it.
Fiddling with it got me thinking about what I wanted of a step-sequencer and
how I wanted to use it when composing music. The gorf to me was a bit to
small. Dont get me wrong small is nice and all, but not the spacing between
controls. I dont preform drunk (or create music drunk either) but I’d like
my stuff to be usable while intoxicated. I would never have done this unless
I had burned the Gorf-kit :D.
Anyways I wanted a bigger display, so that I could fiddle around with
different setups and functions. I wanted rotary encoders (endless) so that
I would need fewer then 1 per step. I wanted MIDI-out, to you know, control
stuff.
Luckily for me I have a interaction-designer-musician friend that I can
discuss with. So we iterated the idea back and forth. He’s suggestions where
all great, but I didn’t take them all. Had this been a mass-market product
I where going to try to sell I’d probably taken them all, right now I only
took the ones that I wanted!

A few resources have been helpful for me while creating the Ass here is a list:

http://www.arduino.cc/playground/Main/RotaryEncoders
Rotary Encoders, read up on these
http://arduino.cc/en/Reference/LiquidCrystal
library for the LCD screen
http://sheepdogguides.com/arduino/aht0button.htm
Information about the built in pullup resistor
http://itp.nyu.edu/physcomp/Labs/MIDIOutput
ever so useful information about the midi connections
http://todbot.com/blog/2006/10/29/spooky-arduino-projects-4-and-musical-arduino/
ever so useful information about the midi connections
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1205879808
library for the rotary encoder that I modified
http://digital-salvage.net/?p=124
very useful in getting the liquidcrystal library to work.

Hardware

1 Arduino
1 5pole DIN (MIDI)
1 Sparkfun screen
1 9v Battery holder
1 Input-connector for the battery
5 Buttons
1 OnOff switch
1 Rotary Encoder
1 Knob
1 Plastic case
2 resistors to get the contrast of the screen sane.

As you can se, we’re under 50 dollars in parts.
Everything was pretty easy putting together, and here is a schematic that
is more of a photo with drawn lines then a schematic. Hope it helps if
you want to build your own Ass.

the source

Video

Software

Everything was dead easy programming, everything except the rotary encoder.
I got a 2 dollar one, so i had problems with bouncing and noise on it, as
you’ve seen on the previous video I programmed it while sick so, yeah.
Another thing to think about is that you shouldent update the LCD screen
every time in you’re Loop function, it goes CRAZY then.
I’ve commented the code a lot so I wont go in on it here, I’ve used the
http://www.ladyada.net/library/arduino/copyforhtml.html excellent tool.

UPDATE  Magellan has supplied a much better schematics, thank you!

Here is the better schematics:

This is the Ass.pde file

/*
   Oo    .oOOOo.  .oOOOo.  
  o  O   o     o  o     o  
 O    o  O.       O.       
oOooOoOo  `OOoo.   `OOoo.  
o      O       `O       `O 
O      o        o        o 
o      O O.    .O O.    .O 
O.     O  `oooO'   `oooO'  
Arduino Step Sequencer                           
                      */     

#include <LiquidCrystal.h>
#include "rotaryEncoder.h"

#define usesMidi 1 /*enable this if you want to send midi, doesent work at all together with Serial.print debugging */
#define debugprint 0 /*this sends midi messages as text instead */

LiquidCrystal lcd(12, 11, 10, 7, 6, 5, 4); /* Utilizing the liquidcrystal library for the screen, this is a setup-call*/

RotaryEncoder rotary(2, 3); /*sweet sweet library that solves a lot of problems, I made a few changes to the original one */

/*defines for the buttons */
#define menuButton 14
#define onOffButton 15
#define lengthButton 16
#define velocityButton 17
#define playPuaseButton 18
#define noteButton  19

/*booleans to tell me if the buttons are pressed or not */
byte menuButtonIsPressed =0;
byte onOffButtonIsPressed =0;
byte lengthButtonIsPressed =0;
byte velocityButtonIsPressed =0;
byte playPuaseButtonIsPressed =0;
byte noteButtonIsPressed =0;

/*useful state variables */
byte currentlyPlaying =0;
byte currentlyEditing =0;
boolean isPlaying=true;

/*channel and instrument are for the midi messages */
byte channel=0;
byte instrument =0;

byte tempo=120;  /* bmp of our sequence */
long timeChange=250; /*a variable for calculating how long we are going to wait*/

/*a struct for our notes in our sequence*/
struct data
{
  byte  velocity;
  byte  onOff;
  byte  length;
  byte  note;
};

int numberofsteps = 8; /* at first we have 8 steps, this is pretty basic */
#define maxNumberOfSteps 17 /*that's 16*/

data myData[maxNumberOfSteps]; /*array for our notes*/

/*remembering the good old times*/
long rememberMillis=0;
long rememberMillisDisplay =0;

/*we'd like to present notenames instead of midi numbers */
char* noteNames[] = {
  "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" };

void setup()
{
  /*telling everybody we're alive */
  lcd.print("Arduino Step Seq");
  delay(500);

  /*setting up the buttons */
  pinMode(menuButton,INPUT);
  pinMode(onOffButton,INPUT);
  pinMode(lengthButton,INPUT);
  pinMode(velocityButton,INPUT);
  pinMode(playPuaseButton,INPUT);
  pinMode(noteButton,INPUT);

  /*starting the builtin pullup-resistor!*/
  digitalWrite(menuButton,HIGH);
  digitalWrite(onOffButton,HIGH);
  digitalWrite(lengthButton,HIGH);
  digitalWrite(velocityButton,HIGH);
  digitalWrite(playPuaseButton,HIGH);
  digitalWrite(noteButton,HIGH);  

  /*initialization of the encoder library */
  rotary.position(0);

#if usesMidi
  Serial.begin(31250);
#else
  Serial.begin(9600);
#endif

  /*calculating the timeChange variable. milliseconds divided by 
   tempo and 16th note, or something like that. I dont remember*/
  timeChange=((60000/tempo)/8);

  /*initializing the steps in the array */
  for(int i=0;iclear();

  /*what time is now*/

  rememberMillis = millis();
  rememberMillisDisplay = millis();
}

/* a little function to print the name of the note */
void printNoteName(byte initialNote)
{
  byte octave = (initialNote / 12) - 1;
  byte noteIndex = (initialNote % 12);

  lcd.print(noteNames[noteIndex]);
  lcd.print(octave,DEC);
}

/* this functions prints what we have in the first "menu" part of the data
 tempo, channel and instrument*/
void populateDisplayMenuButtonPressed()
{

  lcd.setCursor(0, 0) ;

  lcd.print("t:");
  lcd.print(tempo,DEC);
  lcd.print(" c:");
  lcd.print(channel,DEC);
  lcd.print(" i:");
  lcd.print(instrument,DEC);

  if (!isPlaying)
  {
    lcd.setCursor(0, 1) ;
    lcd.print("PAUSED           ");
  }
  else
  {
    lcd.setCursor(0, 1) ;
    for(int i=0;iif(i==currentlyPlaying && i==currentlyEditing )
        lcd.print("+");
      else if(i==currentlyEditing)
        lcd.print("-");
      else if(i==currentlyPlaying)
        lcd.print("|");
      else
        lcd.print(".");
    }
    /* prints spaces for the rest of the line, it is better then using a 
     lcd.clear, atleast if you call lcd.clear fast/often */
    for(int i=numberofsteps;i<  maxNumberOfSteps ;i++)
    {
      lcd.print(" ");

    }
  }
}

/*this is the normal/regular display info printing routine */
void populateDisplay()
{
  lcd.setCursor(0, 0) ;
  if(isPlaying)
  {
    printNoteName(myData[currentlyEditing].note);
    lcd.print(" ");
    /*On if the note is On, Off it is Off, getting it? getting on or getting off, muhahahaha */
    if(myData[currentlyEditing].onOff)
      lcd.print("On");
    else
      lcd.print("Off");

    lcd.print(" ");
    {
      /*what kind of note what we are dealing with */
      switch (myData[currentlyEditing].length)
      {
      case 1:
        lcd.print("32nd");
        break;
      case 2:
        lcd.print("16th");
        break;
      case 3:
        lcd.print("8th");
        break;
      case 4:
        lcd.print("4th");
        break;
      case 5:
        lcd.print("2nd");
        break;
      case 6:
        lcd.print("whole");
        break;
      default:
        lcd.print("FAIL");
        break; /*we made bu bu*/
      }
    }
    lcd.print(" ");

    lcd.print(myData[currentlyEditing].velocity,DEC);
    lcd.print("       ");

    lcd.setCursor(0, 1) ;
    for(int i=0;iif(i==currentlyPlaying && i==currentlyEditing )
        lcd.print("+");
      else if(i==currentlyEditing)
        lcd.print("-");
      else if(i==currentlyPlaying)
        lcd.print("|");
      else
        lcd.print(".");
    }

    /* prints spaces for the rest of the line, it is better then using a 
     lcd.clear, atleast if you call lcd.clear fast/often */

    for(int i=numberofsteps;i<  maxNumberOfSteps ;i++)
    {
      lcd.print(" ");

    }
  }
  else
  {
    lcd.setCursor(0, 1) ;
    lcd.print("PAUSED           ");
  }
}

/* a reading the button function */
int readbutt(int button)
{
  if (digitalRead(button)==LOW)
  {
    return 1;
  }
  return 0;
} 

/* function to iterate through the buttons */
void checkButtons()
{

  if (readbutt(noteButton) && noteButtonIsPressed==false)
    noteButtonIsPressed = true;
  else if (noteButtonIsPressed==true && readbutt(noteButton)==false)
    noteButtonIsPressed = false;
  if (readbutt(menuButton) && menuButtonIsPressed==false)
    menuButtonIsPressed = true;
  else if (menuButtonIsPressed==true && readbutt(menuButton)==false)
    menuButtonIsPressed = false;
  if (readbutt(onOffButton) && onOffButtonIsPressed==false)
    onOffButtonIsPressed = true;
  else if (onOffButtonIsPressed==true && readbutt(onOffButton)==false)
    onOffButtonIsPressed = false;
  if (readbutt(lengthButton) && lengthButtonIsPressed==false)
    lengthButtonIsPressed = true;
  else if (lengthButtonIsPressed==true && readbutt(lengthButton)==false)
    lengthButtonIsPressed = false;
  if (readbutt(velocityButton) && velocityButtonIsPressed==false)
    velocityButtonIsPressed = true;
  else if (velocityButtonIsPressed==true && readbutt(velocityButton)==false)
    velocityButtonIsPressed = false;
  if (readbutt(playPuaseButton) && playPuaseButtonIsPressed==false)
    playPuaseButtonIsPressed = true;
  else if (playPuaseButtonIsPressed==true && readbutt(playPuaseButton)==false)
    playPuaseButtonIsPressed = false;
}

/*our main baby */
void loop()
{

  setCurrentValue();

  /* we dont want to update the screen to often, it freaks out */
  if( millis()-rememberMillisDisplay > 100)
  {
    rememberMillisDisplay=millis();
    checkButtons();
    if (!menuButtonIsPressed)
      populateDisplay();
    else
      populateDisplayMenuButtonPressed();
  }

  if(isPlaying)
  {

    if( millis()-rememberMillis > ((myData[currentlyPlaying].length * myData[currentlyPlaying].length )*(timeChange)))
    {
      //fuck it, we always send off, most MIDI devices wont crap themselves.
      sendMidiMessage(0x80,  myData[currentlyPlaying].note,120); //off

      currentlyPlaying++;
      if (currentlyPlaying==numberofsteps)
        currentlyPlaying=0;

      rememberMillis=millis();

      if( myData[currentlyPlaying].onOff)
        sendMidiMessage(0x90,  myData[currentlyPlaying].note, 120); //on

    }
  }

}

/* This functions makes almost all our values be more correct.
 like, we dont want negative values or out of range values */
void SanitizeValues()
{

  myData[currentlyEditing].onOff= myData[currentlyEditing].onOff % 2; //setting the current value to be inbetween 0 and 1
  myData[currentlyEditing].note= myData[currentlyEditing].note % 128; //setting the current value to be inbetween 0 and 127
  myData[currentlyEditing].velocity= myData[currentlyEditing].velocity % 128; //setting the current value to be inbetween 0 and 127
  currentlyEditing=(currentlyEditing % (numberofsteps));
  isPlaying = isPlaying%2;

  if(numberofsteps > maxNumberOfSteps)
    numberofsteps=maxNumberOfSteps;

  if(numberofsteps < 0)
    numberofsteps=0;

  instrument = instrument % 128;
  channel = channel% 17;
  tempo =tempo % 200;

}

void setCurrentValue()
{
  int encoder0Pos = rotary.position();
  if(encoder0Pos==0)
    return;

  if(menuButtonIsPressed)
  {
    if(playPuaseButtonIsPressed)
    {
      numberofsteps+=encoder0Pos;
      currentlyPlaying =0;
      currentlyEditing =0;
    }
    else if(noteButtonIsPressed)
    {
      tempo+=encoder0Pos;
      timeChange=((60000/tempo)/8);
    }
    else if(onOffButtonIsPressed)
    {
      channel+=encoder0Pos;
    }
    else if(lengthButtonIsPressed)
    {
      instrument+=encoder0Pos;
    }
    else
      currentlyEditing+=encoder0Pos;

  }
  else
  {
    if(noteButtonIsPressed)
      myData[currentlyEditing].note+=encoder0Pos;

    else if(onOffButtonIsPressed==true)
      myData[currentlyEditing].onOff+=encoder0Pos;

    else if(lengthButtonIsPressed)
    {
      myData[currentlyEditing].length+=encoder0Pos;

      if(myData[currentlyEditing].length > 6)
        myData[currentlyEditing].length=6;

      if(myData[currentlyEditing].length < 1)
        myData[currentlyEditing].length=1;

    }
    else if(velocityButtonIsPressed)
      myData[currentlyEditing].velocity+=encoder0Pos;
    else if(playPuaseButtonIsPressed)
      isPlaying+=encoder0Pos; 

    else
      currentlyEditing+=encoder0Pos;
  }

  rotary.position(0);
  SanitizeValues();
}

void sendMidiMessage(char cmd, char data1, char data2)
{

#if debugprint
  Serial.print("Sending MIDI ");
  Serial.print(cmd,DEC);
  Serial.print(" ");
  Serial.print(data1,DEC);
  Serial.print(" ");
  Serial.println(daserial.pta2,DEC);
#endif
  //not sending channel or instrument change yet...
#if usesMidi
  Serial.print(cmd, BYTE);
  Serial.print(data1, BYTE);
  Serial.print(data2, BYTE);
#endif
}

This is the RotaryEncoder.h file

/******************************************************************************
 *  RotaryEncoder.h - Arduino library for reading RotaryEncoder encoders.
 *  Version 0.90
 *  modified by Johan Larsby 2009 May 08
 ******************************************************************************/

#ifndef RotaryEncoder_h
#define RotaryEncoder_h

#define DIGITAL_PINS (13)

class RotaryEncoder
{
    public:
		RotaryEncoder(int encoderPin1, int encoderPin2);

		void minimum(int min);
		int minimum(void);
		void maximum(int max);
		int maximum(void);
		void nominimum(void);
		void nomaximum(void);

		int position(void);
		void position(int pos);

		int pressed(void);

		static void isr(void);

    private:
		int _readEncoderPins(void);
		int _encoderPin1, _encoderPin2;

		volatile int _position;

		int _min, _max;
		bool _usingmin, _usingmax;

		volatile int _previous, _time;

		static RotaryEncoder* _instance;
};

inline void RotaryEncoder::minimum(int min)
{
    _min = min;
    _usingmin = 1;

    //  adjust position if lower than new minimum
    //_position = max(_position, min);
	_position = _position > min ? _position : min;
}

inline int RotaryEncoder::minimum()
{
    return _min;
}

inline void RotaryEncoder::maximum(int max)
{
    _max = max;
    _usingmax = 1;

    //  adjust position if higher than new maximum
    //_position = min(_position, max);
	_position = _position < max ? _position : max;
}

inline int RotaryEncoder::maximum()
{
    return _max;
}

inline void RotaryEncoder::nominimum(void)
{
    _usingmin = 0;
}

inline void RotaryEncoder::nomaximum(void)
{
    _usingmax = 0;
}

inline int RotaryEncoder::position(void)
{
    return _position;
}

inline void RotaryEncoder::position(int pos)
{
    _position = pos;
}

#endif // RotaryEncoder_h

This is the RotaryEncoder.cpp file

/******************************************************************************
 *  RotaryEncoder.cpp - Arduino library for reading RotaryEncoder encoders.
 *  Version 0.90
 *  modified by Johan Larsby 2009 May 08
 ******************************************************************************/

#include "rotaryEncoder.h"

extern "C"
{
	#include
	#include
	#include "WConstants.h"	//all things wiring / arduino
}

const int _quadrature [4][4] =
{
    { 0, 0, 0, 0 },		//  00 -> 10 is silent CW
    { 1, 0, 0, 0 },	//  01 -> 00 is CW
    { -1, 0, 0, 0 },	//  10 -> 11 is CW
    { 0, 0, 0, 0 }		//  11 -> 01 is silent CW
};

RotaryEncoder * RotaryEncoder::_instance;

RotaryEncoder::RotaryEncoder(int encoderPin1, int encoderPin2)
 : _encoderPin1(encoderPin1), _encoderPin2(encoderPin2), _position(0), _min(0), _max(0), _usingmin(0), _usingmax(0), _time(0) // constructor initializer list
{
    pinMode(encoderPin1, INPUT);
    pinMode(encoderPin2, INPUT);
    digitalWrite(encoderPin1, HIGH);	//  activate internal pullups
    digitalWrite(encoderPin2, HIGH);

    _previous = _readEncoderPins();	//  read initial position

    TIMSK2 |= (1 << TOIE2);		//  enable timer 2 overflow interrupt

    _instance = this;
}

inline int RotaryEncoder::_readEncoderPins(void)
{
    return digitalRead(_encoderPin2) << 1 | digitalRead(_encoderPin1);
}

inline void RotaryEncoder::isr(void)
{
	//____________________________________________
	//                                Read Encoder
	int quadbits = _instance->_readEncoderPins();

	if (quadbits != _instance->_previous)
	{
		int position = _instance->_position - _quadrature[_instance->_previous][quadbits];

		//  limit to minimum if assigned
		position = _instance->_usingmin ? max(_instance->_min, position) : position;

		//  limit to maximum if assigned
		_instance->_position = _instance->_usingmax ? min(_instance->_max, position) : position;

		_instance->_previous = quadbits;
	}
}

ISR(TIMER2_OVF_vect)
{
    RotaryEncoder::isr();
}

Disclamer
Everything might be wrong, nothing might be correct. you have been warned!

50 thoughts on “Arduino step sequencer

  1. Hi, I am very interesed to make this. Can you give a bit more detailed info about the :
    connections between lcd and arduino
    ?

    Thank you.

    Regards,
    /M.Q.

  2. Cool, thank you for reply.
    I’ll go and have a check, if I have other questions I will come back and post here.

    Thank you for the help.

    Regards,
    /M.Q.

  3. it power up.. but the encoder does nothing…i´ve uploaded all files and connect like your schematic..

  4. Hmmm, maybe you should dumb it down a bit so that it only takes the encoder data?

  5. Done that too… and nothing.. only one line, but but the scond appear once but don´t know why.. i´m getting other display but keep trying.. Can you guive me a more detailed schematic? Thanks

  6. Would you consider selling pre programed chips so all we need to do is assemble the device? That and a simple schematic would enable a lot more of us to assemble it

  7. The chip is the arduino, and it is dead easy to program.

    the schematics could be improved, I concur.

  8. is the tempo programmable in the current configuration, or is that just something you intend to do on a future project? I wwould love to build an arduino step sequencer like yours but tempo is very important to have control over for my music.

  9. I have build this project succesfully but not after some changes due to a different lcd and some minor changes in the code to get it all working. I only think the red marked stripe in the schematic at the rotary encoder should be black (ground) If there are people intrested I would like to hear from you. I used an anag vision AV1624. This lcd needs an extra row of code lcd.begin(16, 2) to activate both rows, as some people seem to have problems with this issue.

  10. How precise is your stepper?
    Mine drifts off. After 3,5 beats it is a 1/16th off already… compared to my DAW.

  11. there could be drift depending on the type of arduino you’re using.
    what version of atmega are you utilizing?

    also, I’m so happy that there are ppl building this, I never thought that would happen!

  12. I am using an Arduino Duemilanove. Oh and btw there are not so many projects out there that do realtime midi sequencing. Most of them sequence CV. And if you like I could make a better readable schematic. Let me know.

  13. Magellan, a better schematic would be greatly appreciated by not only me I think. I’ll update my post to it and also a link to you if you want it.

  14. I’ll work it out within a few days and I let you know when it’s finished. No problem at all.

  15. would it be possible to have two sequence lines on a 16×4 display. I have some problems using 2 encoders everytime i program a second one nothing happens when I turn one of the encoders only when one is connected it seems no problem. Do I need to include the library twice? Can somebody help to figure this out? Thanks

  16. It is possible yes.
    How do you initialize the second encoder?
    Could you paste that code?

  17. To be honest I am more into schematics and building things in the analog way. I know a bit of programming but this whole arduino thing is not completly new but I have to learn a lot I know that for sure. I simply added a line of code Rotaryencoder2 and set it on the free lines 6 and 10(in my case) then I did the same for everything else in the program like notes channels velocity step lenght etc. etc. Copied it added a number 2 to it and the program compiled actually perfectly without errors. But the encoders did not work anymore. I actually got to sequence lines on the display so I tought that everything was allright so far. I know that I have to initialize the second encoder but do not know how to do that to be honest. Maybe I am thinking a bit to easy about this but you have to start somewhere and since I got this project perfectly working as discribed on this website I thought that I could learn more from it.

  18. Magellan, your solutions should work fine.

    Just send it to me in an email and I’ll have a look at it!

    /Johan

  19. Here’s the complete code as I tried it,
    it compiles without errors in Arduino 0021 but gives a fail 255 on the display for whatever that means. (the board I’m using is a dueminalove)

    /*

    Arduino Step Sequencer

    */

    #include
    #include “rotaryEncoder.h”

    /*
    make video

    upload
    PROFIT!

    */

    #define usesMidi 1 /*enable this if you want to send midi, doesent work at all together with Serial.print debugging */
    #define debugprint 0 /*this sends midi messages as text instead */

    LiquidCrystal lcd(12, 11, 7, 5, 4, 3, 2); /* Utilizing the liquidcrystal library for the screen, this is a setup-call*/

    RotaryEncoder rotary1(8, 9); /*sweet sweet library that solves a lot of problems, I made a few changes to the original one */
    RotaryEncoder rotary2(10, 13);

    /*defines for the buttons */
    #define menuButton 14
    #define onOffButton 15
    #define lengthButton 16
    #define velocityButton 17
    #define playPuaseButton 18
    #define noteButton 19

    /*booleans to tell me if the buttons are pressed or not */
    byte menuButtonIsPressed =0;
    byte onOffButtonIsPressed =0;
    byte lengthButtonIsPressed =0;
    byte velocityButtonIsPressed =0;
    byte playPuaseButtonIsPressed =0;
    byte noteButtonIsPressed =0;

    /*useful state variables */
    byte currentlyPlaying1 =0;
    byte currentlyEditing1 =0;
    boolean isPlaying1=true;
    byte currentlyPlaying2 =0;
    byte currentlyEditing2 =0;
    boolean isPlaying2=true;

    /*channel and instrument are for the midi messages */
    byte channel1 = 0;
    byte instrument1 = 0;
    byte channel2 = 0;
    byte instrument2 = 0;

    byte tempo1=120; /* bmp of our sequence */
    long timeChange1=250; /*a variable for calculating how long we are going to wait*/
    byte tempo2=120; /* bmp of our sequence */
    long timeChange2=250; /*a variable for calculating how long we are going to wait*/

    /*a struct for our notes in our sequence*/
    struct data
    {
    byte velocity1;
    byte onOff1;
    byte length1;
    byte note1;
    byte velocity2;
    byte onOff2;
    byte length2;
    byte note2;
    };

    int numberofsteps1 = 8; /* at first we have 8 steps, this is pretty basic */
    #define maxNumberOfSteps1 17 /*that’s 16*/

    data myData1[maxNumberOfSteps1]; /*array for our notes*/

    int numberofsteps2 = 8; /* at first we have 8 steps, this is pretty basic */
    #define maxNumberOfSteps2 17 /*that’s 16*/

    data myData2[maxNumberOfSteps2]; /*array for our notes*/

    /*remembering the good old times*/
    long rememberMillis1=0;
    long rememberMillisDisplay1=0;
    long rememberMillis2=0;
    long rememberMillisDisplay2=0;

    /*we’d like to present notenames instead of midi numbers */
    char* noteNames1[] = {
    “C”, “C#”, “D”, “D#”, “E”, “F”, “F#”, “G”, “G#”, “A”, “A#”, “B” };
    char* noteNames2[] = {
    “C”, “C#”, “D”, “D#”, “E”, “F”, “F#”, “G”, “G#”, “A”, “A#”, “B” };

    void setup()
    { // set up the LCD’s number of columns and rows:
    lcd.begin(16, 4);
    // Print a message to the LCD.
    lcd.setCursor(4, 1);
    lcd.print(“Startup”);
    lcd.setCursor(-2, 2);
    lcd.print(“version 1.01”);
    delay(1000);
    lcd.clear();
    /*telling everybody we’re alive */
    lcd.setCursor(2, 1);
    lcd.print(“Arduino Step”);
    lcd.setCursor(-1, 2);
    lcd.print(“sequencer”);
    delay(1000);

    /*setting up the buttons */
    pinMode(menuButton,INPUT);
    pinMode(onOffButton,INPUT);
    pinMode(lengthButton,INPUT);
    pinMode(velocityButton,INPUT);
    pinMode(playPuaseButton,INPUT);
    pinMode(noteButton,INPUT);

    /*starting the builtin pullup-resistor!*/
    digitalWrite(menuButton,HIGH);
    digitalWrite(onOffButton,HIGH);
    digitalWrite(lengthButton,HIGH);
    digitalWrite(velocityButton,HIGH);
    digitalWrite(playPuaseButton,HIGH);
    digitalWrite(noteButton,HIGH);

    /*initialization of the encoder library */
    rotary1.position(0);
    rotary2.position(0);

    #if usesMidi
    Serial.begin(31250);
    #else
    Serial.begin(9600);
    #endif

    /*calculating the timeChange variable. milliseconds divided by
    tempo and 16th note, or something like that. I dont remember*/
    timeChange1=((60000/tempo1)/8);
    timeChange2=((60000/tempo1)/8);

    /*initializing the steps in the array */
    for(int i=0;i<maxNumberOfSteps1;i++)
    {
    myData1[i].note1=48+i;
    myData1[i].velocity1=120;
    myData1[i].onOff1=1;
    myData1[i].length1=4;
    myData2[i].note1=48+i;
    myData2[i].velocity1=120;
    myData2[i].onOff1=1;
    myData2[i].length1=4;
    }

    lcd.clear();

    /*what time is now*/

    rememberMillis1 = millis();
    rememberMillisDisplay1 = millis();
    rememberMillis2 = millis();
    rememberMillisDisplay2 = millis();
    }

    /* a little function to print the name of the note */
    void printNoteName(byte initialNote)
    {
    byte octave1 = (initialNote / 12) – 1;
    byte noteIndex1 = (initialNote % 12);
    byte octave2 = (initialNote / 12) – 1;
    byte noteIndex2 = (initialNote % 12);

    lcd.setCursor(0, 0);
    lcd.print(noteNames1[noteIndex1]);
    lcd.print(octave1,DEC);
    lcd.setCursor(-4, 2);
    lcd.print(noteNames2[noteIndex2]);
    lcd.print(octave2,DEC);

    }
    /* this functions prints what we have in the first "menu" part of the data
    tempo, channel and instrument*/
    void populateDisplayMenuButtonPressed()
    {

    lcd.setCursor(0, 0) ;

    lcd.print("t:");
    lcd.print(tempo1,DEC);
    lcd.print(" c:");
    lcd.print(channel1,DEC);
    lcd.print(" i:");
    lcd.print(instrument1,DEC);

    lcd.setCursor(-4, 2) ;

    lcd.print("t:");
    lcd.print(tempo2,DEC);
    lcd.print(" c:");
    lcd.print(channel2,DEC);
    lcd.print(" i:");
    lcd.print(instrument2,DEC);

    if (!isPlaying1)
    if (!isPlaying2)
    {
    lcd.setCursor(0, 1) ;
    lcd.print("PAUSED ");
    lcd.setCursor(-4, 2) ;
    lcd.print("PAUSED ");
    }
    else
    {
    lcd.setCursor(0, 1) ;
    for(int i=0;i<numberofsteps1;i++)
    {
    if(i==currentlyPlaying1 && i==currentlyEditing1 )
    lcd.print("+");
    else if(i==currentlyEditing1)
    lcd.print("-");
    else if(i==currentlyPlaying1)
    lcd.print("|");
    else
    lcd.print(".");

    }
    /* prints spaces for the rest of the line, it is better then using a
    lcd.clear, atleast if you call lcd.clear fast/often */
    for(int i=numberofsteps1;i< maxNumberOfSteps1 ;i++)
    {
    lcd.print(" ");

    }
    }
    {
    lcd.setCursor(-4, 3) ;
    for(int i=0;i<numberofsteps2;i++)
    {
    if(i==currentlyPlaying2 && i==currentlyEditing2 )
    lcd.print("+");
    else if(i==currentlyEditing2)
    lcd.print("-");
    else if(i==currentlyPlaying2)
    lcd.print("|");
    else
    lcd.print(".");

    }
    /* prints spaces for the rest of the line, it is better then using a
    lcd.clear, atleast if you call lcd.clear fast/often */
    for(int i=numberofsteps2;i< maxNumberOfSteps2 ;i++)
    {
    lcd.print(" ");

    }
    }
    }

    /*this is the normal/regular display info printing routine */
    void populateDisplay()
    {

    lcd.setCursor(0, 0) ;
    if(isPlaying1)
    {
    printNoteName(myData1[currentlyEditing1].note1);
    lcd.print(" ");
    /*On if the note is On, Off it is Off, getting it? getting on or getting off, muhahahaha */
    if(myData1[currentlyEditing1].onOff1)
    lcd.print("On");
    else
    lcd.print("Off");

    lcd.print(" ");
    {
    /*what kind of note what we are dealing with */
    switch (myData1[currentlyEditing1].length1)
    {
    case 1:
    lcd.print("32nd");
    break;
    case 2:
    lcd.print("16th");
    break;
    case 3:
    lcd.print("8th");
    break;
    case 4:
    lcd.print("4th");
    break;
    case 5:
    lcd.print("2nd");
    break;
    case 6:
    lcd.print("whole");
    break;
    default:
    lcd.print("FAIL");
    break; /*we made bu bu*/
    }
    }
    lcd.print(" ");

    lcd.print(myData1[currentlyEditing1].velocity1,DEC);
    lcd.print(" ");

    lcd.setCursor(0, 1) ;
    for(int i=0;i<numberofsteps1;i++)
    {
    if(i==currentlyPlaying1 && i==currentlyEditing1 )
    lcd.print("+");
    else if(i==currentlyEditing1)
    lcd.print("-");
    else if(i==currentlyPlaying1)
    lcd.print("|");
    else
    lcd.print(".");
    }

    /* prints spaces for the rest of the line, it is better then using a
    lcd.clear, atleast if you call lcd.clear fast/often */

    for(int i=numberofsteps1;i< maxNumberOfSteps1 ;i++)
    {
    lcd.print(" ");

    }

    }
    else
    {
    lcd.setCursor(0, 1) ;
    lcd.print("PAUSED ");
    }
    lcd.setCursor(-4, 2) ;
    if(isPlaying2)
    {
    printNoteName(myData2[currentlyEditing2].note2);
    lcd.print(" ");
    /*On if the note is On, Off it is Off, getting it? getting on or getting off, muhahahaha */
    if(myData2[currentlyEditing2].onOff2)
    lcd.print("On");
    else
    lcd.print("Off");

    lcd.print(" ");
    {
    /*what kind of note what we are dealing with */
    switch (myData2[currentlyEditing2].length2)
    {
    case 1:
    lcd.print("32nd");
    break;
    case 2:
    lcd.print("16th");
    break;
    case 3:
    lcd.print("8th");
    break;
    case 4:
    lcd.print("4th");
    break;
    case 5:
    lcd.print("2nd");
    break;
    case 6:
    lcd.print("whole");
    break;
    default:
    lcd.print("FAIL");
    break; /*we made bu bu*/
    }
    }
    lcd.print(" ");

    lcd.print(myData2[currentlyEditing2].velocity2,DEC);
    lcd.print(" ");

    lcd.setCursor(0, 1) ;
    for(int i=0;i<numberofsteps2;i++)
    {
    if(i==currentlyPlaying2 && i==currentlyEditing2 )
    lcd.print("+");
    else if(i==currentlyEditing2)
    lcd.print("-");
    else if(i==currentlyPlaying2)
    lcd.print("|");
    else
    lcd.print(".");
    }

    /* prints spaces for the rest of the line, it is better then using a
    lcd.clear, atleast if you call lcd.clear fast/often */

    for(int i=numberofsteps2;i 100)
    if( millis()-rememberMillisDisplay2 > 100)
    {
    rememberMillisDisplay1=millis();
    checkButtons();
    if (!menuButtonIsPressed)
    populateDisplay();
    rememberMillisDisplay2=millis();
    checkButtons();
    if (!menuButtonIsPressed)
    populateDisplay();
    else
    populateDisplayMenuButtonPressed();
    }

    if(isPlaying1)
    if(isPlaying2)
    {

    if( millis()-rememberMillis1 > ((myData1[currentlyPlaying1].length1 * myData1[currentlyPlaying1].length1 )*(timeChange1)))
    if( millis()-rememberMillis2 > ((myData1[currentlyPlaying2].length2 * myData1[currentlyPlaying2].length2 )*(timeChange2)))
    {
    //fuck it, we always send off, most MIDI devices wont crap themselves.
    sendMidiMessage(0x80, myData1[currentlyPlaying1].note1,120); //off
    sendMidiMessage(0x80, myData2[currentlyPlaying2].note2,120);

    currentlyPlaying1++;
    if (currentlyPlaying1==numberofsteps1)
    currentlyPlaying1=0;
    currentlyPlaying2++;
    if (currentlyPlaying2==numberofsteps2)
    currentlyPlaying2=0;

    rememberMillis1=millis();
    rememberMillis2=millis();

    if( myData1[currentlyPlaying1].onOff1)
    sendMidiMessage(0x90, myData1[currentlyPlaying1].note1, 120); //on
    if( myData2[currentlyPlaying2].onOff1)
    sendMidiMessage(0x90, myData2[currentlyPlaying2].note2, 120);

    }
    }

    }

    /* This functions makes almost all our values be more correct.
    like, we dont want negative values or out of range values */
    void SanitizeValues()
    {

    myData1[currentlyEditing1].onOff1= myData1[currentlyEditing1].onOff1 % 2; //setting the current value to be inbetween 0 and 1
    myData1[currentlyEditing1].note1= myData1[currentlyEditing1].note1 % 128; //setting the current value to be inbetween 0 and 127
    myData1[currentlyEditing1].velocity1= myData1[currentlyEditing1].velocity1 % 128; //setting the current value to be inbetween 0 and 127
    currentlyEditing1=(currentlyEditing1 % (numberofsteps1));
    isPlaying1 = isPlaying1%2;

    myData1[currentlyEditing2].onOff2= myData2[currentlyEditing2].onOff2 % 2; //setting the current value to be inbetween 0 and 1
    myData1[currentlyEditing2].note2= myData2[currentlyEditing2].note2 % 128; //setting the current value to be inbetween 0 and 127
    myData1[currentlyEditing2].velocity2= myData2[currentlyEditing2].velocity2 % 128; //setting the current value to be inbetween 0 and 127
    currentlyEditing2=(currentlyEditing2 % (numberofsteps2));
    isPlaying2 = isPlaying2%2;

    if(numberofsteps1 > maxNumberOfSteps1)
    numberofsteps1=maxNumberOfSteps1;
    if(numberofsteps2 > maxNumberOfSteps2)
    numberofsteps2=maxNumberOfSteps2;

    if(numberofsteps1 < 0)
    numberofsteps1=0;
    if(numberofsteps2 6)
    myData1[currentlyEditing1].length1=6;
    if(myData1[currentlyEditing2].length2 > 6)
    myData2[currentlyEditing2].length2=6;

    if(myData1[currentlyEditing1].length1 < 1)
    myData1[currentlyEditing1].length1=1;
    if(myData2[currentlyEditing2].length2 < 1)
    myData2[currentlyEditing2].length2=1;

    }
    else if(velocityButtonIsPressed)
    myData1[currentlyEditing1].velocity1+=encoder0Pos1;
    else if(velocityButtonIsPressed)
    myData2[currentlyEditing2].velocity2+=encoder0Pos2;
    else if(playPuaseButtonIsPressed)
    isPlaying1+=encoder0Pos1;
    else if(playPuaseButtonIsPressed)
    isPlaying2+=encoder0Pos2;

    else
    currentlyEditing1+=encoder0Pos1;
    currentlyEditing2+=encoder0Pos2;
    }

    rotary1.position(0);
    rotary2.position(0);
    SanitizeValues();
    }

    void sendMidiMessage(char cmd, char data1, char data2)
    {

    #if debugprint
    Serial.print("Sending MIDI ");
    Serial.print(cmd,DEC);
    Serial.print(" ");
    Serial.print(data1,DEC);
    Serial.print(" ");
    Serial.println(daserial.pta2,DEC);
    #endif
    //not sending channel or instrument change yet…
    #if usesMidi
    Serial.print(cmd, BYTE);
    Serial.print(data1, BYTE);
    Serial.print(data2, BYTE);
    #endif
    }

  20. Hello,

    I am currently making this projet for my semester project in college. I tried compiling the code but I got an error that had to do something with the .cpp file:

    C:\Documents and Settings\ElectroCSL\Bureau\arduino-0021\libraries\rotaryEncoder\rotaryEncoder.cpp:11:10: error: #include expects “FILENAME” or
    C:\Documents and Settings\ElectroCSL\Bureau\arduino-0021\libraries\rotaryEncoder\rotaryEncoder.cpp:12:10: error: #include expects “FILENAME” or
    C:\Documents and Settings\ElectroCSL\Bureau\arduino-0021\libraries\rotaryEncoder\rotaryEncoder.cpp:66: error: expected constructor, destructor, or type conversion before ‘(‘ token

    Also, I don’t know if I was doing it wrong but from what I understand, I created new .h and .cpp files and copy pasted the code you provided. I put both files in the libraries folder in a new folder called rotaryEncoder. Is this the right way to use the .h and .cpp files?

    My versions of this sequencer will be made with the 4×4 sparkfun button pads and I got RGB LEDs to light up the buttons…it will be a pain in the bum to program the lights -_-

    Thanks!

    -Carlos

  21. @Carlos the encoder files should be in the same directory as the ass.pde file.

    @Magellan this was my fault, sorry, I should have rememberd. You cant make a new encoder like that. If you have a look into the encoder.cpp file you see that there is an interrupt at work. That can only be used once. My suggestion would be to change the library file to take in stuff for one more encoder. I could do that for you if you want.

  22. It would be great if it would not be to much work for you to change the library file so I can use a second encoder. Alltough I’m not sure how my final step sequencer would look like I actually added a midi clock to the code that works fine and I am trying to use the midi input to transpose the running sequence into the key depressed on a midi keyboard. I think that this must be possible. I’ll let you how I’m progressing.

  23. Hmm after including both files in the ass.pde directory, I still get the

    rotaryEncoder.cpp:64: error: expected constructor, destructor, or type conversion before ‘(‘ token

    error which points me to

    ISR (TIMER2_OVF_vect)

    at the bottom of the .cpp file

  24. sorry I havent been around as much lately.

    did you get it to work carlos? or do you want a zip with the files that worked for me?

    if that is the case gimme you’re email.

    @magellan would you still like me to give that a shoot? making a dual encoder version?

  25. I would really appreciate if you would like to give it a shoot. Two midi lines would be great to work with. Hopefully when I see the differnce between one encoder and two encoders I understand the working of the whole concept. I really don’t know where to start. So it would be great if you have some time to give it a shoot.
    – thanks

    maggelan

  26. Would i be using normally closed pushbuttons or normally open Pushbuttons in this build? or would it matter?

  27. Hello guys.. I am so glad i found this page. I just ordered a Arduino Mega 1280 and a LCD Shield. I want to build something similar to this. I am completely new to Arduino. I am very interested in sounds and did already built a fully modded x0xb0x and a midibox sid Mb-6582. Is there already a code that has implemented a midi clock where you could define the BPM ? I have to wait till i get my Arduino to start testing out what i see so far as my understanding is in the real early phase yet. I hope i can get hold of the logic behind arduino coding soon.
    Thank you guys

  28. as the analogue inputs on an arduino are translated to the microcontroller as a number between o and 1024 to create a tempo control set an algorythm to assign a value range of your usable tempos by saying integer tempo=(analogue in)/5) +45) (this gives you approximately 45-250)

    then apply that to the clock of your program by telling the program to do whatever in that time by an algorythm to your microcontroller by either a line of code that multiplies (microcontroller clock time in processes per second)times 60 (to give you a minute) and then divide each of these minutes by the tempo should give a loop repeating at a bpm timing for the program. I’m no good at actually writing in arduino language but that should be an algorythm that should work using any language

  29. Hey, Ive been trying to this together but have trouble in arduino with the library rotary.h . not really sure if I’m installing the library right?
    Is there anyway I could get a zip of this program too?

  30. What is suppose to be going on with this code?

    for(int i=0;iif(i==currentlyPlaying && i==currentlyEditing )
    lcd.print(“+”);
    else if(i==currentlyEditing)
    lcd.print(“-“);
    else if(i==currentlyPlaying)
    lcd.print(“|”);
    else
    lcd.print(“.”);

  31. Hello again,

    I’ve still been trying to get this sequencer work with more than one channel and now with one rotary encoder. What I tried is to write the data in to (to start with) two arrays. I don,t get any errors when I compile but it is not working on the hardware. Can anyone tell me if my ideas can work or that it is simply not possible.

    Thanks for any support.
    /magellan
    /*

    Arduino Step Sequencer

    */

    #include
    #include “rotaryEncoder.h”
    #include
    #include
    #include
    MidiClass Midi;

    /*
    make video

    upload
    PROFIT!

    */

    #define usesMidi 1 /*enable this if you want to send midi, doesent work at all together with Serial.print debugging */
    #define debugprint 0 /*this sends midi messages as text instead */

    LiquidCrystal lcd(12, 11, 7, 5, 4, 3, 2); /* Utilizing the liquidcrystal library for the screen, this is a setup-call*/

    RotaryEncoder rotary(8, 9); /*sweet sweet library that solves a lot of problems, I made a few changes to the original one */

    /*defines for the buttons */
    #define menuButton 14
    #define onOffButton 15
    #define lengthButton 16
    #define velocityButton 17
    #define playPauseButton 18
    #define noteButton 19
    int ledPin = 13;

    /*booleans to tell me if the buttons are pressed or not */
    byte menuButtonIsPressed =0;
    byte onOffButtonIsPressed =0;
    byte lengthButtonIsPressed =0;
    byte velocityButtonIsPressed =0;
    byte playPauseButtonIsPressed =0;
    byte noteButtonIsPressed =0;

    /*useful state variables */
    byte currentlyPlaying =0;
    byte currentlyEditing =0;
    boolean isPlaying=true;

    /*channel and instrument are for the midi messages */
    byte channel=0;
    byte instrument =0;

    byte tempo=120; /* bmp of our sequence */
    long timeChange=250; /*a variable for calculating how long we are going to wait*/

    /*a struct for our notes in our sequence*/
    struct data
    {
    byte velocity;
    byte onOff;
    byte length;
    byte note;
    };

    int numberofsteps = 8; /* at first we have 8 steps, this is pretty basic */
    #define maxNumberOfSteps 17 /*that’s 16*/

    data myData[maxNumberOfSteps];/*array for our notes*/
    data myData1[maxNumberOfSteps];/*array for our notes*/

    int track = 1;
    #define maxtrack 17

    data myData100[maxtrack];

    /*remembering the good old times*/
    long rememberMillis=0;
    long rememberMillisDisplay =0;

    /*we’d like to present notenames instead of midi numbers */
    char* noteNames[] = {
    “C”, “C#”, “D”, “D#”, “E”, “F”, “F#”, “G”, “G#”, “A”, “A#”, “B” };

    void setup()
    {

    pinMode(ledPin, OUTPUT);
    MidiUart.init();
    MidiClock.mode = MidiClock.INTERNAL_MIDI;
    MidiClock.setTempo(tempo);
    MidiClock.transmit = true;
    MidiClock.setOn16Callback(on16Callback);
    MidiClock.start();
    // set up the LCD’s number of columns and rows:
    lcd.begin(16, 2);
    // Print a message to the LCD.
    lcd.print(” Startup”);
    lcd.setCursor(0, 1);
    lcd.print(” version 1.01″);
    delay(1000);
    lcd.clear();
    /*telling everybody we’re alive */
    lcd.print(” Arduino Step”);
    lcd.setCursor(0, 1);
    lcd.print(” sequencer”);
    delay(1000);

    /*setting up the buttons */
    pinMode(menuButton,INPUT);
    pinMode(onOffButton,INPUT);
    pinMode(lengthButton,INPUT);
    pinMode(velocityButton,INPUT);
    pinMode(playPauseButton,INPUT);
    pinMode(noteButton,INPUT);

    /*starting the builtin pullup-resistor!*/
    digitalWrite(menuButton,HIGH);
    digitalWrite(onOffButton,HIGH);
    digitalWrite(lengthButton,HIGH);
    digitalWrite(velocityButton,HIGH);
    digitalWrite(playPauseButton,HIGH);
    digitalWrite(noteButton,HIGH);

    /*initialization of the encoder library */
    rotary.position(0);

    #if usesMidi
    Serial.begin(31250);
    #else
    Serial.begin(9600);
    #endif

    /*calculating the timeChange variable. milliseconds divided by
    tempo and 16th note, or something like that. I dont remember*/
    timeChange=((60000/tempo)/8);

    /*initializing the steps in the array */
    for(int i=0;i<maxNumberOfSteps;i++)
    {
    myData[i].note=48+i;
    myData[i].velocity=120;
    myData[i].onOff=1;
    myData[i].length=4;
    myData1[i].note=48+i;
    myData1[i].velocity=120;
    myData1[i].onOff=1;
    myData1[i].length=4;
    }

    lcd.clear();

    /*what time is now*/

    rememberMillis = millis();
    rememberMillisDisplay = millis();
    }

    void on16Callback() {
    if ((MidiClock.div16th_counter % 4) == 0) {
    digitalWrite(ledPin, HIGH);

    } else if ((MidiClock.div16th_counter % 4) == 2) {
    digitalWrite(ledPin, LOW);

    }
    }
    /* a little function to print the name of the note */
    void printNoteName(byte initialNote)
    {
    byte octave = (initialNote / 12) – 1;
    byte noteIndex = (initialNote % 12);

    lcd.print(noteNames[noteIndex]);
    lcd.print(octave,DEC);
    }

    /* this functions prints what we have in the first "menu" part of the data
    tempo, channel and instrument*/
    void populateDisplayMenuButtonPressed()
    {

    lcd.setCursor(0, 0) ;

    lcd.print("t");
    lcd.print(tempo,DEC);
    lcd.print("c");
    lcd.print(channel,DEC);
    lcd.print("i");
    lcd.print(instrument,DEC);
    lcd.print("tr");
    lcd.print(track,DEC);

    if (!isPlaying)
    {
    lcd.setCursor(0, 1) ;
    lcd.print("PAUSED ");
    }
    else
    {
    lcd.setCursor(0, 1) ;
    for(int i=0;i<numberofsteps;i++)
    {
    if(i==currentlyPlaying && i==currentlyEditing )
    lcd.print("+");
    else if(i==currentlyEditing)
    lcd.print("-");
    else if(i==currentlyPlaying)
    lcd.print("|");
    else
    lcd.print(".");
    }
    /* prints spaces for the rest of the line, it is better then using a
    lcd.clear, atleast if you call lcd.clear fast/often */
    for(int i=numberofsteps;i< maxNumberOfSteps ;i++)
    {
    lcd.print(" ");

    }
    }
    }

    /*this is the normal/regular display info printing routine */
    void populateDisplay()
    {
    lcd.setCursor(0, 0) ;
    if(isPlaying)
    {
    printNoteName(myData[currentlyEditing].note);
    lcd.print(" ");
    /*On if the note is On, Off it is Off, getting it? getting on or getting off, muhahahaha */
    if(myData[currentlyEditing].onOff)
    lcd.print("On");
    else
    lcd.print("Off");

    lcd.print(" ");

    printNoteName(myData1[currentlyEditing].note);
    lcd.print(" ");
    /*On if the note is On, Off it is Off, getting it? getting on or getting off, muhahahaha */
    if(myData1[currentlyEditing].onOff)
    lcd.print("On");
    else
    lcd.print("Off");

    lcd.print(" ");
    {
    /*what kind of note what we are dealing with */
    switch (myData[currentlyEditing].length)
    switch (myData1[currentlyEditing].length)
    {
    case 1:
    lcd.print("32nd");
    break;
    case 2:
    lcd.print("16th");
    break;
    case 3:
    lcd.print("8th");
    break;
    case 4:
    lcd.print("4th");
    break;
    case 5:
    lcd.print("2nd");
    break;
    case 6:
    lcd.print("whole");
    break;
    default:
    lcd.print("FAIL");
    break; /*we made bu bu*/

    }
    }
    lcd.print(" ");

    lcd.print(myData[currentlyEditing].velocity,DEC);
    lcd.print(myData1[currentlyEditing].velocity,DEC);
    lcd.print(" ");

    lcd.setCursor(0, 1) ;
    for(int i=0;i<numberofsteps;i++)
    {
    if(i==currentlyPlaying && i==currentlyEditing )
    lcd.print("+");
    else if(i==currentlyEditing)
    lcd.print("-");
    else if(i==currentlyPlaying)
    lcd.print("|");
    else
    lcd.print(".");
    }

    /* prints spaces for the rest of the line, it is better then using a
    lcd.clear, atleast if you call lcd.clear fast/often */

    for(int i=numberofsteps;i 100)
    {
    rememberMillisDisplay=millis();
    checkButtons();
    if (!menuButtonIsPressed)
    populateDisplay();
    else
    populateDisplayMenuButtonPressed();
    }

    if(isPlaying)
    {

    if( millis()-rememberMillis > ((myData[currentlyPlaying].length * myData[currentlyPlaying].length )*(timeChange)))
    if( millis()-rememberMillis > ((myData1[currentlyPlaying].length * myData1[currentlyPlaying].length )*(timeChange)))
    {
    //fuck it, we always send off, most MIDI devices wont crap themselves.
    sendMidiMessage(0x80, myData[currentlyPlaying].note,120); //off
    sendMidiMessage(0x80, myData1[currentlyPlaying].note,120); //off

    currentlyPlaying++;
    if (currentlyPlaying==numberofsteps)
    currentlyPlaying=0;

    rememberMillis=millis();

    if( myData[currentlyPlaying].onOff)
    sendMidiMessage(0x90, myData[currentlyPlaying].note, 120); //on
    if( myData1[currentlyPlaying].onOff)
    sendMidiMessage(0x90, myData1[currentlyPlaying].note, 120); //on

    }
    }

    while (MidiUart.avail()) {
    Midi.handleByte(MidiUart.getc());
    }
    switch (track){
    case 1:
    myData;
    break;
    case 2:
    myData1;
    break;
    }
    }

    /* This functions makes almost all our values be more correct.
    like, we dont want negative values or out of range values */
    void SanitizeValues()
    {

    myData[currentlyEditing].onOff= myData[currentlyEditing].onOff % 2; //setting the current value to be inbetween 0 and 1
    myData[currentlyEditing].note= myData[currentlyEditing].note % 128; //setting the current value to be inbetween 0 and 127
    myData[currentlyEditing].velocity= myData[currentlyEditing].velocity % 128; //setting the current value to be inbetween 0 and 127
    myData1[currentlyEditing].onOff= myData[currentlyEditing].onOff % 2; //setting the current value to be inbetween 0 and 1
    myData1[currentlyEditing].note= myData[currentlyEditing].note % 128; //setting the current value to be inbetween 0 and 127
    myData1[currentlyEditing].velocity= myData[currentlyEditing].velocity % 128; //setting the current value to be inbetween 0 and 127
    currentlyEditing=(currentlyEditing % (numberofsteps));
    isPlaying = isPlaying%2;

    if(numberofsteps > maxNumberOfSteps)
    numberofsteps=maxNumberOfSteps;

    if(numberofsteps 6)
    myData[currentlyEditing].length=6;

    if(myData1[currentlyEditing].length > 6)
    myData1[currentlyEditing].length=6;

    myData1[currentlyEditing].length+=encoder0Pos;

    if(myData[currentlyEditing].length < 1)
    myData[currentlyEditing].length=1;

    if(myData1[currentlyEditing].length < 1)
    myData1[currentlyEditing].length=1;

    }
    else if(velocityButtonIsPressed)
    myData[currentlyEditing].velocity+=encoder0Pos;
    else if(velocityButtonIsPressed)
    myData1[currentlyEditing].velocity+=encoder0Pos;
    else if(playPauseButtonIsPressed)
    isPlaying+=encoder0Pos;

    else
    currentlyEditing+=encoder0Pos;
    }

    rotary.position(0);
    SanitizeValues();
    }

    void sendMidiMessage(char cmd, char data1, char data2)
    {

    #if debugprint
    Serial.print("Sending MIDI ");
    Serial.print(cmd,DEC);
    Serial.print(" ");
    Serial.print(data1,DEC);
    Serial.print(" ");
    Serial.println(daserial.pta2,DEC);
    #endif
    //not sending channel or instrument change yet…
    #if usesMidi
    Serial.print(cmd, BYTE);
    Serial.print(data1, BYTE);
    Serial.print(data2, BYTE);
    #endif
    }

  32. Magellan, what is not working on the hardware? could you describe the symptoms?

    /Johan

  33. Wilson the encoder.h should be in the same directory.

    The second question you have has the answer of being the the print stuff on screen, you’ll recognize the characters when you watch the video!

  34. Hello again,

    first of all I’ll explain what I want the soft/hardware to do. I want to play more than one track at once. Let’s say 4 or 8 tracks if the hardware is quick enough to check what is going on in every track. Each track must have its own lenght an of course different notes with velocity and lenght. I asume that every track is written into an array and that I have to copy some parts of the program. I copied in the program I posted the part called mydata but thinking of it now I probably need to to the same with the part called currentlyplaying and currentlyediting. The hardware showed on the screen nicely tr1 when pushing the menupressedbutton, but when I change the track to tr2 the notes, onoff, velocity, channel and lenght are exactly the same as in the first track. I want them to be different of course because I select a different track with different note information. All these events should be played at once. I hope this is possible in some way because in this way I should be able to avoid a second rotary encoder. The external midi sync is working good and the abbilaty to play 4 or more tracks at once could make this a very usefull sequencer. Thanks for any support.

    /Magellan

  35. What value is the resistor that’s labeled “5,7”? I haven’t seen that notation before.

  36. The value for the display I used should be 5,7 ohms according to the datasheet. It depends on the display you use.

  37. I compiled the firmware (with the changes in the schematics) with no errors and done the circuit but nothing is working 🙁
    I’m using Arduino 2009 and a Sparkfun display. What I can check guys?

Leave a Reply