Skip to content Skip to main navigation Skip to footer

Матрица-анализатор громкости

Задача


  • Сделать реакцию ленты на звук как в вирусном видео (см. внизу страницы)
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();
  }
}

Видео


Полезный пример?

Похожие примеры
Подписаться
Уведомить о
21 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии