Матрица-анализатор громкости
Содержание
ToggleЗадача
- Сделать реакцию ленты на звук как в вирусном видео (см. внизу страницы)
21.11.2021 — исправил схему, добавил комментариев в код
Базовые уроки
Подключение
- Лента подключается к внешнему питанию и пину D2
- В данной схеме Arduino питается от внешнего источника, подключение к USB необязательно
- Микрофон к питанию и аналоговому пину (А0)
- Пин Gain микрофона задаёт усиление звука (значения также написаны на модуле):
- Никуда не подключен (float) — 60dB (максимум)
- GND — 50dB
- VCC — 40dB
Библиотеки
Программа
Примечание: можно настроить нижний порог шумов, добавив строчку sound.setTrsh(величина) в блок setup(). По умолчанию порог задан 40, его можно увеличить, чтобы снизить чувствительность системы к шумам.
Версия с шумом
// вывод гармонического шума, наложенного на громкость
#define STRIP_PIN 2 // пин ленты
#define SOUND_PIN A0 // пин звука
#define SHOW_MAX 1 // 1/0 - показывать плавающие точки максимума
#define COLOR_MULT -15 // шаг изменения цветовой палитры столбика
#define COLOR_STEP 3 // шаг движения палитры столбика (по времени)
#define COLON_SIZE 50 // высота матрицы
#define COLON_AMOUNT 12 // ширина матрицы
#define COLOR_DEBTH 3
#include <microLED.h>
microLED < COLON_SIZE * COLON_AMOUNT, STRIP_PIN, -1, LED_WS2812, ORDER_GRB, CLI_AVER > strip;
#include <FastLED.h>
#include "VolAnalyzer.h"
VolAnalyzer sound(SOUND_PIN);
byte volume[COLON_AMOUNT];
int maxs[COLON_AMOUNT];
byte colorCount = 0;
int16_t noise;
void setup() {
strip.setBrightness(200); // яркость ленты
sound.setVolMax(100);
sound.setVolK(30);
}
void loop() {
if (sound.tick()) { // если анализ звука завершён (~10мс)
strip.clear(); // чистим ленту
for (int i = 0; i < COLON_AMOUNT; i++) {
// домножаем шум на громкость
// также я домножил на 2, чтобы шум был более амплитудным
int val = inoise8(noise - i * 100) * 2 * sound.getVol() / 100;
// ограничиваем и масштабируем до половины высоты столбика
val = constrain(val, 0, 255);
val = map(val, 0, 255, 0, COLON_SIZE / 2);
volume[i] = val;
}
noise += 90; // двигаем шум (скорость бокового движения картинки)
colorCount += COLOR_STEP; // двигаем цвет
// двигаем точки максимумов
for (int i = 0; i < COLON_AMOUNT; i++) {
if (maxs[i] < volume[i] * 10) maxs[i] = (volume[i] - 1) * 10;
else maxs[i] -= 2;
if (maxs[i] < 0) maxs[i] = 0;
}
// выводим
for (int col = 0; col < COLON_AMOUNT; col++) { // по столбикам
for (int i = 0; i < volume[col]; i++) { // для текущей громкости
mData color = mWheel8(colorCount + i * COLOR_MULT);
strip.leds[COLON_SIZE * col + COLON_SIZE / 2 + i] = color; // вверх от центра
strip.leds[COLON_SIZE * col + COLON_SIZE / 2 - 1 - i] = color; // вниз от центра
}
// отображаем точки максимумов, если включены и больше 0
if (SHOW_MAX && maxs[col] > 0) {
strip.leds[COLON_SIZE * col + COLON_SIZE / 2 + maxs[col] / 10] = mGreen; // вверх от центра
strip.leds[COLON_SIZE * col + COLON_SIZE / 2 - 1 - maxs[col] / 10] = mGreen; // вниз от центра
}
}
// обновляем ленту
strip.show();
}
}
Версия с формой волны
// матрица. Вывод формы волны
#define STRIP_PIN 2 // пин ленты
#define SOUND_PIN A0 // пин звука
#define COLOR_MULT -15 // шаг изменения цветовой палитры столбика
#define COLOR_STEP 3 // шаг движения палитры столбика (по времени)
#define COLON_SIZE 50 // высота матрицы
#define COLON_AMOUNT 12 // ширина матрицы
#define COLOR_DEBTH 3 // глубина цвета (понизь, если не хватает памяти)
#include <microLED.h>
microLED < COLON_SIZE * COLON_AMOUNT, STRIP_PIN, -1, LED_WS2812, ORDER_GRB, CLI_AVER > strip;
#include "VolAnalyzer.h"
VolAnalyzer sound(SOUND_PIN);
byte volume[COLON_AMOUNT];
byte colorCount = 0;
void setup() {
strip.setBrightness(100); // яркость ленты
sound.setVolMax(COLON_SIZE / 2 + 1);
}
void loop() {
if (sound.tick()) { // если анализ звука завершён (~10мс)
strip.clear(); // чистим ленту
// перематываем массив громкости вправо
for (int i = COLON_AMOUNT - 1; i > 0; i--) volume[i] = volume[i - 1];
volume[0] = sound.getVol(); // новое значение громкости на освободившееся место
colorCount += COLOR_STEP; // двигаем цвет
for (int col = 0; col < COLON_AMOUNT; col++) { // для каждого столбика
for (int i = 0; i < volume[col]; i++) { // для его текущей громкости
mData color = mWheel8(colorCount + i * COLOR_MULT); // цвет со смещением по радуге
strip.leds[COLON_SIZE * col + COLON_SIZE / 2 + i] = color; // вверх от центра
strip.leds[COLON_SIZE * col + COLON_SIZE / 2 - 1 - i] = color; // вниз от центра
}
}
// выводим
strip.show();
}
}
Анализ спектра
У меня данный скетч перестал работать по непонятным причинам =)
#define STRIP_PIN 2 // пин ленты
#define SOUND_PIN A0 // пин звука
#define FREQ_NOISE 35
#define FREQ_DIV 20
#define COLOR_MULT -15
#define COLOR_STEP 2
#define COLON_SIZE 16
#define COLON_AMOUNT 16
#define COLOR_DEBTH 3
#include <microLED.h>
microLED < COLON_SIZE * COLON_AMOUNT, STRIP_PIN, -1, LED_WS2812, ORDER_GRB, CLI_AVER > strip;
#define LOG_OUT 1
#define FHT_N 128
#include <FHT.h>
int volume[COLON_AMOUNT];
int maxs[COLON_AMOUNT];
byte colorCount = 0;
void setup() {
analogReference(EXTERNAL);
strip.setBrightness(100);
ADCSRA &= bit(7);
ADCSRA |= 0b110; //110 8 кгц, 101 19 кгц
}
void loop() {
static uint32_t tmr;
if (millis() - tmr >= 20) {
tmr = millis();
for (int i = 0; i < FHT_N; i++) {
fht_input[i] = analogRead(SOUND_PIN);
//delayMicroseconds(50);
}
fht_window();
fht_reorder();
fht_run();
fht_mag_log();
int maxv = 0;
static int maxvf;
int vals[COLON_AMOUNT];
int wholes = (FHT_N / 2 - 2) / COLON_AMOUNT;
for (int i = 0; i < FHT_N / 2; i++) {
if (fht_log_out[i] < FREQ_NOISE) fht_log_out[i] = 0;
else fht_log_out[i] -= FREQ_NOISE;
fht_log_out[i] += (long)fht_log_out[i] * i / FREQ_DIV;
}
for (int col = 0; col < COLON_AMOUNT; col++) {
vals[col] = 0;
for (int i = 0; i < wholes; i++) {
int val = fht_log_out[col * wholes + i + 2];
vals[col] += val;
}
vals[col] *= 100;
if (maxv < vals[col]) maxv = vals[col];
}
if (maxvf < maxv) maxvf += (maxv - maxvf) / 20;
else maxvf += (maxv - maxvf) / 200;
for (int i = 0; i < COLON_AMOUNT; i++) {
vals[i] = constrain(vals[i], 0, maxvf);
if (volume[i] < vals[i]) volume[i] = vals[i];
else volume[i] += (vals[i] - volume[i]) / 10;
vals[i] = map(volume[i], 0, maxvf, 0, COLON_AMOUNT / 2);
}
strip.clear();
colorCount += COLOR_STEP;
/*for (int i = 0; i < COLON_AMOUNT; i++) {
if (maxs[i] < vals[i] * 10) maxs[i] = vals[i] * 10;
else maxs[i] -= 3;
if (maxs[i] < 0) maxs[i] = 0;
}*/
for (int col = 0; col < COLON_AMOUNT; col++) {
for (int i = 0; i < vals[col]; i++) {
mData color = mWheel8(colorCount + i * COLOR_MULT);
//mData color = mGreen;
strip.leds[COLON_SIZE * col + COLON_SIZE / 2 + i] = color;
strip.leds[COLON_SIZE * col + COLON_SIZE / 2 - 1 - i] = color;
}
/*if (maxs[col] > 0) {
strip.leds[COLON_SIZE * col + COLON_SIZE / 2 + maxs[col] / 10] = mRed;
strip.leds[COLON_SIZE * col + COLON_SIZE / 2 - 1 - maxs[col] / 10] = mRed;
}*/
}
strip.show();
}
}
