I need help with sequence with millis()

I am trying to get on with millis() on my MIDI projects. I know how to play notes using millis(), to play multiple notes at the same time. However I can not grasp the idea for creating a sequence of multiple notes that play at given interval. My code with delay() I have been using so far is like this

{ 
    MIDI.sendNoteOn(54,127,c);
    delay(300);
    MIDI.sendNoteOn(58,127,c);
    delay(300);
    MIDI.sendNoteOn(66,127,c);
    delay(100);
    MIDI.sendNoteOff(54,0,c);
    MIDI.sendNoteOff(58,0,c);
    MIDI.sendNoteOff(66,0,c); 

  }

So something like this would happen: send noteOn-100ms-noteOff-wait 300ms; send another noteOn-100ms-noteOff-wait 300ms; send third noteOn-50ms-noteOff-wait 100ms. Or I could construct set of universal delays with millis that I can use to interval any notes just like I use delay()?

vanakaru: I know how to play notes using millis()

millis() doesn't play notes

vanakaru: Or I could construct set of universal delays with millis that I can use to interval any notes just like I use delay()?

State Machine:

enum {FIRST_STATE, SECOND_STATE, THIRD_STATE} state = FIRST_STATE;
unsigned long lastStateChange = millis();

...

switch(state)
{
  case FIRST_STATE:
    // DO SOMETHING CONTINUOUSLY
    if (millis() - lastStateChange > 1000)
    {
      // DO SOMETHING ONCE
      lastStateChange = millis();
      state = SECOND_STATE;
    }
    break;
  case SECOND_STATE:
    // DO SOMETHING ONCE
    lastStateChange = millis();
    state = THIRD_STATE;
    break;
  case THIRD_STATE:
    // DO SOMETHING CONTINUOUSLY
    if (millis() - lastStateChange > 2000)
    {
      // DO SOMETHING ONCE
      lastStateChange = millis();
      state = FIRST_STATE;
    }
    break;
  default:
    // you'll never be here
    break;
}

Thanks! Pretty simple and eloquent. Got it working!

Arrch:  default:    // you'll never be here    break;

I'd change that to:

default:
//you SHOULD never get here...but just in case
state = FIRST_STATE;
// will get you back to the start condition
break;

and maybe add aSerial.println("Oooops!");

i think a possible solution would be to create an array with the sequence you need.

Then you could send note[n], wait as long as you need (if possible without using the delay() ), stop note[n] and play note[n+1], wait, stop note[n+1] and play note[n+2], and so on...

I have used this before and it worked great. You can make arrays for scales, melodies and even for the rhythm (durations). ;)

I did look into array and this would be much better way to do this.
But I do not get it right. This code(with delay() for now) will play notes 0, 1, 2, 3 and not the ones in the array 72, 77, 81,84.

int mynote[] = {
  72,77,81,84};
int noteCount = 4;  

void setup() {
  //  Set MIDI baud rate:
  Serial.begin(31250);
}

void loop() {


  for (int mynote = 0; mynote < noteCount; mynote++) {
    //Note on channel 1 (0x90), some note value (note), middle velocity (0x45):
    noteOn(0x90, mynote, 0x45);
    delay(500);
    //Note on channel 1 (0x90), some note value (note), silent velocity (0x00):
    noteOn(0x90, mynote, 0x00);   
    delay(500);
  }
}

//  plays a MIDI note.  Doesn't check to see that
//  cmd is greater than 127, or that data values are  less than 127:
void noteOn(int cmd, int pitch, int velocity) {
  Serial.print(cmd, BYTE);
  Serial.print(pitch, BYTE);
  Serial.print(velocity, BYTE);
}

vanakaru:
I did look into array and this would be much better way to do this.
But I do not get it right. This code(with delay() for now) will play notes 0, 1, 2, 3 and not the ones in the array 72, 77, 81,84.

Try using a pointer into your array, like this:

void loop() {
  for (int x = 0; x < noteCount; x++) {
    //Note on channel 1 (0x90), some note value (note), middle velocity (0x45):
    noteOn(0x90, mynote[x], 0x45);
    delay(500);
    //Note on channel 1 (0x90), some note value (note), silent velocity (0x00):
    noteOn(0x90, mynote[x], 0x00);   
    delay(500);
  }
}

