Update FX.cpp

* add more accurate version of map() - reduces slight flickering in some 2D effects
* cache fftResult[] in effects (um_data->fftresult might get updated while the effect still calculates)
This commit is contained in:
Frank
2024-07-15 11:57:46 +02:00
parent aa6d275a86
commit d2762c35b0

View File

@@ -79,6 +79,24 @@ static float mapf(float x, float in_min, float in_max, float out_min, float out_
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
// more accurate integer version of map() - based on map3() proposed in https://forum.arduino.cc/t/how-map-loses-precision-and-how-to-fix-it/371026/3
// rounding instead of truncation, better handling of inverted ranges
static long map2(long x, long in_min, long in_max, long out_min, long out_max)
{
long out_range = out_max - out_min;
if (out_range > 0) out_range ++;
else if (out_range < 0) out_range --;
else return out_min; // output range is 0
long in_range = in_max - in_min;
if (in_range > 0) in_range++;
else if (in_range < 0) in_range --;
else return out_min; // input range is 0 - Result is actually infinity but long has no such thing. The least negative long is another choice.
return ((x - in_min) * out_range) / in_range + out_min;
}
// effect functions
/*
@@ -1986,7 +2004,7 @@ uint16_t mode_partyjerk() {
if (SEGENV.aux1 > 254) {
SEGENV.aux1 = 0;
}
if (SEGENV.aux0 > map(SEGMENT.custom1, 0, 255, 0, 14)) {
if (SEGENV.aux0 > map2(SEGMENT.custom1, 0, 255, 0, 14)) {
SEGENV.aux0 = 0;
SEGENV.aux1++;
}
@@ -1995,7 +2013,7 @@ uint16_t mode_partyjerk() {
uint16_t counter = 0;
if (volumeSmth * 2 > (255 - SEGMENT.intensity)) {
speed = SEGMENT.speed * map(SEGMENT.custom2, 0, 255, 0, 100);
speed = SEGMENT.speed * map2(SEGMENT.custom2, 0, 255, 0, 100);
} else {
speed = SEGMENT.speed;
};
@@ -4768,7 +4786,7 @@ uint16_t mode_aurora(void) {
if(SEGENV.aux0 != SEGMENT.intensity || SEGENV.call == 0) {
//Intensity slider changed or first call
SEGENV.aux1 = map(SEGMENT.intensity, 0, 255, 2, W_MAX_COUNT);
SEGENV.aux1 = map2(SEGMENT.intensity, 0, 255, 2, W_MAX_COUNT);
SEGENV.aux0 = SEGMENT.intensity;
if(!SEGENV.allocateData(sizeof(AuroraWave) * SEGENV.aux1)) { // 26 on 32 segment ESP32, 9 on 16 segment ESP8266
@@ -5187,9 +5205,9 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https:
bool allColors = SEGMENT.check1;
bool overlayBG = SEGMENT.check2;
bool wrap = SEGMENT.check3;
byte blur = map(SEGMENT.custom1, 0, 255, 255, 0);
byte blur = map2(SEGMENT.custom1, 0, 255, 255, 0);
bool bgBlendMode = SEGMENT.custom1 > 220 && !overlayBG; // if blur is high and not overlaying, use bg blend mode
byte bgBlur = map(SEGMENT.custom1 - 220, 0, 35, 255, 128);
byte bgBlur = map2(SEGMENT.custom1 - 220, 0, 35, 255, 128);
uint32_t bgColor = SEGCOLOR(1);
uint32_t color = allColors ? random16() * random16() : SEGMENT.color_from_palette(0, false, PALETTE_SOLID_WRAP, 0);
uint16_t cIndex;
@@ -5748,7 +5766,7 @@ uint16_t mode_2DPolarLights(void) { // By: Kostyantyn Matviyevskyy https
}
float adjustHeight = mapf(rows, 8, maxRows, 28, minScale); // maybe use mapf() ??? // WLEDMM yes!
uint16_t adjScale = map(cols, 8, 64, 310, 63);
uint16_t adjScale = map2(cols, 8, 64, 310, 63);
adjustHeight = max(min(adjustHeight, 28.0f), minScale); // WLEDMM bugfix for larger fixtures
adjScale = max(min(adjScale, uint16_t(310)), uint16_t(63)); // WLEDMM
@@ -5767,8 +5785,8 @@ uint16_t mode_2DPolarLights(void) { // By: Kostyantyn Matviyevskyy https
}
}
*/
uint16_t _scale = map(SEGMENT.intensity, 0, 255, 30, adjScale);
byte _speed = map(SEGMENT.speed, 0, 255, 128, 16);
uint16_t _scale = map2(SEGMENT.intensity, 0, 255, 30, adjScale);
byte _speed = map2(SEGMENT.speed, 0, 255, 128, 16);
//WLEDMM add SuperSync control
uint16_t xStart, xEnd, yStart, yEnd;
@@ -6439,6 +6457,10 @@ static const char _data_FX_MODE_2DDRIFTROSE[] PROGMEM = "Drift Rose@Fade,Blur;;;
volumeSmth = *(float*) um_data->u_data[0];
volumeRaw = *(int16_t*) um_data->u_data[1];
fftResult = (uint8_t*) um_data->u_data[2];
or
uint8_t fftResult[NUM_GEQ_CHANNELS] = {0};
if (um_data->u_data != nullptr) memcpy(fftResult, um_data->u_data[2], sizeof(fftResult)); // WLEDMM to buffer curent values
samplePeak = *(uint8_t*) um_data->u_data[3];
FFT_MajorPeak = *(float*) um_data->u_data[4];
my_magnitude = *(float*) um_data->u_data[5];
@@ -6456,6 +6478,8 @@ static const char _data_FX_MODE_2DDRIFTROSE[] PROGMEM = "Drift Rose@Fade,Blur;;;
// a few constants needed for AudioReactive effects
#define NUM_GEQ_CHANNELS 16 // number of audioreactive frequency channels.
// for 22Khz sampling
#define MIN_FREQUENCY 80 // 80 HZ - due to lower resolution
#define MIN_FREQ_LOG10 1.90309f // log10(MIN_FREQUENCY)
@@ -7018,7 +7042,7 @@ uint16_t mode_noisemeter(void) { // Noisemeter. By Andrew Tuline.
if (SEGENV.call == 0) {SEGENV.setUpLeds(); SEGMENT.fill(BLACK);} // WLEDMM use lossless getPixelColor()
//uint8_t fadeRate = map(SEGMENT.speed,0,255,224,255);
uint8_t fadeRate = map(SEGMENT.speed,0,255,200,254);
uint8_t fadeRate = map2(SEGMENT.speed,0,255,200,254);
SEGMENT.fade_out(fadeRate);
float tmpSound2 = volumeRaw * 2.0 * (float)SEGMENT.intensity / 255.0;
@@ -7128,7 +7152,7 @@ static const char _data_FX_MODE_PLASMOID[] PROGMEM = "Plasmoid@Phase,# of pixels
uint16_t mode_puddlepeak(void) { // Puddlepeak. By Andrew Tuline.
uint16_t size = 0;
uint8_t fadeVal = map(SEGMENT.speed,0,255, 224, 254);
uint8_t fadeVal = map2(SEGMENT.speed,0,255, 224, 254);
uint16_t pos = random16(SEGLEN); // Set a random starting position.
um_data_t *um_data;
@@ -7174,7 +7198,7 @@ static const char _data_FX_MODE_PUDDLEPEAK[] PROGMEM = "Puddlepeak@Fade rate,Pud
//////////////////////
uint16_t mode_puddles(void) { // Puddles. By Andrew Tuline.
uint16_t size = 0;
uint8_t fadeVal = map(SEGMENT.speed, 0, 255, 224, 254);
uint8_t fadeVal = map2(SEGMENT.speed, 0, 255, 224, 254);
uint16_t pos = random16(SEGLEN); // Set a random starting position.
if (SEGENV.call == 0) {
@@ -7380,7 +7404,7 @@ uint16_t mode_DJLight(void) { // Written by Stefan Petrick, Ad
//if (color.getLuma() > 12) color.maximizeBrightness(); // for testing
//SEGMENT.setPixelColor(mid, color.fadeToBlackBy(map(fftResult[4], 0, 255, 255, 4))); // 0.13.x fade -> 180hz-260hz
uint8_t fadeVal = map(fftResult[3], 0, 255, 255, 4); // 0.14.x fade -> 216hz-301hz
uint8_t fadeVal = map2(fftResult[3], 0, 255, 255, 4); // 0.14.x fade -> 216hz-301hz
if (SEGENV.check1) fadeVal = constrain(fadeVal, 0, 176); // "candy factory" mode - avoid complete fade-out
SEGMENT.setPixelColor(mid, color.fadeToBlackBy(fadeVal));
@@ -7684,7 +7708,8 @@ uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuli
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
uint8_t *fftResult = (uint8_t*)um_data->u_data[2];
uint8_t fftResult[NUM_GEQ_CHANNELS] = {0};
if (um_data->u_data != nullptr) memcpy(fftResult, um_data->u_data[2], sizeof(fftResult)); // WLEDMM buffer curent values
if (SEGENV.call == 0) {
SEGENV.setUpLeds(); // WLEDMM use lossless getPixelColor()
@@ -7694,11 +7719,11 @@ uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuli
int fadeoutDelay = (256 - SEGMENT.speed) / 96;
if ((fadeoutDelay <= 1 ) || ((SEGENV.call % fadeoutDelay) == 0)) SEGMENT.fadeToBlackBy(4+ SEGMENT.speed/4);
uint8_t numBins = map(SEGMENT.intensity,0,255,0,16); // Map slider to fftResult bins.
uint8_t numBins = map2(SEGMENT.intensity,0,255,0,16); // Map slider to fftResult bins.
for (int i=0; i<numBins; i++) { // How many active bins are we using.
uint16_t locn = inoise16(strip.now*SEGMENT.speed+i*50000, strip.now*SEGMENT.speed); // Get a new pixel location from moving noise.
// if SEGLEN equals 1 locn will be always 0, hence we set the first pixel only
locn = map(locn, 7500, 58000, 0, SEGLEN-1); // Map that to the length of the strand, and ensure we don't go over.
locn = map2(locn, 7500, 58000, 0, SEGLEN-1); // Map that to the length of the strand, and ensure we don't go over.
SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i*64, false, PALETTE_SOLID_WRAP, 0), fftResult[i % 16]*4));
}
@@ -7816,7 +7841,7 @@ static const char _data_FX_MODE_WATERFALL[] PROGMEM = "Waterfall@!,Adjust color,
uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma.
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
const int NUM_BANDS = map(SEGMENT.custom1, 0, 255, 1, 16);
const int NUM_BANDS = map2(SEGMENT.custom1, 0, 255, 1, 16);
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
if ((cols <=1) || (rows <=1)) return mode_static(); // not really a 2D set-up
@@ -7829,7 +7854,9 @@ uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma.
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
uint8_t *fftResult = (uint8_t*)um_data->u_data[2];
uint8_t fftResult[NUM_GEQ_CHANNELS] = {0};
if (um_data->u_data != nullptr) memcpy(fftResult, um_data->u_data[2], sizeof(fftResult)); // WLEDMM buffer curent values
#ifdef SR_DEBUG
uint8_t samplePeak = *(uint8_t*)um_data->u_data[3];
#endif
@@ -7862,7 +7889,7 @@ uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma.
remaining--; //consume remaining
// Serial.printf("x %d b %d n %d w %f %f\n", x, band, NUM_BANDS, bandwidth, remaining);
uint8_t frBand = ((NUM_BANDS < 16) && (NUM_BANDS > 1)) ? map(band, 0, NUM_BANDS - 1, 0, 15):band; // always use full range. comment out this line to get the previous behaviour.
uint8_t frBand = ((NUM_BANDS < 16) && (NUM_BANDS > 1)) ? map2(band, 0, NUM_BANDS - 1, 0, 15):band; // always use full range. comment out this line to get the previous behaviour.
// frBand = constrain(frBand, 0, 15); //WLEDMM can never be out of bounds (I think...)
uint16_t colorIndex = frBand * 17; //WLEDMM 0.255
uint16_t bandHeight = fftResult[frBand]; // WLEDMM we use the original ffResult, to preserve accuracy
@@ -7872,7 +7899,7 @@ uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma.
// get height of next (right side) bar
uint8_t nextband = (remaining < 1)? band +1: band;
nextband = constrain(nextband, 0, 15); // just to be sure
frBand = ((NUM_BANDS < 16) && (NUM_BANDS > 1)) ? map(nextband, 0, NUM_BANDS - 1, 0, 15):nextband; // always use full range. comment out this line to get the previous behaviour.
frBand = ((NUM_BANDS < 16) && (NUM_BANDS > 1)) ? map2(nextband, 0, NUM_BANDS - 1, 0, 15):nextband; // always use full range. comment out this line to get the previous behaviour.
uint16_t nextBandHeight = fftResult[frBand];
// smooth Band height
bandHeight = (7*bandHeight + 3*lastBandHeight + 3*nextBandHeight) / 12; // yeees, its 12 not 13 (10% amplification)
@@ -7920,7 +7947,7 @@ uint16_t mode_2DFunkyPlank(void) { // Written by ??? Adapted by Wil
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
int NUMB_BANDS = map(SEGMENT.custom1, 0, 255, 1, 16);
int NUMB_BANDS = map2(SEGMENT.custom1, 0, 255, 1, 16);
int barWidth = (cols / NUMB_BANDS);
int bandInc = 1;
if (barWidth == 0) {
@@ -7934,7 +7961,8 @@ uint16_t mode_2DFunkyPlank(void) { // Written by ??? Adapted by Wil
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
uint8_t *fftResult = (uint8_t*)um_data->u_data[2];
uint8_t fftResult[NUM_GEQ_CHANNELS] = {0};
if (um_data->u_data != nullptr) memcpy(fftResult, um_data->u_data[2], sizeof(fftResult)); // WLEDMM buffer curent values
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
@@ -7949,7 +7977,7 @@ uint16_t mode_2DFunkyPlank(void) { // Written by ??? Adapted by Wil
int b = 0;
for (int band = 0; band < NUMB_BANDS; band += bandInc, b++) {
int hue = fftResult[band % 16];
int v = map(fftResult[band % 16], 0, 255, 10, 255);
int v = map2(fftResult[band % 16], 0, 255, 10, 255);
for (int w = 0; w < barWidth; w++) {
int xpos = (barWidth * b) + w;
SEGMENT.setPixelColorXY(xpos, 0, CHSV(hue, 255, v));
@@ -8021,11 +8049,12 @@ uint16_t mode_2DAkemi(void) {
const float lightFactor = 0.15f;
const float normalFactor = 0.4f;
um_data_t *um_data;
um_data_t *um_data = nullptr;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
um_data = simulateSound(SEGMENT.soundSim);
}
uint8_t *fftResult = (uint8_t*)um_data->u_data[2];
uint8_t fftResult[NUM_GEQ_CHANNELS] = {0};
if (um_data->u_data != nullptr) memcpy(fftResult, um_data->u_data[2], sizeof(fftResult)); // WLEDMM buffer curent values
float base = fftResult[0]/255.0f;
//draw and color Akemi
@@ -8047,7 +8076,7 @@ uint16_t mode_2DAkemi(void) {
default: color = BLACK; break;
}
if (SEGMENT.intensity > 128 && fftResult && fftResult[0] > 128) { //dance if base is high
if (SEGMENT.intensity > 128 && um_data && fftResult[0] > 128) { //dance if base is high
SEGMENT.setPixelColorXY(x, 0, BLACK);
SEGMENT.setPixelColorXY(x, y+1, color);
} else
@@ -8055,11 +8084,11 @@ uint16_t mode_2DAkemi(void) {
}
//add geq left and right
if (um_data && fftResult) {
if (um_data) {
for (int x=0; x < cols/8; x++) {
uint16_t band = x * cols/8;
band = constrain(band, 0, 15);
uint16_t barHeight = map(fftResult[band], 0, 255, 0, 17*rows/32);
uint16_t barHeight = map2(fftResult[band], 0, 255, 0, 17*rows/32);
CRGB color = SEGMENT.color_from_palette((band * 35), false, PALETTE_SOLID_WRAP, 0);
for (int y=0; y < barHeight; y++) {