Эквалайзер — прибор, который просят сделать чаще, чем спрашивают, зачем он: шестнадцать столбиков, прыгающих в такт музыке, на полке рядом с колонкой. Я собрал свой на панели из двух лент по метру, свёрнутых в матрицу шестнадцать на восемь, и первая версия встретила меня честным нулём: столбики стояли по колено независимо от того, орала ли колонка или в комнате стояла тишина. Причина сидела в первой строке кода — я рисовал высоту столбиков прямо из analogRead, не учитывая, что выход микрофонного модуля качается вокруг середины, а не растёт от нуля.
В этом уроке доводим эквалайзер до рабочего вида за четыре шага: снимаем отклонение от середины через abs(), сглаживаем его огибающей с асимметрией — вверх мгновенно, вниз неторопливо, — ставим на каждый столбик пик-держатель, который держит достижение и медленно сползает, и раскладываем всё на шестнадцать столбиков матрицы. Плюс честный разговор про микрофонные модули: MAX4466 с ручной крутилкой усиления против MAX9814 с автоматической регулировкой — у каждого своя натура, и для полки рядом с колонкой выбор не очевиден.
Сразу договоримся о слове «спектр». Столбики в этом уроке не разбирают звук по частотам — их высота зависит от общего уровня с наклоном влево, где живут басы. Это условность, и я покажу, где она ломается; настоящий FFT на Uno разобран по шагам в уроке про музыкальную подсветку, а здесь — самый дешёвый эквалайзер, который физически возможен: без библиотек, без буферов, на чистом analogRead и арифметике.
Микрофон: MAX4466 против MAX9814
Оба модуля — электретный капсюль плюс усилитель, оба встают на аналоговый вход, но ведут себя по-разному. MAX4466 — открытая схема с подстроечным резистором: усиление до 125 раз крутится отвёрткой, и выход качается вокруг половины питания — при 5 В это 2,5 В, что после АЦП и есть 512. Крутилка — и сила, и обуза: выставил под комнату — и модуль честно повторяет громкость, от шёпота до крика, но переместил прибор в другую комнату — перенастраивай. Как настраивать по графику сигнала, показано в уроке про Serial Plotter — пять минут с волнами на экране экономят вечер наугад.
MAX9814 — та же база, но с автоматической регулировкой усиления: тихий звук дотягивается до видимого размаха, громкий — придавливается, чтобы не упереться в потолок. Потолок усиления выбирается пином GAIN — 40, 50 или 60 децибел, а скорость реакции задаётся отношением атака-спад: по умолчанию 1:4000, резистором меняется на 1:2000 или 1:500. Для эквалайзера-украшения это подарок: втыкается и работает в любой комнате. Но у автомата есть характер, о котором стоит знать: прибор перестаёт измерять громкость — он её нормирует, и тихая музыка на фоне выглядит так же бодро, как оркестр. Для «кто громче» нужен MAX4466, для «чтобы всегда плясало» — MAX9814.
Подключение у обоих одинаковое и скучное: три провода — питание, земля, выход на A0, — модуль ест единицы миллиампер и живёт от платы без разговоров. Два нюанса всё же есть. Первый: земля микрофона и земля ленты — одна и та же точка; через плавающую землю модуль ловит писк импульсных блоков питания, и панель начинает плясать сама по себе. Второй: провод выхода не тащите рядом с лентой — линия данных WS2812 глушит чувствительный вход, и усиление приходится выкручивать до предела.
Матрица шестнадцать на восемь — сто двадцать восемь пикселей, и раскладка у неё та же змейка, что мы подробно разбирали в уроке про матрицу 8x8: функция XY с разворотом нечётных рядов, только восьмёрка в формуле стала шестнадцатью. Кадр из 128 пикселей уезжает в ленту за 3,84 миллисекунды — арифметика кадров и бюджета платы посчитана в уроке про show и FPS, и тридцать кадров в секунду здесь остаются приятным потолком, а не обязанностью.
Огибающая: вверх мгновенно, вниз неторопливо
Ядро эквалайзера — три строки. Отклонение от середины: abs(analogRead(A0) - 512), умноженное на два, — размах сигнала в обе стороны. Огибающая env: если новое значение больше — берём его сразу (атака мгновенная), если меньше — текущее само оседает на четыре единицы за кадр. Спад намеренно медленнее атаки: столбик взлетает вместе с ударом бочки и опадает плавно, как у стрелочного индикатора старого магнитофона. Сделаете спад мгновенным — прибор начнёт мигать в ритме отдельных выбросов, и смотреть на это больно.
Пик-держатель — вторая ступень сглаживания, и работает он на каждый столбик отдельно: достигнутую высоту столбик помнит белой точкой, которая падает на одну строку каждые сто пятьдесят миллисекунд. Без него глаз не успевает зафиксировать, где столбик был на пике; с ним панель читается даже краем глаза, как настоящие студийные мониторы уровня. АЦП Uno отдаёт выборку каждые 112 микросекунд, так что за кадр в 25 миллисекунд на входе успевает смениться больше двух сотен значений — огибающая честно перепахивает весь кадр, хотя формально читается один раз.
Мёртвая зона — последняя деталь, без которой панель шумит в тишине: у любого модуля есть собственный шум в единицы-десяток единиц АЦП, и без порога столбики в два-три пикселя вечно подрагивают. Лечение — одна строка: всё, что ниже порога, считается нулём. Порог подбираю по тишине комнаты: включаю прибор, ничего не трогаю, смотрю на level в мониторе порта и беру верхнюю границу с полуторным запасом. В скетче ниже порог не выделен, чтобы не путать читателя, — на живой плате добавлю его первым делом после настройки усиления.
И оговорка, ради которой я столько места отдал микрофонам. Высота столбика здесь считается как общий уровень, умноженный на наклон: слева множитель полный, к правому краю падает до сорока процентов, — условные «басы» стоят выше. Это не спектр: гитарное соло и бочка дадут одинаковую форму, просто разной высоты. Для честного разложения по частотам нужен FFT — путь через музыкальную подсветку с arduinoFFT. Мой дешёвый вариант выигрывает другим: он понятен целиком, помещается в полсотни строк и не тормозит кадр ни на микросекунду сверх show().
#include <FastLED.h> #define LED_PIN 6#define WIDTH 16 // 16 столбиков#define HEIGHT 8 // 8 строк#define NUM_LEDS (WIDTH * HEIGHT) CRGB leds[NUM_LEDS];int env = 0; // огибающая уровня 0..255int peaks[WIDTH]; // строка пика каждого столбикаint frameNo = 0;unsigned long lastFrame = 0; int XY(int x, int y) { if (x >= WIDTH || y >= HEIGHT) return -1; if (y % 2 == 0) return y * WIDTH + x; // чётный ряд слева направо return y * WIDTH + (WIDTH - 1 - x); // нечётный — справа налево} void setup() { FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS); FastLED.setBrightness(140); FastLED.clear(); FastLED.show(); for (int c = 0; c < WIDTH; c++) { peaks[c] = 0; } Serial.begin(115200);} void loop() { if (millis() - lastFrame < 25) { return; } lastFrame = millis(); frameNo++; int raw = abs(analogRead(A0) - 512) * 2; // отклонение от середины 512 if (raw > 255) { raw = 255; } if (raw > env) { env = raw; } // атака: вверх мгновенно else if (env > 4) { env -= 4; } // спад: 4 за кадр, ~1,6 с на падение else { env = 0; } FastLED.clear(); for (int c = 0; c < WIDTH; c++) { int tilt = 255 - c * 10; // слева «басы» выше — условный спектр int h = (long)env * tilt / 255 / 31; // 0..8 строк if (h > HEIGHT) { h = HEIGHT; } for (int y = 0; y < h; y++) { int hue = 96; // зелёный низ if (y >= 4) { hue = 35; } // жёлтая середина if (y >= 6) { hue = 0; } // красный верх leds[XY(c, y)] = CHSV(hue, 200, 255); } if (h > peaks[c]) { peaks[c] = h; } // пик-держатель: вверх сразу else if (frameNo % 6 == 0 && peaks[c] > 0) { peaks[c]--; } // вниз медленно if (peaks[c] > 0) { leds[XY(c, peaks[c] - 1)] = CRGB(90, 90, 90); } } FastLED.show(); if (frameNo % 20 == 0) { // уровень в монитор раз в полсекунды Serial.print("level: "); Serial.println(env); }}Три места в скетче, где спотыкаются. Первое — умножение env на tilt взято в long: 255 × 255 на восьмибитной плате вылезает за int, и без приведения высота столбиков скачет по мусорным значениям. Второе — каждый столбик начинается с чистого буфера: FastLED.clear() в начале кадра, иначе после падения уровня остаются огарки, которые пик-держатель выдаёт за живой сигнал. Третье — цвет по строкам, а не по столбикам: зелёный низ, жёлтая середина, красный верх — знакомая шкала любого студийного индикатора, и переполнение «красной зоны» видно без чтения цифр.
Про честность ещё раз, потому что это главное решение урока: столбики показывают уровень, а не частоты, и наклон влево — подрисованная условность. В моей полке она работает: музыка с басами выглядит ожидаемо — слева пляшет, справа подрагивает. Она ломается на одном инструменте без басов: свистите в микрофон — и «басовые» столбики всё равно подпрыгнут, потому что общий уровень вырос. Если такое поведение смущает, честнее назвать прибор «VU-метром» и убрать наклон — одна строка, и все шестнадцать столбиков станут равноправными.
Эквалайзер в симуляторе
В симуляторе звук заменяет ручка, и это стоит проговорить прямо: настоящие модули отдают переменное напряжение вокруг середины, ручка держит постоянное — поэтому «прыгающего» звука без вашего участия здесь не будет. Поднимите ручку резко — увидите атаку: столбики взлетают за один кадр. Опустите — спад: панель оседает полторы секунды, и белые маркеры пиков сползают вслед. Среднее положение ручки — мид-скейл 512 — модель тишины: панель пуста, огибающая упёрлась в ноль. На живой плате всё то же самое делает голос, а форму сигнала удобно ловить на графике Serial Plotter.
Матрицу симулятор рисует строкой — это честно: буфер один, и на настоящей панели 16x8 каждая группа пикселей встанет столбиком по змейке. Номера в Serial идут по уровню огибающей — по ним видно, что панель живёт кадрами по 25 миллисекунд, между которыми плата свободна. Каркас тот же, что в millis вместо delay: если захотите тридцать кадров вместо сорока, растите шаг спада пропорционально, иначе панель начнёт таять медленнее, чем настроено.
Спад подстраивается под материал, который слушает панель: четыре единицы за кадр — компромисс для музыки с ударами. Под классику спад замедляйте до двух-трёх, чтобы столбики таяли с достоинством, под ритмичную электронику — наоборот, ускоряйте до шести: панель начинает дышать чаще и жёстче. И про размер: шестнадцать столбиков — не догма, на популярной матрице 32x8 тот же скетч растягивается на тридцать два столбика — ширина в XY и шаг наклона пересчитываются двумя константами, а бюджет кадра удваивается до восьми миллисекунд, что всё ещё далеко от потолка.
| Решение | Усиление | Когда брать |
|---|---|---|
| MAX4466 | Ручка до 125 раз, под комнату | Когда важно измерять громкость честно: тихое и громкое различимы |
| MAX9814 | AGC: 40/50/60 дБ, спад 1:4000/1:2000/1:500 | Когда нужно «чтобы всегда плясало»: втыкай и работай |
| Линейный вход с колонки | Без микрофона, по проводу | Когда шум комнаты мешает: сигнал чистый, но прибор привязан к проводам |
| Огибающая без FFT | Общий уровень с наклоном | Полсотни строк кода; честный спектр — отдельный урок про FFT |
- 1. Почему высота столбиков считается от abs(analogRead(A0) - 512)?
- 2. Атака и спад огибающей сделаны по-разному. Как именно?
- 3. Что делает пик-держатель на столбике?
- 4. Столбики слева выше правых. Что это значит?
- 5. Смена тихого альбома на громкий не меняет картинку с MAX9814. Почему?
- Выход микрофонного модуля качается вокруг 512: громкость — abs(analogRead - 512), а не само значение
- Огибающая асимметрична: атака мгновенная, спад 4 единицы за кадр — столбик взлетает и опадает плавно
- Пик-держатель: белый маркер держит максимум столбика и падает на строку каждые 150 мс
- Столбики = общий уровень с наклоном к «басам»; честный спектр по частотам — arduinoFFT из урока про подсветку
- MAX4466 — ручное усиление и честный уровень; MAX9814 — AGC 40/50/60 дБ и «чтобы всегда плясало»
Частые вопросы по теме урока
- Почему высота столбиков считается от abs(analogRead(A0) - 512)?
- В тишине на пине лежит половина питания — 512. Звук качает сигнал вверх и вниз, и громкость — размах, а не само значение. Выход микрофонного модуля качается вокруг середины: звук — отклонение от неё
- Атака и спад огибающей сделаны по-разному. Как именно?
- Столбик взлетает вместе с ударом и опадает плавно: мгновенный спад превращает панель в мигание отдельных выбросов. Вверх сразу, вниз — по несколько единиц за кадр
- Что делает пик-держатель на столбике?
- Достигнутая высота помнится маркером, который падает на строку каждые 150 мс — глаз успевает увидеть, где был максимум. Держит достигнутую высоту белой точкой и медленно сползает вниз
- Столбики слева выше правых. Что это значит?
- Это условность, а не спектр: свист в микрофон поднимет и «басовые» столбики. Честное разложение по частотам — arduinoFFT. Условный наклон к «басам»: общий уровень умножен на падающий к краю множитель
- Смена тихого альбома на громкий не меняет картинку с MAX9814. Почему?
- Автоматическое усиление выравнивает размах — для украшения хорошо, для измерений плохо. Честный уровень даёт MAX4466. AGC нормирует громкость: тихое дотягивается, громкое придавливается