Refactored all of the led_update_X functions
Led update functions no longer return a colorized array. Colorizing is now done in led_visualization. It makes more sense for the led_update_X functions to return 1D brightness arrays and then to apply color afterwards. This should also improve performance somewhat. Also added a new function leak_saturated_pixels() which allows saturated colors (>255 value) to leak into the adjacent color channels
This commit is contained in:
parent
9e615e0f35
commit
a38a8e1680
@ -136,6 +136,24 @@ def interpolate(y, new_length):
|
|||||||
return z
|
return z
|
||||||
|
|
||||||
|
|
||||||
|
def leak_saturated_pixels(pixels):
|
||||||
|
pixels = np.copy(pixels)
|
||||||
|
for i in range(pixels.shape[0]):
|
||||||
|
excess_red = max(pixels[i, 0] - 255.0, 0.0)
|
||||||
|
excess_green = max(pixels[i, 1] - 255.0, 0.0)
|
||||||
|
excess_blue = max(pixels[i, 2] - 255.0, 0.0)
|
||||||
|
# Share excess red
|
||||||
|
pixels[i, 1] += excess_red
|
||||||
|
pixels[i, 2] += excess_red
|
||||||
|
# Share excess green
|
||||||
|
pixels[i, 0] += excess_green
|
||||||
|
pixels[i, 2] += excess_green
|
||||||
|
# Share excess blue
|
||||||
|
pixels[i, 0] += excess_blue
|
||||||
|
pixels[i, 1] += excess_blue
|
||||||
|
return pixels
|
||||||
|
|
||||||
|
|
||||||
_EA_norm = dsp.ExponentialFilter(np.tile(1e-4, config.N_PIXELS), 0.01, 0.25)
|
_EA_norm = dsp.ExponentialFilter(np.tile(1e-4, config.N_PIXELS), 0.01, 0.25)
|
||||||
"""Onset energy per-bin normalization constants
|
"""Onset energy per-bin normalization constants
|
||||||
|
|
||||||
@ -170,16 +188,10 @@ def update_leds_6(y):
|
|||||||
Array containing the onset energies that should be visualized.
|
Array containing the onset energies that should be visualized.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# Scale y to emphasize large spikes and attenuate small changes
|
|
||||||
# Exponents < 1.0 emphasize small changes and penalize large spikes
|
|
||||||
# Exponents > 1.0 emphasize large spikes and penalize small changes
|
|
||||||
y = np.copy(y)**1.25
|
y = np.copy(y)**1.25
|
||||||
|
|
||||||
# Use automatic gain control to normalize bin values
|
|
||||||
# Update normalization constants and then normalize each bin
|
# Update normalization constants and then normalize each bin
|
||||||
_EA_norm.update(y)
|
_EA_norm.update(y)
|
||||||
y /= _EA_norm.value
|
y /= _EA_norm.value
|
||||||
|
|
||||||
"""Force saturated pixels to leak brighness into neighbouring pixels"""
|
"""Force saturated pixels to leak brighness into neighbouring pixels"""
|
||||||
def smooth():
|
def smooth():
|
||||||
for n in range(1, len(y) - 1):
|
for n in range(1, len(y) - 1):
|
||||||
@ -188,58 +200,26 @@ def update_leds_6(y):
|
|||||||
y[n] = 1.0
|
y[n] = 1.0
|
||||||
y[n - 1] += excess / 2.0
|
y[n - 1] += excess / 2.0
|
||||||
y[n + 1] += excess / 2.0
|
y[n + 1] += excess / 2.0
|
||||||
|
|
||||||
# Several iterations because the adjacent pixels could also be saturated
|
# Several iterations because the adjacent pixels could also be saturated
|
||||||
for i in range(6):
|
for i in range(6):
|
||||||
smooth()
|
smooth()
|
||||||
|
|
||||||
# Update the onset energy low-pass filter and discard value too dim
|
# Update the onset energy low-pass filter and discard value too dim
|
||||||
_EA_smooth.update(y)
|
_EA_smooth.update(y)
|
||||||
_EA_smooth.value[_EA_smooth.value < .1] = 0.0
|
_EA_smooth.value[_EA_smooth.value < .1] = 0.0
|
||||||
|
# Return the pixels
|
||||||
# If some pixels are too bright, allow saturated pixels to become white
|
pixels = np.copy(_EA_smooth.value)**1.5
|
||||||
color = rainbow(config.N_PIXELS) * 255.0
|
|
||||||
|
|
||||||
# Create the pixel array
|
|
||||||
pixels = np.tile(0, (y.shape[0], 3))
|
|
||||||
|
|
||||||
for i in range(config.N_PIXELS):
|
|
||||||
# Update LED strip pixel
|
|
||||||
pixels[i, :] = np.round(color[i, :] * _EA_smooth.value[i]**1.5)
|
|
||||||
# Leak excess red
|
|
||||||
excess_red = max(pixels[i, 0] - 255, 0)
|
|
||||||
pixels[i, 1] += excess_red
|
|
||||||
pixels[i, 2] += excess_red
|
|
||||||
# Leak excess green
|
|
||||||
excess_green = max(pixels[i, 1] - 255, 0)
|
|
||||||
pixels[i, 0] += excess_green
|
|
||||||
pixels[i, 2] += excess_green
|
|
||||||
# Leak excess blue
|
|
||||||
excess_blue = max(pixels[i, 2] - 255, 0)
|
|
||||||
pixels[i, 0] += excess_blue
|
|
||||||
pixels[i, 1] += excess_blue
|
|
||||||
return pixels
|
return pixels
|
||||||
|
|
||||||
|
|
||||||
_EF_norm = dsp.ExponentialFilter(np.tile(1.0, config.N_PIXELS), 0.05, 0.9)
|
_EF_norm = dsp.ExponentialFilter(np.tile(1.0, config.N_PIXELS), 0.05, 0.9)
|
||||||
_EF_smooth = dsp.ExponentialFilter(np.tile(1.0, config.N_PIXELS), 0.08, 0.9)
|
_EF_smooth = dsp.ExponentialFilter(np.tile(1.0, config.N_PIXELS), 0.08, 0.9)
|
||||||
|
|
||||||
_prev_energy = 0.0
|
_prev_energy = 0.0
|
||||||
|
|
||||||
|
|
||||||
# Individually normalized energy flux
|
# Individually normalized energy flux
|
||||||
def update_leds_5(y):
|
def update_leds_5(y):
|
||||||
global _prev_energy
|
global _prev_energy
|
||||||
# Scale y
|
|
||||||
y = np.copy(y)
|
y = np.copy(y)
|
||||||
y = y ** 1.0
|
|
||||||
|
|
||||||
# Calculate raw energy flux
|
|
||||||
# Update previous energy
|
|
||||||
# Rectify energy flux
|
|
||||||
# Update the normalization constants
|
|
||||||
# Normalize the individual energy flux values
|
|
||||||
# Smooth the result using another smoothing filter
|
|
||||||
EF = y - _prev_energy
|
EF = y - _prev_energy
|
||||||
_prev_energy = np.copy(y)
|
_prev_energy = np.copy(y)
|
||||||
EF[EF < 0] = 0.0
|
EF[EF < 0] = 0.0
|
||||||
@ -248,38 +228,18 @@ def update_leds_5(y):
|
|||||||
_EF_smooth.update(EF)
|
_EF_smooth.update(EF)
|
||||||
# Cutoff values below 0.1
|
# Cutoff values below 0.1
|
||||||
_EF_smooth.value[_EF_smooth.value < 0.1] = 0.0
|
_EF_smooth.value[_EF_smooth.value < 0.1] = 0.0
|
||||||
|
pixels = np.copy(_EF_smooth.value)
|
||||||
color = rainbow(config.N_PIXELS) * 255.0
|
|
||||||
pixels = np.tile(0, (y.shape[0], 3))
|
|
||||||
|
|
||||||
for i in range(config.N_PIXELS):
|
|
||||||
pixels[i, :] = np.round(color[i, :] * _EF_smooth.value[i])
|
|
||||||
# Share excess red
|
|
||||||
excess_red = max(pixels[i, 0] - 255, 0)
|
|
||||||
pixels[i, 1] += excess_red
|
|
||||||
pixels[i, 2] += excess_red
|
|
||||||
# Share excess green
|
|
||||||
excess_green = max(pixels[i, 1] - 255, 0)
|
|
||||||
pixels[i, 0] += excess_green
|
|
||||||
pixels[i, 2] += excess_green
|
|
||||||
# Share excess blue
|
|
||||||
excess_blue = max(pixels[i, 2] - 255, 0)
|
|
||||||
pixels[i, 0] += excess_blue
|
|
||||||
pixels[i, 1] += excess_blue
|
|
||||||
return pixels
|
return pixels
|
||||||
|
|
||||||
|
|
||||||
_energy_flux = dsp.ExponentialFilter(1.0, alpha_decay=0.025, alpha_rise=0.9)
|
_energy_flux = dsp.ExponentialFilter(1.0, alpha_decay=0.025, alpha_rise=0.9)
|
||||||
|
|
||||||
|
|
||||||
# Modulate brightness of the entire strip with no individual addressing
|
# Modulate brightness of the entire strip with no individual addressing
|
||||||
def update_leds_4(y):
|
def update_leds_4(y):
|
||||||
y = np.copy(y)**1.0
|
y = np.copy(y)
|
||||||
energy = np.sum(y)
|
energy = np.sum(y)
|
||||||
_energy_flux.update(energy)
|
_energy_flux.update(energy)
|
||||||
energy /= _energy_flux.value
|
pixels = energy / _energy_flux.value
|
||||||
color = rainbow(config.N_PIXELS) * 255.0
|
|
||||||
pixels = np.round((color * energy)).astype(int)
|
|
||||||
return pixels
|
return pixels
|
||||||
|
|
||||||
|
|
||||||
@ -293,85 +253,29 @@ def update_leds_3(y):
|
|||||||
_prev_energy = energy
|
_prev_energy = energy
|
||||||
# Normalize energy flux
|
# Normalize energy flux
|
||||||
_energy_flux.update(energy_flux)
|
_energy_flux.update(energy_flux)
|
||||||
# Update pixels
|
# Update and return pixels
|
||||||
pixels = np.roll(pixels, 1)
|
pixels = np.roll(pixels, 1)
|
||||||
color = np.roll(color, 1, axis=0)
|
|
||||||
pixels *= 0.99
|
|
||||||
pixels[0] = energy_flux
|
pixels[0] = energy_flux
|
||||||
|
return np.copy(pixels)
|
||||||
new_pixels = np.copy(np.round((color.T * pixels).T).astype(int))
|
|
||||||
for i in range(config.N_PIXELS):
|
|
||||||
# Share excess red
|
|
||||||
excess_red = max(new_pixels[i, 0] - 255, 0)
|
|
||||||
new_pixels[i, 1] += excess_red
|
|
||||||
new_pixels[i, 2] += excess_red
|
|
||||||
# Share excess green
|
|
||||||
excess_green = max(new_pixels[i, 1] - 255, 0)
|
|
||||||
new_pixels[i, 0] += excess_green
|
|
||||||
new_pixels[i, 2] += excess_green
|
|
||||||
# Share excess blue
|
|
||||||
excess_blue = max(new_pixels[i, 2] - 255, 0)
|
|
||||||
new_pixels[i, 0] += excess_blue
|
|
||||||
new_pixels[i, 1] += excess_blue
|
|
||||||
# Update LEDs
|
|
||||||
return new_pixels
|
|
||||||
|
|
||||||
|
|
||||||
# Energy based motion across the LED strip
|
# Energy based motion across the LED strip
|
||||||
def update_leds_2(y):
|
def update_leds_2(y):
|
||||||
global pixels, color
|
global pixels
|
||||||
y = np.copy(y)
|
y = np.copy(y)
|
||||||
# Calculate energy
|
# Calculate energy
|
||||||
energy = np.sum(y**2.0)
|
energy = np.sum(y**1.5)
|
||||||
onset_energy.update(energy)
|
onset_energy.update(energy)
|
||||||
energy /= onset_energy.value
|
energy /= onset_energy.value
|
||||||
# Update pixels
|
# Update and return pixels
|
||||||
pixels = np.roll(pixels, 1)
|
pixels = np.roll(pixels, 1)
|
||||||
color = np.roll(color, 1, axis=0)
|
|
||||||
pixels *= 0.99
|
|
||||||
pixels[0] = energy
|
pixels[0] = energy
|
||||||
pixels -= 0.005
|
return np.copy(pixels)
|
||||||
pixels[pixels < 0.0] = 0.0
|
|
||||||
|
|
||||||
new_pixels = np.copy(np.round((color.T * pixels).T).astype(int))
|
|
||||||
for i in range(config.N_PIXELS):
|
|
||||||
# Share excess red
|
|
||||||
excess_red = max(new_pixels[i, 0] - 255, 0)
|
|
||||||
new_pixels[i, 1] += excess_red
|
|
||||||
new_pixels[i, 2] += excess_red
|
|
||||||
# Share excess green
|
|
||||||
excess_green = max(new_pixels[i, 1] - 255, 0)
|
|
||||||
new_pixels[i, 0] += excess_green
|
|
||||||
new_pixels[i, 2] += excess_green
|
|
||||||
# Share excess blue
|
|
||||||
excess_blue = max(new_pixels[i, 2] - 255, 0)
|
|
||||||
new_pixels[i, 0] += excess_blue
|
|
||||||
new_pixels[i, 1] += excess_blue
|
|
||||||
# Update LEDs
|
|
||||||
return new_pixels
|
|
||||||
|
|
||||||
|
|
||||||
def update_leds_1(y):
|
def update_leds_1(y):
|
||||||
"""Display the raw onset spectrum on the LED strip"""
|
"""Display the raw onset spectrum on the LED strip"""
|
||||||
y = np.copy(y)
|
return np.copy(y)**0.5
|
||||||
y = y ** 0.5
|
|
||||||
color = rainbow(y.shape[0]) * 255.0
|
|
||||||
|
|
||||||
pixels = np.copy(np.round((color.T * y).T).astype(int))
|
|
||||||
for i in range(y.shape[0]):
|
|
||||||
# Share excess red
|
|
||||||
excess_red = max(pixels[i, 0] - 255, 0)
|
|
||||||
pixels[i, 1] += excess_red
|
|
||||||
pixels[i, 2] += excess_red
|
|
||||||
# Share excess green
|
|
||||||
excess_green = max(pixels[i, 1] - 255, 0)
|
|
||||||
pixels[i, 0] += excess_green
|
|
||||||
pixels[i, 2] += excess_green
|
|
||||||
# Share excess blue
|
|
||||||
excess_blue = max(pixels[i, 2] - 255, 0)
|
|
||||||
pixels[i, 0] += excess_blue
|
|
||||||
pixels[i, 1] += excess_blue
|
|
||||||
return pixels
|
|
||||||
|
|
||||||
|
|
||||||
def microphone_update(stream):
|
def microphone_update(stream):
|
||||||
@ -432,7 +336,7 @@ pixels = np.tile(0.0, config.N_PIXELS)
|
|||||||
color = rainbow(config.N_PIXELS) * 255.0
|
color = rainbow(config.N_PIXELS) * 255.0
|
||||||
|
|
||||||
# Tracks average onset spectral energy
|
# Tracks average onset spectral energy
|
||||||
onset_energy = dsp.ExponentialFilter(1.0, alpha_decay=0.1, alpha_rise=0.99)
|
onset_energy = dsp.ExponentialFilter(1.0, alpha_decay=0.01, alpha_rise=0.65)
|
||||||
|
|
||||||
# Tracks the location of the spectral median
|
# Tracks the location of the spectral median
|
||||||
median = dsp.ExponentialFilter(val=config.N_SUBBANDS / 2.0,
|
median = dsp.ExponentialFilter(val=config.N_SUBBANDS / 2.0,
|
||||||
@ -462,18 +366,22 @@ y_roll = np.random.rand(config.N_ROLLING_HISTORY, samples_per_frame) / 100.0
|
|||||||
|
|
||||||
|
|
||||||
# Low pass filter for the LEDs being output to the strip
|
# Low pass filter for the LEDs being output to the strip
|
||||||
pixels_filt = dsp.ExponentialFilter(np.tile(0., (config.N_PIXELS, 3)), .2, .9)
|
pixels_filt = dsp.ExponentialFilter(np.tile(0., (config.N_PIXELS, 3)), .35, .9)
|
||||||
|
|
||||||
|
|
||||||
# This is the function responsible for updating LED values
|
# This is the function responsible for updating LED values
|
||||||
# Edit this function to change the visualization
|
# Edit this function to change the visualization
|
||||||
def led_visualization(onset_values):
|
def led_visualization(onset_values):
|
||||||
# Visualizations that we want to use (normalized to [0, 1])
|
# Visualizations that we want to use (normalized to ~[0, 1])
|
||||||
pixels_A = update_leds_6(onset_values).astype(float) / 255.0
|
pixels_A = update_leds_6(onset_values)
|
||||||
pixels_B = update_leds_4(onset_values).astype(float) / 255.0
|
pixels_B = update_leds_4(onset_values)
|
||||||
# Take the product of the visualizations and scale by 255
|
# Take the product of the visualizations and scale by 255
|
||||||
pixels = pixels_A * pixels_B
|
pixels = pixels_A * pixels_B
|
||||||
pixels *= 255.0
|
# Combine pixels with color map
|
||||||
|
color = rainbow(onset_values.shape[0]) * 255.0
|
||||||
|
pixels = (pixels * color.T).T
|
||||||
|
pixels = leak_saturated_pixels(pixels)
|
||||||
|
pixels = np.clip(pixels, 0.0, 255.0)
|
||||||
# Apply low-pass filter to the output
|
# Apply low-pass filter to the output
|
||||||
pixels_filt.update(np.copy(pixels))
|
pixels_filt.update(np.copy(pixels))
|
||||||
# Display values on the LED strip
|
# Display values on the LED strip
|
||||||
|
Loading…
Reference in New Issue
Block a user