Map a LED strip array (1D) to 2D

Hello,

Sorry it’s a noob question, i’m not sure what level is expected here. I’ve been trying for hours, i have to resign myself to ask for help.

I’m trying to adapt the amazing Sound-Rays pattern to a 2D mapping.
I understand it works by filling an array the length of the LED strip, then offsets it following a time base to move the rays along the strip (+some feedback PI work to determine the values).

Now what i want instead of moving the “rays” along the LED strip, is to move them along the y axis of my mapping (with different frequencies on x, but that comes later).
I really struggle how to distribute the array based on y, and not on index…

I’ve tried so many ways, the code i have right now reacts properly, but fills the whole strip…
I converted the strip sized array by an array with the number of LED i have on y axis (which does not look like the correct way at all…)

I managed to run a few simple patterns on this mapping, but i’m afraid arrays are above my level… Any help on how to understand the logic ?

Thank you so much

My code (original one is below):

// Speed that the rays travel down the strip
speed = 0.01

// These vars are set by the external sensor board, if one is connected. We
// don't actually use light readings in this pattern, so if the `light` value
// remains -1, no sensor board is connected.
export var light = -1 
export var maxFrequencyMagnitude
export var maxFrequency
export var delta
export var pos

//hues = array(pixelCount)
//vals = array(pixelCount)
hues = array(19)
vals = array(19)


// A position pointer, in pixels, that turns hues[] and vals[] into a circular
// buffer
pos = 0
// Stores the last brightness value to feed back into the PI gain controller 
lastVal = 0

export var pic = makePIController(.05, .35, 200, 0, 400)

// Make a new PI Controller
function makePIController(kp, ki, start, min, max) {
  var pic = array(5)
  pic[0] = kp
  pic[1] = ki
  pic[2] = start // This is the accumulated error
  pic[3] = min
  pic[4] = max
  return pic
}

function calcPIController(pic, err) {
  pic[2] = clamp(pic[2] + err, pic[3], pic[4])
  return max(pic[0] * err + pic[1] * pic[2], .3)
}

export function beforeRender(delta) {
  // Here the PI controller is aiming for a sensitivity based on chasing recent
  // maxFrequencyMagnitudes to be 0.5
  sensitivity = calcPIController(pic, .5 - lastVal)
  
  // To make the rays travel along the strip, sweep a position offset pointer
  // down the arrays of values and hues
  //pos = (pos + delta * speed) % pixelCount
pos = (pos + delta * speed) % 19
pos=floor(pos)
  
  if (light == -1) simulateSound()  // No sensor board attached
  
  // The brightness value will be determined by the magnitude of the most
  // intense frequency. This is also our feedback to the PI controller.
  lastVal = vals[pos] = pow(maxFrequencyMagnitude * sensitivity, 2)
  
  /*
    The base color will be modified by time and strip position in render(), but
    its hue begins based on the most intense frequency detected. If you played a
    swept tone between 20 Hz and 5 KHz, it'd trace a rainbow. 
  */
  hues[pos] = maxFrequency / 5000

  // Used to subtly advance the hue over time
  t1 = time(6.5 / 65.536)
}

export function render2D(index,x,y) {
  // Reverse indices so that pixels flow to the right
  index = pixelCount - index
  // Shift the index circularly based on the position offset
  i = (y + pos/19)
  
  h = hues[i]
  /*
    This rotates color by adding a component based on time and position.  
    Comment this out to more clearly see the detected maximum frequencies.
    Adding `index / pixelCount / 4` adds a quarter of the hue wheel across the
    strip's entire length. Notice that since index is reversed, *adding* t1 back
    in has the effect of *slowing* the hue progression.
  */
  //h += index / pixelCount / 4 + t1

  v = vals[i]
  v = v * v  // Gamma correction

  hsv(h, 1, v)
}

The original code:

/*
  Sound - rays

  This pattern is designed to use the sensor expansion board, but falls back to
  simulated sound data if the sensor board isn't detected.

  The beginning of the strip will originate pixels with color based on the most
  prevalent frequency in the sound, and brightness based on the magnitude. Those
  rays of color will then travel down the strip.

  Please check out the "sound - blink fade" pattern for more verbose comments
  explaining the PI controller used below for automatic gain control. 
*/


// Speed that the rays travel down the strip
speed = 0.05

// These vars are set by the external sensor board, if one is connected. We
// don't actually use light readings in this pattern, so if the `light` value
// remains -1, no sensor board is connected.
export var light = -1 
export var maxFrequencyMagnitude
export var maxFrequency

hues = array(pixelCount)
vals = array(pixelCount)

// A position pointer, in pixels, that turns hues[] and vals[] into a circular
// buffer
pos = 0
// Stores the last brightness value to feed back into the PI gain controller 
lastVal = 0

export var pic = makePIController(.05, .35, 200, 0, 400)

// Make a new PI Controller
function makePIController(kp, ki, start, min, max) {
  var pic = array(5)
  pic[0] = kp
  pic[1] = ki
  pic[2] = start // This is the accumulated error
  pic[3] = min
  pic[4] = max
  return pic
}

function calcPIController(pic, err) {
  pic[2] = clamp(pic[2] + err, pic[3], pic[4])
  return max(pic[0] * err + pic[1] * pic[2], .3)
}

export function beforeRender(delta) {
  // Here the PI controller is aiming for a sensitivity based on chasing recent
  // maxFrequencyMagnitudes to be 0.5
  sensitivity = calcPIController(pic, .5 - lastVal)
  
  // To make the rays travel along the strip, sweep a position offset pointer
  // down the arrays of values and hues
  pos = (pos + delta * speed) % pixelCount
  
  if (light == -1) simulateSound()  // No sensor board attached
  
  // The brightness value will be determined by the magnitude of the most
  // intense frequency. This is also our feedback to the PI controller.
  lastVal = vals[pos] = pow(maxFrequencyMagnitude * sensitivity, 2)
  
  /*
    The base color will be modified by time and strip position in render(), but
    its hue begins based on the most intense frequency detected. If you played a
    swept tone between 20 Hz and 5 KHz, it'd trace a rainbow. 
  */
  hues[pos] = maxFrequency / 5000

  // Used to subtly advance the hue over time
  t1 = time(6.5 / 65.536)
}

export function render2D(index,x,y) {
  // Reverse indices so that pixels flow to the right
  index = pixelCount - index
  // Shift the index circularly based on the position offset
  i = (index + pos) % pixelCount
  
  h = hues[i]
  /*
    This rotates color by adding a component based on time and position.  
    Comment this out to more clearly see the detected maximum frequencies.
    Adding `index / pixelCount / 4` adds a quarter of the hue wheel across the
    strip's entire length. Notice that since index is reversed, *adding* t1 back
    in has the effect of *slowing* the hue progression.
  */
  h += index / pixelCount / 4 + t1

  v = vals[i]
  v = v * v  // Gamma correction

  hsv(h, 1, v)
}

Edit. I have found the solution !
I’ll post it later when i improve the code !

1 Like