/ 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 }