Advertisement
skizziks_53

Reddit / Adafruit Music Visualizer

Jan 18th, 2019
91
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 34.37 KB | None | 0 0
  1. // Reddit / Arduino code: Adafruit color organ code.
  2. // January 18, 2019
  3.  
  4. // void setup() { <--------------------------------------------------------------------------------------------------------------- this line is a paste error
  5. // put your setup code here, to run once: <--------------------------------------------------------------------------------------- this line is a paste error
  6.  
  7. //} <----------------------------------------------------------------------------------------------------------------------------- this line is a paste error
  8.  
  9. //void loop() { <----------------------------------------------------------------------------------------------------------------- this line is a paste error
  10. // ---------------------------------------------------- And there is one more paste error line, at the very end. The closing bracket of the void loop() function.
  11. // ---------------------------------------------------- When you opened the Arduino IDE, this code was already here and you copied and pasted into the middle of it.
  12. // ---------------------------------------------------- You should have highlighted all of it, and pasted OVER it, to replace it.
  13.  
  14. //Libraries
  15. #include <Adafruit_NeoPixel.h> //Library to simplify interacting with the LED strand
  16. #ifdef __AVR__
  17. #include <avr/power.h> //Includes the library for power reduction registers if your chip supports them.
  18. #endif //More info: http://www.nongnu.org/avr-libc/user-manual/group__avr__power.htlm
  19.  
  20. //Constants (change these as necessary)
  21. #define LED_PIN A5 //Pin for the pixel strand. Can be analog or digital.
  22. #define LED_TOTAL 300 //Change this to the number of LEDs in your strand.
  23. #define LED_HALF LED_TOTAL/2
  24. #define VISUALS 6 //Change this accordingly if you add/remove a visual in the switch-case in Visualize()
  25.  
  26. #define AUDIO_PIN A0 //Pin for the envelope of the sound detector
  27. #define KNOB_PIN A1 //Pin for the trimpot 10K
  28. #define BUTTON_1 6 //Button 1 cycles color palettes
  29. #define BUTTON_2 5 //Button 2 cycles visualization modes
  30. #define BUTTON_3 4 //Button 3 toggles shuffle mode (automated changing of color and visual)
  31.  
  32. //////////<Globals>
  33. // These values either need to be remembered from the last pass of loop() or
  34. // need to be accessed by several functions in one pass, so they need to be global.
  35.  
  36. Adafruit_NeoPixel strand = Adafruit_NeoPixel(LED_TOTAL, LED_PIN, NEO_GRB + NEO_KHZ800); //LED strand objetcs
  37.  
  38. uint16_t gradient = 0; //Used to iterate and loop through each color palette gradually
  39.  
  40. //IMPORTANT:
  41. // This array holds the "threshold" of each color function (i.e. the largest number they take before repeating).
  42. // The values are in the same order as in ColorPalette()'s switch case (Rainbow() is first, etc). This is simply to
  43. // keep "gradient" from overflowing, the color functions themselves can take any positive value. For example, the
  44. // largest value Rainbow() takes before looping is 1529, so "gradient" should reset after 1529, as listed.
  45. // Make sure you add/remove values accordingly if you add/remove a color function in the switch-case in ColorPalette().
  46. uint16_t thresholds[] = {1529, 1019, 764, 764, 764, 1274};
  47.  
  48. uint8_t palette = 0; //Holds the current color palette.
  49. uint8_t visual = 0; //Holds the current visual being displayed.
  50. uint8_t volume = 0; //Holds the volume level read from the sound detector.
  51. uint8_t last = 0; //Holds the value of volume from the previous loop() pass.
  52.  
  53. float maxVol = 15; //Holds the largest volume recorded thus far to proportionally adjust the visual's responsiveness.
  54. float knob = 1023.0; //Holds the percentage of how twisted the trimpot is. Used for adjusting the max brightness.
  55. float avgBump = 0; //Holds the "average" volume-change to trigger a "bump."
  56. float avgVol = 0; //Holds the "average" volume-level to proportionally adjust the visual experience.
  57. float shuffleTime = 0; //Holds how many seconds of runtime ago the last shuffle was (if shuffle mode is on).
  58.  
  59. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  60. //NOTE: The reason "average" is quoted is because it is not a true mathematical average. This is because I have
  61. // found what I call a "sequenced average" is more successful in execution than a real average. The difference
  62. // is that the sequenced average doesn't use the pool of all values recorded thus far, but rather averages the
  63. // last average and the current value received (in sequence). Concretely:
  64. //
  65. // True average: (1 + 2 + 3) / 3 = 2
  66. // Sequenced: (1 + 2) / 2 = 1.5 --> (1.5 + 3) / 2 = 2.25 (if 1, 2, 3 was the order the values were received)
  67. //
  68. // All "averages" in the program operate this way. The difference is subtle, but the reason is that sequenced
  69. // averages are more adaptive to changes in the overall volume. In other words, if you went from loud to quiet,
  70. // the sequenced average is more likely to show an accurate and proportional adjustment more fluently.
  71. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  72.  
  73. bool shuffle = false; //Toggles shuffle mode.
  74. bool bump = false; //Used to pass if there was a "bump" in volume
  75.  
  76. //For Traffic() visual
  77. int8_t pos[LED_TOTAL] = { -2}; //Stores a population of color "dots" to iterate across the LED strand.
  78. uint8_t rgb[LED_TOTAL][3] = {0}; //Stores each dot's specific RGB values.
  79.  
  80. //For Snake() visual
  81. bool left = false; //Determines the direction of iteration. Recycled in PaletteDance()
  82. int8_t dotPos = 0; //Holds which LED in the strand the dot is positioned at. Recycled in most other visuals.
  83. float timeBump = 0; //Holds the time (in runtime seconds) the last "bump" occurred.
  84. float avgTime = 0; //Holds the "average" amount of time between each "bump" (used for pacing the dot's movement).
  85.  
  86. //////////</Globals>
  87.  
  88.  
  89. //////////<Standard Functions>
  90.  
  91. void setup() { //<---------------------------------------------------------------------------------------------- somehow you were missing the opening bracket here.
  92.  
  93. Serial.begin(9600); //<----------------------------------------------------------------------------------------- and this line needed the ending semicolon
  94.  
  95. //Defines the buttons pins to be input.
  96. pinMode(BUTTON_1, INPUT); pinMode(BUTTON_2, INPUT); pinMode(BUTTON_3, INPUT);
  97.  
  98. //Write a "HIGH" value to the button pins.
  99. digitalWrite(BUTTON_1, HIGH); digitalWrite(BUTTON_2, HIGH); digitalWrite(BUTTON_3, HIGH);
  100.  
  101. strand.begin(); //Initialize the LED strand object.
  102. strand.show(); //Show a blank strand, just to get the LED's ready for use.
  103. }
  104.  
  105.  
  106. void loop() { //<-------------------------------------------------------------------------------------------------- the opening bracket was missing from here also.
  107. volume = analogRead(AUDIO_PIN); //Record the volume level from the sound detector
  108. knob = analogRead(KNOB_PIN) / 1023.0; //Record how far the trimpot is twisted
  109.  
  110. //Sets a threshold for volume.
  111. // In practice I've found noise can get up to 15, so if it's lower, the visual thinks it's silent.
  112. // Also if the volume is less than average volume / 2 (essentially an average with 0), it's considered silent.
  113. if (volume < avgVol / 2.0 || volume < 15) volume = 0;
  114.  
  115. else avgVol = (avgVol + volume) / 2.0; //If non-zeo, take an "average" of volumes.
  116.  
  117. //If the current volume is larger than the loudest value recorded, overwrite
  118. if (volume > maxVol) maxVol = volume;
  119.  
  120. //Check the Cycle* functions for specific instructions if you didn't include buttons in your design.
  121. ////////////////////////////////////////////////////////////////////////////////////////////////////
  122. CyclePalette(); //Changes palette for shuffle mode or button press.
  123.  
  124. CycleVisual(); //Changes visualization for shuffle mode or button press.
  125.  
  126. ToggleShuffle(); //Toggles shuffle mode. Delete this if you didn't use buttons.
  127. ////////////////////////////////////////////////////////////////////////////////////////////////////
  128.  
  129. //This is where "gradient" is modulated to prevent overflow.
  130. if (gradient > thresholds[palette]) {
  131. gradient %= thresholds[palette] + 1;
  132.  
  133. //Everytime a palette gets completed is a good time to readjust "maxVol," just in case
  134. // the song gets quieter; we also don't want to lose brightness intensity permanently
  135. // because of one stray loud sound.
  136. maxVol = (maxVol + volume) / 2.0;
  137. }
  138.  
  139. //If there is a decent change in volume since the last pass, average it into "avgBump"
  140. if (volume - last > 10) avgBump = (avgBump + (volume - last)) / 2.0;
  141.  
  142. //If there is a notable change in volume, trigger a "bump"
  143. // avgbump is lowered just a little for comparing to make the visual slightly more sensitive to a beat.
  144. bump = (volume - last > avgBump * .9);
  145.  
  146. //If a "bump" is triggered, average the time between bumps
  147. if (bump) {
  148. avgTime = (((millis() / 1000.0) - timeBump) + avgTime) / 2.0;
  149. timeBump = millis() / 1000.0;
  150. }
  151.  
  152. Visualize(); //Calls the appropriate visualization to be displayed with the globals as they are.
  153.  
  154. gradient++; //Increments gradient
  155.  
  156. last = volume; //Records current volume for next pass
  157.  
  158. delay(30); //Paces visuals so they aren't too fast to be enjoyable
  159. }
  160. //////////</Standard Functions>
  161.  
  162.  
  163. //////////<Visual Functions>
  164.  
  165. //This function calls the appropriate visualization based on the value of "visual"
  166. void Visualize() {
  167. switch (visual) {
  168. case 0: return Pulse();
  169. case 1: return PalettePulse();
  170. case 2: return Traffic();
  171. case 3: return Snake();
  172. case 4: return PaletteDance();
  173. case 5: return Glitter();
  174. case 6: return Paintball();
  175. default: return Pulse();
  176. }
  177. }
  178.  
  179. //////////////////////////////////////////////////////////////////////////////////////////////////////////
  180. //NOTE: The strand displays RGB values as a 32 bit unsigned integer (uint32_t), which is why ColorPalette()
  181. // and all associated color functions' return types are uint32_t. This value is a composite of 3
  182. // unsigned 8bit integer (uint8_t) values (0-255 for each of red, blue, and green). You'll notice the
  183. // function split() (listed below) is used to dissect these 8bit values from the 32-bit color value.
  184. //////////////////////////////////////////////////////////////////////////////////////////////////////////
  185.  
  186.  
  187. //This function calls the appropriate color palette based on "palette"
  188. // If a negative value is passed, returns the appropriate palette withe "gradient" passed.
  189. // Otherwise returns the color palette with the passed value (useful for fitting a whole palette on the strand).
  190. uint32_t ColorPalette(float num) {
  191. switch (palette) {
  192. case 0: return (num < 0) ? Rainbow(gradient) : Rainbow(num);
  193. case 1: return (num < 0) ? Sunset(gradient) : Sunset(num);
  194. case 2: return (num < 0) ? Ocean(gradient) : Ocean(num);
  195. case 3: return (num < 0) ? PinaColada(gradient) : PinaColada(num);
  196. case 4: return (num < 0) ? Sulfur(gradient) : Sulfur(num);
  197. case 5: return (num < 0) ? NoGreen(gradient) : NoGreen(num);
  198. default: return Rainbow(gradient);
  199. }
  200. }
  201.  
  202. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
  203. //NOTE: All of these visualizations feature some aspect that affects brightness based on the volume relative to
  204. // maxVol, so that louder = brighter. Initially, I did simple proportions (volume/maxvol), but I found this
  205. // to be visually indistinct. I then tried an exponential method (raising the value to the power of
  206. // volume/maxvol). While this was more visually satisfying, I've opted for a balance between the two. You'll
  207. // notice something like pow(volume/maxVol, 2.0) in the functions below. This simply squares the ratio of
  208. // volume to maxVol to get a more exponential curve, but not as exaggerated as an actual exponential curve.
  209. // In essence, this makes louder volumes brighter, and lower volumes dimmer, to be more visually distinct.
  210. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
  211.  
  212.  
  213. //PULSE
  214. //Pulse from center of the strand
  215. void Pulse() {
  216.  
  217. fade(0.75); //Listed below, this function simply dims the colors a little bit each pass of loop()
  218.  
  219. //Advances the palette to the next noticeable color if there is a "bump"
  220. if (bump) gradient += thresholds[palette] / 24;
  221.  
  222. //If it's silent, we want the fade effect to take over, hence this if-statement
  223. if (volume > 0) {
  224. uint32_t col = ColorPalette(-1); //Our retrieved 32-bit color
  225.  
  226. //These variables determine where to start and end the pulse since it starts from the middle of the strand.
  227. // The quantities are stored in variables so they only have to be computed once (plus we use them in the loop).
  228. int start = LED_HALF - (LED_HALF * (volume / maxVol));
  229. int finish = LED_HALF + (LED_HALF * (volume / maxVol)) + strand.numPixels() % 2;
  230. //Listed above, LED_HALF is simply half the number of LEDs on your strand. ↑ this part adjusts for an odd quantity.
  231.  
  232. for (int i = start; i < finish; i++) {
  233.  
  234. //"damp" creates the fade effect of being dimmer the farther the pixel is from the center of the strand.
  235. // It returns a value between 0 and 1 that peaks at 1 at the center of the strand and 0 at the ends.
  236. float damp = sin((i - start) * PI / float(finish - start));
  237.  
  238. //Squaring damp creates more distinctive brightness.
  239. damp = pow(damp, 2.0);
  240.  
  241. //Fetch the color at the current pixel so we can see if it's dim enough to overwrite.
  242. uint32_t col2 = strand.getPixelColor(i);
  243.  
  244. //Takes advantage of one for loop to do the following:
  245. // Appropriatley adjust the brightness of this pixel using location, volume, and "knob"
  246. // Take the average RGB value of the intended color and the existing color, for comparison
  247. uint8_t colors[3];
  248. float avgCol = 0, avgCol2 = 0;
  249. for (int k = 0; k < 3; k++) {
  250. colors[k] = split(col, k) * damp * knob * pow(volume / maxVol, 2);
  251. avgCol += colors[k];
  252. avgCol2 += split(col2, k);
  253. }
  254. avgCol /= 3.0, avgCol2 /= 3.0;
  255.  
  256. //Compare the average colors as "brightness". Only overwrite dim colors so the fade effect is more apparent.
  257. if (avgCol > avgCol2) strand.setPixelColor(i, strand.Color(colors[0], colors[1], colors[2]));
  258. }
  259. }
  260. //This command actually shows the lights. If you make a new visualization, don't forget this!
  261. strand.show();
  262. }
  263.  
  264.  
  265. //PALETTEPULSE
  266. //Same as Pulse(), but colored the entire pallet instead of one solid color
  267. void PalettePulse() {
  268. fade(0.75);
  269. if (bump) gradient += thresholds[palette] / 24;
  270. if (volume > 0) {
  271. int start = LED_HALF - (LED_HALF * (volume / maxVol));
  272. int finish = LED_HALF + (LED_HALF * (volume / maxVol)) + strand.numPixels() % 2;
  273. for (int i = start; i < finish; i++) {
  274. float damp = sin((i - start) * PI / float(finish - start));
  275. damp = pow(damp, 2.0);
  276.  
  277. //This is the only difference from Pulse(). The color for each pixel isn't the same, but rather the
  278. // entire gradient fitted to the spread of the pulse, with some shifting from "gradient".
  279. int val = thresholds[palette] * (i - start) / (finish - start);
  280. val += gradient;
  281. uint32_t col = ColorPalette(val);
  282.  
  283. uint32_t col2 = strand.getPixelColor(i);
  284. uint8_t colors[3];
  285. float avgCol = 0, avgCol2 = 0;
  286. for (int k = 0; k < 3; k++) {
  287. colors[k] = split(col, k) * damp * knob * pow(volume / maxVol, 2);
  288. avgCol += colors[k];
  289. avgCol2 += split(col2, k);
  290. }
  291. avgCol /= 3.0, avgCol2 /= 3.0;
  292. if (avgCol > avgCol2) strand.setPixelColor(i, strand.Color(colors[0], colors[1], colors[2]));
  293. }
  294. }
  295. strand.show();
  296. }
  297.  
  298.  
  299. //TRAFFIC
  300. //Dots racing into each other
  301. void Traffic() {
  302.  
  303. //fade() actually creates the trail behind each dot here, so it's important to include.
  304. fade(0.8);
  305.  
  306. //Create a dot to be displayed if a bump is detected.
  307. if (bump) {
  308.  
  309. //This mess simply checks if there is an open position (-2) in the pos[] array.
  310. int8_t slot = 0;
  311. for (slot; slot < sizeof(pos); slot++) {
  312. if (pos[slot] < -1) break;
  313. else if (slot + 1 >= sizeof(pos)) {
  314. slot = -3;
  315. break;
  316. }
  317. }
  318.  
  319. //If there is an open slot, set it to an initial position on the strand.
  320. if (slot != -3) {
  321.  
  322. //Evens go right, odds go left, so evens start at 0, odds at the largest position.
  323. pos[slot] = (slot % 2 == 0) ? -1 : strand.numPixels();
  324.  
  325. //Give it a color based on the value of "gradient" during its birth.
  326. uint32_t col = ColorPalette(-1);
  327. gradient += thresholds[palette] / 24;
  328. for (int j = 0; j < 3; j++) {
  329. rgb[slot][j] = split(col, j);
  330. }
  331. }
  332. }
  333.  
  334. //Again, if it's silent we want the colors to fade out.
  335. if (volume > 0) {
  336.  
  337. //If there's sound, iterate each dot appropriately along the strand.
  338. for (int i = 0; i < sizeof(pos); i++) {
  339.  
  340. //If a dot is -2, that means it's an open slot for another dot to take over eventually.
  341. if (pos[i] < -1) continue;
  342.  
  343. //As above, evens go right (+1) and odds go left (-1)
  344. pos[i] += (i % 2) ? -1 : 1;
  345.  
  346. //Odds will reach -2 by subtraction, but if an even dot goes beyond the LED strip, it'll be purged.
  347. if (pos[i] >= strand.numPixels()) pos[i] = -2;
  348.  
  349. //Set the dot to its new position and respective color.
  350. // I's old position's color will gradually fade out due to fade(), leaving a trail behind it.
  351. strand.setPixelColor( pos[i], strand.Color(
  352. float(rgb[i][0]) * pow(volume / maxVol, 2.0) * knob,
  353. float(rgb[i][1]) * pow(volume / maxVol, 2.0) * knob,
  354. float(rgb[i][2]) * pow(volume / maxVol, 2.0) * knob)
  355. );
  356. }
  357. }
  358. strand.show(); //Again, don't forget to actually show the lights!
  359. }
  360.  
  361.  
  362. //SNAKE
  363. //Dot sweeping back and forth to the beat
  364. void Snake() {
  365. if (bump) {
  366.  
  367. //Change color a little on a bump
  368. gradient += thresholds[palette] / 30;
  369.  
  370. //Change the direction the dot is going to create the illusion of "dancing."
  371. left = !left;
  372. }
  373.  
  374. fade(0.975); //Leave a trail behind the dot.
  375.  
  376. uint32_t col = ColorPalette(-1); //Get the color at current "gradient."
  377.  
  378. //The dot should only be moved if there's sound happening.
  379. // Otherwise if noise starts and it's been moving, it'll appear to teleport.
  380. if (volume > 0) {
  381.  
  382. //Sets the dot to appropriate color and intensity
  383. strand.setPixelColor(dotPos, strand.Color(
  384. float(split(col, 0)) * pow(volume / maxVol, 1.5) * knob,
  385. float(split(col, 1)) * pow(volume / maxVol, 1.5) * knob,
  386. float(split(col, 2)) * pow(volume / maxVol, 1.5) * knob)
  387. );
  388.  
  389. //This is where "avgTime" comes into play.
  390. // That variable is the "average" amount of time between each "bump" detected.
  391. // So we can use that to determine how quickly the dot should move so it matches the tempo of the music.
  392. // The dot moving at normal loop speed is pretty quick, so it's the max speed if avgTime < 0.15 seconds.
  393. // Slowing it down causes the color to update, but only change position every other amount of loops.
  394. if (avgTime < 0.15) dotPos += (left) ? -1 : 1;
  395. else if (avgTime >= 0.15 && avgTime < 0.5 && gradient % 2 == 0) dotPos += (left) ? -1 : 1;
  396. else if (avgTime >= 0.5 && avgTime < 1.0 && gradient % 3 == 0) dotPos += (left) ? -1 : 1;
  397. else if (gradient % 4 == 0) dotPos += (left) ? -1 : 1;
  398. }
  399.  
  400. strand.show(); // Display the lights
  401.  
  402. //Check if dot position is out of bounds.
  403. if (dotPos < 0) dotPos = strand.numPixels() - 1;
  404. else if (dotPos >= strand.numPixels()) dotPos = 0;
  405. }
  406.  
  407.  
  408. //PALETTEDANCE
  409. //Projects a whole palette which oscillates to the beat, similar to the snake but a whole gradient instead of a dot
  410. void PaletteDance() {
  411. //This is the most calculation-intensive visual, which is why it doesn't need delayed.
  412.  
  413. if (bump) left = !left; //Change direction of iteration on bump
  414.  
  415. //Only show if there's sound.
  416. if (volume > avgVol) {
  417.  
  418. //This next part is convoluted, here's a summary of what's happening:
  419. // First, a sin wave function is introduced to change the brightness of all the pixels (stored in "sinVal")
  420. // This is to make the dancing effect more obvious. The trick is to shift the sin wave with the color so it all appears
  421. // to be the same object, one "hump" of color. "dotPos" is added here to achieve this effect.
  422. // Second, the entire current palette is proportionally fitted to the length of the LED strand (stored in "val" each pixel).
  423. // This is done by multiplying the ratio of position and the total amount of LEDs to the palette's threshold.
  424. // Third, the palette is then "shifted" (what color is displayed where) by adding "dotPos."
  425. // "dotPos" is added to the position before dividing, so it's a mathematical shift. However, "dotPos"'s range is not
  426. // the same as the range of position values, so the function map() is used. It's basically a built in proportion adjuster.
  427. // Lastly, it's all multiplied together to get the right color, and intensity, in the correct spot.
  428. // "gradient" is also added to slowly shift the colors over time.
  429. for (int i = 0; i < strand.numPixels(); i++) {
  430.  
  431. float sinVal = abs(sin(
  432. (i + dotPos) *
  433. (PI / float(strand.numPixels() / 1.25) )
  434. ));
  435. sinVal *= sinVal;
  436. sinVal *= volume / maxVol;
  437. sinVal *= knob;
  438.  
  439. unsigned int val = float(thresholds[palette] + 1)
  440. //map takes a value between -LED_TOTAL and +LED_TOTAL and returns one between 0 and LED_TOTAL
  441. * (float(i + map(dotPos, -1 * (strand.numPixels() - 1), strand.numPixels() - 1, 0, strand.numPixels() - 1))
  442. / float(strand.numPixels()))
  443. + (gradient);
  444.  
  445. val %= thresholds[palette]; //make sure "val" is within range of the palette
  446.  
  447. uint32_t col = ColorPalette(val); //get the color at "val"
  448.  
  449. strand.setPixelColor(i, strand.Color(
  450. float(split(col, 0))*sinVal,
  451. float(split(col, 1))*sinVal,
  452. float(split(col, 2))*sinVal)
  453. );
  454. }
  455.  
  456. //After all that, appropriately reposition "dotPos."
  457. dotPos += (left) ? -1 : 1;
  458. }
  459.  
  460. //If there's no sound, fade.
  461. else fade(0.8);
  462.  
  463. strand.show(); //Show lights.
  464.  
  465. //Loop "dotPos" if it goes out of bounds.
  466. if (dotPos < 0) dotPos = strand.numPixels() - strand.numPixels() / 6;
  467. else if (dotPos >= strand.numPixels() - strand.numPixels() / 6) dotPos = 0;
  468. }
  469.  
  470.  
  471. //GLITTER
  472. //Creates white sparkles on a color palette to the beat
  473. void Glitter() {
  474.  
  475. //This visual also fits a whole palette on the entire strip
  476. // This just makes the palette cycle more quickly so it's more visually pleasing
  477. gradient += thresholds[palette] / 204;
  478.  
  479. //"val" is used again as the proportional value to pass to ColorPalette() to fit the whole palette.
  480. for (int i = 0; i < strand.numPixels(); i++) {
  481. unsigned int val = float(thresholds[palette] + 1) *
  482. (float(i) / float(strand.numPixels()))
  483. + (gradient);
  484. val %= thresholds[palette];
  485. uint32_t col = ColorPalette(val);
  486.  
  487. //We want the sparkles to be obvious, so we dim the background color.
  488. strand.setPixelColor(i, strand.Color(
  489. split(col, 0) / 6.0 * knob,
  490. split(col, 1) / 6.0 * knob,
  491. split(col, 2) / 6.0 * knob)
  492. );
  493. }
  494.  
  495. //Create sparkles every bump
  496. if (bump) {
  497.  
  498. //Random generator needs a seed, and micros() gives a large range of values.
  499. // micros() is the amount of microseconds since the program started running.
  500. randomSeed(micros());
  501.  
  502. //Pick a random spot on the strand.
  503. dotPos = random(strand.numPixels() - 1);
  504.  
  505. //Draw sparkle at the random position, with appropriate brightness.
  506. strand.setPixelColor(dotPos, strand.Color(
  507. 255.0 * pow(volume / maxVol, 2.0) * knob,
  508. 255.0 * pow(volume / maxVol, 2.0) * knob,
  509. 255.0 * pow(volume / maxVol, 2.0) * knob
  510. ));
  511. }
  512. bleed(dotPos);
  513. strand.show(); //Show the lights.
  514. }
  515.  
  516.  
  517. //PAINTBALL
  518. //Recycles Glitter()'s random positioning; simulates "paintballs" of
  519. // color splattering randomly on the strand and bleeding together.
  520. void Paintball() {
  521.  
  522. //If it's been twice the average time for a "bump" since the last "bump," start fading.
  523. if ((millis() / 1000.0) - timeBump > avgTime * 2.0) fade(0.99);
  524.  
  525. //Bleeds colors together. Operates similarly to fade. For more info, see its definition below
  526. bleed(dotPos);
  527.  
  528. //Create a new paintball if there's a bump (like the sparkles in Glitter())
  529. if (bump) {
  530.  
  531. //Random generator needs a seed, and micros() gives a large range of values.
  532. // micros() is the amount of microseconds since the program started running.
  533. randomSeed(micros());
  534.  
  535. //Pick a random spot on the strip. Random was already reseeded above, so no real need to do it again.
  536. dotPos = random(strand.numPixels() - 1);
  537.  
  538. //Grab a random color from our palette.
  539. uint32_t col = ColorPalette(random(thresholds[palette]));
  540.  
  541. //Array to hold final RGB values
  542. uint8_t colors[3];
  543.  
  544. //Relates brightness of the color to the relative volume and potentiometer value.
  545. for (int i = 0; i < 3; i++) colors[i] = split(col, i) * pow(volume / maxVol, 2.0) * knob;
  546.  
  547. //Splatters the "paintball" on the random position.
  548. strand.setPixelColor(dotPos, strand.Color(colors[0], colors[1], colors[2]));
  549.  
  550. //This next part places a less bright version of the same color next to the left and right of the
  551. // original position, so that the bleed effect is stronger and the colors are more vibrant.
  552. for (int i = 0; i < 3; i++) colors[i] *= .8;
  553. strand.setPixelColor(dotPos - 1, strand.Color(colors[0], colors[1], colors[2]));
  554. strand.setPixelColor(dotPos + 1, strand.Color(colors[0], colors[1], colors[2]));
  555. }
  556. strand.show(); //Show lights.
  557. }
  558.  
  559.  
  560. /////////////////////////////////////////////////////////////////////////////////////////////////////
  561. //DEBUG CYCLE
  562. //No reaction to sound, merely to see gradient progression of color palettes
  563. //NOT implemented in code as is, but is easily includable in the switch-case.
  564. void Cycle() {
  565. for (int i = 0; i < strand.numPixels(); i++) {
  566. float val = float(thresholds[palette]) * (float(i) / float(strand.numPixels())) + (gradient);
  567. val = int(val) % thresholds[palette];
  568. strand.setPixelColor(i, ColorPalette(val));
  569. }
  570. strand.show();
  571. gradient += 32;
  572. }
  573. /////////////////////////////////////////////////////////////////////////////////////////////////////
  574.  
  575. //////////</Visual Functions>
  576.  
  577.  
  578. //////////<Helper Functions>
  579.  
  580. void CyclePalette() {
  581.  
  582. //IMPORTANT: Delete this whole if-block if you didn't use buttons//////////////////////////////////
  583.  
  584. //If a button is pushed, it sends a "false" reading
  585. if (!digitalRead(BUTTON_1)) {
  586.  
  587. palette++; //This is this button's purpose, to change the color palette.
  588.  
  589. //If palette is larger than the population of thresholds[], start back at 0
  590. // This is why it's important you add a threshold to the array if you add a
  591. // palette, or the program will cylce back to Rainbow() before reaching it.
  592. if (palette >= sizeof(thresholds) / 2) palette = 0;
  593.  
  594. gradient %= thresholds[palette]; //Modulate gradient to prevent any overflow that may occur.
  595.  
  596. //The button is close to the microphone on my setup, so the sound of pushing it is
  597. // relatively loud to the sound detector. This causes the visual to think a loud noise
  598. // happened, so the delay simply allows the sound of the button to pass unabated.
  599. delay(350);
  600.  
  601. maxVol = avgVol; //Set max volume to average for a fresh experience.
  602. }
  603. ///////////////////////////////////////////////////////////////////////////////////////////////////
  604.  
  605. //If shuffle mode is on, and it's been 30 seconds since the last shuffle, and then a modulo
  606. // of gradient to get a random decision between palette or visualization shuffle
  607. if (shuffle && millis() / 1000.0 - shuffleTime > 30 && gradient % 2) {
  608.  
  609. shuffleTime = millis() / 1000.0; //Record the time this shuffle happened.
  610.  
  611. palette++;
  612. if (palette >= sizeof(thresholds) / 2) palette = 0;
  613. gradient %= thresholds[palette];
  614. maxVol = avgVol; //Set the max volume to average for a fresh experience.
  615. }
  616. }
  617.  
  618.  
  619. void CycleVisual() {
  620.  
  621. //IMPORTANT: Delete this whole if-block if you didn't use buttons//////////////////////////////////
  622. if (!digitalRead(BUTTON_2)) {
  623.  
  624. visual++; //The purpose of this button: change the visual mode
  625.  
  626. gradient = 0; //Prevent overflow
  627.  
  628. //Resets "visual" if there are no more visuals to cycle through.
  629. if (visual > VISUALS) visual = 0;
  630. //This is why you should change "VISUALS" if you add a visual, or the program loop over it.
  631.  
  632. //Resets the positions of all dots to nonexistent (-2) if you cycle to the Traffic() visual.
  633. if (visual == 1) memset(pos, -2, sizeof(pos));
  634.  
  635. //Gives Snake() and PaletteDance() visuals a random starting point if cycled to.
  636. if (visual == 2 || visual == 3) {
  637. randomSeed(analogRead(0));
  638. dotPos = random(strand.numPixels());
  639. }
  640.  
  641. //Like before, this delay is to prevent a button press from affecting "maxVol."
  642. delay(350);
  643.  
  644. maxVol = avgVol; //Set max volume to average for a fresh experience
  645. }
  646.  
  647. ///////////////////////////////////////////////////////////////////////////////////////////////////
  648.  
  649. //If shuffle mode is on, and it's been 30 seconds since the last shuffle, and then a modulo
  650. // of gradient WITH INVERTED LOGIC to get a random decision between what to shuffle.
  651. // This guarantees one and only one of these shuffles will occur.
  652. if (shuffle && millis() / 1000.0 - shuffleTime > 30 && !(gradient % 2)) {
  653.  
  654. shuffleTime = millis() / 1000.0; //Record the time this shuffle happened.
  655.  
  656. visual++;
  657. gradient = 0;
  658. if (visual > VISUALS) visual = 0;
  659. if (visual == 1) memset(pos, -2, sizeof(pos));
  660. if (visual == 2 || visual == 3) {
  661. randomSeed(analogRead(0));
  662. dotPos = random(strand.numPixels());
  663. }
  664. maxVol = avgVol;
  665. }
  666. }
  667.  
  668.  
  669. //IMPORTANT: Delete this function if you didn't use buttons./////////////////////////////////////////
  670. void ToggleShuffle() {
  671. if (!digitalRead(BUTTON_3)) {
  672.  
  673. shuffle = !shuffle; //This button's purpose: toggle shuffle mode.
  674.  
  675. //This delay is to prevent the button from taking another reading while you're pressing it
  676. delay(500);
  677.  
  678. //Reset these things for a fresh experience.
  679. maxVol = avgVol;
  680. avgBump = 0;
  681. }
  682. }
  683. //////////////////////////////////////////////////////////////////////////////////////////////////////
  684.  
  685.  
  686. //Fades lights by multiplying them by a value between 0 and 1 each pass of loop().
  687. void fade(float damper) {
  688.  
  689. //"damper" must be between 0 and 1, or else you'll end up brightening the lights or doing nothing.
  690.  
  691. for (int i = 0; i < strand.numPixels(); i++) {
  692.  
  693. //Retrieve the color at the current position.
  694. uint32_t col = strand.getPixelColor(i);
  695.  
  696. //If it's black, you can't fade that any further.
  697. if (col == 0) continue;
  698.  
  699. float colors[3]; //Array of the three RGB values
  700.  
  701. //Multiply each value by "damper"
  702. for (int j = 0; j < 3; j++) colors[j] = split(col, j) * damper;
  703.  
  704. //Set the dampened colors back to their spot.
  705. strand.setPixelColor(i, strand.Color(colors[0] , colors[1], colors[2]));
  706. }
  707. }
  708.  
  709.  
  710. //"Bleeds" colors currently in the strand by averaging from a designated "Point"
  711. void bleed(uint8_t Point) {
  712. for (int i = 1; i < strand.numPixels(); i++) {
  713.  
  714. //Starts by look at the pixels left and right of "Point"
  715. // then slowly works its way out
  716. int sides[] = {Point - i, Point + i};
  717.  
  718. for (int i = 0; i < 2; i++) {
  719.  
  720. //For each of Point+i and Point-i, the pixels to the left and right, plus themselves, are averaged together.
  721. // Basically, it's setting one pixel to the average of it and its neighbors, starting on the left and right
  722. // of the starting "Point," and moves to the ends of the strand
  723. int point = sides[i];
  724. uint32_t colors[] = {strand.getPixelColor(point - 1), strand.getPixelColor(point), strand.getPixelColor(point + 1) };
  725.  
  726. //Sets the new average values to just the central point, not the left and right points.
  727. strand.setPixelColor(point, strand.Color(
  728. float( split(colors[0], 0) + split(colors[1], 0) + split(colors[2], 0) ) / 3.0,
  729. float( split(colors[0], 1) + split(colors[1], 1) + split(colors[2], 1) ) / 3.0,
  730. float( split(colors[0], 2) + split(colors[1], 2) + split(colors[2], 2) ) / 3.0)
  731. );
  732. }
  733. }
  734. }
  735.  
  736.  
  737. //As mentioned above, split() gives you one 8-bit color value
  738. //from the composite 32-bit value that the NeoPixel deals with.
  739. //This is accomplished with the right bit shift operator, ">>"
  740. uint8_t split(uint32_t color, uint8_t i ) {
  741.  
  742. //0 = Red, 1 = Green, 2 = Blue
  743.  
  744. if (i == 0) return color >> 16;
  745. if (i == 1) return color >> 8;
  746. if (i == 2) return color >> 0;
  747. return -1;
  748. }
  749.  
  750. //////////</Helper Functions>
  751.  
  752.  
  753. //////////<Palette Functions>
  754.  
  755. //These functions simply take a value and return a gradient color
  756. // in the form of an unsigned 32-bit integer
  757.  
  758. //The gradients return a different, changing color for each multiple of 255
  759. // This is because the max value of any of the 3 RGB values is 255, so it's
  760. // an intuitive cutoff for the next color to start appearing.
  761. // Gradients should also loop back to their starting color so there's no jumps in color.
  762.  
  763. uint32_t Rainbow(unsigned int i) {
  764. if (i > 1529) return Rainbow(i % 1530);
  765. if (i > 1274) return strand.Color(255, 0, 255 - (i % 255)); //violet -> red
  766. if (i > 1019) return strand.Color((i % 255), 0, 255); //blue -> violet
  767. if (i > 764) return strand.Color(0, 255 - (i % 255), 255); //aqua -> blue
  768. if (i > 509) return strand.Color(0, 255, (i % 255)); //green -> aqua
  769. if (i > 255) return strand.Color(255 - (i % 255), 255, 0); //yellow -> green
  770. return strand.Color(255, i, 0); //red -> yellow
  771. }
  772.  
  773. uint32_t Sunset(unsigned int i) {
  774. if (i > 1019) return Sunset(i % 1020);
  775. if (i > 764) return strand.Color((i % 255), 0, 255 - (i % 255)); //blue -> red
  776. if (i > 509) return strand.Color(255 - (i % 255), 0, 255); //purple -> blue
  777. if (i > 255) return strand.Color(255, 128 - (i % 255) / 2, (i % 255)); //orange -> purple
  778. return strand.Color(255, i / 2, 0); //red -> orange
  779. }
  780.  
  781. uint32_t Ocean(unsigned int i) {
  782. if (i > 764) return Ocean(i % 765);
  783. if (i > 509) return strand.Color(0, i % 255, 255 - (i % 255)); //blue -> green
  784. if (i > 255) return strand.Color(0, 255 - (i % 255), 255); //aqua -> blue
  785. return strand.Color(0, 255, i); //green -> aqua
  786. }
  787.  
  788. uint32_t PinaColada(unsigned int i) {
  789. if (i > 764) return PinaColada(i % 765);
  790. if (i > 509) return strand.Color(255 - (i % 255) / 2, (i % 255) / 2, (i % 255) / 2); //red -> half white
  791. if (i > 255) return strand.Color(255, 255 - (i % 255), 0); //yellow -> red
  792. return strand.Color(128 + (i / 2), 128 + (i / 2), 128 - i / 2); //half white -> yellow
  793. }
  794.  
  795. uint32_t Sulfur(unsigned int i) {
  796. if (i > 764) return Sulfur(i % 765);
  797. if (i > 509) return strand.Color(i % 255, 255, 255 - (i % 255)); //aqua -> yellow
  798. if (i > 255) return strand.Color(0, 255, i % 255); //green -> aqua
  799. return strand.Color(255 - i, 255, 0); //yellow -> green
  800. }
  801.  
  802. uint32_t NoGreen(unsigned int i) {
  803. if (i > 1274) return NoGreen(i % 1275);
  804. if (i > 1019) return strand.Color(255, 0, 255 - (i % 255)); //violet -> red
  805. if (i > 764) return strand.Color((i % 255), 0, 255); //blue -> violet
  806. if (i > 509) return strand.Color(0, 255 - (i % 255), 255); //aqua -> blue
  807. if (i > 255) return strand.Color(255 - (i % 255), 255, i % 255); //yellow -> aqua
  808. return strand.Color(255, i, 0); //red -> yellow
  809. }
  810.  
  811. //NOTE: This is an example of a non-gradient palette: you will get straight red, white, or blue
  812. // This works fine, but there is no gradient effect, this was merely included as an example.
  813. // If you wish to include it, put it in the switch-case in ColorPalette() and add its
  814. // threshold (764) to thresholds[] at the top.
  815. uint32_t USA(unsigned int i) {
  816. if (i > 764) return USA(i % 765);
  817. if (i > 509) return strand.Color(0, 0, 255); //blue
  818. if (i > 255) return strand.Color(128, 128, 128); //white
  819. return strand.Color(255, 0, 0); //red
  820. }
  821.  
  822. //////////</Palette Functions>
  823.  
  824.  
  825. //} <--------------------------------------------------------------------------------------- this line is a paste error
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement