39浏览
查看: 39|回复: 2

[项目] 【Arduino 动手做】矩阵 16X16 图形音频频谱分析仪

[复制链接]
具有大量设置和功能的 Graph Spectrum Analyzer

光谱输出开启:
显示器 1602
4 个块的矩阵 8x8 (MAX7219)
WS2812 可寻址矩阵
亮度调整
色域调整(适用于 WS2812)
调整增益和杂色减少
调整动画平滑度
音量设置:
固定
来自电位计
自动
最高积分
开 关
挂起时间
坠落速度
按频率手动采样

算法
频谱分析,在输出端,我们有一个光谱波段值数组(128 个波段)
按每个波段的较低值(128 个波段)进行筛选
从 128 个通道过渡到 16 个通道,同时保持基于线性的带间值
求最大值以校正矩阵上条形的高度
将带材的净“重量”转换为模具的高度
将条带发送到矩阵
计算最高积分的位置并将其发送到 mtaritsa
顺便过滤较高的峰,从体积中校正柱的高度,等等

【Arduino 动手做】矩阵 16X16 图形音频频谱分析仪图2

【Arduino 动手做】矩阵 16X16 图形音频频谱分析仪图1

【Arduino 动手做】矩阵 16X16 图形音频频谱分析仪图3

【Arduino 动手做】矩阵 16X16 图形音频频谱分析仪图4

【Arduino 动手做】矩阵 16X16 图形音频频谱分析仪图5

【Arduino 动手做】矩阵 16X16 图形音频频谱分析仪图6

【Arduino 动手做】矩阵 16X16 图形音频频谱分析仪图7

【Arduino 动手做】矩阵 16X16 图形音频频谱分析仪图8

驴友花雕  中级技神
 楼主|

发表于 前天 07:34

【Arduino 动手做】矩阵 16X16 图形音频频谱分析仪

