Skip to content

Commit bc99df8

Browse files
committed
DRAFT: LEDs
1 parent 1ff1a78 commit bc99df8

File tree

4 files changed

+392
-0
lines changed

4 files changed

+392
-0
lines changed

src/Experiments/LED.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
namespace audio_tools {
2+
3+
/**
4+
* Abstract LED api for updating an LED matrix. We provide an implementation for updating
5+
* the Arduino UNO R4 LED matrix and for FastLED
6+
*/
7+
8+
struct LEDFunctions {
9+
virtual void clear();
10+
virtual void show();
11+
};
12+
13+
struct LED {
14+
void setColor(Color* color){
15+
};
16+
17+
struct Color {};
18+
19+
}

src/Experiments/LEDFastLED.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#include <FastLED.h>
2+
#include "LED.h"
3+
4+
/**
5+
* LED API for the FastLED which is used by the LEDOutput
6+
*/
7+
8+
namespace audio_tools {
9+
10+
struct FunctionsFastLED : public LEDFunctions {
11+
void clear() override { FastLED.clear(); }
12+
void show() override { FastLED.show(); }
13+
};
14+
15+
struct ColorFastLED : public Color {
16+
ColorFastLED(CHSV color) { this->color = color; }
17+
CHSV color;
18+
};
19+
20+
class LEDFastLED : public LED {
21+
void setColor(Color* color) { crgb = *(ColorFastLED*)color; }
22+
23+
protected:
24+
CRGB crgb;
25+
};
26+
27+
/// @brief Default logic to update the color for the indicated x,y position
28+
CHSV getDefaultColor(int x, int y, int magnitude) {
29+
int color = map(magnitude, 0, 7, 255, 0);
30+
return CHSV(color, 255, 100); // blue CHSV(160, 255, 255
31+
}
32+
33+
34+
} // namespace audio_tools

src/Experiments/LEDOutput.h

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
#pragma once
2+
#include "AudioLibs/AudioFFT.h"
3+
#include <FastLED.h>
4+
5+
namespace audio_tools {
6+
class LEDOutput;
7+
struct LEDOutputConfig;
8+
9+
LEDOutput *selfLEDOutput = nullptr;
10+
// default callback function which implements led update
11+
void fftLEDOutput(LEDOutputConfig *cfg, LEDOutput *matrix);
12+
// led update for volume
13+
void volumeLEDOutput(LEDOutputConfig *cfg, LEDOutput *matrix);
14+
// default color
15+
//CHSV getDefaultColor(int x, int y, int magnitude);
16+
// fft mutex
17+
Mutex fft_mux;
18+
19+
/**
20+
* LED Matrix Configuration. Provide the number of leds in x and y direction and
21+
* the data pin.
22+
* @author Phil Schatzmann
23+
*/
24+
struct LEDOutputConfig {
25+
/// Number of leds in x direction
26+
int x = 0;
27+
/// Number of leds in y direction
28+
int y = 1;
29+
/// optinal custom logic to provide CHSV color: Prividing a 'rainbow' color
30+
/// with hue 0-255, saturating 0-255, and brightness (value) 0-255 (v2)
31+
Color& (*color_callback)(int x, int y, int magnitude);// = getDefaultColor;
32+
/// Custom callback logic to update the LEDs - by default we use
33+
/// fftLEDOutput()
34+
void (*update_callback)(LEDOutputConfig *cfg, LEDOutput *matrix) = nullptr;
35+
/// Update the leds only ever nth call
36+
int update_frequency = 1; // update every call
37+
bool is_serpentine_layout = true;
38+
bool is_matrix_vertical = true;
39+
/// start bin which is displayed
40+
int fft_start_bin = 0;
41+
/// group result by adding subsequent bins
42+
int fft_group_bin = 1;
43+
/// Influences the senitivity
44+
int fft_max_magnitude = 700;
45+
};
46+
47+
/**
48+
* @brief LEDOutput using the FastLED library. You write the data to the FFT Stream.
49+
* This displays the result of the FFT to a LED matrix.
50+
* @ingroup io
51+
* @author Phil Schatzmann
52+
*/
53+
template <class LED, class Color, LEDFunctions>
54+
class LEDOutput {
55+
public:
56+
LEDOutput() = default;
57+
58+
/// @brief Default Constructor
59+
/// @param fft
60+
LEDOutput(AudioFFTBase &fft) {
61+
selfLEDOutput = this;
62+
p_fft = &fft;
63+
cfg.update_callback = fftLEDOutput;
64+
}
65+
66+
LEDOutput(VolumeOutput &vol) {
67+
selfLEDOutput = this;
68+
p_vol = &vol;
69+
cfg.update_callback = volumeLEDOutput;
70+
}
71+
72+
/// Provides the default config object
73+
LEDOutputConfig defaultConfig() { return cfg; }
74+
75+
/// Setup Led matrix
76+
bool begin(LEDOutputConfig config) {
77+
cfg = config;
78+
// if (!*p_fft) {
79+
// LOGE("fft not started");
80+
// return false;
81+
// }
82+
if (ledCount() == 0) {
83+
LOGE("x or y == 0");
84+
return false;
85+
}
86+
87+
// allocate leds
88+
leds.resize(ledCount());
89+
for (int j = 0; j < ledCount(); j++) {
90+
led(j) = CRGB::Black;
91+
}
92+
93+
// clear LED
94+
FastLED.clear(); // clear all pixel data
95+
96+
if (p_fft != nullptr) {
97+
// assign fft callback
98+
AudioFFTConfig &fft_cfg = p_fft->config();
99+
fft_cfg.callback = fftCallback;
100+
101+
// number of bins
102+
magnitudes.resize(p_fft->size());
103+
for (int j = 0; j < p_fft->size(); j++) {
104+
magnitudes[j] = 0;
105+
}
106+
}
107+
108+
return true;
109+
}
110+
111+
/// Provides the number of LEDs: call begin() first!
112+
int ledCount() {
113+
int num_leds = cfg.x * cfg.y;
114+
return num_leds;
115+
}
116+
117+
/// Provides the address fo the CRGB array: call begin() first!
118+
LED *ledData() {
119+
if (ledCount() == 0) {
120+
LOGE("x or y == 0");
121+
return nullptr;
122+
}
123+
// leds.resize(ledCount());
124+
return leds.data();
125+
}
126+
127+
/// Updates the display: call this method in your loop
128+
virtual void update() {
129+
if (cfg.update_callback != nullptr && count++ % cfg.update_frequency == 0) {
130+
// use custom update logic defined in config
131+
cfg.update_callback(&cfg, this);
132+
}
133+
}
134+
135+
/// Determine the led with the help of the x and y pos
136+
LED &ledXY(uint8_t x, uint8_t y) {
137+
if (x > cfg.x)
138+
x = cfg.x - 1;
139+
if (x < 0)
140+
x = 0;
141+
if (y > cfg.y)
142+
y = cfg.y - 1;
143+
if (y < 0)
144+
y = 0;
145+
int index = xy(x, y);
146+
return leds[index];
147+
}
148+
149+
/// Determine the led with the help of the index pos
150+
LED &led(uint8_t index) {
151+
if (index > cfg.x * cfg.y)
152+
return not_valid;
153+
return leds[index];
154+
}
155+
156+
/// Returns the magnitude for the indicated led x position. We might
157+
/// need to combine values from the magnitudes array if this is much bigger.
158+
virtual float getMagnitude(int x) {
159+
// get magnitude from fft
160+
float total = 0;
161+
for (int j = 0; j < cfg.fft_group_bin; j++) {
162+
int idx = cfg.fft_start_bin + (x * cfg.fft_group_bin) + j;
163+
if (idx >= magnitudes.size()) {
164+
idx = magnitudes.size() - 1;
165+
}
166+
total += magnitudes[idx];
167+
}
168+
return total / cfg.fft_group_bin;
169+
}
170+
171+
/// @brief Provodes the max magnitude
172+
virtual float getMaxMagnitude() {
173+
// get magnitude from
174+
if (p_vol != nullptr) {
175+
return p_vol->volume();
176+
}
177+
float max = 0;
178+
for (int j = 0; j < cfg.x; j++) {
179+
float value = getMagnitude(j);
180+
if (value > max) {
181+
max = value;
182+
}
183+
}
184+
return max;
185+
}
186+
187+
/// Update the indicated column with the indicated bar
188+
void updateColumn(int x, int currY) {
189+
// update vertical bar
190+
for (uint8_t y = 0; y < currY; y++) {
191+
// determine color
192+
CHSV color = cfg.color_callback(x, y, currY);
193+
// update LED
194+
ledXY(x, y) = color;
195+
}
196+
for (uint8_t y = currY; y < cfg.y; y++) {
197+
ledXY(x, y) = CRGB::Black;
198+
}
199+
}
200+
201+
/// Update the last column with the indicated bar
202+
void updateColumn(int currY) { updateColumn(cfg.x - 1, currY); }
203+
204+
/// Adds an empty column to the end shifting the content to the left
205+
void addEmptyColumn() {
206+
for (int x = 1; x < cfg.x; x++) {
207+
for (int y = 0; y < cfg.y; y++) {
208+
ledXY(x - 1, y) = ledXY(x, y);
209+
}
210+
}
211+
for (int y = 0; y < cfg.y; y++) {
212+
ledXY(cfg.x - 1, y) = CRGB::Black;
213+
}
214+
}
215+
216+
/// Provides access to the actual config object. E.g. to change the update
217+
/// logic
218+
LEDOutputConfig &config() { return cfg; }
219+
220+
protected:
221+
friend class AudioFFTBase;
222+
LEDFunctions functions;
223+
LED not_valid;
224+
Vector<LED> leds{0};
225+
Vector<float> magnitudes{0};
226+
LEDOutputConfig cfg;
227+
AudioFFTBase *p_fft = nullptr;
228+
VolumeOutput *p_vol = nullptr;
229+
uint64_t count = 0;
230+
231+
uint16_t xy(uint8_t x, uint8_t y) {
232+
uint16_t i;
233+
234+
if (cfg.is_serpentine_layout == false) {
235+
if (cfg.is_matrix_vertical == false) {
236+
i = (y * cfg.x) + x;
237+
} else {
238+
i = cfg.y * (cfg.x - (x + 1)) + y;
239+
}
240+
}
241+
242+
if (cfg.is_serpentine_layout == true) {
243+
if (cfg.is_matrix_vertical == false) {
244+
if (y & 0x01) {
245+
// Odd rows run backwards
246+
uint8_t reverseX = (cfg.x - 1) - x;
247+
i = (y * cfg.x) + reverseX;
248+
} else {
249+
// Even rows run forwards
250+
i = (y * cfg.x) + x;
251+
}
252+
} else { // vertical positioning
253+
if (x & 0x01) {
254+
i = cfg.y * (cfg.x - (x + 1)) + y;
255+
} else {
256+
i = cfg.y * (cfg.x - x) - (y + 1);
257+
}
258+
}
259+
}
260+
261+
return i;
262+
}
263+
264+
/// callback method which provides updated data from fft
265+
static void fftCallback(AudioFFTBase &fft) {
266+
// just save magnitudes to be displayed
267+
LockGuard guard(fft_mux);
268+
for (int j = 0; j < fft.size(); j++) {
269+
float value = fft.magnitude(j);
270+
selfLEDOutput->magnitudes[j] = value;
271+
}
272+
};
273+
};
274+
275+
/// Default update implementation which provides the fft result as "barchart"
276+
void fftLEDOutput(LEDOutputConfig *cfg, LEDOutput *matrix) {
277+
{
278+
LockGuard guard(fft_mux);
279+
// process horizontal
280+
for (int x = 0; x < cfg->x; x++) {
281+
// max y determined by magnitude
282+
int currY = mapFloat(matrix->getMagnitude(x), 0, cfg->fft_max_magnitude,
283+
0.0f, static_cast<float>(cfg->y));
284+
LOGD("x: %d, y: %d", x, currY);
285+
matrix->updateColumn(x, currY);
286+
}
287+
}
288+
functions.show();
289+
}
290+
291+
/// Default update implementation which provides the fft result as "barchart"
292+
void volumeLEDOutput(LEDOutputConfig *cfg, LEDOutput *matrix) {
293+
float vol = matrix->getMaxMagnitude();
294+
int currY = mapFloat(matrix->getMagnitude(vol), 0, cfg->fft_max_magnitude,
295+
0.0f, static_cast<float>(cfg->y));
296+
matrix->addEmptyColumn();
297+
matrix->updateColumn(currY);
298+
functions.show();
299+
}
300+
301+
302+
} // namespace audio_tools

src/Experiments/LEDUnoR4.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#include "Arduino_LED_Matrix.h"
2+
#include "LED.h"
3+
4+
/**
5+
* Functionality to manage the Arduino UNO R4 LED matrix which is used by the LEDOutput
6+
*/
7+
8+
namespace audio_tools {
9+
10+
struct FunctionsUnoR4 : public LEDFunctions {
11+
void clear() override {
12+
for (int j = 0; j > 96; j++) {
13+
frame[j] = false;
14+
}
15+
show();
16+
}
17+
void show() override { matrix.renderBitmap(frame, 8, 12); }
18+
19+
protected:
20+
bool frame[96] = {0};
21+
ArduinoLEDMatrix matrix;
22+
};
23+
24+
struct ColorUnoR4 : public Color {
25+
ColorUnoR4(bool on) { this->on = on; }
26+
bool on = false;
27+
};
28+
29+
class LEDUnoR4 : public LED {
30+
void setColor(Color* color) { *value = *((ColorUnoR4*)on); }
31+
32+
protected:
33+
bool* value;
34+
;
35+
};
36+
37+
} // namespace audio_tools

0 commit comments

Comments
 (0)