#ifdef MIC_EFFECTS
class EffectFreq : public EffectCalc {
private:
uint8_t type=1;
int8_t peakX[2][WIDTH];
float samp_freq;
double last_freq = 0;
uint8_t last_min_peak, last_max_peak;
float x[WIDTH+1];
float maxVal;
uint8_t freqDiv = 2U-scale/128; //1...2
bool freqAnalyseRoutine(CRGB *leds, EffectWorker *param);
void load() override;
public:
bool run(CRGB *ledarr, EffectWorker *opt=nullptr) override;
void setDynCtrl(UIControl*_val) override;
};
#endif
//-------------------------------------------------
// Частотный (спектральный) анализатор
#ifdef MIC_EFFECTS
void EffectFreq::load()
{
palettesload(); // подгружаем дефолтные палитры
memset(peakX,0,sizeof(peakX));
memset(x,0,sizeof(x));
}
void EffectFreq::setDynCtrl(UIControl*_val){
EffectCalc::setDynCtrl(_val);
if(_val->getId()==4){
if(isRandDemo()){
type = random(_val->getMin().toInt(),_val->getMax().toInt()+1);
} else {
type = _val->getVal().toInt();
}
}
}
bool EffectFreq::run(CRGB *ledarr, EffectWorker *opt){
myLamp.setMicAnalyseDivider(0); // отключить авто-работу микрофона, т.к. тут все анализируется отдельно, т.е. не нужно выполнять одну и ту же работу дважды
if (dryrun(3.0))
return false;
return freqAnalyseRoutine(*&ledarr, &*opt);
}
bool EffectFreq::freqAnalyseRoutine(CRGB *leds, EffectWorker *param)
{
if(isMicOn())
{ // вот этот блок медленный, особенно нагружающим будет вызов заполенния массива
MICWORKER *mw = new MICWORKER(myLamp.getMicScale(),myLamp.getMicNoise());
if(mw!=nullptr){
samp_freq = mw->process(myLamp.getMicNoiseRdcLevel()); // частота семплирования
last_min_peak = mw->getMinPeak();
last_max_peak = mw->getMaxPeak()*2;
EVERY_N_MILLIS(MIC_POLLRATE){
maxVal=mw->fillSizeScaledArray(x,WIDTH/freqDiv); // массив должен передаваться на 1 ед. большего размера, т.е. для 16 полос его размер 17!!!
}
samp_freq = samp_freq; last_min_peak=last_min_peak; last_freq=last_freq; // давим варнинги
}
delete mw;
} else {
EVERY_N_MILLIS(random(50,300)){
last_max_peak=random(0,128);
maxVal=random(0,last_max_peak)/10.0;
for(uint16_t i=0; i<(sizeof(x)/sizeof(float))-1U;i++){
x[i] = random(0,128)/100.0;
}
x[sizeof(x)/sizeof(float)-1] = random(60,20000);
}
}
float _scale = (maxVal==0? 0 : last_max_peak/maxVal);
_scale = _scale * ((scale%128)/127.0);
freqDiv = 2U-scale/128; //1...2
// #ifdef LAMP_DEBUG
// EVERY_N_SECONDS(1){
// for(uint8_t i=0; i<WIDTH/freqDiv; i++)
// LOG(printf_P,PSTR("%5.2f "),x[i]);
// LOG(printf_P,PSTR("F: %8.2f SC: %5.2f\n"),x[WIDTH/freqDiv], scale);
// }
// #endif
TProgmemRGBPalette16 const *_curPalette;
float _ptPallete; // сколько пунктов приходится на одну палитру; 255.1 - диапазон ползунка, не включая 255, т.к. растягиваем только нужное :)
uint8_t _pos; // позиция в массиве указателей паллитр
uint8_t _curVal; // curVal == либо var как есть, либо getScale
if(paletteIdx){
_ptPallete = ptPallete;
_pos = palettepos;
_curVal = paletteIdx;
} else {
_ptPallete = ptPallete/2.0;
_pos = (uint8_t)((float)(scale%128)/_ptPallete);
_curVal = scale%128;
}
// EVERY_N_SECONDS(1){
// LOG(printf_P,PSTR("palettepos: %d\n"),_pos);
// }
_curPalette = palettes.at(_pos); // выбираем из доп. регулятора
for(uint8_t xpos=0; xpos<WIDTH/freqDiv; xpos++){
for(uint8_t ypos=0; ypos<HEIGHT; ypos++){
uint32_t color = (x[xpos]*_scale*(1.0/(ypos+1)))>5?255:0;
if(color==255 && peakX[1][xpos] < ypos){
peakX[1][xpos]=ypos;
peakX[0][xpos]=10;
}
if(ypos>(1.5*HEIGHT/2.0)){
color=color<<16;
} else if(ypos>(HEIGHT/2.0)){
color=(color<<8)|(color<<16);
} else {
//color=color<<8;
if(color){
CRGB tColor;
if(!(_curVal%(uint8_t)(_ptPallete*(_pos+1)))) // для крайней точки рандом, иначе возьмем по индексу/2
tColor = ColorFromPalette(*_curPalette,random8(15)); // sizeof(TProgmemRGBPalette16)/sizeof(uint32_t)
else
tColor = ColorFromPalette(*_curPalette,constrain(ypos,0,15)); // sizeof(TProgmemRGBPalette16)/sizeof(uint32_t)
color=((unsigned long)tColor.r<<16)+((unsigned long)tColor.g<<8)+(unsigned long)tColor.b; // извлекаем и конвертируем цвет :)
}
}
if(!(type>=2)){
color = 0;
}
EffectMath::setLed(myLamp.getPixelNumber(freqDiv*xpos,ypos), color);
if(freqDiv>1)
EffectMath::setLed(myLamp.getPixelNumber(freqDiv*xpos+1,ypos), color);
//}
}
}
bool isfall=false;
EVERY_N_MILLISECONDS(70){
isfall = true;
}
for (size_t i = 0; i < WIDTH/freqDiv; i++)
{
uint32_t color = 255;
int8_t &ypos=peakX[1][i];
if(peakX[0][i])
peakX[0][i]--;
if(isfall && ypos>0 && !peakX[0][i]) ypos--;
if(ypos>(1.5*HEIGHT/2.0)){
color=color<<16;
} else if(ypos>(HEIGHT/2.0)){
color=(color<<8)|(color<<16);
} else {
//color=color<<8;
if(color){
CRGB tColor;
if(!(_curVal%(uint8_t)(_ptPallete*(_pos+1)))) // для крайней точки рандом, иначе возьмем по индексу/2
tColor = ColorFromPalette(*_curPalette,random8(15)); // sizeof(TProgmemRGBPalette16)/sizeof(uint32_t)
else
tColor = ColorFromPalette(*_curPalette,constrain(ypos,0,15)); // sizeof(TProgmemRGBPalette16)/sizeof(uint32_t)
color=((unsigned long)tColor.r<<16)+((unsigned long)tColor.g<<8)+(unsigned long)tColor.b; // извлекаем и конвертируем цвет :)
}
}
if(type<=2){
EffectMath::setLed(myLamp.getPixelNumber(freqDiv*i,ypos), color);
if(freqDiv>1)
EffectMath::setLed(myLamp.getPixelNumber(freqDiv*i+1,ypos), color);
}
}
return true;
}
#endif