Матрица-анализатор громкости
Содержание
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(); } }