Help with Code for Timing Lights to Song Lyrics

I’m trying to make a sign that has 7 words on it where each of the words turns on as they are sung in the song.

I went though the Into to Pixelblaze Pattern but unfortunately I am still stumped on how I would do this. I am struggling with how I would be able to start the pattern at the exact right time, it seems like all the patterns are on a loop and just start automatically when you click on them.

Would anyone be able to help me out with a code that would do the following?

Press button on computer
Pixels 1-20 turn on at 0ms
Pixels 21-40 turn on at 750ms
Pixels 41-60 turn on at 1500ms
Pixels 61-80 turn on at 2250ms
Pixels 81-100 turn on at 3000ms
Pixels 101-120 turn on at 3750ms
Pixels 121-140 turn on at 4125ms
Pixels 1-140 turn off at 4500ms

Thanks in advance to anyone who could help me out!

Is there a timing between the end and the start?

At 4.5 when we turn all off, how long time the first word light?

As for timing, I’d say adding a button is your best best for exact timing. But I suspect you could click on the pattern and get a consistant start.

But if you want to stop and start, and restart, add a button.

Oh wait, You want a ONE time running: press button, light up, turn off…

Yeah, add a hardware button. Or you can use the software slider.

1 Like

Hey Spencer!

I wrote this up for you to get you started - it’s pretty specific to your spec, but I’d eventually like to make a more full-featured Choreography pattern that’s quite flexible for animating patterns to a song.

// Spec:
//   Press button on computer
//   Pixels 1-20 turn on at 0ms
//   Pixels 21-40 turn on at 750ms
//   Pixels 41-60 turn on at 1500ms
//   Pixels 61-80 turn on at 2250ms
//   Pixels 81-100 turn on at 3000ms
//   Pixels 101-120 turn on at 3750ms
//   Pixels 121-140 turn on at 4125ms
//   Pixels 1-140 turn off at 4500ms

// There's no keyboard events, but you can slide this sldier when the 
// song restarts.
export function sliderRetrigger(v) { accumMs = 0 }

var numModes = 8 // Includes all active modes and a final halting look
var modeStartAt = array(numModes)
modeStartAt[0] = 0
modeStartAt[1] = 750
modeStartAt[2] = 1500
modeStartAt[3] = 2250
modeStartAt[4] = 3000
modeStartAt[5] = 3750
modeStartAt[6] = 4125
modeStartAt[7] = 4500

var idxRangeEnd = array(numModes)
idxRangeEnd[0] = 20
idxRangeEnd[1] = 40
idxRangeEnd[2] = 60
idxRangeEnd[3] = 80
idxRangeEnd[4] = 100
idxRangeEnd[5] = 120
idxRangeEnd[6] = 140
idxRangeEnd[7] = 0

var accumMs = 0
export function beforeRender(delta) {
  accumMs += delta
  accumMs > 4500 ? accumMs = 4501 : 0 // Halt
  
  t1 = time(.1)
  mode = msToMode(accumMs)
}

export function render(index) {
  h = t1 + index/pixelCount
  s = 1
  v = pixelOn(index, mode)
  hsv(h, s, v)
}

// Assumes modeStartAt[i] vaues sorted ascending
// Returns mode index for a given number of milliseconds into a song
function msToMode(ms) {
  for (i = 0; i < numModes - 1; i++) {
    if (ms >= modeStartAt[i] && ms < modeStartAt[i + 1]) 
      return i
  }
  return numModes - 1 // Past the last mode's start time, use the last mode
}

function pixelOn(index, modeIdx) {
  if (modeIdx == 0) return index < idxRangeEnd[modeIdx]
  return index >= idxRangeEnd[modeIdx - 1] && index < idxRangeEnd[modeIdx]
}
5 Likes

Hey Scruffynerf, thanks for the reply! The software slider seems like what I’m looking for.

Hey Jeff, thanks so much for this! This was exactly what I was looking for. I tried it out and it works perfectly! Thanks a million for taking the time to write this up for me!

4 Likes

share a video of it when you’re done! I’m curious to see how it turned out

1 Like

Hi Jeff,

Would you be able to alter this code slightly to accommodate a range of pixels for each timing instead of from the end of the previous to the end of the active range? (I think that is how it is working)

The reason ask is because my led strip lights can only be cut every 3rd pixel. So for the case that a word uses 20 pixels I would want the 21st to not be light up.

Example:

var modeStartAt = array(numModes)
modeStartAt[0] = 0
modeStartAt[1] = 750

var idxRangeStart = array(numModes)
idxRangeStart[0] = 0
idxRangeStart[1] = 21

var idxRangeEnd = array(numModes)
idxRangeEnd[0] = 19
idxRangeEnd[1] = 40

Huge thanks for any help you would be able to provide, and I’ll be sure to send a video when it’s done!

Sure - we can switch to a more flexible approach then where we have a separate function for each part of the song. This is the same technique seen in the “Example: Modes and waveforms” pattern.

// There's no keyboard events, but you can slide this sldier when the 
// song restarts.
export function sliderRetrigger(v) { accumMs = 0 }

var numModes = 8 // Includes all active modes and a final look

var modes = array(numModes)
modes[0] = (i) => i >= 0 && i < 21
modes[1] = (i) => i >= 21 && i < 42
modes[2] = (i) => i >= 42 && i < 60
modes[3] = (i) => i >= 60 && i < 81
modes[4] = (i) => i >= 81 && i < 102
modes[5] = (i) => i >= 102 && i < 120
modes[6] = (i) => i >= 120  && i < 141
modes[7] = (i) => 0

var modeStartAt = array(numModes)
modeStartAt[0] = 0
modeStartAt[1] = 750
modeStartAt[2] = 1500
modeStartAt[3] = 2250
modeStartAt[4] = 3000
modeStartAt[5] = 3750
modeStartAt[6] = 4125
modeStartAt[7] = 4500

var accumMs = 0
export function beforeRender(delta) {
  accumMs += delta
  accumMs > 4500 ? accumMs = 4501 : 0 // Halt
  
  t1 = time(.1)
  mode = msToMode(accumMs)
}

export function render(index) {
  h = t1 + index/pixelCount
  s = 1
  // Call the current mode function
  // which will overwrite globals h, s and/or v
  v = modes[mode](index)
  hsv(h, s, v)
}

// Assumes modeStartAt[i] vaues sorted ascending
// Returns mode index for a given number of milliseconds into a song
function msToMode(ms) {
  for (i = 0; i < numModes - 1; i++) {
    if (ms >= modeStartAt[i] && ms < modeStartAt[i + 1]) 
      return i
  }
  return numModes - 1 // Past the last mode's start time, use the last mode
}
4 Likes

Thanks a million Jeff! This is great!

1 Like