Advertisement
Chinamat

Soundspout V3

May 19th, 2014
536
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 18.24 KB | None | 0 0
  1. #include <FastLED.h>
  2. #include <elapsedMillis.h>
  3. #include "RunningAverage.h"
  4.  
  5. #define STROBE_PIN  4
  6. #define RESET_PIN   5
  7. #define AUDIO_PIN   A5
  8. #define SPL_PIN     A4
  9. #define BANDEQ_PIN  A0
  10. #define LED_POWER   6
  11. #define LED_PIN     7
  12. #define POWER_SWITCH 2
  13. #define POT_INPUT A3
  14. #define COLOR_ORDER GRB
  15. #define CHIPSET     WS2811
  16. #define NUM_LEDS    144
  17. #define BRIGHTNESS  255
  18. #define FRAMES_PER_SECOND 60
  19.  
  20. #define NUMPARTS 28 // note that blobs recycles the particle array but only NUMBLOBS of them
  21. #define NUMBLOBS 7
  22.  
  23. byte COOLING = 55;
  24. byte SPARKING = 120;
  25. bool sleepmode = 0;
  26. bool lastpower = 1;
  27. byte lastvizval = 255; // Never have this many viz so this forces an init
  28.  
  29. typedef struct PARTICLE {
  30.     float accel;
  31.     float pos;
  32.     bool alive;
  33. };
  34.  
  35. PARTICLE parts[NUMPARTS];
  36.  
  37. CRGB leds[NUM_LEDS];
  38.  
  39. RunningAverage soundlevel(20);
  40. elapsedMillis splTimer;
  41. elapsedMillis splTest;
  42.  
  43. int tspechigh[7];           // high water marks for MSGEQ7 bands
  44. float spectrumdata[7];      // where 0 is no spectral energy in the MSGEQ7 band and 1 is maximum
  45. RunningAverage spectrumhigh[7] = {RunningAverage(30),RunningAverage(30),RunningAverage(30),RunningAverage(30),RunningAverage(30),RunningAverage(30),RunningAverage(30)};
  46.  
  47. void setup() {
  48.     Serial.begin(115200);
  49.     pinMode(SPL_PIN, INPUT);        // For general audio level
  50.     pinMode(AUDIO_PIN, INPUT);      // For raw analog in such as FFTs, dunno if the Leonardo has the grunt...
  51.     pinMode(STROBE_PIN, OUTPUT);    // MSGEQ7 reset
  52.     pinMode(RESET_PIN, OUTPUT);     // MSGEQ7strobe
  53.     pinMode(BANDEQ_PIN, INPUT);     // MSGEQ7 analog in, multiplexed based on reset and strobe
  54.     analogReference(DEFAULT);
  55.     pinMode(LED_POWER, OUTPUT);     // For WS2812 strip - currently not working
  56.     pinMode(POWER_SWITCH, INPUT);     // For WS2812 strip - currently not working
  57.     digitalWrite(LED_POWER, HIGH);  // if it did work, this is what we'd do to switch them on.
  58.     FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS);
  59.     FastLED.setBrightness( BRIGHTNESS ); // One must be very careful if attempting to power 144 leds over USB...
  60.     FastLED.setCorrection( CRGB(180,160,255));
  61.     //
  62.     for (int i = 0;i<7;i++) {
  63.         tspechigh[i]=750;
  64.     }
  65.     initparticles();
  66. }
  67.  
  68. // Function to read 7 band equalizers
  69. // You control the decay on an MSGEQ7 by the read rate, so essentially
  70. // feeding this function a number will just loop through 'decay' times
  71. // saving the value from the first iteration only. This was more important
  72. // for other visualisations, here the low decay rate is okay.
  73. void readMSGEQ7(byte decay)
  74. {
  75.     for (byte d = 0; d < decay; d++)
  76.     {
  77.         digitalWrite(RESET_PIN, HIGH);
  78.         digitalWrite(RESET_PIN, LOW);
  79.         for (byte band=0; band <7; band++)
  80.         {
  81.             digitalWrite(STROBE_PIN,LOW); // strobe pin on the chip - kicks the IC up to the next band
  82.             delayMicroseconds(30);
  83.             if (d == 0)
  84.             {
  85.                 int bandac = analogRead(BANDEQ_PIN);
  86.                 spectrumdata[band] = scaleadc(band,bandac);
  87.             }
  88.             digitalWrite(STROBE_PIN,HIGH);
  89.         }
  90.     }
  91. }
  92.  
  93. int readpot()
  94. {
  95.     return analogRead(POT_INPUT);
  96. }
  97.  
  98. bool getpowerswitch()
  99. {
  100.     return !digitalRead(POWER_SWITCH);
  101. }
  102.  
  103. // This scales the input adc value in MSEQ7 spectrum band 'band'
  104. // and puts in global variables
  105. float scaleadc(byte band,int bandac)
  106. {
  107.     static byte scaleiter = 0;
  108.     #define tickval 30               // this controls speed of the auto scaling effect on the MSGEQ7
  109.     #define noisefloor 80
  110.     scaleiter++;
  111.     if (bandac > tspechigh[band]) tspechigh[band] = bandac;     // Either we have a high water mark
  112.     else tspechigh[band] -= tickval;                            // Or we don't so we reduce the high water mark slightly
  113.     if ((scaleiter % 2) == 0) // only grab averages averages half as often
  114.     {
  115.         spectrumhigh[band].addValue(tspechigh[band]);
  116.     }
  117.     float bandwidth = spectrumhigh[band].getAverage();
  118.     if (bandwidth < 200) bandwidth = 200;   // 1/3rd scale is the minimum
  119.     float headroom = 1;
  120.     if (bandwidth < 820) headroom = 1.2;    // 820 is 80% of 1024
  121.     float scalebandwidth = (1024.0 / (bandwidth*headroom)); // allow a little headroom
  122.     int scaleout = ((bandac-noisefloor) * scalebandwidth);
  123.     float floatscale = (scaleout / 1024.0) * (0.9+(band * 0.05)); // bit on the end is a bit of emphasis for higher freqs
  124.     //float floatscale = (scaleout / 1024.0);
  125.     //if (band == 6) floatscale = floatscale * 1.2;
  126.     if (band == 0)
  127.         {
  128.             //Serial.print(bandwidth);
  129.             //Serial.print(" ");
  130.             //Serial.print(bandac);
  131.             //Serial.print(" ");
  132.             //Serial.print(floatscale);
  133.             //Serial.println();
  134.         }
  135.     if (floatscale < 0) floatscale = 0; // we can get negatives because of the noise floor value
  136.     if (floatscale > 1) floatscale = 1;
  137.     return floatscale;
  138. }
  139.  
  140. void loop()
  141. {
  142.     #define NUMVIZ 6
  143.     if (getpowerswitch() == 0 and lastpower == 1)
  144.     {
  145.         fill_solid( &(leds[0]), NUM_LEDS, CRGB( 0,0,0) );           // blank the leds
  146.         FastLED.show();
  147.         digitalWrite(LED_POWER, LOW);  // Turn off power to LED strip (which doesn't really work)
  148.         sleepmode = 1;
  149.         lastpower = 0;
  150.     }
  151.     if (getpowerswitch() == 1 and lastpower == 0)
  152.     {
  153.         digitalWrite(LED_POWER, HIGH);  // Turn off power to LED strip
  154.         delayMicroseconds(50); // Wait for WS2812 to power up
  155.         sleepmode = 0;
  156.         lastpower = 1;
  157.     }
  158.     if (!sleepmode) {
  159.         // do for all viz
  160.         readMSGEQ7(10);
  161.         random16_add_entropy( random() );
  162.         int potval = readpot();
  163.         byte vizval = potval / (1024/NUMVIZ); // get a viz number based on the pot position
  164.         bool newviz = 0;
  165.         if (vizval != lastvizval) newviz = 1; // if it's changed, trigger the initialisations
  166.         switch (vizval)
  167.         {
  168.             case 0:
  169.                 if (newviz) initparticles();
  170.                 moveparticles();
  171.                 renderparticles();
  172.                 break;        
  173.             case 1:
  174.                 if (newviz) initblobs();
  175.                 moveblobs();
  176.                 renderblobs();
  177.                 break;
  178.             case 2:
  179.                 Fire2012();
  180.                 break;
  181.             case 3:
  182.                 if (newviz) fill_solid( &(leds[0]), NUM_LEDS, CRGB( 0,0,0) );
  183.                 specrain();
  184.                 break;
  185.             case 4:
  186.                 if (newviz) fill_solid( &(leds[0]), NUM_LEDS, CRGB( 0,0,0) );
  187.                 specrain2();
  188.                 break;
  189.             case 5:
  190.                 if (newviz) fill_solid( &(leds[0]), NUM_LEDS, CRGB( 0,0,0) );
  191.                 randomspec();
  192.                 break;
  193.         }
  194.         lastvizval = vizval;
  195.         newviz = 0;
  196.         FastLED.show(); // display this frame
  197.         FastLED.delay(1000 / FRAMES_PER_SECOND);
  198.         if (splTimer > 100) // 20 samples over 100ms = two seconds
  199.         {
  200.             int rawspl = analogRead(SPL_PIN);
  201.             soundlevel.addValue(rawspl);
  202.             splTimer = 0;
  203.             //Serial.println(soundlevel.getAverage());
  204.             //Serial.println(calcpower(leds,NUM_LEDS,BRIGHTNESS));
  205.             //Serial.println(getpowerswitch());
  206.         }
  207.         if (splTest > 1000)
  208.         {
  209.             int curav = soundlevel.getAverage();
  210.             curav = constrain(curav,0,200);
  211.             COOLING = map(curav,0,200,250,20);
  212.             splTest=0;
  213.             SPARKING = curav;
  214.         }  
  215.     } else {
  216.         // if we're in sleep mode
  217.         delay(100);
  218.     }
  219. }
  220.  
  221. void initparticles()
  222. {
  223.     for (byte b = 0; b<NUMPARTS;b++)
  224.     {
  225.         parts[b].pos = 0;       // at the bottom
  226.         parts[b].accel = 0;     // not moving
  227.         parts[b].alive = 0;     // and dead...
  228.     }
  229. }
  230.  
  231. void moveparticles()
  232. {
  233.     for (byte b = 0; b<NUMPARTS;b++)
  234.     {
  235.         if (parts[b].alive)
  236.         {
  237.             parts[b].pos += parts[b].accel;                      // more accurately accel is velocity
  238.             // parts[b].accel += 0.01;                              // adds a little acceleration upwards
  239.             // if (parts[b].pos > (NUM_LEDS-1)) parts[b].alive = 0; // reached the end of the line
  240.             if (parts[b].pos > (NUM_LEDS-1))
  241.             {
  242.                 parts[b].accel = -parts[b].accel;
  243.                 parts[b].pos = NUM_LEDS-1;
  244.             }
  245.             else if (parts[b].pos < 0)
  246.             {
  247.                 parts[b].alive = 0;
  248.             }
  249.             else parts[b].accel -= 0.02;
  250.         }
  251.         else
  252.         {
  253.             // one-in-ten chance (meaning on average
  254.             if (random16(3,400) < (100*spectrumdata[b%7]))     // if basing spawn chance on spectrum energy
  255.             //if (random(5,1023) < soundlevel.getAverage())          // or overall sound level
  256.             {
  257.                 parts[b].pos = 0;
  258.                 //parts[b].accel = 1+(spectrumdata[b%7]*2.5*(0.75+(soundlevel.getAverage()/750)));     // sets particle speed based on that particle's spectrum energy
  259.                 parts[b].accel = 1+(spectrumdata[b%7]*1.75);
  260.                 parts[b].alive = 1;
  261.             }
  262.         }  
  263.     }
  264. }
  265.  
  266. void renderparticles()
  267. {
  268.     //for (byte l = 0; l < 144; l++) leds[l].setRGB(0,0,0);   // black out all the leds
  269.     fill_solid( &(leds[0]), NUM_LEDS, CRGB( 0,0,0) );           // do it properly :)
  270.     for (byte b = 0; b<NUMPARTS;b++)
  271.     {
  272.         if (parts[b].alive) // only render shit that's alive
  273.         {
  274.             CRGB tempcol;
  275.             byte lpos = parts[b].pos;               // converts float position to a byte which is our led number      
  276.             float specval = spectrumdata[b%7];      // particles 0-6 are MSGEQ bands, 7-13 the same bands etc
  277.             byte lumval = (32+(223*specval));       // calculates base luminance
  278.            //byte hueval = 160-(160*specval)+(10*(b%7));
  279.             byte hueval = (26*(b%7));   // new fixed hue per band starting from 0 (red) for bass up to blue for treble
  280.            
  281.             tempcol.setHSV(hueval,255,lumval); // static or slow
  282.            
  283.  
  284.             int direction;
  285.             if (parts[b].accel > 0) direction = -1;
  286.             else direction = 1;
  287.             setled(lpos,tempcol);
  288.             if (spectrumdata[b%7] > 0.1) setled(lpos-direction,tempcol);
  289.             if (abs(parts[b].accel) > 0.25) // slow
  290.             {
  291.                 tempcol %=160;
  292.                 setled(lpos+(direction),tempcol);
  293.             }
  294.             if (abs(parts[b].accel) >0.75) // fast trail
  295.             {
  296.                 tempcol %=160;
  297.                 setled(lpos+(direction*2),tempcol);
  298.             }
  299.             if (abs(parts[b].accel) >1) // really fast trail
  300.             {
  301.                 tempcol %=160;
  302.                 setled(lpos+(direction*3),tempcol);
  303.             }
  304.             if (abs(parts[b].accel) >1.5) // really fast trail
  305.             {
  306.                 tempcol %=160;
  307.                 setled(lpos+(direction*4),tempcol);
  308.             }
  309.         }
  310.     }
  311. }  
  312.  
  313. // Easy way to make sure led positions are in bounds
  314. void setled(byte ledpos, CRGB ledval)
  315. {
  316.     if (ledpos >=0 and ledpos <= (NUM_LEDS-1))
  317.     {
  318.         leds[ledpos] += ledval;
  319.     }
  320. }
  321.  
  322. void Fire2012()
  323. {
  324. // Array of temperature readings at each simulation cell
  325.   static byte heat[NUM_LEDS];
  326.  
  327.   // Step 1.  Cool down every cell a little
  328.     for( int i = 0; i < NUM_LEDS; i++) {
  329.       heat[i] = qsub8( heat[i],  random8(0, ((COOLING * 10) / NUM_LEDS) + 2));
  330.     }
  331.  
  332.     // Step 2.  Heat from each cell drifts 'up' and diffuses a little
  333.     for( int k= NUM_LEDS - 3; k > 0; k--) {
  334.       heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
  335.     }
  336.    
  337.     // Step 3.  Randomly ignite new 'sparks' of heat near the bottom
  338.     if( random8() < SPARKING ) {
  339.       int y = random8(7);
  340.       heat[y] = qadd8( heat[y], random8(160,255) );
  341.     }
  342.  
  343.     // Step 4.  Map from heat cells to LED colors
  344.     for( int j = 0; j < NUM_LEDS; j++) {
  345.         leds[j] = HeatColor( heat[j]);
  346.     }
  347. }
  348.  
  349. // CRGB HeatColor( uint8_t temperature)
  350. // [to be included in the forthcoming FastLED v2.1]
  351. //
  352. // Approximates a 'black body radiation' spectrum for
  353. // a given 'heat' level.  This is useful for animations of 'fire'.
  354. // Heat is specified as an arbitrary scale from 0 (cool) to 255 (hot).
  355. // This is NOT a chromatically correct 'black body radiation'
  356. // spectrum, but it's surprisingly close, and it's extremely fast and small.
  357. //
  358. // On AVR/Arduino, this typically takes around 70 bytes of program memory,
  359. // versus 768 bytes for a full 256-entry RGB lookup table.
  360.  
  361. CRGB HeatColor( uint8_t temperature)
  362. {
  363.   CRGB heatcolor;
  364.  
  365.   // Scale 'heat' down from 0-255 to 0-191,
  366.   // which can then be easily divided into three
  367.   // equal 'thirds' of 64 units each.
  368.   uint8_t t192 = scale8_video( temperature, 192);
  369.  
  370.   // calculate a value that ramps up from
  371.   // zero to 255 in each 'third' of the scale.
  372.   uint8_t heatramp = t192 & 0x3F; // 0..63
  373.   heatramp <<= 2; // scale up to 0..252
  374.  
  375.   // now figure out which third of the spectrum we're in:
  376.   if( t192 & 0x80) {
  377.     // we're in the hottest third
  378.     heatcolor.r = 255; // full red
  379.     heatcolor.g = 255; // full green
  380.     heatcolor.b = heatramp; // ramp up blue
  381.    
  382.   } else if( t192 & 0x40 ) {
  383.     // we're in the middle third
  384.     heatcolor.r = 255; // full red
  385.     heatcolor.g = heatramp; // ramp up green
  386.     heatcolor.b = 0; // no blue
  387.    
  388.   } else {
  389.     // we're in the coolest third
  390.     heatcolor.r = heatramp; // ramp up red
  391.     heatcolor.g = 0; // no green
  392.     heatcolor.b = 0; // no blue
  393.   }
  394.  
  395.   return heatcolor;
  396. }
  397.  
  398. // from the WS2812 data sheet http://www.adafruit.com/datasheets/WS2812.pdf
  399. // leds draw 20ma per channel. This is lies. Measured current suggests 12.7ma
  400. // per channel when all are white and with mimimal voltage drop, e.g. distance from power.
  401. // Voltage drop happens even with 60leds/m at 3m in length
  402. // Therefore one should power led strip from both ends. This calculation is also 'worst case'
  403. // power consumption assuming you have done this.
  404. float calcpower(CRGB leds[], int numleds, byte globalbright)
  405. {
  406.     #define maperled    12.7  
  407.     float totalcurrent = 0;
  408.     for (int l = 0; l<numleds; l++)
  409.     {
  410.         totalcurrent += ((float)leds[l].r/255.0) * maperled;
  411.         totalcurrent += ((float)leds[l].g/255.0) * maperled;
  412.         totalcurrent += ((float)leds[l].b/255.0) * maperled;
  413.     }
  414.     return (totalcurrent/1000) * ((float)globalbright/255.0) * 5;
  415. }
  416.  
  417. //
  418. // Blobs viz
  419. //
  420.  
  421. void initblobs()
  422. {
  423.     for (byte b = 0; b<NUMBLOBS;b++)
  424.     {
  425.         parts[b].pos = 0;       // at the bottom
  426.         parts[b].accel = 0.5+(b*0.25);     // not moving
  427.         parts[b].alive = 1;     // and alive...
  428.     }
  429. }
  430.  
  431. void moveblobs()
  432. {
  433.     for (byte b = 0; b<NUMBLOBS;b++)
  434.     {
  435.         parts[b].pos += parts[b].accel;                      // more accurately accel is velocity
  436.         if (parts[b].pos > (NUM_LEDS-1))
  437.         {
  438.             parts[b].accel = -0.25-(4*spectrumdata[b%7]);
  439.             parts[b].pos = NUM_LEDS-1;
  440.         }
  441.         else if (parts[b].pos < 0)
  442.         {
  443.             parts[b].accel = 0.25+(4*spectrumdata[b%7]);
  444.             parts[b].pos = 0;
  445.         }
  446.         else {
  447.             if (parts[b].pos > (NUM_LEDS/2)) parts[b].accel -= 0.04+(spectrumdata[b%7]/9);
  448.             else parts[b].accel += 0.04-(spectrumdata[b%7]/9);
  449.         }
  450.     }
  451. }
  452.  
  453. void renderblobs()
  454. {
  455.     //for (byte l = 0; l < 144; l++) leds[l].setRGB(0,0,0);   // black out all the leds
  456.     fill_solid( &(leds[0]), NUM_LEDS, CRGB( 0,0,0) );           // do it properly :)
  457.     for (byte b = 0; b<NUMBLOBS;b++)
  458.     {
  459.         CRGB tempcol;
  460.         byte lpos = parts[b].pos;               // converts float position to a byte which is our led number  
  461.         byte specval = 1+spectrumdata[b%7]*30;      // particles 0-6 are MSGEQ bands, 7-13 the same bands etc
  462.         //byte hueval = 160-(160*specval)+(10*(b%7));
  463.         byte hueval = (35*(b%7));   // new fixed hue per band starting from 0 (red) for bass up to blue for treble
  464.         byte lumval = (64+(191*spectrumdata[b%7]));       // calculates base luminance
  465.         tempcol.setHSV(hueval,255,lumval); // static or slow
  466.         bool hitbound = 0;
  467.         for (byte l=0; l<specval; l++)
  468.         {
  469.             tempcol %=128;
  470.             setled(lpos+l,tempcol);
  471.             setled(lpos-1-l,tempcol);
  472.         }
  473.     }
  474. }
  475.  
  476. // spectral rain
  477.  
  478. void specrain()
  479. {
  480.     for (int i = 0; i<7; i++)
  481.     {
  482.        
  483.         // move each one down
  484.          memmove( &leds[(i*20)+0], &leds[(i*20)+1], (19) * sizeof(CRGB) );
  485.          byte hueval = 160-(160*spectrumdata[i]);
  486.          byte lumval = 255*spectrumdata[i];
  487.          leds[(i*20)+19].setHSV(hueval,255,lumval);
  488.     }
  489. }
  490.  
  491. void specrain2()
  492. {
  493.     static byte noflicker = 0;
  494.     static byte maxband = 0;
  495.     fill_solid( &(leds[0]), NUM_LEDS, CRGB( 0,0,0) );           // do it properly :)
  496.     noflicker++;
  497.     if (noflicker > 4)
  498.     {
  499.         noflicker = 0;
  500.         float maxbandval = 0;
  501.         for (int i = 0; i<7; i++)
  502.         {
  503.             if (spectrumdata[i] > maxbandval)
  504.             {
  505.                 maxband = i;
  506.                 maxbandval = spectrumdata[i];
  507.             }
  508.         }
  509.     }
  510.     CRGB newcol;
  511.     byte hueval = maxband*26;
  512.     byte lumval = 255*spectrumdata[maxband];
  513.     newcol.setHSV(hueval,255,lumval);
  514.     for (int d = 0; d<12; d++)
  515.     {
  516.         for (int y = 0; y<6;y++)
  517.         {
  518.             if (spectrumdata[maxband] >= ((1.0/6)*(y+1)))
  519.             {
  520.                 leds[(d*12)+y] = newcol;
  521.                 leds[(d*12)+11-y] = newcol;
  522.             }
  523.         }
  524.     }
  525. }
  526.  
  527. void randomspec()
  528. {
  529.     for (int i = 0; i<7; i++)
  530.     {
  531.         byte lpos = random8(0,47);
  532.         if (random8(100) < spectrumdata[i]*100)
  533.         {
  534.             byte hueval = i*26;
  535.             byte lumval = 255*spectrumdata[i];
  536.             CRGB setcol;
  537.             setcol.setHSV(hueval,255,lumval);
  538.             leds[(lpos*3)+1] = setcol;
  539.             setcol %= 64;
  540.             leds[(lpos*3)] = setcol;
  541.             leds[(lpos*3)+2] = setcol;
  542.            
  543.         } else {
  544.             leds[(lpos*3)] = CRGB::Black;
  545.             leds[(lpos*3)+1] = CRGB::Black;
  546.             leds[(lpos*3)+2] = CRGB::Black;
  547.         }
  548.     }
  549. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement