Skip to content

Commit

Permalink
Add Popcorn (Audio) effect, Leds constructor and destructor (WIP)
Browse files Browse the repository at this point in the history
index.js: createHTML: don't create var if rowNr >= value length (for controls)

AppEffects:
- add PopCorn1D (WIP)
- setEffect: remove va["n"] vars with value array only nulls (old controls)

App(Mod)Leds: add Leds constructor and destructor (WIP)
  • Loading branch information
ewowi committed Feb 16, 2024
1 parent b22e0b2 commit eae2f12
Show file tree
Hide file tree
Showing 9 changed files with 1,208 additions and 1,096 deletions.
2 changes: 1 addition & 1 deletion data/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ function createHTML(json, parentNode = null, rowNr = UINT8_MAX) {
let variable = json;

if (Array.isArray(variable.value) && rowNr != UINT8_MAX) {
if (rowNr < variable.value.length && variable.value[rowNr] == null) {
if (rowNr >= variable.value.length || (rowNr < variable.value.length && variable.value[rowNr] == null)) {
// console.log("not showing this var as value is null", variable, rowNr);
return;
}
Expand Down
141 changes: 124 additions & 17 deletions src/App/AppEffects.h
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@ class Frizzles2D: public Effect {
}
leds.blur2d(mdl->getValue("blur", leds.rowNr));
}

void controls(JsonObject parentVar, Leds &leds) {
addPalette(parentVar, 4);
ui->initSlider(parentVar, "BPM", 60);
Expand Down Expand Up @@ -570,16 +571,16 @@ class Lissajous2D: public Effect {

#define maxNumBalls 16

//BouncingBalls1D inspired by WLED
//each needs 12 bytes
typedef struct Ball {
unsigned long lastBounceTime;
float impactVelocity;
float height;
} ball;

class BouncingBalls1D: public Effect {
public:
//BouncingBalls1D inspired by WLED
//each needs 12 bytes
typedef struct Ball {
unsigned long lastBounceTime;
float impactVelocity;
float height;
} ball;

const char * name() {
return "Bouncing Balls 1D";
}
Expand Down Expand Up @@ -763,6 +764,94 @@ class Waverly2D: public Effect {
}
};

#define maxNumPopcorn 21 // max 21 on 16 segment ESP8266
#define NUM_COLORS 3 /* number of colors per segment */

//each needs 19 bytes
//Spark type is used for popcorn, 1D fireworks, and drip
typedef struct Spark {
float pos, posX;
float vel, velX;
uint16_t col;
uint8_t colIndex;
} spark;


class PopCorn1D: public Effect {
public:
const char * name() {
return "PopCorn 1D";
}

void loop(Leds &leds) {
CRGBPalette16 pal = getPalette(leds.rowNr);
uint8_t speed = mdl->getValue("speed", leds.rowNr);
uint8_t intensity = mdl->getValue("intensity", leds.rowNr);
bool useaudio = mdl->getValue("useaudio", leds.rowNr);

sharedData.allocate(sizeof(Spark) * maxNumPopcorn);
Spark *popcorn = sharedData.bind<Spark>(maxNumPopcorn); //array
if (!sharedData.allocated()) return;

float gravity = -0.0001 - (speed/200000.0); // m/s/s
gravity *= leds.nrOfLeds;

uint8_t numPopcorn = intensity*maxNumPopcorn/255;
if (numPopcorn == 0) numPopcorn = 1;

for(int i = 0; i < numPopcorn; i++) {
if (popcorn[i].pos >= 0.0f) { // if kernel is active, update its position
popcorn[i].pos += popcorn[i].vel;
popcorn[i].vel += gravity;
} else { // if kernel is inactive, randomly pop it
bool doPopCorn = false; // WLEDMM allows to inhibit new pops
// WLEDMM begin
if (useaudio) {
if ( (wledAudioMod->sync.volumeSmth > 1.0f) // no pops in silence
// &&((wledAudioMod->sync.samplePeak > 0) || (wledAudioMod->sync.volumeRaw > 128)) // try to pop at onsets (our peek detector still sucks)
&&(random8() < 4) ) // stay somewhat random
doPopCorn = true;
} else {
if (random8() < 2) doPopCorn = true; // default POP!!!
}
// WLEDMM end

if (doPopCorn) { // POP!!!
popcorn[i].pos = 0.01f;

uint16_t peakHeight = 128 + random8(128); //0-255
peakHeight = (peakHeight * (leds.nrOfLeds -1)) >> 8;
popcorn[i].vel = sqrtf(-2.0f * gravity * peakHeight);

// if (SEGMENT.palette)
// {
popcorn[i].colIndex = random8();
// } else {
// byte col = random8(0, NUM_COLORS);
// if (!SEGCOLOR(2) || !SEGCOLOR(col)) col = 0;
// popcorn[i].colIndex = col;
// }
}
}
if (popcorn[i].pos >= 0.0f) { // draw now active popcorn (either active before or just popped)
// uint32_t col = SEGMENT.color_wheel(popcorn[i].colIndex);
// if (!SEGMENT.palette && popcorn[i].colIndex < NUM_COLORS) col = SEGCOLOR(popcorn[i].colIndex);
uint16_t ledIndex = popcorn[i].pos;
CRGB col = ColorFromPalette(pal, popcorn[i].colIndex*(256/maxNumPopcorn), 255);
if (ledIndex < leds.nrOfLeds) leds.setPixelColor(ledIndex, col);
}
}

}
void controls(JsonObject parentVar, Leds &leds) {
addPalette(parentVar, 4);
ui->initSlider(parentVar, "speed", 128);
ui->initSlider(parentVar, "intensity", 128);
ui->initCheckBox(parentVar, "useaudio");
ui->initSlider(parentVar, "nrOfPopCorn", 10, 1, 21);
}
}; //PopCorn1D


#ifdef STARMOD_USERMOD_WLEDAUDIO

Expand Down Expand Up @@ -1019,6 +1108,7 @@ class Effects {
effects.push_back(new RingRandomFlow);
effects.push_back(new ScrollingText2D);
effects.push_back(new Waverly2D);
effects.push_back(new PopCorn1D);
#ifdef STARMOD_USERMOD_WLEDAUDIO
effects.push_back(new GEQEffect);
effects.push_back(new AudioRings);
Expand Down Expand Up @@ -1120,25 +1210,42 @@ class Effects {

//check if post init added
bool postInit = false;
for (JsonObject var: var["n"].as<JsonArray>()) {
for (JsonObject childVar: var["n"].as<JsonArray>()) {

if (var["o"].as<int>() >= 0) { //post init, just added,
if (childVar["o"].as<int>() >= 0) { //post init, just added,
postInit = true;
break;
}
}
if (postInit) {
for (JsonObject var: var["n"].as<JsonArray>()) {
for (JsonObject childVar: var["n"].as<JsonArray>()) {
if (childVar["value"].is<JsonArray>())
{
JsonArray valArray = childVar["value"].as<JsonArray>();

if (childVar["o"].as<int>() < 0) { //if not updated
valArray[rowNr] = (char*)0; //null
// mdl->setValue(var, -99, rowNr); //set value -99
childVar["o"] = -childVar["o"].as<int>(); //make positive again
//if some values in array are not -99
}

if (var["o"].as<int>() < 0) { //if not updated
var["value"][rowNr] = (char*)0; //null
// mdl->setValue(var, -99, rowNr); //set value -99
var["o"] = -var["o"].as<int>(); //make positive again
//if some values in array are not -99
//if all values null, remove value
bool allNull = true;
for (JsonVariant element: valArray) {
if (!element.isNull())
allNull = false;
}
if (allNull) {
print->printJson("remove allnulls", childVar);
var["n"].as<JsonArray>().remove(childVar);
}
}
print->printJson("control", var);

}
}

print->printJson("control", var);
// if (var["o"].as<int>() >= 0) { //post init
// var["o"] = -var["o"].as<int>(); //make positive again
//set unused vars to inactive
Expand Down
8 changes: 5 additions & 3 deletions src/App/AppFixture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@
//create new physMaps if needed
if (indexV >= leds->mappingTable.size()) {
for (int i = leds->mappingTable.size(); i<=indexV;i++) {
// USER_PRINTF("mapping %d,%d,%d add physMap %d %d\n", pixel.y, pixely, pixel.z, indexV, leds->mappingTable.size());
// USER_PRINTF("mapping %d,%d,%d add physMap before %d %d\n", pixel.y, pixel.y, pixel.z, indexV, leds->mappingTable.size());
std::vector<uint16_t> physMap;
leds->mappingTable.push_back(physMap); //abort() was called at PC 0x40191473 on core 1 std::allocator<unsigned short> >&&)
}
Expand Down Expand Up @@ -319,14 +319,16 @@
// }
}

USER_PRINTF("projectAndMap V:%dx%dx%d V:%dx%dx%d and V:%d P:%d\n", leds->size.x, leds->size.y, leds->size.z, size.x, size.y, size.z, leds->nrOfLeds, nrOfLeds);
USER_PRINTF("projectAndMap V:%dx%dx%d = %d\n", leds->size.x, leds->size.y, leds->size.z, leds->nrOfLeds);

mdl->setValueV("fxSize", rowNr, "%d x %d x %d = %d", leds->size.x, leds->size.y, leds->size.z, leds->nrOfLeds);

USER_PRINTF("leds[%d].size = %d + %d\n", leds->rowNr, sizeof(Leds), leds->mappingTable.size()); //44

rowNr++;
}
} // leds

USER_PRINTF("projectAndMap P:%dx%dx%d = %d\n", size.x, size.y, size.z, nrOfLeds);

mdl->setValue("fixSize", size);
mdl->setValue("fixCount", nrOfLeds);
Expand Down
2 changes: 1 addition & 1 deletion src/App/AppLeds.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ CRGB Leds::getPixelColor(uint16_t indexV) {
return CRGB::Black;
}
else if (!mappingTable[indexV].size()) //if no physMap // Core 1 panic'ed (LoadProhibited). Exception was unhandled. - std::vector<unsigned short, std::allocator<unsigned short> >::size()
// by blurrows CRGB cur = getPixelColor(XY(i,row));?
// by blurrows CRGB cur = getPixelColor(XY(i,row));? XY(i,row) = 0
{
USER_PRINTF(" dev gPC P:%d >= %d", mappingTable[indexV][0], NUM_LEDS_Max);
return CRGB::Black;
Expand Down
16 changes: 16 additions & 0 deletions src/App/AppLeds.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,22 @@ class Leds {

uint16_t indexVLocal = 0; //set in operator[], used by operator=

Leds(uint8_t rowNr, Fixture &fixture) {
USER_PRINTF("Leds[%d] constructor\n", rowNr);
this->rowNr = rowNr;
this->fixture = &fixture;
this->fx = 13;
this->projectionNr = 2;
}

~Leds() {
USER_PRINTF("Leds[%d] destructor\n", rowNr);
fadeToBlackBy(100);
for (std::vector<std::vector<uint16_t>> ::iterator physMap=mappingTable.begin(); physMap!=mappingTable.end(); ++physMap)
physMap->clear();
mappingTable.clear();
}

// indexVLocal stored to be used by other operators
Leds& operator[](uint16_t indexV) {
indexVLocal = indexV;
Expand Down
2 changes: 1 addition & 1 deletion src/App/AppModFixtureGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,7 @@ class AppModFixtureGen:public SysModule {

//generate dynamic html for fixture controls
void fixtureGenChFun(JsonObject var) {
JsonObject parentVar = mdl->findVar(var["id"]);
JsonObject parentVar = mdl->findVar(var["id"]); //local parentVar
parentVar.remove("n"); //tbd: we should also remove the varFun !!
uint8_t value = var["value"];

Expand Down
23 changes: 5 additions & 18 deletions src/App/AppModLeds.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,9 @@ class AppModLeds:public SysModule {
Fixture fixture = Fixture();

AppModLeds() :SysModule("Leds") {
Leds leds = Leds();
leds.rowNr = 0;
leds.fixture = &fixture;
leds.fx = 13;
leds.projectionNr = 2;
fixture.ledsList.push_back(leds);
leds = Leds();
leds.rowNr = 1;
leds.fixture = &fixture;
leds.projectionNr = 2;
leds.fx = 14;
fixture.ledsList.push_back(leds);
fixture.ledsList.push_back(Leds(fixture.ledsList.size(), fixture));
fixture.ledsList.push_back(Leds(fixture.ledsList.size(), fixture));
USER_PRINTF("Leds created\n");
};

void setup() {
Expand All @@ -86,12 +77,8 @@ class AppModLeds:public SysModule {

web->getResponseObject()["addRow"]["rowNr"] = rowNr;

Leds leds = Leds();
leds.rowNr = rowNr;
leds.fixture = &fixture;
leds.projectionNr = 2;
leds.fx = 14;
fixture.ledsList.push_back(leds);
if (rowNr >= fixture.ledsList.size())
fixture.ledsList.push_back(Leds(fixture.ledsList.size(), fixture));
return true;
}
case f_DelRow: {
Expand Down
3 changes: 1 addition & 2 deletions src/SysModules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ void SysModules::setup() {
}

//do its own setup: will be shown as last module
JsonObject parentVar;
parentVar = ui->initSysMod(parentVar, "Modules");
JsonObject parentVar = ui->initSysMod(parentVar, "Modules");
if (parentVar["o"] > -1000) parentVar["o"] = -5000; //set default order. Don't use auto generated order as order can be changed in the ui (WIP)

JsonObject tableVar = ui->initTable(parentVar, "mdlTbl", nullptr, true, [](JsonObject var, uint8_t rowNr, uint8_t funType) { switch (funType) { //varFun
Expand Down
Loading

0 comments on commit eae2f12

Please sign in to comment.