Skip to content

Instantly share code, notes, and snippets.

@dz0ny
Last active May 7, 2016 13:49
Show Gist options
  • Save dz0ny/8d6c2650ffa3609ac035803ba6969d9d to your computer and use it in GitHub Desktop.
Save dz0ny/8d6c2650ffa3609ac035803ba6969d9d to your computer and use it in GitHub Desktop.
#define NumOfFreqBins 32
#define RedPin 9
#define GreenPin 10
#define BluePin 11
#define ButtonPin 8
#define LIN_OUT 1 // use linear fht output function
#define FHT_N NumOfFreqBins
#define ADCReset 0b11110111 // reset the adc, freq = 1000 kHz / 13 =~ 74 kHz sampling rate
#define ADCFreeRun 0b11100111 // set the adc to free running mode, freq = 1000 kHz / 13.5 =~ 74 kHz sampling rate
#define PeakPWMValue 255
#define RedMinLimit 1350
#define GreenMinLimit 125
#define BlueMinLimit 350
#define PopThreshold 120 // large delta in ADC samples means the reading has lots of error
#include <FHT.h> // http://wiki.openmusiclabs.com/wiki/ArduinoFHT
#include <Pushbutton.h>
static const uint8_t PROGMEM
// This is low-level noise that's subtracted from each FFT output column:
noise[64] = { 8, 6, 6, 5, 3, 4, 4, 4, 3, 4, 4, 3, 2, 3, 3, 4,
2, 1, 2, 1, 3, 2, 3, 2, 1, 2, 3, 1, 2, 3, 4, 4,
3, 2, 2, 2, 2, 2, 2, 1, 3, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 4
};
/* Create a Pushbutton object with default settings, which
enables the internal pull-up on the pin and interprets a HIGH
pin value as the default (unpressed) state of the
button. (Optional arguments can be passed to the constructor
to specify other button types and connection methods; see the
documentation for details.) */
Pushbutton button(ButtonPin);
unsigned int OutputGreen = 0;
unsigned int OutputRed = 0;
unsigned int OutputBlue = 0;
unsigned int RedPeak = 0;
unsigned int GreenPeak = 0;
unsigned int BluePeak = 0;
unsigned int RedMin = 65535; // set to max so a new min is always found the first time
unsigned int GreenMin = 65535;
unsigned int BlueMin = 65535;
unsigned int RedFilterStorage = 0;
unsigned int GreenFilterStorage = 0;
unsigned int BlueFilterStorage = 0;
unsigned int MainArray[NumOfFreqBins / 2];
unsigned int PeakArray[NumOfFreqBins / 2];
unsigned int MinArray[NumOfFreqBins / 2];
byte RangeErrorRed = 0;
byte RangeErrorGreen = 0;
byte RangeErrorBlue = 0;
byte BadSample = 0;
int Delta = 0;
int Mode = 0;
int LastValue = 32767;
void setup()
{
pinMode(RedPin, OUTPUT);
pinMode(GreenPin, OUTPUT);
pinMode(BluePin, OUTPUT);
// 16MHZ/prescaller / 13(CPU cycles) > target freq range
ADCSRA = ADCFreeRun; // set the adc to free running mode
ADCSRB = 0; // Free run mode, no high MUX bit
ADMUX = _BV(REFS1) | _BV(MUX0);
DIDR0 = 0x01; // turn off the digital input for adc5
}
int ReadADC()
{
noInterrupts(); // get lots more nasty pops/error on the measurements without doing this
while (!(ADCSRA & 0x10)); // wait for adc to be ready
ADCSRA = ADCReset; // reset the adc
int Output = ADCW; // read the adc
interrupts();
return Output;
}
void loop() {
checkButton();
if (Mode <= 2) {
Sound2RGB();
}
if (Mode == 3) {
fade(RedPin);
fade(GreenPin);
fade(BluePin);
}
}
void checkButton() {
if (button.getSingleDebouncedRelease())
{
Mode++;
if (Mode >= 3) {
Mode = 0;
}
}
}
void ProcessSound(){
uint8_t L;
BadSample = 0;
for (byte i = 0 ; i < NumOfFreqBins ; i++) // save NumOfFreqBins samples for FHT
{
int Sample;
if (LastValue != 32767)
{
Sample = ReadADC();
Delta = Sample - LastValue;
if (abs(Delta) > 100) // looks like a pop, don't process this sample
{
BadSample = 1;
}
}
else {
Sample = ReadADC(); // it's the first sample, just read
}
LastValue = Sample;
Sample <<= 6; // form into a 16b signed int
fht_input[i] = Sample; // put real data into bins
}
fht_window(); // window the data for better frequency response
fht_reorder(); // reorder the data before doing the fht
fht_run(); // process the data in the fht
fht_mag_lin(); // take the linear output of the fht
for (byte Index = 0; Index < (NumOfFreqBins / 2); Index++)
{
if (fht_lin_out[Index] > MainArray[Index]) {
MainArray[Index] = fht_lin_out[Index];
if (Mode == 2) {
// Remove noise and apply EQ levels
L = pgm_read_byte(&noise[Index]);
}
}
if (MainArray[Index] > PeakArray[Index])
{
PeakArray[Index] = MainArray[Index];
}
}
}
void Sound2RGB() {
ProcessSound();
unsigned int RedMap = ArrayRedParser(MainArray);
unsigned int GreenMap = ArrayGreenParser(MainArray);
unsigned int BlueMap = ArrayBlueParser(MainArray);
if (RedMap > RedPeak){RedPeak = RedMap;}
if (RedMap < RedMin){RedMin = RedMap;}
if (GreenMap > GreenPeak){GreenPeak = GreenMap;}
if (GreenMap < GreenMin){GreenMin = GreenMap;}
if (BlueMap > BluePeak){BluePeak = BlueMap;}
if (BlueMap < BlueMin){BlueMin = BlueMap;}
if (RedPeak < RedMinLimit){RedPeak = RedMinLimit;}
if (GreenPeak < GreenMinLimit){GreenPeak = GreenMinLimit;}
if (BluePeak < BlueMinLimit){BluePeak = BlueMinLimit;}
RedFilterStorage = map(RedMap, RedMin, RedPeak, 0, PeakPWMValue);
GreenFilterStorage = map(GreenMap, GreenMin, GreenPeak, 0, PeakPWMValue);
BlueFilterStorage = map(BlueMap, BlueMin, BluePeak, 0, PeakPWMValue);
if (RedFilterStorage > PeakPWMValue){RedFilterStorage = PeakPWMValue; RangeErrorRed = 1;}
if (GreenFilterStorage > PeakPWMValue){GreenFilterStorage = PeakPWMValue; RangeErrorGreen = 1;}
if (BlueFilterStorage > PeakPWMValue){BlueFilterStorage = PeakPWMValue; RangeErrorBlue = 1;}
OutputRed = RedFilterStorage;
OutputGreen = GreenFilterStorage;
OutputBlue = BlueFilterStorage;
analogWrite(RedPin, OutputRed);
analogWrite(GreenPin, OutputGreen);
analogWrite(BluePin, OutputBlue);
for (byte Index = 0; Index < (NumOfFreqBins / 2); Index++) {
MainArray[Index] = 0; // Clean the main array
}
}
unsigned int ArrayRedParser(unsigned int Array[])
{
unsigned int RedParser = ((Array[0]) + Array[1] + (Array[2] * 0.65));
return RedParser;
}
unsigned int ArrayGreenParser(unsigned int Array[])
{
unsigned int GreenParser = ((Array[2] * 0.95) + Array[3] + (Array[4] * 0.75) + (Array[5]) * 0.25);
return GreenParser;
}
unsigned int ArrayBlueParser(unsigned int Array[])
{
unsigned int BlueParser = ((Array[4] * 0.25) + (Array[5] * 0.75));
for (byte BlueIndex = 6; BlueIndex < (NumOfFreqBins/2); BlueIndex++){BlueParser = (BlueParser + Array[BlueIndex]);}
return BlueParser;
}
void fade(int ledPin) {
// fade in from min to max in increments of 5 points:
for (int fadeValue = 0 ; fadeValue <= 255; fadeValue += 5) {
// sets the value (range from 0 to 255):
analogWrite(ledPin, fadeValue);
delayMicroseconds(500);
if (button.isPressed()) {
checkButton();
}
}
// fade out from max to min in increments of 5 points:
for (int fadeValue = 255 ; fadeValue >= 0; fadeValue -= 5) {
// sets the value (range from 0 to 255):
analogWrite(ledPin, fadeValue);
delayMicroseconds(500);
if (button.isPressed()) {
checkButton();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment