/ Sound_Reactive_NeoPixel_Peace_Pendant / Sound_Reactive_NeoPixel_Peace_Pendant.ino
Sound_Reactive_NeoPixel_Peace_Pendant.ino
 1  // SPDX-FileCopyrightText: 2017 Limor Fried for Adafruit Industries
 2  //
 3  // SPDX-License-Identifier: MIT
 4  
 5  #include <Adafruit_NeoPixel.h>
 6  
 7  #define N_PIXELS  12  // Number of pixels you are using
 8  #define MIC_PIN   A1  // Microphone is attached to Trinket GPIO #2/Gemma D2 (A1)
 9  #define LED_PIN    0  // NeoPixel LED strand is connected to GPIO #0 / D0
10  #define DC_OFFSET  0  // DC offset in mic signal - if unusure, leave 0
11  #define NOISE     100  // Noise/hum/interference in mic signal
12  #define SAMPLES   60  // Length of buffer for dynamic level adjustment
13  #define TOP       (N_PIXELS +1) // Allow dot to go slightly off scale
14  
15  byte
16    peak      = 0,      // Used for falling dot
17    dotCount  = 0,      // Frame counter for delaying dot-falling speed
18    volCount  = 0;      // Frame counter for storing past volume data
19    
20  int
21    vol[SAMPLES],       // Collection of prior volume samples
22    lvl       = 10,     // Current "dampened" audio level
23    minLvlAvg = 0,      // For dynamic adjustment of graph low & high
24    maxLvlAvg = 512;
25  
26  Adafruit_NeoPixel  strip = Adafruit_NeoPixel(N_PIXELS, LED_PIN, NEO_GRB + NEO_KHZ800);
27  
28  void setup() {
29    memset(vol, 0, sizeof(vol));
30    strip.begin();
31  }
32  void loop() {
33    uint8_t  i;
34    uint16_t minLvl, maxLvl;
35    int      n, height;
36    n   = analogRead(MIC_PIN);                 // Raw reading from mic 
37    n   = abs(n - 512 - DC_OFFSET);            // Center on zero
38    n   = (n <= NOISE) ? 0 : (n - NOISE);      // Remove noise/hum
39    lvl = ((lvl * 7) + n) >> 3;    // "Dampened" reading (else looks twitchy)
40    
41    // Calculate bar height based on dynamic min/max levels (fixed point):
42    height = TOP * (lvl - minLvlAvg) / (long)(maxLvlAvg - minLvlAvg);
43  
44    if(height < 0L)       height = 0;      // Clip output
45    else if(height > TOP) height = TOP;
46    if(height > peak)     peak   = height; // Keep 'peak' dot at top
47  
48    // Color pixels based on rainbow gradient
49    for(i=0; i<N_PIXELS; i++) {  
50      if(i >= height)               
51         strip.setPixelColor(i,   0,   0, 0);
52      else 
53         strip.setPixelColor(i,Wheel(map(i,0,strip.numPixels()-1,30,150)));
54      } 
55  
56     strip.show(); // Update strip
57  
58    vol[volCount] = n;                      // Save sample for dynamic leveling
59    if(++volCount >= SAMPLES) volCount = 0; // Advance/rollover sample counter
60  
61    // Get volume range of prior frames
62    minLvl = maxLvl = vol[0];
63    for(i=1; i<SAMPLES; i++) {
64      if(vol[i] < minLvl)      minLvl = vol[i];
65      else if(vol[i] > maxLvl) maxLvl = vol[i];
66    }
67    // minLvl and maxLvl indicate the volume range over prior frames, used
68    // for vertically scaling the output graph (so it looks interesting
69    // regardless of volume level).  If they're too close together though
70    // (e.g. at very low volume levels) the graph becomes super coarse
71    // and 'jumpy'...so keep some minimum distance between them (this
72    // also lets the graph go to zero when no sound is playing):
73    if((maxLvl - minLvl) < TOP) maxLvl = minLvl + TOP;
74    minLvlAvg = (minLvlAvg * 63 + minLvl) >> 6; // Dampen min/max levels
75    maxLvlAvg = (maxLvlAvg * 63 + maxLvl) >> 6; // (fake rolling average)
76  }
77  
78  // Input a value 0 to 255 to get a color value.
79  // The colors are a transition r - g - b - back to r.
80  uint32_t Wheel(byte WheelPos) {
81    if(WheelPos < 85) {
82     return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
83    } else if(WheelPos < 170) {
84     WheelPos -= 85;
85     return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
86    } else {
87     WheelPos -= 170;
88     return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
89    }
90  }