Talk:DreamTeam: Difference between revisions
Jump to navigation
Jump to search
(arduino entrainment code scratchpad) |
|||
Line 4: | Line 4: | ||
https://github.com/nbdt == NB dream team | https://github.com/nbdt == NB dream team | ||
<pre> | |||
// Audio Spectrum Display | |||
// Copyright 2013 Tony DiCola (tony@tonydicola.com) | |||
// This code is part of the guide at http://learn.adafruit.com/fft-fun-with-fourier-transforms/ | |||
#define ARM_MATH_CM4 | |||
#include <arm_math.h> | |||
#include "FastLED.h" | |||
// ebw code | |||
#include <avr/pgmspace.h> // PROGMEM | |||
#include <Cycler.h> | |||
#include <EightBitWaves.h> | |||
//#define LED_COUNT 13 | |||
//#define LED_CLASS WS2812 | |||
//#define LED_COLOR_ORDER GRB | |||
#define LED_MAX_BRIGHTNESS 64 // 1/8 | |||
//#define LED_DT 2 // SERIAL DATA PIN | |||
//#define LED_CK 6 // SERIAL CLOCK PIN | |||
//#define SERIAL_BAUDRATE 9600 | |||
#define SERIAL_TIMEOUT 5 | |||
//Ticker ticker; | |||
//Cycler hue_cycler, brightness_cycler; | |||
Cycler brightness_cycler; | |||
//struct CRGB pixel, fastled_buffer[LED_COUNT]; | |||
uint8_t minWave = 0, maxWave = 255; | |||
uint32_t ticks_per_second = 1000; | |||
uint32_t prevMillis = millis(); | |||
// ebw code | |||
//////////////////////////////////////////////////////////////////////////////// | |||
// CONIFIGURATION | |||
// These values can be changed to alter the behavior of the spectrum display. | |||
//////////////////////////////////////////////////////////////////////////////// | |||
#define DEBUG false | |||
#define OUTPUT_LED_DATA false | |||
#define FULL_SET false | |||
#define BAUD_RATE 38400 | |||
#define NUM_LEDS 12 | |||
#define DATA_PIN 2 | |||
#define ARRAY_SZ(x) (sizeof(x) / sizeof((x)[0])) | |||
uint SAMPLE_RATE_HZ = 9000; // Sample rate of the audio in hertz. | |||
float SPECTRUM_MIN_DB = 30.0; // Audio intensity (in decibels) that maps to low LED brightness. | |||
float SPECTRUM_MAX_DB = 60.0; // Audio intensity (in decibels) that maps to high LED brightness. | |||
const int FFT_SIZE = 256; // Size of the FFT. Realistically can only be at most 256 | |||
// without running out of memory for buffers and other state. | |||
const int AUDIO_INPUT_PIN = 14; // Input ADC pin for audio data. | |||
const int ANALOG_READ_RESOLUTION = 10; // Bits of resolution for the ADC. | |||
const int ANALOG_READ_AVERAGING = 16; // Number of samples to average with each ADC reading. | |||
const int ONBOARD_LED_PIN = 13; // Output pin for power LED (pin 13 to use Teensy 3.0's onboard LED). | |||
const int MAX_CHARS = 65; // Max size of the input command buffer | |||
uint8_t brightness = 128; | |||
CRGB leds[NUM_LEDS]; | |||
//////////////////////////////////////////////////////////////////////////////// | |||
// INTERNAL STATE | |||
// These shouldn't be modified unless you know what you're doing. | |||
//////////////////////////////////////////////////////////////////////////////// | |||
IntervalTimer samplingTimer; | |||
float samples[FFT_SIZE*2]; | |||
float magnitudes[FFT_SIZE]; | |||
int sampleCounter = 0; | |||
char commandBuffer[MAX_CHARS]; | |||
float frequencyWindow[NUM_LEDS+1]; | |||
boolean ledState = false; | |||
uint loopCounter; | |||
//////////////////////////////////////////////////////////////////////////////// | |||
// setup | |||
//////////////////////////////////////////////////////////////////////////////// | |||
void setup() { | |||
// Set up serial port. | |||
Serial.begin(BAUD_RATE); | |||
delay(1000); | |||
loopCounter = 0; | |||
// Set up ADC and audio input. | |||
pinMode(AUDIO_INPUT_PIN, INPUT); | |||
analogReadResolution(ANALOG_READ_RESOLUTION); | |||
analogReadAveraging(ANALOG_READ_AVERAGING); | |||
// Turn on the power indicator LED. | |||
pinMode(ONBOARD_LED_PIN, OUTPUT); | |||
digitalWrite(ONBOARD_LED_PIN, LOW); | |||
FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS); | |||
// Clear the input command buffer | |||
memset(commandBuffer, 0, sizeof(commandBuffer)); | |||
// Initialize spectrum display | |||
spectrumSetup(); | |||
// Serial.println("** setup() spectrumSetup **********************"); | |||
// delay(1000); | |||
brightness_cycler.setup((float) 6.0, ticks_per_second); | |||
// Begin sampling audio | |||
samplingBegin(); | |||
} | |||
//////////////////////////////////////////////////////////////////////////////// | |||
// loop | |||
//////////////////////////////////////////////////////////////////////////////// | |||
void loop() { | |||
// Calculate FFT if a full sample is available. | |||
if (samplingIsDone()) { | |||
processFFT(); | |||
} | |||
updateBrightness(); | |||
} | |||
//////////////////////////////////////////////////////////////////////////////// | |||
// UTILITY FUNCTIONS | |||
//////////////////////////////////////////////////////////////////////////////// | |||
void updateBrightness() { | |||
uint16_t delta_ticks = (uint16_t)(millis() - prevMillis); | |||
prevMillis += delta_ticks; | |||
brightness_cycler.update(delta_ticks); | |||
// unsigned char brightness = EightBitWaves::sine(brightness_cycler.phase(), minWave, maxWave); | |||
brightness = EightBitWaves::sine(brightness_cycler.phase(), minWave, maxWave); | |||
} | |||
void processFFT() { | |||
// Run FFT on sample data. | |||
arm_cfft_radix4_instance_f32 fft_inst; | |||
// These functions have been depricated | |||
arm_cfft_radix4_init_f32(&fft_inst, FFT_SIZE, 0, 1); | |||
arm_cfft_radix4_f32(&fft_inst, samples); | |||
// arm_cfft_f32(&fft_inst, samples, 0, 1); | |||
// Calculate magnitude of complex numbers output by the FFT. | |||
arm_cmplx_mag_f32(samples, magnitudes, FFT_SIZE); | |||
updateLEDs(); | |||
// Restart audio sampling. | |||
samplingBegin(); | |||
} | |||
void outputFFTData() { | |||
if (DEBUG && FULL_SET) { | |||
Serial.println("** FFT Data ****************************"); | |||
Serial.print("** Total size of magnitude array: "); | |||
Serial.println(sizeof(magnitudes)); | |||
Serial.print("** Number of elements in magnitude: "); | |||
Serial.println(sizeof(magnitudes)/sizeof(*magnitudes)); | |||
// Serial.print("** Number of elements in magnitude: "); | |||
// Serial.println(sizeOfFloatArray(magnitudes)); | |||
} | |||
// for (int i = 0; i < sizeof(magnitudes)/sizeof(*magnitudes); ++i) { // was FFT_SIZE | |||
for (size_t i = 0; i < ARRAY_SZ(magnitudes); ++i) { | |||
float intensity = magnitudes[i]; | |||
intensity = 20.0*log10(intensity); | |||
// Scale the intensity and clamp between 0 and 1.0. | |||
intensity -= SPECTRUM_MIN_DB; | |||
intensity = intensity < 0.0 ? 0.0 : intensity; | |||
intensity /= (SPECTRUM_MAX_DB-SPECTRUM_MIN_DB); | |||
intensity = intensity > 1.0 ? 1.0 : intensity; | |||
if (FULL_SET) { | |||
if (DEBUG) { | |||
Serial.print("FFT["); | |||
Serial.print(i); | |||
Serial.print("] = "); | |||
Serial.println((byte)(254 * intensity)); | |||
} else { | |||
Serial.write((byte)(254 * intensity)); | |||
} | |||
} | |||
} | |||
if (FULL_SET) { | |||
if (DEBUG) { | |||
Serial.println("** End FFT Results **********************"); | |||
} else { | |||
Serial.write(255); | |||
} | |||
} | |||
} | |||
/* | |||
if (DEBUG) { | |||
Serial.print("FFT["); | |||
Serial.print(i); | |||
Serial.print("] = "); | |||
Serial.println(magnitudes[i]); | |||
} else { | |||
// Serial.write(); // send out the data | |||
Serial.write(255); // Send stop Byte | |||
} | |||
} | |||
*/ | |||
//************************************************************************** | |||
// updateGrnLED | |||
//************************************************************************** | |||
// | |||
void updateGrnLED() { | |||
// The measured time between two consecutive events (rock solid on the scope): | |||
// FHT_N mSec Baud Rate | |||
// | |||
// Serial.print("updateOnboardLED"); | |||
digitalWrite(ONBOARD_LED_PIN, HIGH && ledState); // turn the LED on or off (HIGH is the voltage level) | |||
ledState = !ledState; | |||
} | |||
//************************************************************************** | |||
// frequencyToBin | |||
// Function - Convert a frequency to the appropriate FFT bin it will fall within. | |||
//************************************************************************** | |||
// | |||
int frequencyToBin(float frequency) { | |||
float binFrequency = float(SAMPLE_RATE_HZ) / float(FFT_SIZE); | |||
return int(frequency / binFrequency); | |||
} | |||
//************************************************************************** | |||
// updateLEDs | |||
// Function - Processes the FFT data and outputs to LEDs. Update each LED based | |||
// on the intensity of the audio in the associated frequency window. | |||
//************************************************************************** | |||
// | |||
void updateLEDs() { | |||
float intensity, otherMean; | |||
if (OUTPUT_LED_DATA && DEBUG) | |||
Serial.println("** Start LED FFT Results **********************"); | |||
// Serial.print("ARRAY_SZ(leds) = "); | |||
// Serial.print(ARRAY_SZ(leds)); | |||
// Serial.print(", sizeof magnitudes = "); | |||
// Serial.println(ARRAY_SZ(magnitudes)); | |||
for (size_t i = 0; i < ARRAY_SZ(leds); ++i) { | |||
// windowMean(magnitudes, | |||
// frequencyToBin(frequencyWindow[i]), | |||
windowMean(frequencyToBin(frequencyWindow[i]), | |||
frequencyToBin(frequencyWindow[i+1]), | |||
&intensity, | |||
&otherMean); | |||
// Serial.print("**1 intensity["); | |||
// Serial.print(i); | |||
// Serial.print("] = "); | |||
// Serial.println(intensity); | |||
// Convert intensity to decibels. | |||
intensity = 20.0*log10(intensity); | |||
// Scale the intensity and clamp between 0 and 1.0. | |||
intensity -= SPECTRUM_MIN_DB; | |||
intensity = intensity < 0.0 ? 0.0 : intensity; | |||
intensity /= (SPECTRUM_MAX_DB-SPECTRUM_MIN_DB); | |||
intensity = intensity > 1.0 ? 1.0 : intensity; | |||
// Serial.print("**2 intensity["); | |||
// Serial.print(i); | |||
// Serial.print("] = "); | |||
// Serial.println(intensity); | |||
// Output intensity value to LED | |||
float newHue = 255*intensity; | |||
if (newHue < 64) { | |||
newHue = 0; | |||
} else { | |||
newHue = map(newHue, 64, 255, 0, 255); | |||
} | |||
leds[i] = CHSV(newHue, 255, brightness); | |||
if (OUTPUT_LED_DATA) { | |||
if (DEBUG) { | |||
Serial.print("FFT["); | |||
Serial.print(i); | |||
Serial.print("] = "); | |||
Serial.println(255.0 * intensity); | |||
} else { | |||
// Serial.write((byte)(254 * intensity)); | |||
Serial.write((byte)(newHue)); | |||
} | |||
} | |||
} | |||
if (OUTPUT_LED_DATA) { | |||
if (DEBUG) { | |||
Serial.println("** End FFT Results **********************"); | |||
} else { | |||
Serial.write(255); | |||
} | |||
} | |||
// pixels.show(); | |||
FastLED.show(); | |||
} | |||
/* | |||
//************************************************************************** | |||
// setHSVColor | |||
// Function - Converts HSV values to RGB and assigns the result to the specified LED | |||
//************************************************************************** | |||
// | |||
void setHSVColor(int ledIndex, float hue, float saturation, float brightness) { | |||
uint32_t myColor = pixelHSVtoRGBColor(hue, saturation, brightness); | |||
pixels.setPixelColor(ledIndex, myColor); | |||
// Serial.print("LED num = "); | |||
// Serial.print(ledIndex); | |||
// Serial.print(", color = "); | |||
// Serial.println(myColor); | |||
} | |||
//************************************************************************** | |||
// setHSVColor | |||
// Function - Convert from HSV values to RGB colors usable by neo pixel functions. | |||
// hue: 0.0 - 360.0 | |||
// saturation: 0.0 - 1.0 | |||
// value: 0.0 - 1.0 | |||
//************************************************************************** | |||
// | |||
uint32_t pixelHSVtoRGBColor(float hue, float saturation, float value) { | |||
// Implemented from algorithm at http://en.wikipedia.org/wiki/HSL_and_HSV#From_HSV | |||
float chroma = value * saturation; | |||
float h1 = float(hue)/60.0; | |||
float x = chroma*(1.0-fabs(fmod(h1, 2.0)-1.0)); | |||
float r = 0; | |||
float g = 0; | |||
float b = 0; | |||
if (h1 < 1.0) { | |||
r = chroma; | |||
g = x; | |||
} | |||
else if (h1 < 2.0) { | |||
r = x; | |||
g = chroma; | |||
} | |||
else if (h1 < 3.0) { | |||
g = chroma; | |||
b = x; | |||
} | |||
else if (h1 < 4.0) { | |||
g = x; | |||
b = chroma; | |||
} | |||
else if (h1 < 5.0) { | |||
r = x; | |||
b = chroma; | |||
} | |||
else // h1 <= 6.0 | |||
{ | |||
r = chroma; | |||
b = x; | |||
} | |||
float m = value - chroma; | |||
r += m; | |||
g += m; | |||
b += m; | |||
return pixels.Color(int(255*r), int(255*g), int(255*b)); | |||
} | |||
*/ | |||
//////////////////////////////////////////////////////////////////////////////// | |||
// SPECTRUM DISPLAY FUNCTIONS | |||
/////////////////////////////////////////////////////////////////////////////// | |||
void spectrumSetup() { | |||
// Set the frequency window values by evenly dividing the possible frequency | |||
// spectrum across the number of neo pixels. | |||
// Serial.println("** spectrumSetup **********************"); | |||
float windowSize = (SAMPLE_RATE_HZ / 2.0) / float(NUM_LEDS); | |||
for (size_t i = 0; i < ARRAY_SZ(leds) + 1; ++i) { | |||
frequencyWindow[i] = i*windowSize; | |||
// Serial.print("frequencyWindow["); | |||
// Serial.print(i); | |||
// Serial.print("] = "); | |||
// Serial.println(frequencyWindow[i]); | |||
} | |||
} | |||
//************************************************************************** | |||
// windowMean | |||
// Function - Compute the average magnitude of a target frequency window vs. | |||
// all other frequencies. | |||
// Parameters magnitudes, | |||
// frequencyToBin(frequencyWindow[i]), | |||
// frequencyToBin(frequencyWindow[i+1]), | |||
// &intensity, | |||
// &otherMean | |||
//************************************************************************** | |||
// | |||
//void windowMean(float* magnitudes, uint lowBin, uint highBin, float* windowMean, float* otherMean) { | |||
void windowMean(uint lowBin, uint highBin, float* windowMean, float* otherMean) { | |||
*windowMean = 0; | |||
*otherMean = 0; | |||
// Notice the first magnitude bin is skipped because it represents the | |||
// average power of the signal. | |||
// Serial.print("sizeof magnitudes: "); | |||
// Serial.print( sizeof(magnitudes) ); | |||
// Serial.print(", ARRAY_SZ(magnitudes): "); | |||
// Serial.println( ARRAY_SZ(magnitudes) ); | |||
for (size_t i = 1; i < (ARRAY_SZ(magnitudes))/2; ++i) { | |||
// Serial.print("i = "); | |||
// Serial.print(i); | |||
// | |||
// Serial.print(", lowBin = "); | |||
// Serial.print(lowBin); | |||
// | |||
// Serial.print(", highBin = "); | |||
// Serial.print(highBin); | |||
if (i >= lowBin && i <= highBin) { | |||
*windowMean += magnitudes[i]; | |||
// Serial.print(", magnitude: "); | |||
// Serial.print(magnitudes[i]); | |||
} | |||
else { | |||
*otherMean += magnitudes[i]; | |||
} | |||
// Serial.println(); | |||
} | |||
*windowMean /= (highBin - lowBin) + 1; | |||
// Serial.print(" **** windowMean: "); | |||
// Serial.println(*windowMean); | |||
*otherMean /= (FFT_SIZE / 2 - (highBin - lowBin)); | |||
} | |||
//////////////////////////////////////////////////////////////////////////////// | |||
// SAMPLING FUNCTIONS | |||
//////////////////////////////////////////////////////////////////////////////// | |||
//************************************************************************** | |||
// samplingCallback | |||
// Function - Does an analog to digital conversion of the microphone input. | |||
//************************************************************************** | |||
// | |||
void samplingCallback() { | |||
// Read from the microphone input pin and store the sample data | |||
samples[sampleCounter] = (float32_t)analogRead(AUDIO_INPUT_PIN); | |||
// Complex FFT functions require a coefficient for the imaginary part of the input. | |||
// Since we only have real data, set this coefficient to zero. | |||
samples[sampleCounter+1] = 0.0; | |||
// Update sample buffer position and stop after the buffer is filled | |||
sampleCounter += 2; | |||
if (sampleCounter >= FFT_SIZE*2) { | |||
samplingTimer.end(); | |||
} | |||
} | |||
//************************************************************************** | |||
// samplingBegin | |||
// Function - Starts the microphone ADC sampling timer function. | |||
//************************************************************************** | |||
// | |||
void samplingBegin() { | |||
// Reset sample buffer position and start callback at necessary rate. | |||
sampleCounter = 0; | |||
samplingTimer.begin(samplingCallback, 1000000/SAMPLE_RATE_HZ); // 1,000,000 / 9000 = 111 uSec | |||
} | |||
//************************************************************************** | |||
// samplingIsDone | |||
// Function - Indicates if a complete sample set of microphone data has been captured. | |||
//************************************************************************** | |||
// | |||
boolean samplingIsDone() { | |||
return sampleCounter >= FFT_SIZE*2; | |||
} | |||
//////////////////////////////////////////////////////////////////////////////// | |||
// COMMAND PARSING FUNCTIONS | |||
// These functions allow parsing simple commands input on the serial port. | |||
// Commands allow reading and writing variables that control the device. | |||
// | |||
// All commands must end with a semicolon character. | |||
// | |||
// Example commands are: | |||
// GET SAMPLE_RATE_HZ; | |||
// - Get the sample rate of the device. | |||
// SET SAMPLE_RATE_HZ 400; | |||
// - Set the sample rate of the device to 400 hertz. | |||
// | |||
//////////////////////////////////////////////////////////////////////////////// | |||
void parserLoop() { | |||
// Process any incoming characters from the serial port | |||
while (Serial.available() > 0) { | |||
char c = Serial.read(); | |||
// Add any characters that aren't the end of a command (semicolon) to the input buffer. | |||
if (c != ';') { | |||
c = toupper(c); | |||
strncat(commandBuffer, &c, 1); | |||
} | |||
else | |||
{ | |||
// Parse the command because an end of command token was encountered. | |||
parseCommand(commandBuffer); | |||
// Clear the input buffer | |||
memset(commandBuffer, 0, sizeof(commandBuffer)); | |||
} | |||
} | |||
} | |||
// Macro used in parseCommand function to simplify parsing get and set commands for a variable | |||
#define GET_AND_SET(variableName) \ | |||
else if (strcmp(command, "GET " #variableName) == 0) { \ | |||
Serial.println(variableName); \ | |||
} \ | |||
else if (strstr(command, "SET " #variableName " ") != NULL) { \ | |||
variableName = (typeof(variableName)) atof(command+(sizeof("SET " #variableName " ")-1)); \ | |||
} | |||
//************************************************************************** | |||
// parseCommand | |||
// Function - Parses commands received via the serial input. | |||
//************************************************************************** | |||
// | |||
void parseCommand(char* command) { | |||
if (strcmp(command, "GET MAGNITUDES") == 0) { | |||
for (size_t i = 0; i < ARRAY_SZ(magnitudes); ++i) { | |||
Serial.println(magnitudes[i]); | |||
} | |||
} | |||
else if (strcmp(command, "GET SAMPLES") == 0) { | |||
for (size_t i = 0; i < ARRAY_SZ(samples); i+=2) { | |||
Serial.println(samples[i]); | |||
} | |||
} | |||
else if (strcmp(command, "GET FFT_SIZE") == 0) { | |||
Serial.println(FFT_SIZE); | |||
} | |||
GET_AND_SET(SAMPLE_RATE_HZ) | |||
// GET_AND_SET(LEDS_ENABLED) | |||
GET_AND_SET(SPECTRUM_MIN_DB) | |||
GET_AND_SET(SPECTRUM_MAX_DB) | |||
// Update spectrum display values if sample rate was changed. | |||
if (strstr(command, "SET SAMPLE_RATE_HZ ") != NULL) { | |||
spectrumSetup(); | |||
} | |||
} | |||
</pre> |
Revision as of 20:45, 3 June 2015
https://github.com/search?q=neurosky&type=&ref=simplesearch (found 48 repository results)
https://github.com/nbdt == NB dream team
// Audio Spectrum Display // Copyright 2013 Tony DiCola (tony@tonydicola.com) // This code is part of the guide at http://learn.adafruit.com/fft-fun-with-fourier-transforms/ #define ARM_MATH_CM4 #include <arm_math.h> #include "FastLED.h" // ebw code #include <avr/pgmspace.h> // PROGMEM #include <Cycler.h> #include <EightBitWaves.h> //#define LED_COUNT 13 //#define LED_CLASS WS2812 //#define LED_COLOR_ORDER GRB #define LED_MAX_BRIGHTNESS 64 // 1/8 //#define LED_DT 2 // SERIAL DATA PIN //#define LED_CK 6 // SERIAL CLOCK PIN //#define SERIAL_BAUDRATE 9600 #define SERIAL_TIMEOUT 5 //Ticker ticker; //Cycler hue_cycler, brightness_cycler; Cycler brightness_cycler; //struct CRGB pixel, fastled_buffer[LED_COUNT]; uint8_t minWave = 0, maxWave = 255; uint32_t ticks_per_second = 1000; uint32_t prevMillis = millis(); // ebw code //////////////////////////////////////////////////////////////////////////////// // CONIFIGURATION // These values can be changed to alter the behavior of the spectrum display. //////////////////////////////////////////////////////////////////////////////// #define DEBUG false #define OUTPUT_LED_DATA false #define FULL_SET false #define BAUD_RATE 38400 #define NUM_LEDS 12 #define DATA_PIN 2 #define ARRAY_SZ(x) (sizeof(x) / sizeof((x)[0])) uint SAMPLE_RATE_HZ = 9000; // Sample rate of the audio in hertz. float SPECTRUM_MIN_DB = 30.0; // Audio intensity (in decibels) that maps to low LED brightness. float SPECTRUM_MAX_DB = 60.0; // Audio intensity (in decibels) that maps to high LED brightness. const int FFT_SIZE = 256; // Size of the FFT. Realistically can only be at most 256 // without running out of memory for buffers and other state. const int AUDIO_INPUT_PIN = 14; // Input ADC pin for audio data. const int ANALOG_READ_RESOLUTION = 10; // Bits of resolution for the ADC. const int ANALOG_READ_AVERAGING = 16; // Number of samples to average with each ADC reading. const int ONBOARD_LED_PIN = 13; // Output pin for power LED (pin 13 to use Teensy 3.0's onboard LED). const int MAX_CHARS = 65; // Max size of the input command buffer uint8_t brightness = 128; CRGB leds[NUM_LEDS]; //////////////////////////////////////////////////////////////////////////////// // INTERNAL STATE // These shouldn't be modified unless you know what you're doing. //////////////////////////////////////////////////////////////////////////////// IntervalTimer samplingTimer; float samples[FFT_SIZE*2]; float magnitudes[FFT_SIZE]; int sampleCounter = 0; char commandBuffer[MAX_CHARS]; float frequencyWindow[NUM_LEDS+1]; boolean ledState = false; uint loopCounter; //////////////////////////////////////////////////////////////////////////////// // setup //////////////////////////////////////////////////////////////////////////////// void setup() { // Set up serial port. Serial.begin(BAUD_RATE); delay(1000); loopCounter = 0; // Set up ADC and audio input. pinMode(AUDIO_INPUT_PIN, INPUT); analogReadResolution(ANALOG_READ_RESOLUTION); analogReadAveraging(ANALOG_READ_AVERAGING); // Turn on the power indicator LED. pinMode(ONBOARD_LED_PIN, OUTPUT); digitalWrite(ONBOARD_LED_PIN, LOW); FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS); // Clear the input command buffer memset(commandBuffer, 0, sizeof(commandBuffer)); // Initialize spectrum display spectrumSetup(); // Serial.println("** setup() spectrumSetup **********************"); // delay(1000); brightness_cycler.setup((float) 6.0, ticks_per_second); // Begin sampling audio samplingBegin(); } //////////////////////////////////////////////////////////////////////////////// // loop //////////////////////////////////////////////////////////////////////////////// void loop() { // Calculate FFT if a full sample is available. if (samplingIsDone()) { processFFT(); } updateBrightness(); } //////////////////////////////////////////////////////////////////////////////// // UTILITY FUNCTIONS //////////////////////////////////////////////////////////////////////////////// void updateBrightness() { uint16_t delta_ticks = (uint16_t)(millis() - prevMillis); prevMillis += delta_ticks; brightness_cycler.update(delta_ticks); // unsigned char brightness = EightBitWaves::sine(brightness_cycler.phase(), minWave, maxWave); brightness = EightBitWaves::sine(brightness_cycler.phase(), minWave, maxWave); } void processFFT() { // Run FFT on sample data. arm_cfft_radix4_instance_f32 fft_inst; // These functions have been depricated arm_cfft_radix4_init_f32(&fft_inst, FFT_SIZE, 0, 1); arm_cfft_radix4_f32(&fft_inst, samples); // arm_cfft_f32(&fft_inst, samples, 0, 1); // Calculate magnitude of complex numbers output by the FFT. arm_cmplx_mag_f32(samples, magnitudes, FFT_SIZE); updateLEDs(); // Restart audio sampling. samplingBegin(); } void outputFFTData() { if (DEBUG && FULL_SET) { Serial.println("** FFT Data ****************************"); Serial.print("** Total size of magnitude array: "); Serial.println(sizeof(magnitudes)); Serial.print("** Number of elements in magnitude: "); Serial.println(sizeof(magnitudes)/sizeof(*magnitudes)); // Serial.print("** Number of elements in magnitude: "); // Serial.println(sizeOfFloatArray(magnitudes)); } // for (int i = 0; i < sizeof(magnitudes)/sizeof(*magnitudes); ++i) { // was FFT_SIZE for (size_t i = 0; i < ARRAY_SZ(magnitudes); ++i) { float intensity = magnitudes[i]; intensity = 20.0*log10(intensity); // Scale the intensity and clamp between 0 and 1.0. intensity -= SPECTRUM_MIN_DB; intensity = intensity < 0.0 ? 0.0 : intensity; intensity /= (SPECTRUM_MAX_DB-SPECTRUM_MIN_DB); intensity = intensity > 1.0 ? 1.0 : intensity; if (FULL_SET) { if (DEBUG) { Serial.print("FFT["); Serial.print(i); Serial.print("] = "); Serial.println((byte)(254 * intensity)); } else { Serial.write((byte)(254 * intensity)); } } } if (FULL_SET) { if (DEBUG) { Serial.println("** End FFT Results **********************"); } else { Serial.write(255); } } } /* if (DEBUG) { Serial.print("FFT["); Serial.print(i); Serial.print("] = "); Serial.println(magnitudes[i]); } else { // Serial.write(); // send out the data Serial.write(255); // Send stop Byte } } */ //************************************************************************** // updateGrnLED //************************************************************************** // void updateGrnLED() { // The measured time between two consecutive events (rock solid on the scope): // FHT_N mSec Baud Rate // // Serial.print("updateOnboardLED"); digitalWrite(ONBOARD_LED_PIN, HIGH && ledState); // turn the LED on or off (HIGH is the voltage level) ledState = !ledState; } //************************************************************************** // frequencyToBin // Function - Convert a frequency to the appropriate FFT bin it will fall within. //************************************************************************** // int frequencyToBin(float frequency) { float binFrequency = float(SAMPLE_RATE_HZ) / float(FFT_SIZE); return int(frequency / binFrequency); } //************************************************************************** // updateLEDs // Function - Processes the FFT data and outputs to LEDs. Update each LED based // on the intensity of the audio in the associated frequency window. //************************************************************************** // void updateLEDs() { float intensity, otherMean; if (OUTPUT_LED_DATA && DEBUG) Serial.println("** Start LED FFT Results **********************"); // Serial.print("ARRAY_SZ(leds) = "); // Serial.print(ARRAY_SZ(leds)); // Serial.print(", sizeof magnitudes = "); // Serial.println(ARRAY_SZ(magnitudes)); for (size_t i = 0; i < ARRAY_SZ(leds); ++i) { // windowMean(magnitudes, // frequencyToBin(frequencyWindow[i]), windowMean(frequencyToBin(frequencyWindow[i]), frequencyToBin(frequencyWindow[i+1]), &intensity, &otherMean); // Serial.print("**1 intensity["); // Serial.print(i); // Serial.print("] = "); // Serial.println(intensity); // Convert intensity to decibels. intensity = 20.0*log10(intensity); // Scale the intensity and clamp between 0 and 1.0. intensity -= SPECTRUM_MIN_DB; intensity = intensity < 0.0 ? 0.0 : intensity; intensity /= (SPECTRUM_MAX_DB-SPECTRUM_MIN_DB); intensity = intensity > 1.0 ? 1.0 : intensity; // Serial.print("**2 intensity["); // Serial.print(i); // Serial.print("] = "); // Serial.println(intensity); // Output intensity value to LED float newHue = 255*intensity; if (newHue < 64) { newHue = 0; } else { newHue = map(newHue, 64, 255, 0, 255); } leds[i] = CHSV(newHue, 255, brightness); if (OUTPUT_LED_DATA) { if (DEBUG) { Serial.print("FFT["); Serial.print(i); Serial.print("] = "); Serial.println(255.0 * intensity); } else { // Serial.write((byte)(254 * intensity)); Serial.write((byte)(newHue)); } } } if (OUTPUT_LED_DATA) { if (DEBUG) { Serial.println("** End FFT Results **********************"); } else { Serial.write(255); } } // pixels.show(); FastLED.show(); } /* //************************************************************************** // setHSVColor // Function - Converts HSV values to RGB and assigns the result to the specified LED //************************************************************************** // void setHSVColor(int ledIndex, float hue, float saturation, float brightness) { uint32_t myColor = pixelHSVtoRGBColor(hue, saturation, brightness); pixels.setPixelColor(ledIndex, myColor); // Serial.print("LED num = "); // Serial.print(ledIndex); // Serial.print(", color = "); // Serial.println(myColor); } //************************************************************************** // setHSVColor // Function - Convert from HSV values to RGB colors usable by neo pixel functions. // hue: 0.0 - 360.0 // saturation: 0.0 - 1.0 // value: 0.0 - 1.0 //************************************************************************** // uint32_t pixelHSVtoRGBColor(float hue, float saturation, float value) { // Implemented from algorithm at http://en.wikipedia.org/wiki/HSL_and_HSV#From_HSV float chroma = value * saturation; float h1 = float(hue)/60.0; float x = chroma*(1.0-fabs(fmod(h1, 2.0)-1.0)); float r = 0; float g = 0; float b = 0; if (h1 < 1.0) { r = chroma; g = x; } else if (h1 < 2.0) { r = x; g = chroma; } else if (h1 < 3.0) { g = chroma; b = x; } else if (h1 < 4.0) { g = x; b = chroma; } else if (h1 < 5.0) { r = x; b = chroma; } else // h1 <= 6.0 { r = chroma; b = x; } float m = value - chroma; r += m; g += m; b += m; return pixels.Color(int(255*r), int(255*g), int(255*b)); } */ //////////////////////////////////////////////////////////////////////////////// // SPECTRUM DISPLAY FUNCTIONS /////////////////////////////////////////////////////////////////////////////// void spectrumSetup() { // Set the frequency window values by evenly dividing the possible frequency // spectrum across the number of neo pixels. // Serial.println("** spectrumSetup **********************"); float windowSize = (SAMPLE_RATE_HZ / 2.0) / float(NUM_LEDS); for (size_t i = 0; i < ARRAY_SZ(leds) + 1; ++i) { frequencyWindow[i] = i*windowSize; // Serial.print("frequencyWindow["); // Serial.print(i); // Serial.print("] = "); // Serial.println(frequencyWindow[i]); } } //************************************************************************** // windowMean // Function - Compute the average magnitude of a target frequency window vs. // all other frequencies. // Parameters magnitudes, // frequencyToBin(frequencyWindow[i]), // frequencyToBin(frequencyWindow[i+1]), // &intensity, // &otherMean //************************************************************************** // //void windowMean(float* magnitudes, uint lowBin, uint highBin, float* windowMean, float* otherMean) { void windowMean(uint lowBin, uint highBin, float* windowMean, float* otherMean) { *windowMean = 0; *otherMean = 0; // Notice the first magnitude bin is skipped because it represents the // average power of the signal. // Serial.print("sizeof magnitudes: "); // Serial.print( sizeof(magnitudes) ); // Serial.print(", ARRAY_SZ(magnitudes): "); // Serial.println( ARRAY_SZ(magnitudes) ); for (size_t i = 1; i < (ARRAY_SZ(magnitudes))/2; ++i) { // Serial.print("i = "); // Serial.print(i); // // Serial.print(", lowBin = "); // Serial.print(lowBin); // // Serial.print(", highBin = "); // Serial.print(highBin); if (i >= lowBin && i <= highBin) { *windowMean += magnitudes[i]; // Serial.print(", magnitude: "); // Serial.print(magnitudes[i]); } else { *otherMean += magnitudes[i]; } // Serial.println(); } *windowMean /= (highBin - lowBin) + 1; // Serial.print(" **** windowMean: "); // Serial.println(*windowMean); *otherMean /= (FFT_SIZE / 2 - (highBin - lowBin)); } //////////////////////////////////////////////////////////////////////////////// // SAMPLING FUNCTIONS //////////////////////////////////////////////////////////////////////////////// //************************************************************************** // samplingCallback // Function - Does an analog to digital conversion of the microphone input. //************************************************************************** // void samplingCallback() { // Read from the microphone input pin and store the sample data samples[sampleCounter] = (float32_t)analogRead(AUDIO_INPUT_PIN); // Complex FFT functions require a coefficient for the imaginary part of the input. // Since we only have real data, set this coefficient to zero. samples[sampleCounter+1] = 0.0; // Update sample buffer position and stop after the buffer is filled sampleCounter += 2; if (sampleCounter >= FFT_SIZE*2) { samplingTimer.end(); } } //************************************************************************** // samplingBegin // Function - Starts the microphone ADC sampling timer function. //************************************************************************** // void samplingBegin() { // Reset sample buffer position and start callback at necessary rate. sampleCounter = 0; samplingTimer.begin(samplingCallback, 1000000/SAMPLE_RATE_HZ); // 1,000,000 / 9000 = 111 uSec } //************************************************************************** // samplingIsDone // Function - Indicates if a complete sample set of microphone data has been captured. //************************************************************************** // boolean samplingIsDone() { return sampleCounter >= FFT_SIZE*2; } //////////////////////////////////////////////////////////////////////////////// // COMMAND PARSING FUNCTIONS // These functions allow parsing simple commands input on the serial port. // Commands allow reading and writing variables that control the device. // // All commands must end with a semicolon character. // // Example commands are: // GET SAMPLE_RATE_HZ; // - Get the sample rate of the device. // SET SAMPLE_RATE_HZ 400; // - Set the sample rate of the device to 400 hertz. // //////////////////////////////////////////////////////////////////////////////// void parserLoop() { // Process any incoming characters from the serial port while (Serial.available() > 0) { char c = Serial.read(); // Add any characters that aren't the end of a command (semicolon) to the input buffer. if (c != ';') { c = toupper(c); strncat(commandBuffer, &c, 1); } else { // Parse the command because an end of command token was encountered. parseCommand(commandBuffer); // Clear the input buffer memset(commandBuffer, 0, sizeof(commandBuffer)); } } } // Macro used in parseCommand function to simplify parsing get and set commands for a variable #define GET_AND_SET(variableName) \ else if (strcmp(command, "GET " #variableName) == 0) { \ Serial.println(variableName); \ } \ else if (strstr(command, "SET " #variableName " ") != NULL) { \ variableName = (typeof(variableName)) atof(command+(sizeof("SET " #variableName " ")-1)); \ } //************************************************************************** // parseCommand // Function - Parses commands received via the serial input. //************************************************************************** // void parseCommand(char* command) { if (strcmp(command, "GET MAGNITUDES") == 0) { for (size_t i = 0; i < ARRAY_SZ(magnitudes); ++i) { Serial.println(magnitudes[i]); } } else if (strcmp(command, "GET SAMPLES") == 0) { for (size_t i = 0; i < ARRAY_SZ(samples); i+=2) { Serial.println(samples[i]); } } else if (strcmp(command, "GET FFT_SIZE") == 0) { Serial.println(FFT_SIZE); } GET_AND_SET(SAMPLE_RATE_HZ) // GET_AND_SET(LEDS_ENABLED) GET_AND_SET(SPECTRUM_MIN_DB) GET_AND_SET(SPECTRUM_MAX_DB) // Update spectrum display values if sample rate was changed. if (strstr(command, "SET SAMPLE_RATE_HZ ") != NULL) { spectrumSetup(); } }