Arduino Mega + WS2812B + MIDI

Hey guys so I've been search online for days now and on this site for a while, I'm finding bits and peices for my issue but no true answer. I've followed guides and combined things and knowledge to be able to get to this point, but I'm new to Arudino and C++. I'm using Traktor to send MIDI out to LoopBe1 using Hairless Serial into my Arduino. It works perfectly. I would like to create 16 different 'LED animations' (like the 1 below) and have them all linked to separate buttons, that when pressed, stops the current animation and begins the next animation on the next 1/4 note (represented as 24 out of 96) with 96 being the full measure. Can this be done in 1 Arduino?

I would like loop this LED animation infinitely.
I would like the loop to only stop, if I trigger another loop using 1 of my 16 buttons.

How do I connect 16 buttons to 1 code of 16 different animations, to have each button trigger another animation, beginning on the next quarter note (24). I don't know where to even start

#include <FastLED.h>

#define WHOLE 96
#define HALF 48
#define QUARTER 24
#define EIGHTH 12
#define SIXTEENTH 6

CRGB leds[150];

byte data;
byte midi_clock = 0xf8; // MIDI Clock Byte
int mclock = 1; // Time through current measure 0 -> 96
int measure = 1; // Current measure

void setup()
{
  Serial.begin(115200); // Start Serial Port for Hairless MIDI
  FastLED.addLeds<WS2812, 2, GRB>(leds, 144); // Init LED Strip
}

void loop()
{

  if (Serial.available() > 0) // Check if we got a byte
  {
    data = Serial.read(); // Read in the byte
    if (data == midi_clock) // Make sure the byte is a midi clock byte
    {
      mclock += 1;

      if (mclock >= (24 * 4))
      {
        measure += 1;
        mclock = 0;
      }

      SetStripOff();

      if (measure >= 1 && measure <= 4)
      {
        SetStrip(mclock, QUARTER * 0, QUARTER, 0, 150, 0, 0, 255);
        SetStrip(mclock, QUARTER * 1, QUARTER, 0, 150, 255, 0, 0);
        SetStrip(mclock, QUARTER * 2, QUARTER, 0, 150, 0, 255, 0);
        SetStrip(mclock, QUARTER * 3, QUARTER, 0, 150, 255, 255, 0);
      }
      if (measure >= 5 && measure <= 8)
      {
        SetStrip(mclock, QUARTER * 0, QUARTER, 0, 30, 0, 0, 255);
        SetStrip(mclock, QUARTER * 1, QUARTER, 30, 60, 255, 0, 0);
        SetStrip(mclock, QUARTER * 2, EIGHTH, 60, 90, 0, 255, 0);
        SetStrip(mclock, QUARTER * 2 + EIGHTH, EIGHTH, 90, 120, 255, 255, 0);
        SetStrip(mclock, QUARTER * 3, QUARTER, 120, 150, 255, 0, 255);
      }
      if (measure >= 9 && measure <= 12)
      {
        SetStrip(mclock, QUARTER * 0, QUARTER, 0, 150, 0, 0, 255);
        SetStrip(mclock, QUARTER * 1, QUARTER, 0, 150, 255, 0, 0);
        SetStrip(mclock, QUARTER * 2, QUARTER, 0, 150, 0, 255, 0);
        SetStrip(mclock, QUARTER * 3, QUARTER, 0, 150, 255, 255, 0);
      }
      if (measure >= 13 && measure <= 16)
      {
        SetStrip(mclock, QUARTER * 0, QUARTER, 0, 30, 0, 0, 255);
        SetStrip(mclock, QUARTER * 1, QUARTER, 30, 60, 255, 0, 0);
        SetStrip(mclock, QUARTER * 2, EIGHTH, 60, 90, 0, 255, 0);
        SetStrip(mclock, QUARTER * 2 + EIGHTH, EIGHTH, 90, 120, 255, 255, 0);
        SetStrip(mclock, QUARTER * 3, QUARTER, 120, 150, 255, 0, 255);
      }



      FastLED.show();
    }
  }
}

void SetStripOff()
{
  for (int i = 0; i < 150; i++)
  {
    leds[i] = CRGB(0, 0, 0);
  }
}