yep, like Henry-Best said, the trick using arrays is that to access each of the elements of the array you have to use a pointer, something like:

mynote[1] //would be note 77, the second element of your mynote array

Then, by using a for loop, you can access all of the notes in a sequence.

one other idea... You are sending both your noteOn and your noteOff like this:

noteOn(0x90, mynote, 0x45);

You are always using as first parameter for your function 0x90. 0x90 is the MIDI noteOn command, so that is fine to send when you want to play a note (send a noteOn command).

But the way you are doing things now you aren't really sending a noteOff, you are just sending a noteOn with 0 velocity (0 "volume"), which kind of "sounds" the same. But I would suggest that you change that and use the MIDI noteOff command: 0x80.

Here is a nice page with some useful information about MIDI protocol: https://ccrma.stanford.edu/~craig/articles/linuxmidi/misc/essenmidi.html

;)

Henry-Best and boguz, thank you for helping with this. Finally I start to understand. This is the bit I was missing - ”pointer”. Somehow I could not understand it from the Arduino - Array page. I wish there would be more comments with examples.

i remember i also had some trouble understanding Arrays. But when you get the hang of it, you will see they are really useful and not so difficult to use!

In your case, you could even use an array for the notes, another for the velocities and another for the durations (if needed!), to have some more practice on using arrays... This would allow you to play a melody with rhythm and dynamics. Not bad... ;)

I tried to construct something, but I am not sure what this is doing actually. Can you explain it to me and how it should be written.

int mynote[] = {
  51, 60, 96, 88, 63};//write midi sequence
int noteCount = 5; //need to tell how many notes in array
int dur[] = {
  3000, 500, 3000, 1000, 2000};//array of delays/durations
int durCount = 5;
long previousMillis = 0;  //call millis     


void setup() {
  //  Set MIDI baud rate:
  Serial.begin(31250);



}



void loop() {





  //declare x=0 as object in array
  for (int x = 0; x < noteCount; x++)
    for (int t= 0; t <durCount; t++) {
      unsigned long currentMillis = millis();//start millis
      if(currentMillis - previousMillis >dur[t]) {//dur[t] should be pulling from array of delays

        //Note on channel 1 (0x90), some note value (x will draw number from array), velocity 127 (0x7F):
        noteOn(0x90, mynote[x], 0x7F);
        //Note off channel 1 (0x80), some note value (note), silent velocity (0x00):
        noteOn(0x80, mynote[x], 0x00); 
        previousMillis = currentMillis; 
      }
    }

}



//  plays a MIDI note.  Doesn't check to see that
//  cmd is greater than 127, or that data values are  less than 127:
void noteOn(int cmd, int pitch, int velocity) {
  Serial.print(cmd, BYTE);
  Serial.print(pitch, BYTE);
  Serial.print(velocity, BYTE);
}

the noteCount and the durCount will be the same length, so you’ll need only one of them (maybe the noteCount).

then you can use the for loop like before, but use the pointers to your arrays. Something like

for (int x = 0; x < noteCount; x++) {
    noteOn(0x90, mynote[x], 0x45);   // send note
    delay(dur[x]);                   // wait corresponding delay
    noteOn(0x80, mynote[x], 0x00);   // send "noteOff"
}

but you should replace that delay() with the “delay” idea from the BlinkWithoutDelay example.

What you need to do, is in the right places use the pointers. So for the notes you “point” to the mynote array, for the durations you “point” to the dur array,…

Yes, but I am trying to do this with millis(). And It seems to use the lowest given value in duration array - 500. Between every note played.

Yes, but I am trying to do this with millis().

Posting code in zero point font is not useful.

The code is one post up as well. Or what do you mean by 0 point font?

int mynote[] = {
  51, 60, 96, 88, 63};//write midi sequence
int noteCount = 5; //need to tell how many notes in array
int dur[] = {
  3000, 500, 3000, 6000, 2000};//array of delays/durations
int durCount = 5;
long previousMillis = 0;  //call millis     


void setup() {
  //  Set MIDI baud rate:
  Serial.begin(31250);



}



