Skip to content

Instantly share code, notes, and snippets.

@jedgarpark
Last active February 6, 2018 04:55
Show Gist options
  • Save jedgarpark/efedd7047e014c8058134cca5f71ba8f to your computer and use it in GitHub Desktop.
Save jedgarpark/efedd7047e014c8058134cca5f71ba8f to your computer and use it in GitHub Desktop.
/* Use the FFT library with a Feather M0 Express and MAX9814 mic amplifier.
* Requires the Adafruit Zero FFT library
*
* The LEDs will map around the circle when frequencies between FREQ_MIN
* and FREQ_MAX are detected
*/
#include <Adafruit_NeoPixel.h>
#include "Adafruit_ZeroFFT.h"
#define MIC_PIN A0 // Microphone is attached to this analog pin
#define LED_PIN 6 // NeoPixel LED strand is connected to this pin
//set this to 0 to disable autoranging and scale to FFT_MAX
#define AUTOSCALE 1
//this must be a power of 2
#define DATA_SIZE 1024
#define NUM_PIXELS 47 // how many neopixesl on the strip
//the sample rate
#define FS 22000
//the lowest frequency that will register on the meter
#define FREQ_MIN 600
//the highest frequency that will register on the meter
#define FREQ_MAX 3000
#define MIN_INDEX FFT_INDEX(FREQ_MIN, FS, DATA_SIZE)
#define MAX_INDEX FFT_INDEX(FREQ_MAX, FS, DATA_SIZE)
#define SCALE_FACTOR 32
int16_t data[DATA_SIZE];
int16_t pixelData[NUM_PIXELS + 1];
int16_t inputData[DATA_SIZE];
const uint8_t
// R,G,B values for color wheel covering 10 NeoPixels:
reds[] = { 0xAD, 0x9A, 0x84, 0x65, 0x00, 0x00, 0x00, 0x00, 0x65, 0x84 },
greens[] = { 0x00, 0x66, 0x87, 0x9E, 0xB1, 0x87, 0x66, 0x00, 0x00, 0x00 },
blues[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0xC3, 0xE4, 0xFF, 0xE4, 0xC3 },
gamma8[] = { // Gamma correction improves the appearance of midrange colors
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06,
0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09,
0x0A, 0x0A, 0x0A, 0x0B, 0x0B, 0x0B, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0E,
0x0E, 0x0F, 0x0F, 0x10, 0x10, 0x11, 0x11, 0x12, 0x12, 0x13, 0x13, 0x14,
0x14, 0x15, 0x15, 0x16, 0x16, 0x17, 0x18, 0x18, 0x19, 0x19, 0x1A, 0x1B,
0x1B, 0x1C, 0x1D, 0x1D, 0x1E, 0x1F, 0x1F, 0x20, 0x21, 0x22, 0x22, 0x23,
0x24, 0x25, 0x26, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2A, 0x2B, 0x2C, 0x2D,
0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x44, 0x45, 0x46,
0x47, 0x48, 0x49, 0x4B, 0x4C, 0x4D, 0x4E, 0x50, 0x51, 0x52, 0x54, 0x55,
0x56, 0x58, 0x59, 0x5A, 0x5C, 0x5D, 0x5E, 0x60, 0x61, 0x63, 0x64, 0x66,
0x67, 0x69, 0x6A, 0x6C, 0x6D, 0x6F, 0x70, 0x72, 0x73, 0x75, 0x77, 0x78,
0x7A, 0x7C, 0x7D, 0x7F, 0x81, 0x82, 0x84, 0x86, 0x88, 0x89, 0x8B, 0x8D,
0x8F, 0x91, 0x92, 0x94, 0x96, 0x98, 0x9A, 0x9C, 0x9E, 0xA0, 0xA2, 0xA4,
0xA6, 0xA8, 0xAA, 0xAC, 0xAE, 0xB0, 0xB2, 0xB4, 0xB6, 0xB8, 0xBA, 0xBC,
0xBF, 0xC1, 0xC3, 0xC5, 0xC7, 0xCA, 0xCC, 0xCE, 0xD1, 0xD3, 0xD5, 0xD7,
0xDA, 0xDC, 0xDF, 0xE1, 0xE3, 0xE6, 0xE8, 0xEB, 0xED, 0xF0, 0xF2, 0xF5,
0xF7, 0xFA, 0xFC, 0xFF };
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_PIXELS, LED_PIN, NEO_GRBW + NEO_KHZ800);
// the setup routine runs once when you press reset:
void setup() {
strip.begin();
strip.show(); // Initialize all pixels to 'off'
}
// Track low and high levels for dynamic adjustment
int minLvl = 0, maxLvl = 1000;
void loop() {
int i;
/*
//CircuitPlayground.mic.capture(inputData, DATA_SIZE);
// Center data on average amplitude
int32_t avg = 0;
for(i=0; i<DATA_SIZE; i++) avg += inputData[i];
avg /= DATA_SIZE;
// Scale for FFT
for(i=0; i<DATA_SIZE; i++)
inputData[i] = (inputData[i] - avg) * SCALE_FACTOR;
*/
int32_t avg = 0;
for(int i=0; i<DATA_SIZE; i++){
int16_t val = analogRead(MIC_PIN);
avg += val;
inputData[i] = val;
Serial.println(val);
}
//remove DC offset and gain up to 16 bits
avg = avg/DATA_SIZE;
for(int i=0; i<DATA_SIZE; i++) inputData[i] = (inputData[i] - avg) * 64;
//run the FFT
ZeroFFT(inputData, DATA_SIZE);
// Sum inputData[] (FFT output) into pixelData[] bins.
// Note that pixelData[] has one element more than the number of
// pixels actually displayed -- this is on purpose, as the last
// element tends to be zero and not visually interesting.
memset(pixelData, 0, sizeof pixelData); // Clear pixelData[] buffer
for(i=MIN_INDEX; i<=MAX_INDEX; i++){
int ix = map(i, MIN_INDEX, MAX_INDEX, 0, NUM_PIXELS);
pixelData[ix] += inputData[i];
}
// Figure the max and min levels for the current frame
int most = pixelData[0], least = pixelData[0];
for(i=1; i<NUM_PIXELS; i++) {
if(pixelData[i] > most) most = pixelData[i];
if(pixelData[i] < least) least = pixelData[i];
}
// Always have some minimum space between, else it's too "jumpy"
if((most - least) < 15) most = least + 15;
// Dynamic max/min is sort of a fake rolling average...
maxLvl = (most > maxLvl) ?
(maxLvl * 3 + most + 1) / 4 : // Climb fast
(maxLvl * 31 + most + 15) / 32; // Fall slow
minLvl = (least < minLvl) ?
(minLvl * 3 + least + 3) / 4 : // Fall fast
(minLvl * 31 + least + 31) / 32; // Climb slow
//display the data
int n;
for(i=0; i<NUM_PIXELS; i++) {
// Scale pixel data to 0-511ish range based on dynamic levels
n = map(pixelData[i], minLvl, maxLvl, 0, 511);
if(n < 0) n = 0;
else if(n > 511) n = 511;
int r, g, b;
if(n < 256) {
// Lower half of range: interp from black to RGB pixel color
r = map(n, 0, 255, 0, reds[i]);
g = map(n, 0, 255, 0, greens[i]);
b = map(n, 0, 255, 0, blues[i]);
} else {
// Upper half of range: interp from RGB color to white
r = map(n, 256, 511, reds[i] , 255);
g = map(n, 256, 511, greens[i], 255);
b = map(n, 256, 511, blues[i] , 255);
}
strip.setPixelColor(i, gamma8[r], gamma8[g], gamma8[b]);
}
strip.show();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment