forked from buddhabrot/fusion-zauberstab
Merge branch 'master' of git.binary-kitchen.de:buddhabrot/fusion-zauberstab into master
This commit is contained in:
commit
94bcfd3451
@ -1,222 +0,0 @@
|
||||
#include <FastLED.h>
|
||||
|
||||
|
||||
//lichterkette: PWM 2
|
||||
//mikrofon: A1
|
||||
#define LED_PIN 2
|
||||
#define NUM_LEDS 240
|
||||
|
||||
|
||||
#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
|
||||
|
||||
|
||||
CRGB leds[NUM_LEDS];
|
||||
|
||||
unsigned long sampling_period_bp = 1000000L/SAMPLING_FREQUENCY_BP;
|
||||
unsigned long sampling_period_control = 1000000L/SAMPLING_FREQUENCY_CONTROL;
|
||||
double energy = 0;
|
||||
unsigned long last_us_bp = 0L;
|
||||
unsigned long last_us_control = 0L;
|
||||
|
||||
float a0[n_BP];
|
||||
float a1[n_BP];
|
||||
float a2[n_BP];
|
||||
float b0[n_BP];
|
||||
//float b1[n_BP];
|
||||
float b2[n_BP];
|
||||
|
||||
float a[n_BP];
|
||||
float w0[n_BP];
|
||||
|
||||
float yy1[n_BP];
|
||||
float yy2[n_BP];
|
||||
float yy3[n_BP];
|
||||
float yy4[n_BP];
|
||||
float yy5[n_BP];
|
||||
float yy6[n_BP];
|
||||
|
||||
float u1[n_BP];
|
||||
float u2[n_BP];
|
||||
float y[n_BP];
|
||||
float y_fil[n_BP];
|
||||
|
||||
float angle;
|
||||
float angle2;
|
||||
|
||||
double energy_fil = 800.;
|
||||
|
||||
|
||||
float pos_target = NUM_LEDS/2;
|
||||
float pos_target_filtered = NUM_LEDS/2;
|
||||
|
||||
float microphone_offset = 512;
|
||||
long initial_time;
|
||||
|
||||
int active = 15;
|
||||
int candidate = 15;
|
||||
int rounds = 0;
|
||||
|
||||
|
||||
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
FastLED.addLeds<WS2812, LED_PIN, RGB>(leds, NUM_LEDS);
|
||||
FastLED.setMaxPowerInVoltsAndMilliamps(5, 300);
|
||||
|
||||
// for(int i = 0; i < NUM_LEDS; i++)
|
||||
// { int brightness = get_value(i, pos_target_filtered);
|
||||
// leds[i].setRGB(brightness, brightness, brightness); }
|
||||
// FastLED.show();
|
||||
//
|
||||
// long sumsamples = 0;
|
||||
// for(int j = 1; j<1000; j++)
|
||||
// {
|
||||
// int sample = analogRead(1);
|
||||
// sumsamples += sample;
|
||||
// delay(1);
|
||||
// if(j==500)
|
||||
// {
|
||||
// sumsamples = 0;
|
||||
// }
|
||||
// }
|
||||
// microphone_offset = sumsamples/500;
|
||||
|
||||
|
||||
set_filter();
|
||||
initial_time = micros();
|
||||
|
||||
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
int get_value(int pos, float pos0) {
|
||||
if (abs(pos0-pos) > 20) { return 0; }
|
||||
else { return (40-abs(pos0-pos)*2); }
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
int sample = int(analogRead(1) - microphone_offset);
|
||||
energy += abs(sample)*abs(sample);
|
||||
|
||||
if (micros() - last_us_bp > sampling_period_bp)
|
||||
{
|
||||
|
||||
Serial.println(sample);
|
||||
|
||||
microphone_offset += (analogRead(1)-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) ? 60 : 0, brightness);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
53
firmware/.vscode/settings.json
vendored
Normal file
53
firmware/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"array": "cpp",
|
||||
"atomic": "cpp",
|
||||
"*.tcc": "cpp",
|
||||
"cctype": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"complex": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"cstring": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"cwctype": "cpp",
|
||||
"deque": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"unordered_set": "cpp",
|
||||
"vector": "cpp",
|
||||
"exception": "cpp",
|
||||
"algorithm": "cpp",
|
||||
"functional": "cpp",
|
||||
"iterator": "cpp",
|
||||
"map": "cpp",
|
||||
"memory": "cpp",
|
||||
"memory_resource": "cpp",
|
||||
"numeric": "cpp",
|
||||
"optional": "cpp",
|
||||
"random": "cpp",
|
||||
"string": "cpp",
|
||||
"string_view": "cpp",
|
||||
"system_error": "cpp",
|
||||
"tuple": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"utility": "cpp",
|
||||
"fstream": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"iomanip": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"istream": "cpp",
|
||||
"limits": "cpp",
|
||||
"new": "cpp",
|
||||
"ostream": "cpp",
|
||||
"sstream": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"cinttypes": "cpp",
|
||||
"typeinfo": "cpp"
|
||||
}
|
||||
}
|
BIN
firmware/assets/Fairydust.png
Normal file
BIN
firmware/assets/Fairydust.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 84 KiB |
BIN
firmware/assets/Fusion_Font.png
Normal file
BIN
firmware/assets/Fusion_Font.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
BIN
firmware/assets/fairydust.bin
Normal file
BIN
firmware/assets/fairydust.bin
Normal file
Binary file not shown.
BIN
firmware/assets/grayout.bin
Normal file
BIN
firmware/assets/grayout.bin
Normal file
Binary file not shown.
BIN
firmware/assets/out.png
Normal file
BIN
firmware/assets/out.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
@ -1,10 +1,38 @@
|
||||
#ifndef ZAUBERSTAB_APP_H
|
||||
#define ZAUBERSTAB_APP_H
|
||||
#pragma once
|
||||
|
||||
struct App {
|
||||
void (*loop)(void);
|
||||
void (*setup)(void);
|
||||
void virtual init() = 0;
|
||||
void virtual deinit() = 0;
|
||||
void virtual loop() = 0;
|
||||
};
|
||||
|
||||
struct App
|
||||
#endif
|
||||
struct BeatDetectApp : public App {
|
||||
void init();
|
||||
void deinit();
|
||||
void loop();
|
||||
};
|
||||
|
||||
|
||||
struct VuMeterApp: public App {
|
||||
void init();
|
||||
void deinit();
|
||||
void loop();
|
||||
};
|
||||
|
||||
struct FFTTestApp: public App {
|
||||
void init();
|
||||
void deinit();
|
||||
void loop();
|
||||
};
|
||||
|
||||
struct FackelApp: public App {
|
||||
void init();
|
||||
void deinit();
|
||||
void loop();
|
||||
};
|
||||
|
||||
struct ImageDisplayApp: public App {
|
||||
void init();
|
||||
void deinit();
|
||||
void loop();
|
||||
};
|
46
firmware/include/biquad.h
Normal file
46
firmware/include/biquad.h
Normal file
@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
template <class T>
|
||||
struct Biquad
|
||||
{
|
||||
T b0;
|
||||
T b1;
|
||||
T b2;
|
||||
T a1;
|
||||
T a2;
|
||||
T xn1;
|
||||
T xn2;
|
||||
T yn1;
|
||||
T yn2;
|
||||
|
||||
Biquad() = default;
|
||||
Biquad(T a1, T a2, T b0, T b1, T b2) : b0(b0), b1(b1), b2(b2), a1(a1), a2(a2){};
|
||||
Biquad(T a0, T a1, T a2, T b0, T b1, T b2)
|
||||
{
|
||||
this->a1 = a1 / a0;
|
||||
this->a2 = a2 / a0;
|
||||
this->b0 = b0 / a0;
|
||||
this->b1 = b1 / a0;
|
||||
this->b2 = b2 / a0;
|
||||
}
|
||||
|
||||
T update(T x)
|
||||
{
|
||||
T y = this->b0 * x + this->b1 * this->xn1 + this->b2 * this->xn2 - this->yn1 * this->a1 - this->yn2 * this->a2;
|
||||
|
||||
this->xn2 = this->xn1;
|
||||
this->xn1 = x;
|
||||
|
||||
this->yn2 = this->yn1;
|
||||
this->yn1 = y;
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
this->xn1 = 0.0;
|
||||
this->xn2 = 0.0;
|
||||
this->yn1 = 0.0;
|
||||
this->yn1 = 0.0;
|
||||
}
|
||||
};
|
@ -1,11 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
struct dc_cancelation_state {
|
||||
float x_n1;
|
||||
float y_n1;
|
||||
float R;
|
||||
};
|
||||
template <class T>
|
||||
struct DcCancelation
|
||||
{
|
||||
T x_n1;
|
||||
T y_n1;
|
||||
T R;
|
||||
|
||||
void dc_cancelation_init(struct dc_cancelation_state *state, float R);
|
||||
void dc_cancelation_set_R(struct dc_cancelation_state *state, float R);
|
||||
float dc_cancelation_update(struct dc_cancelation_state *state, float x);
|
||||
DcCancelation(T R) : R(R){};
|
||||
|
||||
T update(T x)
|
||||
{
|
||||
T y = x - this->x_n1 + this->R * this->y_n1;
|
||||
this->x_n1 = x;
|
||||
this->y_n1 = y;
|
||||
|
||||
return y;
|
||||
};
|
||||
};
|
@ -2,5 +2,75 @@
|
||||
|
||||
#include <complex>
|
||||
|
||||
void fft(std::complex<float> *input, std::complex<float> *output, uint32_t N);
|
||||
void rfft(std::complex<float> *input, std::complex<float> *output, uint32_t N);
|
||||
template <class T>
|
||||
struct FFT
|
||||
{
|
||||
static void fft(std::complex<T> *samples, std::complex<T> *output, uint32_t N)
|
||||
{
|
||||
uint8_t log2n = (uint8_t)std::log2(N) + 0.5f;
|
||||
std::complex<T> I(0.0, 1.0);
|
||||
if (N == 1)
|
||||
{
|
||||
output[0] = samples[0];
|
||||
return;
|
||||
}
|
||||
|
||||
// shuffle array
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
|
||||
output[i] = samples[FFT::bitReverse(i, log2n)];
|
||||
}
|
||||
for (int s = 1; s <= log2n; s++)
|
||||
{
|
||||
uint32_t m = 1 << s; // 2^s
|
||||
std::complex<T> wm = std::exp(-2.0f * (T)M_PI * I / (std::complex<T>)m);
|
||||
for (int k = 0; k < N; k += m)
|
||||
{
|
||||
std::complex<T> w = 1.f;
|
||||
for (int j = 0; j < m / 2; j++)
|
||||
{
|
||||
std::complex<T> t = w * output[k + j + m / 2];
|
||||
std::complex<T> u = output[k + j];
|
||||
output[k + j] = u + t;
|
||||
output[k + j + m / 2] = u - t;
|
||||
w = w * wm;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void rfft(std::complex<T> *input, std::complex<T> *output, uint32_t N)
|
||||
{
|
||||
std::complex<T> I(0.0, 1.0);
|
||||
for (int i = 0; i < N / 2; i++)
|
||||
{
|
||||
input[i] = input[i] + I * input[i + N / 2];
|
||||
}
|
||||
|
||||
FFT<T>::fft(input, output, N / 2);
|
||||
|
||||
for (int i = 0; i < N / 2; i++)
|
||||
{
|
||||
output[i] = (output[i] + std::conj(output[(N / 2) - i])) / 2.;
|
||||
}
|
||||
|
||||
for (int i = N / 2; i < N; i++)
|
||||
{
|
||||
output[i] = -I * (output[i] - std::conj(output[(N / 2) - i])) / 2.;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static unsigned int bitReverse(unsigned int x, int log2n)
|
||||
{
|
||||
int n = 0;
|
||||
for (int i = 0; i < log2n; i++)
|
||||
{
|
||||
n <<= 1;
|
||||
n |= (x & 1);
|
||||
x >>= 1;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
};
|
@ -1,10 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
struct pt1_state {
|
||||
float y_n1;
|
||||
float K;
|
||||
float T;
|
||||
};
|
||||
template <class T>
|
||||
struct Pt1
|
||||
{
|
||||
T y_n1;
|
||||
T K;
|
||||
T T1;
|
||||
|
||||
void pt1_init(struct pt1_state *state, float K, float T);
|
||||
float pt1_update(struct pt1_state *state, float u, float dt);
|
||||
/* PT1: y = y_(n-1) + (Ku - y_(n-1)) * dt/T1 */
|
||||
Pt1(T K, T T1) : T1(T1), K(K){};
|
||||
Pt1() = default;
|
||||
|
||||
T update(T u, T dt)
|
||||
{
|
||||
T y = this->y_n1 + (this->K * u - this->y_n1) * dt / this->T1;
|
||||
this->y_n1 = y;
|
||||
return y;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
this->y_n1 = 0.f;
|
||||
}
|
||||
};
|
@ -1,12 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "Arduino.h"
|
||||
#include <FastLED.h>
|
||||
#include<Wire.h>
|
||||
#include<ADXL345_WE.h>
|
||||
|
||||
#define ADXL345_I2CADDR 0x53 // 0x1D if SDO = HIGH
|
||||
|
||||
#define LED_PIN 12
|
||||
#define NUM_LEDS 144
|
||||
#define NUM_LEDS 48
|
||||
#define MIC_PIN 15
|
||||
|
||||
extern CRGB leds[NUM_LEDS];
|
||||
extern ADXL345_WE myAcc;
|
||||
|
||||
int zauberstab_init();
|
||||
float get_sample();
|
||||
float get_sample();
|
||||
void switch_app();
|
||||
bool acc_has_event();
|
@ -10,7 +10,9 @@
|
||||
|
||||
[env]
|
||||
framework = arduino
|
||||
lib_deps = fastled/FastLED @ ^3.5.0
|
||||
lib_deps =
|
||||
fastled/FastLED @ ^3.5.0
|
||||
wollewald/ADXL345_WE @ ^2.1.4
|
||||
|
||||
[env:esp32doit-devkit-v1]
|
||||
platform = espressif32
|
||||
|
@ -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()
|
||||
|
||||
|
BIN
firmware/src/applications/assets/fusion_font.bin
Normal file
BIN
firmware/src/applications/assets/fusion_font.bin
Normal file
Binary file not shown.
221
firmware/src/applications/beat_detect.cpp
Normal file
221
firmware/src/applications/beat_detect.cpp
Normal file
@ -0,0 +1,221 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include "app.h"
|
||||
#include "biquad.h"
|
||||
#include "pt1.h"
|
||||
#include "zauberstab.h"
|
||||
|
||||
#undef NUM_LEDS
|
||||
#define NUM_LEDS 48
|
||||
|
||||
#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 30. // quality factor of band pass filters
|
||||
#define PI 3.1415926535897932384626433832795
|
||||
#define n_BP 40 // number of band pass filters
|
||||
|
||||
static const unsigned long sampling_period_bp = 1000000L / SAMPLING_FREQUENCY_BP;
|
||||
static const unsigned long sampling_period_control = 1000000L / SAMPLING_FREQUENCY_CONTROL;
|
||||
static float energy = 0;
|
||||
static unsigned long last_us_bp = 0L;
|
||||
static unsigned long last_us_control = 0L;
|
||||
|
||||
static Biquad<float> bp_filters[n_BP];
|
||||
static Pt1<float> y_filter[n_BP];
|
||||
static Pt1<float> pos_filter{1.f, 1.f};
|
||||
|
||||
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 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 long initial_time;
|
||||
|
||||
static int active = 15;
|
||||
static int rounds = 0;
|
||||
static int n_samples = 0;
|
||||
|
||||
static int
|
||||
get_value(int pos, float pos0)
|
||||
{
|
||||
if (abs(pos0 - pos) > 5)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (30 - abs(pos0 - pos) * 6);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
set_filter()
|
||||
{
|
||||
for (int i = 0; i < n_BP; i++)
|
||||
{
|
||||
float frequency = 1.75 + i * (2.5 - 1.75) / n_BP;
|
||||
float a, a0, a1, a2, b0, b1, b2, w0;
|
||||
w0 = 2. * PI * frequency / SAMPLING_FREQUENCY_BP;
|
||||
a = sin(w0 / (2. * Q));
|
||||
b0 = a;
|
||||
b1 = 0.f;
|
||||
b2 = -a;
|
||||
a0 = 1.f + a;
|
||||
a1 = -2.f * cos(w0);
|
||||
a2 = 1.f - a;
|
||||
bp_filters[i] = Biquad<float>{a0, a1, a2, b0, b1, b2};
|
||||
y_filter[i] = Pt1<float>{1.f, 1.f};
|
||||
}
|
||||
}
|
||||
|
||||
void BeatDetectApp::init()
|
||||
{
|
||||
set_filter();
|
||||
initial_time = micros();
|
||||
pos_target = NUM_LEDS / 2;
|
||||
pos_target_filtered = NUM_LEDS / 2;
|
||||
active = 15;
|
||||
rounds = 0;
|
||||
n_samples = 0;
|
||||
|
||||
pos_filter.reset();
|
||||
|
||||
for (int i = 0; i<n_BP; i++){
|
||||
bp_filters[i].reset();
|
||||
}
|
||||
}
|
||||
|
||||
void BeatDetectApp::deinit()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void BeatDetectApp::loop()
|
||||
{
|
||||
float sample = get_sample();
|
||||
|
||||
energy += std::abs(sample) * std::abs(sample);
|
||||
n_samples++;
|
||||
|
||||
if (micros() - last_us_bp > sampling_period_bp)
|
||||
{
|
||||
|
||||
n_samples = 0;
|
||||
last_us_bp = micros();
|
||||
// energy_fil += (energy - energy_fil) * 0.01;
|
||||
|
||||
for (int i = 0; i < n_BP; i++)
|
||||
{
|
||||
y[i] = bp_filters[i].update(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] = y_filter[i].update(std::abs(y[i]),
|
||||
0.005f); // linie der scheitelpunkte
|
||||
// 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.5 - 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);
|
||||
//pos_target = NUM_LEDS * (sin(angle2)+1)/2;
|
||||
|
||||
|
||||
|
||||
if (pos_target > pos_target_filtered)
|
||||
{
|
||||
pos_target_filtered = pos_filter.update(pos_target, 0.35f);
|
||||
}
|
||||
else
|
||||
{
|
||||
pos_filter.y_n1 = pos_target;
|
||||
pos_target_filtered = pos_target;
|
||||
}
|
||||
|
||||
|
||||
energy = 0;
|
||||
|
||||
for (int i = 0; i < NUM_LEDS; i++)
|
||||
{
|
||||
leds[i].g = get_value(i, pos_target_filtered);
|
||||
leds[i].r = get_value(i, pos_target_filtered + 2);
|
||||
leds[i].b = get_value(i, pos_target_filtered - 2);
|
||||
|
||||
//leds[i].g = get_value(i, pos_target_filtered);
|
||||
//leds[i].g = get_value(i, pos_target_filtered + 2);
|
||||
//leds[i].b = get_value(i, pos_target_filtered - 2);
|
||||
|
||||
}
|
||||
FastLED.show();
|
||||
}
|
||||
|
||||
if (micros() - last_us_control > sampling_period_control)
|
||||
{
|
||||
last_us_control = micros();
|
||||
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 != active)
|
||||
{
|
||||
rounds ++;
|
||||
}
|
||||
|
||||
if (rounds > 5)
|
||||
{
|
||||
active = argmax;
|
||||
rounds = 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
160
firmware/src/applications/fackel.cpp
Normal file
160
firmware/src/applications/fackel.cpp
Normal file
@ -0,0 +1,160 @@
|
||||
#include "app.h"
|
||||
#include "pt1.h"
|
||||
#include "zauberstab.h"
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
#undef NUM_LEDS
|
||||
#define NUM_LEDS 48
|
||||
|
||||
static unsigned long last_sample_time;
|
||||
static unsigned long sample_counter;
|
||||
static float rms_avg;
|
||||
|
||||
static uint8_t energy[NUM_LEDS + 2];
|
||||
static uint8_t spark_energy[NUM_LEDS + 2];
|
||||
char string[128];
|
||||
|
||||
Pt1<float> energy_pt1{1.f, 3.f};
|
||||
Pt1<float> rms_pet1{1.f, 0.05f};
|
||||
CRGB palette[128];
|
||||
|
||||
static void hsl_to_rgb(uint32_t hue, uint32_t sat, uint32_t lum, uint8_t *r, uint8_t *g, uint8_t *b)
|
||||
{
|
||||
uint32_t v;
|
||||
|
||||
v = (lum < 128) ? (lum * (256 + sat)) >> 8 : (((lum + sat) << 8) - lum * sat) >> 8;
|
||||
|
||||
if (v <= 0)
|
||||
{
|
||||
*r = *g = *b = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int32_t m;
|
||||
int32_t sextant;
|
||||
int32_t fract, vsf, mid1, mid2;
|
||||
|
||||
m = lum + lum - v;
|
||||
hue *= 6;
|
||||
sextant = hue >> 8;
|
||||
fract = hue - (sextant << 8);
|
||||
vsf = v * fract * (v - m) / v >> 8;
|
||||
mid1 = m + vsf;
|
||||
mid2 = v - vsf;
|
||||
switch (sextant)
|
||||
{
|
||||
case 0:
|
||||
*r = v;
|
||||
*g = mid1;
|
||||
*b = m;
|
||||
break;
|
||||
case 1:
|
||||
*r = mid2;
|
||||
*g = v;
|
||||
*b = m;
|
||||
break;
|
||||
case 2:
|
||||
*r = m;
|
||||
*g = v;
|
||||
*b = mid1;
|
||||
break;
|
||||
case 3:
|
||||
*r = m;
|
||||
*g = mid2;
|
||||
*b = v;
|
||||
break;
|
||||
case 4:
|
||||
*r = mid1;
|
||||
*g = m;
|
||||
*b = v;
|
||||
break;
|
||||
case 5:
|
||||
*r = v;
|
||||
*g = m;
|
||||
*b = mid2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void update_energy(uint8_t *energy, size_t s)
|
||||
{
|
||||
for (int i = s; i >= 2; i--)
|
||||
{
|
||||
energy[i] = (uint8_t)((float)(energy[i - 1] + energy[i - 2]) * 0.45f);
|
||||
}
|
||||
}
|
||||
|
||||
void FackelApp::init()
|
||||
{
|
||||
memset(&energy, 0, NUM_LEDS);
|
||||
memset(&spark_energy, 0, NUM_LEDS);
|
||||
|
||||
// generate palette
|
||||
for (int i = 0; i < 128; i++)
|
||||
{
|
||||
uint8_t r, g, b;
|
||||
hsl_to_rgb(i / 5, 255, i * 2 > 128 ? 128 : i * 2, &r, &g, &b);
|
||||
//hsl_to_rgb(i / 5 + 180, 255, i * 2 > 128 ? 128 : i * 2, &r, &g, &b);
|
||||
g = g == 1 ? 0 : g;
|
||||
b = b == 1 ? 0 : b;
|
||||
palette[i].r = r;
|
||||
palette[i].g = g;
|
||||
palette[i].b = b;
|
||||
}
|
||||
}
|
||||
|
||||
void FackelApp::deinit()
|
||||
{
|
||||
}
|
||||
|
||||
void FackelApp::loop()
|
||||
{
|
||||
if (micros() - last_sample_time >= 500)
|
||||
{
|
||||
last_sample_time = micros();
|
||||
int32_t sample = get_sample();
|
||||
float in = sample * sample;
|
||||
sample_counter++;
|
||||
rms_avg += (in - rms_avg) / (sample_counter + 1);
|
||||
}
|
||||
|
||||
EVERY_N_MILLISECONDS(10)
|
||||
{
|
||||
float rms = rms_pet1.update(rms_avg, 0.01f);
|
||||
float e_f = energy_pt1.update(rms_avg, 0.01f);
|
||||
//sprintf(string, "/*%f,%f*/", 1.3 * e_f, rms);
|
||||
//Serial.println(string);
|
||||
|
||||
Serial.print(rms_avg);
|
||||
Serial.print(",");
|
||||
Serial.println(e_f);
|
||||
|
||||
if (rms > 1.15 * e_f)
|
||||
{
|
||||
energy[0] = 128;
|
||||
energy[1] = 128;
|
||||
}
|
||||
else
|
||||
{
|
||||
energy[0] = 0;
|
||||
energy[1] = 10;
|
||||
}
|
||||
|
||||
rms_avg = 0.0f;
|
||||
sample_counter = 0;
|
||||
}
|
||||
|
||||
EVERY_N_MILLISECONDS(45)
|
||||
{
|
||||
update_energy(energy, NUM_LEDS+2);
|
||||
|
||||
for (int i = 0; i < NUM_LEDS; i++)
|
||||
{
|
||||
leds[NUM_LEDS-i-1] = palette[energy[i+2]];
|
||||
|
||||
}
|
||||
FastLED.show();
|
||||
}
|
||||
}
|
68
firmware/src/applications/fft_test.cpp
Normal file
68
firmware/src/applications/fft_test.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
#include "app.h"
|
||||
#include "fft.h"
|
||||
#include "zauberstab.h"
|
||||
|
||||
#define N_SAMPLES 256
|
||||
|
||||
static std::complex<float> samples[N_SAMPLES];
|
||||
static std::complex<float> z[N_SAMPLES];
|
||||
static uint32_t sample_counter = 0;
|
||||
static unsigned long max_dt = 0;
|
||||
static unsigned long last_sample = 0;
|
||||
|
||||
void FFTTestApp::init()
|
||||
{
|
||||
}
|
||||
|
||||
void FFTTestApp::deinit()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void FFTTestApp::loop()
|
||||
{
|
||||
if (micros() - last_sample >= 200)
|
||||
{
|
||||
unsigned long dt = micros() - last_sample;
|
||||
last_sample = micros();
|
||||
if (dt > max_dt)
|
||||
{
|
||||
max_dt = dt;
|
||||
}
|
||||
|
||||
samples[sample_counter++] = get_sample();
|
||||
|
||||
if (sample_counter == N_SAMPLES)
|
||||
{
|
||||
unsigned long start = micros();
|
||||
FFT<float>::fft(samples, z, N_SAMPLES);
|
||||
unsigned long end = micros();
|
||||
|
||||
float max = 0.f;
|
||||
for (int i = 0; i < N_SAMPLES / 2; i++)
|
||||
{
|
||||
float v = std::log10(std::abs(z[i]));
|
||||
if (v > max)
|
||||
{
|
||||
max = v;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < N_SAMPLES / 2; i++)
|
||||
{
|
||||
float v = std::log10(std::abs(z[i]));
|
||||
if (v < 2.f)
|
||||
{
|
||||
v = 0.f;
|
||||
}
|
||||
uint8_t led_value = v * 20;
|
||||
leds[i].setRGB(led_value, led_value, led_value);
|
||||
}
|
||||
|
||||
sample_counter = 0;
|
||||
start = micros();
|
||||
FastLED.show();
|
||||
end = micros();
|
||||
}
|
||||
}
|
||||
}
|
BIN
firmware/src/applications/fusion_font.bin
Normal file
BIN
firmware/src/applications/fusion_font.bin
Normal file
Binary file not shown.
60
firmware/src/applications/image_display.cpp
Normal file
60
firmware/src/applications/image_display.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
#include "app.h"
|
||||
#include "zauberstab.h"
|
||||
|
||||
#define __stringify_1(x) #x
|
||||
#define __stringify(x) __stringify_1(x)
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#define BASE_DIR "C:/Users/Binarykitchen/Documents/tom/fusion-zauberstab/firmware/"
|
||||
asm(
|
||||
".macro inc_sample name, filename\n\t"
|
||||
".pushsection .rodata\n\t"
|
||||
"\\name:\n\t"
|
||||
".incbin \"\\filename\"\n\t"
|
||||
"\\name\\()_size:\n\t"
|
||||
".int \\name\\()_size - \\name\n\t"
|
||||
".popsection\n\t"
|
||||
".endm\n\t"
|
||||
);
|
||||
|
||||
#define incbin(label, filename) \
|
||||
asm("inc_sample " __stringify(label) ", " filename "\n\t"); \
|
||||
extern const unsigned char label[]; \
|
||||
extern const unsigned int label##_size; \
|
||||
|
||||
|
||||
|
||||
incbin(fusion_font, BASE_DIR "assets/fairydust.bin")
|
||||
}
|
||||
|
||||
static CRGB get_pixel(unsigned int x, unsigned int y, unsigned int sx, const unsigned char *data) {
|
||||
unsigned int idx = (x + y * sx) * 3;
|
||||
|
||||
CRGB color{};
|
||||
color.r = data[idx];
|
||||
color.g = data[idx + 1];
|
||||
color.b = data[idx + 2];
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
void ImageDisplayApp::init() {
|
||||
|
||||
}
|
||||
|
||||
void ImageDisplayApp::deinit() {
|
||||
|
||||
}
|
||||
|
||||
void ImageDisplayApp::loop() {
|
||||
static unsigned int col = 0;
|
||||
unsigned int sx = fusion_font_size/(3*48);
|
||||
|
||||
for (int i = 0; i< NUM_LEDS; i++) {
|
||||
leds[i] = get_pixel(col, i, sx, fusion_font);
|
||||
}
|
||||
col++;
|
||||
col = col % 218;
|
||||
FastLED.show();
|
||||
}
|
@ -1,58 +1,63 @@
|
||||
#include "zauberstab.h"
|
||||
#include "app.h"
|
||||
#include "pt1.h"
|
||||
#include "zauberstab.h"
|
||||
|
||||
unsigned long last_sample_time;
|
||||
static int sample_counter = 0;
|
||||
unsigned int top_led_pos = 0;
|
||||
unsigned int top_led_pos = 0;
|
||||
float rms_avg = 0;
|
||||
float vu_filt = 0.0f;
|
||||
float vu_filt_slow = 0.0f;
|
||||
float dt;
|
||||
|
||||
struct pt1_state vu_pt1_fast;
|
||||
struct pt1_state vu_pt1_slow;
|
||||
Pt1<float> vu_pt1_fast{1.f, 0.05f};
|
||||
Pt1<float> vu_pt1_slow{1.f, 1.f};
|
||||
|
||||
void setup()
|
||||
void VuMeterApp::init()
|
||||
{
|
||||
zauberstab_init();
|
||||
Serial.begin(115200);
|
||||
FastLED.setBrightness(100);
|
||||
|
||||
pt1_init(&vu_pt1_slow, 1, 1.f);
|
||||
pt1_init(&vu_pt1_fast, 1, 0.05);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (micros()-last_sample_time >= 500){
|
||||
void VuMeterApp::deinit()
|
||||
{
|
||||
}
|
||||
|
||||
void VuMeterApp::loop()
|
||||
{
|
||||
if (micros() - last_sample_time >= 500)
|
||||
{
|
||||
last_sample_time = micros();
|
||||
int32_t sample = get_sample();
|
||||
float in = sample*sample;
|
||||
float in = sample * sample;
|
||||
sample_counter++;
|
||||
rms_avg += (in - rms_avg)/(sample_counter + 1);
|
||||
rms_avg += (in - rms_avg) / (sample_counter + 1);
|
||||
}
|
||||
|
||||
EVERY_N_MILLIS(10){
|
||||
|
||||
EVERY_N_MILLIS(10)
|
||||
{
|
||||
|
||||
float vu = 20 * log10f(rms_avg);
|
||||
vu_filt = pt1_update(&vu_pt1_fast, vu, 0.01f);
|
||||
vu_filt_slow = pt1_update(&vu_pt1_slow, vu_filt, 0.01f);
|
||||
//Serial.println(vu);
|
||||
vu_filt = vu_pt1_fast.update(vu, 0.01f);
|
||||
vu_filt_slow = vu_pt1_slow.update(vu_filt, 0.01f);
|
||||
// Serial.println(vu);
|
||||
int max_led = vu_filt;
|
||||
int top_led = vu_filt_slow;
|
||||
|
||||
max_led = max_led > 0xFF ? 0xFF : max_led;
|
||||
|
||||
if (top_led < max_led){
|
||||
if (top_led < max_led)
|
||||
{
|
||||
vu_pt1_slow.y_n1 = vu_filt;
|
||||
top_led = max_led;
|
||||
}
|
||||
|
||||
fill_solid(leds, NUM_LEDS, CRGB::Black);
|
||||
for(int i = 0; i < max_led; i++) {
|
||||
for (int i = 0; i < max_led; i++)
|
||||
{
|
||||
int idx = map(i, 0, NUM_LEDS, 0, 0xFF);
|
||||
leds[i] = ColorFromPalette(RainbowColors_p, idx);
|
||||
}
|
||||
|
||||
|
||||
leds[top_led] = CRGB::White;
|
||||
|
||||
FastLED.show();
|
@ -1,18 +0,0 @@
|
||||
#include "dc_cancelation.h"
|
||||
|
||||
void dc_cancelation_init(struct dc_cancelation_state *state, float R){
|
||||
state->R = R;
|
||||
state->x_n1 = 0.0f;
|
||||
state->y_n1 = 0.0f;
|
||||
}
|
||||
|
||||
void dc_cancelation_set_R(struct dc_cancelation_state *state, float R) {
|
||||
state->R = R;
|
||||
}
|
||||
|
||||
float dc_cancelation_update(struct dc_cancelation_state *state, float x) {
|
||||
float y = x-state->x_n1 + state->R * state->y_n1;
|
||||
state->x_n1 = x;
|
||||
state->y_n1 = y;
|
||||
return y;
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
#include "fft.h"
|
||||
#include "math.h"
|
||||
|
||||
unsigned int bitReverse(unsigned int x, int log2n)
|
||||
{
|
||||
int n = 0;
|
||||
for (int i = 0; i < log2n; i++)
|
||||
{
|
||||
n <<= 1;
|
||||
n |= (x & 1);
|
||||
x >>= 1;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void fft(std::complex<float> *samples, std::complex<float> *output, uint32_t N)
|
||||
{
|
||||
uint8_t log2n = (uint8_t)std::log2(N) + 0.5f;
|
||||
std::complex<float> I(0.0, 1.0);
|
||||
if (N == 1) {
|
||||
output[0] = samples[0];
|
||||
return;
|
||||
}
|
||||
|
||||
//shuffle array
|
||||
for (int i = 0; i < N; i++) {
|
||||
|
||||
output[i] = samples[bitReverse(i, log2n)];
|
||||
}
|
||||
for(int s = 1; s <= log2n; s++) {
|
||||
uint32_t m = 1 << s; // 2^s
|
||||
std::complex<float> wm = std::exp(-2.0f*(float)M_PI*I/(std::complex<float>)m);
|
||||
for (int k = 0; k < N; k += m) {
|
||||
std::complex<float> w = 1.f;
|
||||
for (int j = 0; j < m/2; j++) {
|
||||
std::complex<float> t = w * output[k+j+m/2];
|
||||
std::complex<float> u = output[k+j];
|
||||
output[k+j] = u+t;
|
||||
output[k+j+m/2] = u-t;
|
||||
w = w*wm;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rfft(std::complex<float> *input, std::complex<float> *output, uint32_t N){
|
||||
std::complex<float> I(0.0, 1.0);
|
||||
for(int i = 0; i< N/2; i++){
|
||||
input[i] = input[i] + I * input[i + N/2];
|
||||
}
|
||||
|
||||
fft(input, output, N/2);
|
||||
|
||||
for(int i = 0; i < N/2; i++){
|
||||
output[i] = (output[i] + std::conj(output[(N/2) - i]))/2.f;
|
||||
}
|
||||
|
||||
for(int i = N/2; i < N; i++) {
|
||||
output[i] = -I * (output[i] - std::conj(output[(N/2) - i]))/2.f;
|
||||
}
|
||||
|
||||
}
|
93
firmware/src/main.cpp
Normal file
93
firmware/src/main.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
#include "app.h"
|
||||
#include "driver/adc.h"
|
||||
#include "zauberstab.h"
|
||||