项目代码

  1. // -------------------------------- НАСТРОЙКИ --------------------------------
  2. // матрица
  3. #define WIDTH 16          // ширина матрицы (число диодов)
  4. #define HEIGHT 16         // высота матрицы (число диодов)
  5. #define BRIGHTNESS 240     // яркость (0 - 255)
  6. // цвета высоты полос спектра. Длины полос задаются примерно в строке 95
  7. #define COLOR1 CRGB::Green
  8. #define COLOR2 CRGB::Yellow
  9. #define COLOR3 CRGB::Orange
  10. #define COLOR4 CRGB::Red
  11. // сигнал
  12. #define INPUT_GAIN 1.5    // коэффициент усиления входного сигнала
  13. #define LOW_PASS 30       // нижний порог чувствительности шумов (нет скачков при отсутствии звука)
  14. #define MAX_COEF 1.1      // коэффициент, который делает "максимальные" пики чуть меньше максимума, для более приятного восприятия
  15. #define NORMALIZE 0       // нормализовать пики (столбики низких и высоких частот будут одинаковой длины при одинаковой громкости) (1 вкл, 0 выкл)
  16. // анимация
  17. #define SMOOTH 0.3        // плавность движения столбиков (0 - 1)
  18. #define DELAY 4           // задержка между обновлениями матрицы (периодичность основного цикла), миллиисекунды
  19. // громкость
  20. #define DEF_GAIN 50       // максимальный порог по умолчанию (при MANUAL_GAIN или AUTO_GAIN игнорируется)
  21. #define MANUAL_GAIN 0     // ручная настройка потенциометром на громкость (1 вкл, 0 выкл)
  22. #define AUTO_GAIN 1       // автонастройка по громкости (экспериментальная функция) (1 вкл, 0 выкл)
  23. // точки максимума
  24. #define MAX_DOTS 1        // включить/выключить отрисовку точек максимума (1 вкл, 0 выкл)
  25. #define MAX_COLOR CRGB::Red // цвет точек максимума
  26. #define FALL_DELAY 50     // скорость падения точек максимума (задержка, миллисекунды)
  27. #define FALL_PAUSE 700    // пауза перед падением точек максимума, миллисекунды
  28. // массив тонов, расположены примерно по параболе. От 80 Гц до 16 кГц
  29. byte posOffset[17] = {2, 3, 4, 6, 8, 10, 12, 14, 16, 20, 25, 30, 35, 60, 80, 100, 120};
  30. // -------------------------------- НАСТРОЙКИ --------------------------------
  31. // ---------------------- ПИНЫ ----------------------
  32. // для увеличения точности уменьшаем опорное напряжение,
  33. // выставив EXTERNAL и подключив Aref к выходу 3.3V на плате через делитель
  34. // GND ---[10-20 кОм] --- REF --- [10 кОм] --- 3V3
  35. #define AUDIO_IN 0         // пин, куда подключен звук
  36. #define DIN_PIN 6         // пин Din ленты (через резистор!)
  37. #define POT_PIN 7         // пин потенциометра настройки (если нужен MANUAL_GAIN)
  38. // ---------------------- ПИНЫ ----------------------
  39. // --------------- ДЛЯ РАЗРАБОТЧИКОВ ---------------
  40. #define NUM_LEDS WIDTH * HEIGHT
  41. #define FHT_N 256         // ширина спектра х2
  42. #define LOG_OUT 1
  43. #include <FHT.h>           // преобразование Хартли
  44. #include <FastLED.h>
  45. CRGB leds[NUM_LEDS];
  46. #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
  47. #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
  48. int gain = DEF_GAIN;   // усиление по умолчанию
  49. unsigned long gainTimer, fallTimer;
  50. byte maxValue;
  51. float k = 0.05, maxValue_f = 0.0;
  52. int maxLevel[16];
  53. byte posLevel_old[16];
  54. unsigned long timeLevel[16], mainDelay;
  55. boolean fallFlag;
  56. // --------------- ДЛЯ РАЗРАБОТЧИКОВ ---------------
  57. void setup() {
  58.   // поднимаем частоту опроса аналогового порта до 38.4 кГц, по теореме
  59.   // Котельникова (Найквиста) максимальная частота дискретизации будет 19 кГц
  60.   // http://yaab-arduino.blogspot.ru/2015/02/fast-sampling-from-analog-input.html
  61.   sbi(ADCSRA, ADPS2);
  62.   cbi(ADCSRA, ADPS1);
  63.   sbi(ADCSRA, ADPS0);
  64.   analogReference(EXTERNAL);
  65.   Serial.begin(9600);
  66.   FastLED.setBrightness(BRIGHTNESS);
  67.   FastLED.addLeds<WS2812B, DIN_PIN, GRB>(leds, NUM_LEDS);
  68. }
  69. void loop() {
  70.   if (millis() - mainDelay > DELAY) {     // итерация главного цикла
  71.     mainDelay = millis();
  72.     analyzeAudio();   // функция FHT, забивает массив fht_log_out[] величинами по спектру
  73.     for (int i = 0; i < 128; i ++) {
  74.       // вот здесь сразу фильтруем весь спектр по минимальному LOW_PASS
  75.       if (fht_log_out[i] < LOW_PASS) fht_log_out[i] = 0;
  76.       // усиляем сигнал
  77.       fht_log_out[i] = (float)fht_log_out[i] * INPUT_GAIN;
  78.       // уменьшаем громкость высоких частот (пропорционально частоте) если включено
  79.       if (NORMALIZE) fht_log_out[i] = (float)fht_log_out[i] / ((float)1 + (float)i / 128);
  80.     }
  81.     maxValue = 0;
  82.     FastLED.clear();  // очистить матрицу
  83.     for (byte pos = 0; pos < WIDTH; pos++) {    // для кажого столбца матрицы
  84.       int posLevel = fht_log_out[posOffset[pos]];
  85.       byte linesBetween;
  86.       if (pos > 0 && pos < WIDTH) {
  87.         linesBetween = posOffset[pos] - posOffset[pos - 1];
  88.         for (byte i = 0; i < linesBetween; i++) {  // от предыдущей полосы до текущей
  89.           posLevel += (float) ((float)i / linesBetween) * fht_log_out[posOffset[pos] - linesBetween + i];
  90.         }
  91.         linesBetween = posOffset[pos + 1] - posOffset[pos];
  92.         for (byte i = 0; i < linesBetween; i++) {  // от предыдущей полосы до текущей
  93.           posLevel += (float) ((float)i / linesBetween) * fht_log_out[posOffset[pos] + linesBetween - i];
  94.         }
  95.       }
  96.       // найти максимум из пачки тонов
  97.       if (posLevel > maxValue) maxValue = posLevel;
  98.       // фильтрация длины столбиков, для их плавного движения
  99.       posLevel = posLevel * SMOOTH + posLevel_old[pos] * (1 - SMOOTH);
  100.       posLevel_old[pos] = posLevel;
  101.       // преобразовать значение величины спектра в диапазон 0..HEIGHT с учётом настроек
  102.       posLevel = map(posLevel, LOW_PASS, gain, 0, HEIGHT);
  103.       posLevel = constrain(posLevel, 0, HEIGHT);
  104.       if (posLevel > 0) {
  105.         for (int j = 0; j < posLevel; j++) {                 // столбцы
  106.           uint32_t color;
  107.           if (j < 5) color = COLOR1;
  108.           else if (j < 10) color = COLOR2;
  109.           else if (j < 13) color = COLOR3;
  110.           else if (j < 15) color = COLOR4;
  111.           if (pos % 2 != 0)                                 // если чётная строка
  112.             leds[pos * WIDTH + j] = color;                  // заливаем в прямом порядке
  113.           else                                              // если нечётная
  114.             leds[pos * WIDTH + WIDTH - j - 1] = color;      // заливаем в обратном порядке
  115.         }
  116.       }
  117.       if (posLevel > 0 && posLevel > maxLevel[pos]) {    // если для этой полосы есть максимум, который больше предыдущего
  118.         maxLevel[pos] = posLevel;                        // запомнить его
  119.         timeLevel[pos] = millis();                       // запомнить время
  120.       }
  121.       // если точка максимума выше нуля (или равна ему) - включить пиксель
  122.       if (maxLevel[pos] >= 0 && MAX_DOTS) {
  123.         if (pos % 2 != 0)                                 // если чётная строка
  124.           leds[pos * WIDTH + maxLevel[pos]] = MAX_COLOR;                  // заливаем в прямом порядке
  125.         else                                              // если нечётная
  126.           leds[pos * WIDTH + WIDTH - maxLevel[pos] - 1] = MAX_COLOR;      // заливаем в обратном порядке
  127.       }
  128.       if (fallFlag) {                                           // если падаем на шаг
  129.         if ((long)millis() - timeLevel[pos] > FALL_PAUSE) {     // если максимум держался на своей высоте дольше FALL_PAUSE
  130.           if (maxLevel[pos] >= 0) maxLevel[pos]--;              // уменьшить высоту точки на 1
  131.           // внимание! Принимает минимальное значение -1 !
  132.         }
  133.       }
  134.     }
  135.     FastLED.show();  // отправить на матрицу
  136.     fallFlag = 0;                                 // сбросить флаг падения
  137.     if (millis() - fallTimer > FALL_DELAY) {      // если настало время следующего падения
  138.       fallFlag = 1;                               // поднять флаг
  139.       fallTimer = millis();
  140.     }
  141.     // если разрешена ручная настройка уровня громкости
  142.     if (MANUAL_GAIN) gain = map(analogRead(POT_PIN), 0, 1023, 0, 150);
  143.     // если разрешена авто настройка уровня громкости
  144.     if (AUTO_GAIN) {
  145.       if (millis() - gainTimer > 10) {      // каждые 10 мс
  146.         maxValue_f = maxValue * k + maxValue_f * (1 - k);
  147.         // если максимальное значение больше порога, взять его как максимум для отображения
  148.         if (maxValue_f > LOW_PASS) gain = (float) MAX_COEF * maxValue_f;
  149.         // если нет, то взять порог побольше, чтобы шумы вообще не проходили
  150.         else gain = 100;
  151.         gainTimer = millis();
  152.       }
  153.     }
  154.   }
  155. }
  156. void analyzeAudio() {
  157.   for (int i = 0 ; i < FHT_N ; i++) {
  158.     int sample = analogRead(AUDIO_IN);
  159.     fht_input[i] = sample; // put real data into bins
  160.   }
  161.   fht_window();  // window the data for better frequency response
  162.   fht_reorder(); // reorder the data before doing the fht
  163.   fht_run();     // process the data in the fht
  164.   fht_mag_log(); // take the output of the fht
  165. }
  166. /*
  167.    Алгоритм работы:
  168.    Анализ спектра, на выходе имеем массив величин полос спектра (128 полос)
  169.    Фильтрация по нижним значениям для каждой полосы (128 полос)
  170.    Переход от 128 полос к 16 полосам с сохранением межполосных значений по линейной зависимости
  171.    Поиск максимумов для коррекции высоты столбиков на матрице
  172.    Перевод чистого "веса" полосы к высоте матрицы
  173.    Отправка полос на матрицу
  174.    Расчёт позиций точек максимума и отправка их на мтарицу
  175.    Мимоходом фильтрация верхних пиков, коррекция высоты столбиков от громкости и прочее
  176. */
复制代码


回复

使用道具 举报

驴友花雕  中级技神
 楼主|

发表于 前天 07:38

【Arduino 动手做】矩阵 16X16 图形音频频谱分析仪

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4 备案 沪公网安备31011502402448

© 2013-2025 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail