So, Iām revisiting this idea with the hope of getting some help to push it over the finish line.
A little context: the installation is for a multi day event. I donāt expect there will be people around the art piece the entire timeā¦ likely there will be times when people in the area will activate the sound response, and other times when no one will be around. The intent is to have sound response when people are around, but when no one is around, the PB reverts to the no-sound portion of the code after a specific timeout (30 sec or so). This is so that when people are far away, they will be drawn towards the piece. This is less likely when the PB is running in sound reactive mode as the light response is subtle when there is no sound present.
Thanks to all the wonderful help Iāve received so far, the code seems to be running as intended except for one aspectā¦ the 30 second timeout that keeps the code in sound mode before returning to no-sound mode. Right now the code jumps to sound reactive mode when there is sound (as expected), but then jumps right back to non-sound mode without a timeout. The effect is a glitchy jump back and forth between the modes. The video posted previously kind of shows this, but the glitchy effect is much more pronounced when there is talking or music with short interim quiet periods.
So any help resolving this timeout problems would be greatly appreciated. Below is code I have cobbled together from the previous suggestions, but Iām not sure what Iām doing wrongā¦
// these variables hold data used by the timer functions
// put them at the top of your code
var maxTimers = 10
var timerFns = array(maxTimers)
export var timerTimes = array(maxTimers)
export var timerId = -1
//start rainbow melt (NO SOUND) pattern variable(s)
hl = pixelCount/2
//end rainbow melt (NO SOUND) pattern variable(s)
//start spectromatrix optim (SOUND) pattern variables and functions
//This pattern uses the sensor expansion board
//it supports pixel mapped configurations with 2D or 3D maps
export var frequencyData
//start - SR added - built in variables
//export var maxFrequency
//export var maxFrequencyMagnitude
export var energyAverage //used for threshold for heardsound
//SR created - true or false variable based on energyAverage above or below set threshold in checkThreshold() function
export var heardsound
//end - SR added / created
width = 14
zigzag = true
averageWindowMs =500
fade = .6
speed = 1
zoom = .3
targetFill = 0.15
var pic = makePIController(1, .1, 300, 0, 300)
var sensitivity = 0
brightnessFeedback = 0
var averages = array(32)
pixels = array(pixelCount)
vals = array(32)
/**
- Schedule a function to run after some time (in seconds) elapses.
- If successful, a non-negative id is returned and can be used
- to cancel it using clearTimeout(id). Returns -1 on error.
*/
function setTimeout(fn, timeout) {
var nextTimer = -1 //id
//find an empty timer slot
for (var i = 0; i < maxTimers; i++) {
if (!timerFns[i]) {
nextTimer = i
break
}
}
if (nextTimer >= 0) {
timerFns[nextTimer] = fn
timerTimes[nextTimer] = timeout
}
return nextTimer
}
/**
- Cancel a timeout by id
*/
function clearTimeout(id) {
if (id >= 0) {
timerFns[id] = false
}
}
/**
- Update timers based on elapsed time, and call any callbacks.
- Call this from beforeRender(delta)
*/
function updateTimers(delta) {
var seconds = delta/1000
//check timers
for (var i = 0; i < maxTimers; i++) {
//see if there is a function for this slot
if (timerFns[i]) {
//reduce times until zero
timerTimes[i] = timerTimes[i] - seconds // max(0, timerTimes[i] - seconds)
//see if timer is up
if (timerTimes[i] == 0) {
var fn = timerFns[i]
//blank out this slot so we donāt keep executing it and it can be reused
timerFns[i] = false
fn() //run the function //before render delta
}
}
}
}
//To use, youāll call updateTimers(delta) inside your beforeRender implementation. You can register a function (or even a lambda) to run later, and cancel them. A common pattern is to
//cancel the old timer and re-schedule it every time an even occurs to know some time after the last one happened.
//You could use this code, and fill out what you want to happen in stoppedHearingSound(). Call heardSound() every time your code detects sufficient sound to start/reset the timer.
function checkThreshold(){
if(energyAverage >= 0.001000){ //ADJUSTS THRESHOLD FOR SOUND PATTERN VS NO SOUND PATTERN
heardSound()
}
else {
stoppedHearingSound()
}
}
function stoppedHearingSound() {
//do something 30 seconds after the last time sound was heard here
heardsound = false
//setTimeout()
//cancel a previous timer if one was set
// if (timerId != -1)
// clearTimeout(timerId)
// //set a timer to trigger in 30 seconds
// timerId = setTimeout(() => {
// timerId = -1
// heardsound = false
// checkThreshold();
// }, 30)
return heardsound
}
var timerId = -1
function heardSound() {
heardsound = true
// //cancel a previous timer if one was set
if (timerId != -1)
clearTimeout(timerId)
//set a timer to trigger in 30 seconds
timerId = setTimeout(() => {
timerId = -1
//heardsound = true
stoppedHearingSound();
}, 30)
return heardsound
}
// Makes a new PI Controller
function makePIController(kp, ki, start, min, max) {
var pic = array(5)
pic[0] = kp
pic[1] = ki
pic[2] = start
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)
}
//end spectromatrix optim pattern variables and functions
export function beforeRender(delta) {
updateTimers(delta)
//checkThreshold()
//if(heardsound){
if(energyAverage >= 0.001000){ //<-- THIS NEEDS TO BE ADJUSTED FOR THRESHOLD FOR SOUND PATTERN VS NO SOUND PATTERN AND ALSO CAN PLAY
// //AROUND WITH averages AND vals ARRAYS FOR DIFFERENT RANGES TO PLAY WITH
// // heardsound = true
heardSound()
//through if statement for sound pattern set-up
t1 = time(1)
t2 = time(.6)
wt1 = wave(t1 * speed)
sensitivity = calcPIController(pic, targetFill - brightnessFeedback / pixelCount);
brightnessFeedback = 0
dw = delta / averageWindowMs
// avg = 0.000000
// sum = 0.000000
for (i = 0; i < 32; i++) {
averages[i] = max(.00001, averages[i] * (1 - dw) + frequencyData[i] * dw * sensitivity)
vals[i] = (frequencyData[i] * sensitivity - averages[i]*2) * 10 * (averages[i] * 1000 + 1)
// sum += frequencyData[i] * 10
// if(energyAverage >= 0.000800){ //<-- THIS NEEDS TO BE ADJUSTED FOR THRESHOLD FOR SOUND PATTERN VS NO SOUND PATTERN AND ALSO CAN PLAY
// //AROUND WITH averages AND vals ARRAYS FOR DIFFERENT RANGES TO PLAY WITH
// // heardsound = true
// heardSound()
}
}
else { //below sets up non-sound pattern
// setTimeout(render, 30000) //gives āundefined symbol setTimeoutā error; uncertain if calling render function is correct
stoppedHearingSound()
//heardsound = false
t1 = time(.1)
t2 = time(0.13)
}
// avg = (sum / 32) * 1000
// if(avg >= 0.000700){ //<-- THIS NEEDS TO BE ADJUSTED FOR THRESHOLD FOR SOUND PATTERN VS NO SOUND PATTERN AND ALSO CAN PLAY
// //AROUND WITH averages AND vals ARRAYS FOR DIFFERENT RANGES TO PLAY WITH
// heardsound = true
// }
// else { //below sets up non-sound pattern
// heardsound = false
// t1 = time(.1)
// t2 = time(0.13)
// }
}
//start spectromatrix optim only functions
export function render3D(index, x, y, z) {
var i, h, s, v
i = triangle((wave((x+z)*zoom + wt1) + wave((y+z)*zoom - wt1)) *.5 + t2) * 31
v = vals[i]
h = i / 60 + t1
v = v > 0 ? v*v : 0
s = 1 - v
pixels[index] = pixels[index] * fade + v
v = pixels[index];
brightnessFeedback += clamp(v, 0, 2)
hsv(h, s, v)
}
//support 2D pixel mapped configurations
export function render2D(index, x, y) {
render3D(index, x, y, 0)
}
//end spectromatrix optim only functions
//this pixel mapper shim will work without a pixel map or on older Pixelblazes
//it calculates x/y based on a 2D LED matrix display given a known width and height
export function render(index) {
if(heardsound){ //renders sound pattern
var width = 8, height = 8
var y = floor(index / width)
var x = index % width
//comment out this next line if you don't have zigzag wiring:
x = (y % 2 == 0 ? x : width - 1 - x)
x /= width
y /= height
render2D(index, x, y)
}
else { //renders non-sound pattern
c1 = 1-abs(index - hl)/hl
c2 = wave(c1)
c3 = wave(c2 + t1)
v = wave(c3 + t1)
v = v*v
hsv(c1 + t2,1,v)
}
}
Also, It looks like I missed the PB coding academy. Is there another one upcoming?
Thanks in advance!