void loop() {





  //declare x=0 as object in array
  for (int x = 0; x < noteCount; x++)
    for (int t= 0; t <durCount; t++) {
      unsigned long currentMillis = millis();//start millis
      if(currentMillis - previousMillis > dur[t]) {//dur[t] should be pulling from array of delays

        //Note on channel 1 (0x90), some note value (x will draw number from array), velocity 127 (0x7F):
        noteOn(0x90, mynote[x], 0x7F);
        //Note off channel 1 (0x80), some note value (note), silent velocity (0x00):
        noteOn(0x80, mynote[x], 0x00); 
        previousMillis = currentMillis; 
      }
    }

}



//  plays a MIDI note.  Doesn't check to see that
//  cmd is greater than 127, or that data values are  less than 127:
void noteOn(int cmd, int pitch, int velocity) {
  Serial.print(cmd, BYTE);
  Serial.print(pitch, BYTE);
  Serial.print(velocity, BYTE);
}
  for (int x = 0; x < noteCount; x++)
    for (int t= 0; t <durCount; t++)
    {
      unsigned long currentMillis = millis();//start millis
      if(currentMillis - previousMillis > dur[t])
      {//dur[t] should be pulling from array of delays
        previousMillis = currentMillis; 
      }
    }

Suppose that previousMillis is 0. The current time is stored in currentMillis. This code being in loop, and called pretty quickly after the Arduino starts, currentMillis is going to be a small value. dur[ 0 ] is 3000. What do you suppose is the likelyhood of the two nested for loops taking more than 3 seconds to execute? Pretty small, wouldn’t you say?

I’m pretty sure that it was recommended that you read, understand, and embrace the philosophy of the blink without delay example. It is clear only that you read the example.

There is no way that you can play a series of notes, for a period of time each, using a for loop.

There is NO reason to use two for loops to iterate through two arrays of the same length, when the data in each is important for each step.

There is no delay in your code between note on and note off! I'm surprised it's playing anything. Two for loops, one inside the other, will give you A1, A2, A3 ....B1, B2, B3....C1, C2, C3...etc. and NOT A1, B2, C3... as you want.

This code will play the notes with given(1000) inteval(constructed from BlinkWithoutDelay)

int mynote[] = {
  72,77,81,84};
int noteCount = 4; 
long previousMillis = 0;       
long interval = 1000;  

void setup() {
  //  Set MIDI baud rate:
  Serial.begin(31250);



}



void loop() {





  //declare x=0 as object in array
  for (int x = 0; x < noteCount; x++) {
    unsigned long currentMillis = millis();
    if(currentMillis - previousMillis > interval) {

      //Note on channel 1 (0x90), some note value (x will draw number from array), velocity 127 (0x7F):
      noteOn(0x90, mynote[x], 0x7F);
      //Note off channel 1 (0x80), some note value (note), silent velocity (0x00):
      noteOn(0x80, mynote[x], 0x00); 
      previousMillis = currentMillis; 
    }
  }

}



//  plays a MIDI note.  Doesn't check to see that
//  cmd is greater than 127, or that data values are  less than 127:
void noteOn(int cmd, int pitch, int velocity) {
  Serial.print(cmd, BYTE);
  Serial.print(pitch, BYTE);
  Serial.print(velocity, BYTE);
}

The rest is a struggle to get intervals in different length.

There is no way that you can play a series of notes, for a period of time each, using a for loop.

Thank you for the ”no” answer. Can you give me the ”yes” one too.

Can you give me the ”yes” one too.

Sure. It involves a state machine. If you don’t know what that means, go Google it. If you do, it should be perfectly obvious how to do it.

Is it time to play a different note? ← That’s what the blink without delay example shows you how to answer
If so, which note do I play now? ← That’s what the state machine is for. As you can probably imagine, the for loop is a kind of state machine, and x is the state. Since you need to get rid of the for loop, you might be concerned that x will go away, too. It doesn’t need to. It becomes a global (or static) variable. But, x is a poor name for a global variable. Give it a meaningful name, like currentNote.

Thanks, Paul! StateMachine has been suggested by Arrch in second post. I constructed one and tested it. While it works very well, I did run in to difficulties to change anything quickly. Also I do not see how this could be used to play multiple sequences at the same time?