void SetStrip(int time_current, int time_start, int time_length, int first_pixel, int last_pixel, int red, int green, int blue)
{
  if (time_current >= time_start && time_current < time_start + time_length)
  {
    for (int i = first_pixel; i < last_pixel; i++)
    {
      leds[i] = CRGB(red, green, blue);
    }
  }
}

I guess the BPM can vary as well? Does MIDI produce a beat clock?
I think it would be good to start with thinking about timing in your script on a conceptual level. How do frames of the animation relate to the beat of the music? WS2812 has some flexibility, same as your music. 144 LED's can be refreshed say at 100Hz maximum. What would be a good framerate per beat is you say the BPM can go from 100-160. The minimum beat length is 60000/160=375ms. Let's divide that to 24th beats: 15,625ms/beat minimum. This is 64Hz, perfect for the WS2812's.

So you can start with a framework for the frame timing:

int bpm=160; //we can change this later to adjust to the MIDI clock.
unsigned long: nextTick; // time when next frame is displayed.
unsigned long tickLength=2500000/bpm; //2500000=60000000/24
int currentAnimation=0; // number of active animation
int animationFrame=0; // frame number in current animation

void setup(){
  Serial1.begin(31250); //start MIDI serial, not 115200 but 31250 baud! I use Serial 1 as I have a Leonardo with MIDI shield. This depends on the board.
  Serial.begin(9600); USB serial monitor.
  nextTick=micros()+tickLength;
}

void Loop(){
  if (micros()>nextTick){
    nextTick+=tickLength;
    drawAnimationFrame(currentAnimation,animationFrame); // Make a function that draws frame animationFrame of currentAnimation
    animationFrame++;
    // put some check here to decide whether to restart the animation
  }
  readMidi(); // make a function that pulls MIDI data from the Serial buffer and interpret it.
}

You can’t use a WS2812 led strip with a constant MIDI stream as the LEDs require the interrupts to be disabled when sending data and that screws up the MIDI reception.

You can however do this with the dot star type led strips, because they use an SPI interface, which you can either bit bang or use the SPI hardware on the Arduino.

Okay, so this is basically the only thing I've done, so I apologize if this is vague and not explicitly 'written code' helpful, but first, here is a thing I'm working on:

Two buttons control various states of the LEDs.

So barring the thing said above about that particular LED strip not working with MIDI, here is how I would approach what you are trying to do.

If you look at the Arduino tutorials in the program itself, there is one on button use (I think it might be the same one as 'debounce' which will be useful as well). The basic idea is that you have it looping through the current animation and the check for buttons as a default *and you would designate a particular animation that it would play on startup). Once it detects a button press, you can have that set a flag, like "All_Buttons == False" and "Button_7" (or whatever one was pressed) == True. If X button is true, then run Y animation.

As for having the change over happen on the quarter note, I think the easiest way to do that would be to have a variable associated with the BPM of the particular MIDI in that animation, and use it to count to set another flag once the button is pressed.

So let's say a quarter note is happening every 250 ms. Once the button is pressed, you have a "current time" variable in the current animation, say it's at 12001 ms. This is now the time you will compare to. So then it would basically need to loop through the code, waiting until the number got to 12001 - (whatever number is over a clean multiple of 250, in this case 1) + 250 = 12250. Once that number is reached, the code can give the go ahead to move over to the next button's animation and MIDI.

I hope this logic makes sense. I've never done sound before, so I'm not sure how that will screw with everything, but I'm reasonably sure what I'm thinking of here would work otherwise. Good luck!

Grumpy_Mike:
You can’t use a WS2812 led strip with a constant MIDI stream as the LEDs require the interrupts to be disabled when sending data and that screws up the MIDI reception.

Should not be needed, there is 4 interrupt timers, it just requires some tweaking.

blnkjns:
Should not be needed, there is 4 interrupt timers, it just requires some tweaking.

Well I would be interested to know what sort of tweaking you think it needs.

I don’t think you understand the problem.

Just in case you are thinking I am making this up then have a look at this very old post.
https://forums.adafruit.com/viewtopic.php?p=277061

Mike, where have you been for (almost) two weeks?

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.