Gav... efects to test audio

Anti-aliased setPixelColor() with support for normalized x & y
This commit is contained in:
Blaz Kristan 2022-06-13 21:28:10 +02:00
parent f9c933bf3b
commit cdef8472e3
4 changed files with 1457 additions and 1096 deletions

View File

@ -739,7 +739,7 @@ class AudioReactive : public Usermod {
// usermod exchangeable data // usermod exchangeable data
// we will assign all usermod exportable data here as pointers to original variables or arrays and allocate memory for pointers // we will assign all usermod exportable data here as pointers to original variables or arrays and allocate memory for pointers
um_data = new um_data_t; um_data = new um_data_t;
um_data->u_size = 11; um_data->u_size = 14;
um_data->u_type = new um_types_t[um_data->u_size]; um_data->u_type = new um_types_t[um_data->u_size];
um_data->u_data = new void*[um_data->u_size]; um_data->u_data = new void*[um_data->u_size];
um_data->u_data[0] = &maxVol; um_data->u_data[0] = &maxVol;
@ -764,17 +764,20 @@ class AudioReactive : public Usermod {
um_data->u_type[9] = UMT_BYTE; um_data->u_type[9] = UMT_BYTE;
um_data->u_data[10] = &sampleAgc; um_data->u_data[10] = &sampleAgc;
um_data->u_type[10] = UMT_FLOAT; um_data->u_type[10] = UMT_FLOAT;
um_data->u_data[11] = &multAgc;
um_data->u_type[11] = UMT_FLOAT;
um_data->u_data[12] = &sampleReal;
um_data->u_type[12] = UMT_FLOAT;
um_data->u_data[13] = &sampleGain;
um_data->u_type[13] = UMT_FLOAT;
//... //...
// these are values used by effects in soundreactive fork // these are values used by effects in soundreactive fork
//float sampleReal = um_data->;
//float multAgc = um_data->;
//int16_t rawSampleAgc = um_data->; //int16_t rawSampleAgc = um_data->;
//bool samplePeak = um_data->; //bool samplePeak = um_data->;
//uint8_t squelch = um_data->; //uint8_t squelch = um_data->;
//uint8_t soundSquelch = um_data->; //uint8_t soundSquelch = um_data->;
//uint8_t binNum = um_data->; //uint8_t binNum = um_data->;
//uint16_t *myVals = um_data->; //uint16_t *myVals = um_data->;
//int16_t sample = um_data->;
// Reset I2S peripheral for good measure // Reset I2S peripheral for good measure
i2s_driver_uninstall(I2S_NUM_0); i2s_driver_uninstall(I2S_NUM_0);

View File

@ -5977,6 +5977,287 @@ static const char *_data_FX_MODE_DRIFT_ROSE PROGMEM = "2D Drift Rose@Fade,Blur;;
//************************* audio routines ********************************** //************************* audio routines **********************************
// float version of map()
static float mapf(float x, float in_min, float in_max, float out_min, float out_max){
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
// Gravity struct requited for GRAV* effects
typedef struct Gravity {
int topLED;
int gravityCounter;
} gravity;
///////////////////////
// * GRAVCENTER //
///////////////////////
uint16_t WS2812FX::mode_gravcenter(void) { // Gravcenter. By Andrew Tuline.
const uint16_t dataSize = sizeof(gravity);
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
Gravity* gravcen = reinterpret_cast<Gravity*>(SEGENV.data);
um_data_t *um_data;
float tmpSound = (float)inoise8(23333); // I have no idea what that does
if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
//soundAgc = *(uint8_t*)um_data->u_data[9];
//sampleAgc = *(float*)um_data->u_data[10];
//sampleAvg = *(float*)um_data->u_data[8];
tmpSound = *(uint8_t*)um_data->u_data[9] ? *(float*)um_data->u_data[10] : *(float*)um_data->u_data[8];
}
fade_out(240);
float segmentSampleAvg = tmpSound * (float)SEGMENT.intensity / 255.0f;
segmentSampleAvg *= 0.125; // divide by 8, to compensate for later "sensitivty" upscaling
float mySampleAvg = mapf(segmentSampleAvg*2.0, 0, 32, 0, (float)SEGLEN/2.0); // map to pixels available in current segment
uint16_t tempsamp = constrain(mySampleAvg, 0, SEGLEN/2); // Keep the sample from overflowing.
uint8_t gravity = 8 - SEGMENT.speed/32;
for (int i=0; i<tempsamp; i++) {
uint8_t index = inoise8(i*segmentSampleAvg+millis(), 5000+i*segmentSampleAvg);
setPixelColor(i+SEGLEN/2, color_blend(SEGCOLOR(1), color_from_palette(index, false, PALETTE_SOLID_WRAP, 0), segmentSampleAvg*8));
setPixelColor(SEGLEN/2-i-1, color_blend(SEGCOLOR(1), color_from_palette(index, false, PALETTE_SOLID_WRAP, 0), segmentSampleAvg*8));
}
if (tempsamp >= gravcen->topLED)
gravcen->topLED = tempsamp-1;
else if (gravcen->gravityCounter % gravity == 0)
gravcen->topLED--;
if (gravcen->topLED >= 0) {
setPixelColor(gravcen->topLED+SEGLEN/2, color_from_palette(millis(), false, PALETTE_SOLID_WRAP, 0));
setPixelColor(SEGLEN/2-1-gravcen->topLED, color_from_palette(millis(), false, PALETTE_SOLID_WRAP, 0));
}
gravcen->gravityCounter = (gravcen->gravityCounter + 1) % gravity;
return FRAMETIME;
} // mode_gravcenter()
static const char *_data_FX_MODE_GRAVCENTER PROGMEM = " ♪ Gravcenter@Rate of fall,Sensitivity=128;,!;!";
///////////////////////
// * GRAVCENTRIC //
///////////////////////
uint16_t WS2812FX::mode_gravcentric(void) { // Gravcentric. By Andrew Tuline.
uint16_t dataSize = sizeof(gravity);
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
Gravity* gravcen = reinterpret_cast<Gravity*>(SEGENV.data);
um_data_t *um_data;
float tmpSound = (float)inoise8(23333); // I have no idea what that does
if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
//soundAgc = *(uint8_t*)um_data->u_data[9];
//sampleAgc = *(float*)um_data->u_data[10];
//sampleAvg = *(float*)um_data->u_data[8];
tmpSound = *(uint8_t*)um_data->u_data[9] ? *(float*)um_data->u_data[10] : *(float*)um_data->u_data[8];
}
fade_out(240);
fade_out(240); // twice? really?
float segmentSampleAvg = tmpSound * (float)SEGMENT.intensity / 255.0;
segmentSampleAvg *= 0.125f; // divide by 8, to compensate for later "sensitivty" upscaling
float mySampleAvg = mapf(segmentSampleAvg*2.0, 0.0f, 32.0f, 0.0f, (float)SEGLEN/2.0); // map to pixels availeable in current segment
int tempsamp = constrain(mySampleAvg, 0, SEGLEN/2); // Keep the sample from overflowing.
uint8_t gravity = 8 - SEGMENT.speed/32;
for (int i=0; i<tempsamp; i++) {
uint8_t index = segmentSampleAvg*24+millis()/200;
setPixelColor(i+SEGLEN/2, color_from_palette(index, false, PALETTE_SOLID_WRAP, 0));
setPixelColor(SEGLEN/2-1-i, color_from_palette(index, false, PALETTE_SOLID_WRAP, 0));
}
if (tempsamp >= gravcen->topLED)
gravcen->topLED = tempsamp-1;
else if (gravcen->gravityCounter % gravity == 0)
gravcen->topLED--;
if (gravcen->topLED >= 0) {
setPixelColor(gravcen->topLED+SEGLEN/2, CRGB::Gray);
setPixelColor(SEGLEN/2-1-gravcen->topLED, CRGB::Gray);
}
gravcen->gravityCounter = (gravcen->gravityCounter + 1) % gravity;
return FRAMETIME;
} // mode_gravcentric()
static const char *_data_FX_MODE_GRAVCENTRIC PROGMEM = " ♪ Gravcentric@Rate of fall,Sensitivity=128;!;!";
///////////////////////
// * GRAVIMETER //
///////////////////////
#ifndef SR_DEBUG_AGC
uint16_t WS2812FX::mode_gravimeter(void) { // Gravmeter. By Andrew Tuline.
uint16_t dataSize = sizeof(gravity);
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
Gravity* gravcen = reinterpret_cast<Gravity*>(SEGENV.data);
um_data_t *um_data;
float tmpSound = (float)inoise8(23333); // I have no idea what that does
if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
//soundAgc = *(uint8_t*)um_data->u_data[9];
//sampleAgc = *(float*)um_data->u_data[10];
//sampleAvg = *(float*)um_data->u_data[8];
tmpSound = *(uint8_t*)um_data->u_data[9] ? *(float*)um_data->u_data[10] : *(float*)um_data->u_data[8];
}
fade_out(240);
float segmentSampleAvg = tmpSound * (float)SEGMENT.intensity / 255.0;
segmentSampleAvg *= 0.25; // divide by 4, to compensate for later "sensitivty" upscaling
float mySampleAvg = mapf(segmentSampleAvg*2.0, 0, 64, 0, (SEGLEN-1)); // map to pixels availeable in current segment
int tempsamp = constrain(mySampleAvg,0,SEGLEN-1); // Keep the sample from overflowing.
uint8_t gravity = 8 - SEGMENT.speed/32;
for (int i=0; i<tempsamp; i++) {
uint8_t index = inoise8(i*segmentSampleAvg+millis(), 5000+i*segmentSampleAvg);
setPixelColor(i, color_blend(SEGCOLOR(1), color_from_palette(index, false, PALETTE_SOLID_WRAP, 0), segmentSampleAvg*8));
}
if (tempsamp >= gravcen->topLED)
gravcen->topLED = tempsamp;
else if (gravcen->gravityCounter % gravity == 0)
gravcen->topLED--;
if (gravcen->topLED > 0) {
setPixelColor(gravcen->topLED, color_from_palette(millis(), false, PALETTE_SOLID_WRAP, 0));
}
gravcen->gravityCounter = (gravcen->gravityCounter + 1) % gravity;
return FRAMETIME;
} // mode_gravimeter()
#else
// This an abuse of the gravimeter effect for AGC debugging
// instead of sound volume, it uses the AGC gain multiplier as input
uint16_t WS2812FX::mode_gravimeter(void) { // Gravmeter. By Andrew Tuline.
uint16_t dataSize = sizeof(gravity);
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
Gravity* gravcen = reinterpret_cast<Gravity*>(SEGENV.data);
um_data_t *um_data;
uint16_t sample = 0;
uint8_t soundAgc = 0;
float sampleAgc = 0.0f, sampleAgv = 0.0f, multAgc = 0.0f, sampleReal = 0.0f;
if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
sample = *(uint16_t*)um_data->u_data[2];
soundAgc = *(uint8_t*)um_data->u_data[9];
sampleAgc = *(float*)um_data->u_data[10];
sampleAvg = *(float*)um_data->u_data[8];
multAgc = *(float*)um_data->u_data[11];
sampleReal = *(float*)um_data->u_data[12];
sampleGain = *(float*)um_data->u_data[13];
}
fade_out(240);
float tmpSound = multAgc; // AGC gain
if (soundAgc == 0) {
if ((sampleAvg> 1.0f) && (sampleReal > 0.05f))
tmpSound = (float)sample / sampleReal; // current non-AGC gain
else
tmpSound = ((float)sampleGain/40.0f * (float)inputLevel/128.0f) + 1.0f/16.0f; // non-AGC gain from presets
}
if (tmpSound > 2) tmpSound = ((tmpSound -2.0f) / 2) +2; //compress ranges > 2
if (tmpSound > 1) tmpSound = ((tmpSound -1.0f) / 2) +1; //compress ranges > 1
float segmentSampleAvg = 64.0f * tmpSound * (float)SEGMENT.intensity / 128.0f;
float mySampleAvg = mapf(segmentSampleAvg, 0.0f, 128.0f, 0, (SEGLEN-1)); // map to pixels availeable in current segment
int tempsamp = constrain(mySampleAvg, 0, SEGLEN-1); // Keep the sample from overflowing.
//tempsamp = SEGLEN - tempsamp; // uncomment to invert direction
segmentSampleAvg = fmax(64.0f - fmin(segmentSampleAvg, 63),8); // inverted brightness
uint8_t gravity = 8 - SEGMENT.speed/32;
if (sampleAvg > 1) // disable bar "body" if below squelch
{
for (int i=0; i<tempsamp; i++) {
uint8_t index = inoise8(i*segmentSampleAvg+millis(), 5000+i*segmentSampleAvg);
setPixelColor(i, color_blend(SEGCOLOR(1), color_from_palette(index, false, PALETTE_SOLID_WRAP, 0), segmentSampleAvg*4.0));
}
}
if (tempsamp >= gravcen->topLED)
gravcen->topLED = tempsamp;
else if (gravcen->gravityCounter % gravity == 0)
gravcen->topLED--;
if (gravcen->topLED > 0) {
setPixelColor(gravcen->topLED, color_from_palette(millis(), false, PALETTE_SOLID_WRAP, 0));
}
gravcen->gravityCounter = (gravcen->gravityCounter + 1) % gravity;
return FRAMETIME;
} // mode_gravimeter()
#endif
static const char *_data_FX_MODE_GRAVIMETER PROGMEM = " ♪ Gravimeter@Rate of fall,Sensitivity=128;,!;!";
///////////////////////
// ** Gravfreq //
///////////////////////
uint16_t WS2812FX::mode_gravfreq(void) { // Gravfreq. By Andrew Tuline.
uint16_t dataSize = sizeof(gravity);
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
Gravity* gravcen = reinterpret_cast<Gravity*>(SEGENV.data);
um_data_t *um_data;
uint8_t soundAgc = 0;
float sampleAgc = 0.0f, sampleAvg = 0.0f;
double FFT_MajorPeak = 0.0;
if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
FFT_MajorPeak = *(double*)um_data->u_data[6];
soundAgc = *(uint8_t*)um_data->u_data[9];
sampleAgc = *(float*)um_data->u_data[10];
sampleAvg = *(float*)um_data->u_data[8];
} else {
// add support for no audio data
sampleAvg = inoise8(12345); // I have no idea what that does
}
fade_out(240);
float tmpSound = (soundAgc) ? sampleAgc : sampleAvg;
float segmentSampleAvg = tmpSound * (float)SEGMENT.intensity / 255.0;
segmentSampleAvg *= 0.125; // divide by 8, to compensate for later "sensitivty" upscaling
float mySampleAvg = mapf(segmentSampleAvg*2.0, 0,32, 0, (float)SEGLEN/2.0); // map to pixels availeable in current segment
int tempsamp = constrain(mySampleAvg,0,SEGLEN/2); // Keep the sample from overflowing.
uint8_t gravity = 8 - SEGMENT.speed/32;
for (int i=0; i<tempsamp; i++) {
uint8_t index = (log10((int)FFT_MajorPeak) - (3.71-1.78)) * 255; //int? shouldn't it be floor() or similar
setPixelColor(i+SEGLEN/2, color_from_palette(index, false, PALETTE_SOLID_WRAP, 0));
setPixelColor(SEGLEN/2-i-1, color_from_palette(index, false, PALETTE_SOLID_WRAP, 0));
}
if (tempsamp >= gravcen->topLED)
gravcen->topLED = tempsamp-1;
else if (gravcen->gravityCounter % gravity == 0)
gravcen->topLED--;
if (gravcen->topLED >= 0) {
setPixelColor(gravcen->topLED+SEGLEN/2, CRGB::Gray);
setPixelColor(SEGLEN/2-1-gravcen->topLED, CRGB::Gray);
}
gravcen->gravityCounter = (gravcen->gravityCounter + 1) % gravity;
return FRAMETIME;
} // mode_gravfreq()
static const char *_data_FX_MODE_GRAVFREQ PROGMEM = " ♫ Gravfreq@Rate of fall,Sensivity=128;,!;!";
///////////////////////// /////////////////////////
// * 2D Waverly // // * 2D Waverly //
///////////////////////// /////////////////////////
@ -5996,12 +6277,12 @@ uint16_t WS2812FX::mode_2DWaverly(void) {
} }
um_data_t *um_data; um_data_t *um_data;
uint8_t *soundAgc = nullptr; uint8_t soundAgc = 0;
float *sampleAgc, *sampleAvg; float sampleAgc = 0.0f, sampleAvg = 0.0f;
if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
soundAgc = (uint8_t*)um_data->u_data[9]; soundAgc = *(uint8_t*)um_data->u_data[9];
sampleAgc = (float*)um_data->u_data[10]; sampleAgc = *(float*)um_data->u_data[10];
sampleAvg = (float*)um_data->u_data[8]; sampleAvg = *(float*)um_data->u_data[8];
} }
fadeToBlackBy(leds, SEGMENT.speed); fadeToBlackBy(leds, SEGMENT.speed);
@ -6010,9 +6291,9 @@ uint16_t WS2812FX::mode_2DWaverly(void) {
for (uint16_t i = 0; i < cols; i++) { for (uint16_t i = 0; i < cols; i++) {
uint16_t thisVal = (1 + SEGMENT.intensity/64) * inoise8(i * 45 , t , t)/2; uint16_t thisVal = (1 + SEGMENT.intensity/64) * inoise8(i * 45 , t , t)/2;
// use audio if available // use audio if available
if (um_data && soundAgc) { if (um_data) {
thisVal /= 32; // reduce intensity of inoise8() thisVal /= 32; // reduce intensity of inoise8()
thisVal *= (*soundAgc) ? *sampleAgc : *sampleAvg; thisVal *= (soundAgc) ? sampleAgc : sampleAvg;
} }
uint16_t thisMax = map(thisVal, 0, 512, 0, rows); uint16_t thisMax = map(thisVal, 0, 512, 0, rows);
@ -6178,6 +6459,10 @@ const char *WS2812FX::_modeData[MODE_COUNT] = {
_data_FX_MODE_GHOST_RIDER, _data_FX_MODE_GHOST_RIDER,
_data_FX_MODE_BLOBS, _data_FX_MODE_BLOBS,
_data_FX_MODE_SCROLL_TEXT, _data_FX_MODE_SCROLL_TEXT,
_data_FX_MODE_DRIFT_ROSE _data_FX_MODE_DRIFT_ROSE,
_data_FX_MODE_GRAVCENTER,
_data_FX_MODE_GRAVCENTRIC,
_data_FX_MODE_GRAVIMETER,
_data_FX_MODE_GRAVFREQ
}; };

View File

@ -267,8 +267,12 @@
#define FX_MODE_BLOBS 144 #define FX_MODE_BLOBS 144
#define FX_MODE_SCROLL_TEXT 145 #define FX_MODE_SCROLL_TEXT 145
#define FX_MODE_DRFIT_ROSE 146 #define FX_MODE_DRFIT_ROSE 146
#define FX_MODE_GRAVCENTER 147
#define FX_MODE_GRAVCENTRIC 148
#define FX_MODE_GRAVIMETER 149
#define FX_MODE_GRAVFREQ 150
#define MODE_COUNT 147 #define MODE_COUNT 151
class WS2812FX { class WS2812FX {
@ -667,6 +671,10 @@ class WS2812FX {
_mode[FX_MODE_BLOBS] = &WS2812FX::mode_2Dfloatingblobs; _mode[FX_MODE_BLOBS] = &WS2812FX::mode_2Dfloatingblobs;
_mode[FX_MODE_SCROLL_TEXT] = &WS2812FX::mode_2Dscrollingtext; _mode[FX_MODE_SCROLL_TEXT] = &WS2812FX::mode_2Dscrollingtext;
_mode[FX_MODE_DRFIT_ROSE] = &WS2812FX::mode_2Ddriftrose; _mode[FX_MODE_DRFIT_ROSE] = &WS2812FX::mode_2Ddriftrose;
_mode[FX_MODE_GRAVCENTER] = &WS2812FX::mode_gravcenter;
_mode[FX_MODE_GRAVCENTRIC] = &WS2812FX::mode_gravcentric;
_mode[FX_MODE_GRAVIMETER] = &WS2812FX::mode_gravimeter;
_mode[FX_MODE_GRAVFREQ] = &WS2812FX::mode_gravfreq;
_brightness = DEFAULT_BRIGHTNESS; _brightness = DEFAULT_BRIGHTNESS;
currentPalette = CRGBPalette16(CRGB::Black); currentPalette = CRGBPalette16(CRGB::Black);
@ -924,6 +932,7 @@ class WS2812FX {
void void
setUpMatrix(), setUpMatrix(),
setPixelColorXY(uint16_t x, uint16_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0), setPixelColorXY(uint16_t x, uint16_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0),
setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = false),
blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend), blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend),
blur1d(CRGB* leds, fract8 blur_amount), blur1d(CRGB* leds, fract8 blur_amount),
blur1d(uint16_t i, bool vertical, fract8 blur_amount, CRGB* leds=nullptr), // 1D box blur (with weight) blur1d(uint16_t i, bool vertical, fract8 blur_amount, CRGB* leds=nullptr), // 1D box blur (with weight)
@ -944,6 +953,8 @@ class WS2812FX {
inline void setPixelColorXY(uint16_t x, uint16_t y, uint32_t c) { setPixelColorXY(x, y, byte(c>>16), byte(c>>8), byte(c), byte(c>>24)); } inline void setPixelColorXY(uint16_t x, uint16_t y, uint32_t c) { setPixelColorXY(x, y, byte(c>>16), byte(c>>8), byte(c), byte(c>>24)); }
inline void setPixelColorXY(uint16_t x, uint16_t y, CRGB c) { setPixelColorXY(x, y, c.red, c.green, c.blue); } inline void setPixelColorXY(uint16_t x, uint16_t y, CRGB c) { setPixelColorXY(x, y, c.red, c.green, c.blue); }
inline void setPixelColorXY(float x, float y, uint32_t c, bool aa) { setPixelColorXY(x, y, byte(c>>16), byte(c>>8), byte(c), byte(c>>24), aa); }
inline void setPixelColorXY(float x, float y, CRGB c, bool aa) { setPixelColorXY(x, y, c.red, c.green, c.blue, aa); }
inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) { drawLine(x0, y0, x1, y1, CRGB(byte(c>>16), byte(c>>8), byte(c))); } inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) { drawLine(x0, y0, x1, y1, CRGB(byte(c>>16), byte(c>>8), byte(c))); }
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint32_t c) { drawCharacter(chr, x, y, CRGB(byte(c>>16), byte(c>>8), byte(c))); } inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint32_t c) { drawCharacter(chr, x, y, CRGB(byte(c>>16), byte(c>>8), byte(c))); }
@ -954,6 +965,8 @@ class WS2812FX {
uint32_t uint32_t
getPixelColorXY(uint16_t, uint16_t); getPixelColorXY(uint16_t, uint16_t);
// end 2D support
// 2D modes // 2D modes
uint16_t uint16_t
mode_2DBlackHole(void), mode_2DBlackHole(void),
@ -986,7 +999,13 @@ class WS2812FX {
mode_2Dscrollingtext(void), mode_2Dscrollingtext(void),
mode_2Ddriftrose(void); mode_2Ddriftrose(void);
// end 2D support // audio modes
uint16_t
mode_gravcenter(void),
mode_gravcentric(void),
mode_gravimeter(void),
mode_gravfreq(void);
private: private:
uint32_t crgb_to_col(CRGB fastled); uint32_t crgb_to_col(CRGB fastled);

View File

@ -184,6 +184,60 @@ void IRAM_ATTR WS2812FX::setPixelColorXY(uint16_t x, uint16_t y, byte r, byte g,
} }
} }
// anti-aliased version of setPixelColorXY()
void /*IRAM_ATTR*/ WS2812FX::setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w, bool aa)
{
if (x<0.0f || x>1.0f || y<0.0f || y>1.0f) return; // not normalized
const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight();
if (aa) {
uint16_t xL = floorf(x * (cols-1));
uint16_t xR = ceilf(x * (cols-1));
uint16_t yT = floorf(y * (rows-1));
uint16_t yB = ceilf(y * (rows-1));
uint32_t cXLYT = getPixelColorXY(xL, yT);
uint32_t cXRYT = getPixelColorXY(xR, yT);
uint32_t cXLYB = getPixelColorXY(xL, yB);
uint32_t cXRYB = getPixelColorXY(xR, yB);
if (xL!=xR && yT!=yB) {
// blend TL pixel
cXLYT = color_blend(RGBW32(r,g,b,w), cXLYT, sqrtf((x - floor(x))*(y - floorf(y)))*UINT16_MAX, true);
setPixelColorXY(xR, yT, R(cXLYT), G(cXLYT), B(cXLYT), W(cXLYT));
// blend TR pixel
cXRYT = color_blend(RGBW32(r,g,b,w), cXRYT, sqrtf((ceilf(x) - x)*(y - floorf(y)))*UINT16_MAX, true);
setPixelColorXY(xR, yT, R(cXRYT), G(cXRYT), B(cXRYT), W(cXRYT));
// blend BL pixel
cXLYB = color_blend(RGBW32(r,g,b,w), cXLYB, sqrtf((x - floor(x))*(ceil(y) - y))*UINT16_MAX, true);
setPixelColorXY(xL, yB, R(cXLYB), G(cXLYB), B(cXLYB), W(cXLYB));
// blend BR pixel
cXRYB = color_blend(RGBW32(r,g,b,w), cXRYB, sqrtf((ceilf(x) - x)*(ceil(y) - y))*UINT16_MAX, true);
setPixelColorXY(xR, yB, R(cXRYB), G(cXRYB), B(cXRYB), W(cXRYB));
} else if (xR!=xL && yT==yB) {
// blend L pixel
cXLYT = color_blend(RGBW32(r,g,b,w), cXLYT, (x - floor(x))*UINT16_MAX, true);
setPixelColorXY(xR, yT, R(cXLYT), G(cXLYT), B(cXLYT), W(cXLYT));
// blend R pixel
cXRYT = color_blend(RGBW32(r,g,b,w), cXRYT, (ceilf(x) - x)*UINT16_MAX, true);
setPixelColorXY(xR, yT, R(cXRYT), G(cXRYT), B(cXRYT), W(cXRYT));
} else if (xR==xL && yT!=yB) {
// blend T pixel
cXLYT = color_blend(RGBW32(r,g,b,w), cXLYT, (y - floorf(y))*UINT16_MAX, true);
setPixelColorXY(xR, yT, R(cXLYT), G(cXLYT), B(cXLYT), W(cXLYT));
// blend B pixel
cXLYB = color_blend(RGBW32(r,g,b,w), cXLYB, (ceil(y) - y)*UINT16_MAX, true);
setPixelColorXY(xL, yB, R(cXLYB), G(cXLYB), B(cXLYB), W(cXLYB));
} else {
// exact match (x & y land on a pixel)
setPixelColorXY(xL, yT, r, g, b, w);
}
} else {
setPixelColorXY((uint16_t)roundf(x * (cols-1)), (uint16_t)roundf(y * (rows-1)), r, g, b, w);
}
}
// returns RGBW values of pixel // returns RGBW values of pixel
uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) { uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) {
if (SEGMENT.getOption(SEG_OPTION_TRANSPOSED)) { uint16_t t = x; x = y; y = t; } // swap X & Y if segment transposed if (SEGMENT.getOption(SEG_OPTION_TRANSPOSED)) { uint16_t t = x; x = y; y = t; } // swap X & Y if segment transposed