forked from buddhabrot/fusion-zauberstab
updates from labor
Signed-off-by: Thomas Schmid <tom@binary-kitchen.de>
This commit is contained in:
parent
543a2588a3
commit
7400d18c0d
@ -4,7 +4,7 @@
|
||||
//lichterkette: PWM 2
|
||||
//mikrofon: A1
|
||||
#define LED_PIN 2
|
||||
#define NUM_LEDS 240
|
||||
#define NUM_LEDS 144
|
||||
|
||||
|
||||
#define SAMPLING_FREQUENCY_BP 40 // number of energy chunks per second
|
||||
|
@ -1,163 +0,0 @@
|
||||
|
||||
/* sound_wave
|
||||
*
|
||||
* By: Andrew Tuline
|
||||
*
|
||||
* Date: February, 2017
|
||||
*
|
||||
* Basic code to read from the Sparkfun INMP401 microphone, and create waves based on sampled input. This does NOT include sensitivity adjustment.
|
||||
*
|
||||
* My hardware setup:
|
||||
*
|
||||
* Arduino Nano & Addressable LED strips
|
||||
* - Powered by USB power bank
|
||||
* - APA102 or WS2812 data connected to pin 12.
|
||||
* - APA102 clock connected to pin 11.
|
||||
* - 5V on APA102 or WS2812 connected to 5V on Nano (good for short strips).
|
||||
* - Gnd to Gnd on Nano.
|
||||
*
|
||||
*
|
||||
* Sparkfun MEMS microphone
|
||||
* - Vcc on microphone is connected to 3.3V on Nano.
|
||||
* - AREF on Nano connected to 3.3V on Nano.
|
||||
* - Mic out connected to A5.
|
||||
* - Gnd to Gnd on Nano.
|
||||
*
|
||||
* Note: If you are using a microphone powered by the 3.3V signal, such as the Sparkfun MEMS microphone, then connect 3.3V to the AREF pin.
|
||||
*
|
||||
*/
|
||||
|
||||
//#define FASTLED_ALLOW_INTERRUPTS 0 // Used for ESP8266.
|
||||
#include <FastLED.h> // FastLED library.
|
||||
#include "zauberstab.h"
|
||||
|
||||
uint8_t squelch = 7; // Anything below this is background noise, so we'll make it '0'.
|
||||
int sample; // Current sample.
|
||||
float sampleAvg = 0; // Smoothed Average.
|
||||
float micLev = 0; // Used to convert returned value to have '0' as minimum.
|
||||
uint8_t maxVol = 11; // Reasonable value for constant volume for 'peak detector', as it won't always trigger.
|
||||
bool samplePeak = 0; // Boolean flag for peak. Responding routine must reset this flag.
|
||||
|
||||
int sampleAgc, multAgc;
|
||||
uint8_t targetAgc = 60; // This is our setPoint at 20% of max for the adjusted output.
|
||||
|
||||
|
||||
// Fixed definitions cannot change on the fly.
|
||||
#define LED_DT LED_PIN // Data pin to connect to the strip.
|
||||
#define LED_CK 11 // Clock pin for WS2801 or APA102.
|
||||
#define COLOR_ORDER GRB // It's GRB for WS2812 and BGR for APA102.
|
||||
#define LED_TYPE WS2812 // Using APA102, WS2812, WS2801. Don't forget to modify LEDS.addLeds to suit.
|
||||
|
||||
|
||||
struct CRGB leds[NUM_LEDS]; // Initialize our LED array.
|
||||
|
||||
int max_bright = 255;
|
||||
|
||||
CRGBPalette16 currentPalette = OceanColors_p;
|
||||
CRGBPalette16 targetPalette = OceanColors_p;
|
||||
TBlendType currentBlending = LINEARBLEND; // NOBLEND or LINEARBLEND
|
||||
|
||||
|
||||
|
||||
void setup() {
|
||||
//analogReference(EXTERNAL); // 3.3V reference for analog input.
|
||||
|
||||
Serial.begin(115200); // Initialize serial port for debugging.
|
||||
delay(1000); // Soft startup to ease the flow of electrons.
|
||||
|
||||
LEDS.addLeds<LED_TYPE, LED_DT, COLOR_ORDER>(leds, NUM_LEDS); // Use this for WS2812B
|
||||
// LEDS.addLeds<LED_TYPE, LED_DT, LED_CK, COLOR_ORDER>(leds, NUM_LEDS); // Use this for WS2801 or APA102
|
||||
|
||||
FastLED.setBrightness(max_bright);
|
||||
FastLED.setMaxPowerInVoltsAndMilliamps(5, 500); // FastLED Power management set at 5V, 500mA.
|
||||
|
||||
} // setup()
|
||||
|
||||
|
||||
void getSample() {
|
||||
|
||||
int16_t micIn; // Current sample starts with negative values and large values, which is why it's 16 bit signed.
|
||||
static long peakTime;
|
||||
|
||||
micIn = analogRead(MIC_PIN)>>2; // Poor man's analog Read.
|
||||
micLev = ((micLev * 31) + micIn) / 32; // Smooth it out over the last 32 samples for automatic centering.
|
||||
micIn -= micLev; // Let's center it to 0 now.
|
||||
micIn = abs(micIn); // And get the absolute value of each sample.
|
||||
sample = (micIn <= squelch) ? 0 : (sample + micIn) / 2; // Using a ternary operator, the resultant sample is either 0 or it's a bit smoothed out with the last sample.
|
||||
sampleAvg = ((sampleAvg * 31) + sample) / 32; // Smooth it out over the last 32 samples.
|
||||
|
||||
if (sample > (sampleAvg+maxVol) && millis() > (peakTime + 50)) { // Poor man's beat detection by seeing if sample > Average + some value.
|
||||
samplePeak = 1; // Then we got a peak, else we don't. Display routines need to reset the samplepeak value in case they miss the trigger.
|
||||
peakTime=millis();
|
||||
}
|
||||
|
||||
} // getSample()
|
||||
|
||||
|
||||
void agcAvg() { // A simple averaging multiplier to automatically adjust sound sensitivity.
|
||||
|
||||
multAgc = (sampleAvg < 1) ? targetAgc : targetAgc / sampleAvg; // Make the multiplier so that sampleAvg * multiplier = setpoint
|
||||
sampleAgc = sample * multAgc;
|
||||
if (sampleAgc > 255) sampleAgc = 255;
|
||||
|
||||
//------------ Oscilloscope output ---------------------------
|
||||
Serial.print(targetAgc); Serial.print(" ");
|
||||
Serial.print(multAgc); Serial.print(" ");
|
||||
Serial.print(sampleAgc); Serial.print(" ");
|
||||
|
||||
Serial.print(micLev); Serial.print(" ");
|
||||
Serial.print(sample); Serial.println(" ");
|
||||
// Serial.print(sampleAvg); Serial.print(" ");
|
||||
// Serial.print(samplePeak); Serial.print(" "); samplePeak = 0;
|
||||
// Serial.print(100); Serial.print(" ");
|
||||
// Serial.print(0); Serial.print(" ");
|
||||
// Serial.println(" ");
|
||||
|
||||
} // agcAvg()
|
||||
|
||||
|
||||
|
||||
void sndwave() {
|
||||
|
||||
leds[NUM_LEDS/2] = ColorFromPalette(currentPalette, sampleAgc, sampleAgc, currentBlending); // Put the sample into the center
|
||||
|
||||
for (int i = NUM_LEDS - 1; i > NUM_LEDS/2; i--) { //move to the left // Copy to the left, and let the fade do the rest.
|
||||
leds[i] = leds[i - 1];
|
||||
}
|
||||
|
||||
for (int i = 0; i < NUM_LEDS/2; i++) { // move to the right // Copy to the right, and let the fade to the rest.
|
||||
leds[i] = leds[i + 1];
|
||||
}
|
||||
|
||||
} // sndwave()
|
||||
|
||||
|
||||
void loop() {
|
||||
|
||||
|
||||
EVERY_N_SECONDS(5) { // Change the palette every 5 seconds.
|
||||
for (int i = 0; i < 16; i++) {
|
||||
targetPalette[i] = CHSV(random8(), 255, 255);
|
||||
}
|
||||
}
|
||||
|
||||
EVERY_N_MILLISECONDS(100) { // AWESOME palette blending capability once they do change.
|
||||
uint8_t maxChanges = 24;
|
||||
nblendPaletteTowardPalette(currentPalette, targetPalette, maxChanges);
|
||||
}
|
||||
|
||||
|
||||
EVERY_N_MILLIS_I(thistimer,20) { // For fun, let's make the animation have a variable rate.
|
||||
uint8_t timeval = beatsin8(10,20,50); // Use a sinewave for the line below. Could also use peak/beat detection.
|
||||
thistimer.setPeriod(timeval); // Allows you to change how often this routine runs.
|
||||
fadeToBlackBy(leds, NUM_LEDS, 16); // 1 = slow, 255 = fast fade. Depending on the faderate, the LED's further away will fade out.
|
||||
getSample();
|
||||
agcAvg();
|
||||
sndwave();
|
||||
}
|
||||
|
||||
FastLED.show();
|
||||
|
||||
} // loop()
|
||||
|
||||
|
@ -1,204 +0,0 @@
|
||||
#include "zauberstab.h"
|
||||
#include "app.h"
|
||||
|
||||
#define SAMPLING_FREQUENCY_BP 40 // number of energy chunks per second
|
||||
#define SAMPLING_FREQUENCY_CONTROL 1 // check number of times per second if the current band pass is the best one
|
||||
#define Q 20. // quality factor of band pass filters
|
||||
#define PI 3.1415926535897932384626433832795
|
||||
#define n_BP 30 //number of band pass filters
|
||||
|
||||
static unsigned long sampling_period_bp = 1000000L / SAMPLING_FREQUENCY_BP;
|
||||
static unsigned long sampling_period_control = 1000000L / SAMPLING_FREQUENCY_CONTROL;
|
||||
static double energy = 0;
|
||||
static unsigned long last_us_bp = 0L;
|
||||
static unsigned long last_us_control = 0L;
|
||||
|
||||
static float a0[n_BP];
|
||||
static float a1[n_BP];
|
||||
static float a2[n_BP];
|
||||
static float b0[n_BP];
|
||||
//static float b1[n_BP];
|
||||
static float b2[n_BP];
|
||||
|
||||
static float a[n_BP];
|
||||
static float w0[n_BP];
|
||||
|
||||
static float yy1[n_BP];
|
||||
static float yy2[n_BP];
|
||||
static float yy3[n_BP];
|
||||
static float yy4[n_BP];
|
||||
static float yy5[n_BP];
|
||||
static float yy6[n_BP];
|
||||
|
||||
static float u1[n_BP];
|
||||
static float u2[n_BP];
|
||||
static float y[n_BP];
|
||||
static float y_fil[n_BP];
|
||||
|
||||
static float angle;
|
||||
static float angle2;
|
||||
|
||||
static double energy_fil = 800.;
|
||||
|
||||
static float pos_target = NUM_LEDS / 2;
|
||||
static float pos_target_filtered = NUM_LEDS / 2;
|
||||
|
||||
static float microphone_offset = 675;
|
||||
static long initial_time;
|
||||
|
||||
static int active = 15;
|
||||
static int candidate = 15;
|
||||
static int rounds = 0;
|
||||
|
||||
static int get_value(int pos, float pos0)
|
||||
{
|
||||
if (abs(pos0 - pos) > 20)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (40 - abs(pos0 - pos) * 2);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_filter()
|
||||
{
|
||||
for (int i = 0; i < n_BP; i++)
|
||||
{
|
||||
float frequency = 1.75 + i * (2.4 - 1.75) / n_BP;
|
||||
w0[i] = 2. * PI * frequency / SAMPLING_FREQUENCY_BP;
|
||||
a[i] = sin(w0[i] / (2. * Q));
|
||||
b0[i] = a[i];
|
||||
//b1[i] = 0;
|
||||
b2[i] = -a[i];
|
||||
a0[i] = 1. + a[i];
|
||||
a1[i] = -2. * cos(w0[i]);
|
||||
a2[i] = 1. - a[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void abeat_setup()
|
||||
{
|
||||
zauberstab_init();
|
||||
Serial.begin(115200);
|
||||
set_filter();
|
||||
initial_time = micros();
|
||||
}
|
||||
static void abeat_loop()
|
||||
{
|
||||
|
||||
int sample = int(analogRead(MIC_PIN) - microphone_offset);
|
||||
energy += abs(sample) * abs(sample);
|
||||
|
||||
if (micros() - last_us_bp > sampling_period_bp)
|
||||
{
|
||||
|
||||
Serial.println(sample);
|
||||
|
||||
microphone_offset += (analogRead(MIC_PIN) - microphone_offset) * 0.001;
|
||||
|
||||
//Serial.println(microphone_offset);
|
||||
|
||||
last_us_bp += sampling_period_bp;
|
||||
energy_fil += (energy - energy_fil) * 0.01;
|
||||
//Serial.println(energy);
|
||||
for (int i = 0; i < n_BP; i++)
|
||||
{
|
||||
y[i] = (b0[i] / a0[i]) * energy + 0. + (b2[i] / a0[i]) * u2[i] - (a1[i] / a0[i]) * yy1[i] - (a2[i] / a0[i]) * yy2[i];
|
||||
u2[i] = u1[i];
|
||||
u1[i] = energy;
|
||||
yy6[i] = yy5[i];
|
||||
yy5[i] = yy4[i];
|
||||
yy4[i] = yy3[i];
|
||||
yy3[i] = yy2[i];
|
||||
yy2[i] = yy1[i];
|
||||
yy1[i] = y[i];
|
||||
y_fil[i] += (abs(y[i]) - y_fil[i]) * 0.005; //linie der scheitelpunkte
|
||||
}
|
||||
|
||||
float delays = constrain(SAMPLING_FREQUENCY_BP * 0.25 / (1.75 + active * (2.4 - 1.75) / n_BP), 4., 6.);
|
||||
|
||||
float delayed = 0;
|
||||
if (delays > 5)
|
||||
{
|
||||
delayed = yy5[active] * (1 - delays + 5) + yy6[active] * (delays - 5);
|
||||
}
|
||||
else
|
||||
{
|
||||
delayed = yy4[active] * (1 - delays + 4) + yy5[active] * (delays - 4);
|
||||
}
|
||||
|
||||
angle = atan2(delayed, y[active]);
|
||||
|
||||
if (PI < abs(angle - angle2) && abs(angle - angle2) < 3 * PI)
|
||||
{
|
||||
angle2 = angle + 2 * PI;
|
||||
}
|
||||
else
|
||||
{
|
||||
angle2 = angle;
|
||||
}
|
||||
|
||||
pos_target = map(angle2, -PI, 3 * PI, -0.3 * NUM_LEDS, NUM_LEDS * 1.5);
|
||||
|
||||
if (pos_target > pos_target_filtered)
|
||||
{
|
||||
pos_target_filtered += (pos_target - pos_target_filtered) * 0.35;
|
||||
}
|
||||
else
|
||||
{
|
||||
pos_target_filtered = pos_target;
|
||||
}
|
||||
|
||||
// Serial.print(y_fil[active]);
|
||||
// Serial.print(",");
|
||||
// Serial.println(y[active]);
|
||||
|
||||
energy = 0;
|
||||
|
||||
for (int i = 0; i < NUM_LEDS; i++)
|
||||
{
|
||||
int brightness = get_value(i, pos_target_filtered);
|
||||
//leds[i].setRGB(brightness, brightness, brightness);
|
||||
|
||||
//leds[i].setHSV(160, (rounds == 6) ? 0xFF : 0, brightness);
|
||||
leds[i] = CRGB::White;
|
||||
}
|
||||
FastLED.show();
|
||||
}
|
||||
|
||||
if (micros() - last_us_control > sampling_period_control)
|
||||
{
|
||||
last_us_control += sampling_period_control;
|
||||
int argmax = -1;
|
||||
float valuemax = 0;
|
||||
for (int i = 0; i < n_BP; i++)
|
||||
{
|
||||
if (y_fil[i] > valuemax)
|
||||
{
|
||||
valuemax = y_fil[i];
|
||||
argmax = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (argmax > -1)
|
||||
{
|
||||
if (argmax == candidate)
|
||||
{
|
||||
rounds++;
|
||||
}
|
||||
else
|
||||
{
|
||||
rounds = 0;
|
||||
candidate = argmax;
|
||||
}
|
||||
if (rounds > 6)
|
||||
{
|
||||
rounds = 0;
|
||||
active = candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "zauberstab.h"
|
||||
#include <algorithm>
|
||||
|
||||
#define SAMPLING_FREQUENCY_BP 40 // number of energy chunks per second
|
||||
#define SAMPLING_FREQUENCY_CONTROL 1 // check number of times per second if the current band pass is the best one
|
||||
@ -33,11 +34,13 @@ static float u1[n_BP];
|
||||
static float u2[n_BP];
|
||||
static float y[n_BP];
|
||||
static float y_fil[n_BP];
|
||||
static float y_fil_avg;
|
||||
static float transience = 0;
|
||||
|
||||
static float angle;
|
||||
static float angle2;
|
||||
|
||||
static double energy_fil = 800.;
|
||||
//static double energy_fil = 800.;
|
||||
|
||||
static float pos_target = NUM_LEDS / 2;
|
||||
static float pos_target_filtered = NUM_LEDS / 2;
|
||||
@ -49,15 +52,15 @@ static int active = 15;
|
||||
static int candidate = 15;
|
||||
static int rounds = 0;
|
||||
|
||||
static int get_value(int pos, float pos0)
|
||||
static float get_value(int pos, float pos0)
|
||||
{
|
||||
if (abs(pos0 - pos) > 20)
|
||||
if (abs(pos0 - pos) > 5)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (40 - abs(pos0 - pos) * 2);
|
||||
return (40 - abs(pos0 - pos) * 8);
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,16 +95,11 @@ void loop()
|
||||
|
||||
if (micros() - last_us_bp > sampling_period_bp)
|
||||
{
|
||||
|
||||
Serial.println(sample);
|
||||
|
||||
microphone_offset += (analogRead(MIC_PIN) - microphone_offset) * 0.001;
|
||||
|
||||
//Serial.println(microphone_offset);
|
||||
|
||||
last_us_bp += sampling_period_bp;
|
||||
energy_fil += (energy - energy_fil) * 0.01;
|
||||
//Serial.println(energy);
|
||||
//energy_fil += (energy - energy_fil) * 0.01;
|
||||
y_fil_avg = 0;
|
||||
for (int i = 0; i < n_BP; i++)
|
||||
{
|
||||
y[i] = (b0[i] / a0[i]) * energy + 0. + (b2[i] / a0[i]) * u2[i] - (a1[i] / a0[i]) * yy1[i] - (a2[i] / a0[i]) * yy2[i];
|
||||
@ -114,7 +112,9 @@ void loop()
|
||||
yy2[i] = yy1[i];
|
||||
yy1[i] = y[i];
|
||||
y_fil[i] += (abs(y[i]) - y_fil[i]) * 0.005; //linie der scheitelpunkte
|
||||
y_fil_avg += y_fil[i];
|
||||
}
|
||||
y_fil_avg /= n_BP;
|
||||
|
||||
float delays = constrain(SAMPLING_FREQUENCY_BP * 0.25 / (1.75 + active * (2.4 - 1.75) / n_BP), 4., 6.);
|
||||
|
||||
@ -150,20 +150,20 @@ void loop()
|
||||
pos_target_filtered = pos_target;
|
||||
}
|
||||
|
||||
// Serial.print(y_fil[active]);
|
||||
// Serial.print(",");
|
||||
// Serial.println(y[active]);
|
||||
|
||||
energy = 0;
|
||||
|
||||
transience += ((y_fil[active]/y_fil_avg-1.6) - transience)*0.02;
|
||||
transience = max(transience, 0.0f);
|
||||
transience = min(transience, 1.0f);
|
||||
|
||||
for (int i = 0; i < NUM_LEDS; i++)
|
||||
{
|
||||
int brightness = get_value(i, pos_target_filtered);
|
||||
if (brightness >= 1) {
|
||||
brightness = 10;
|
||||
}
|
||||
//leds[i].setRGB(brightness, brightness, brightness);
|
||||
leds[i].setHSV(160, (rounds == 6) ? 0xFF : 0, brightness);
|
||||
leds[i].g = int(get_value(i, pos_target_filtered)*transience);
|
||||
leds[i].r = int(get_value(i, pos_target_filtered+2)*transience);
|
||||
leds[i].b = int(get_value(i, pos_target_filtered-2)*transience);
|
||||
|
||||
//leds[i].setRGB(brightness_red, brightness_green, brightness_blue);
|
||||
//leds[i].setHSV(160, (rounds == 6) ? 0xFF : 0, brightness);
|
||||
}
|
||||
FastLED.show();
|
||||
}
|
||||
@ -193,7 +193,7 @@ void loop()
|
||||
rounds = 0;
|
||||
candidate = argmax;
|
||||
}
|
||||
if (rounds > 6)
|
||||
if (rounds > 3)
|
||||
{
|
||||
rounds = 0;
|
||||
active = candidate;
|
||||
|
Loading…
Reference in New Issue
Block a user