Если вы уже знакомы с LoL-shield - до этой идеи просто невозможно не додуматься. Сначала она была озвучена мне Юрием Соловьевым, а затем показана в реализации Andy Doro.
Итак, аудио-сигнал поступает с выхода проигрывающего устройства (плеер, телефон или даже выход звуковой карты на компе) и подается на аналоговый вход Arduino / Freeduino. Далее, он оцифровывается и выводится в виде гистограммки на LoL-Shield.
Библиотека LoL-Shield имеет базовую функцию, которая позволяет зажигать и гасить один светодиод прямым заданием координат, так что здесь все просто.
А вот для преобразования аналоговых отсчетов в спектрограмму можно воспользоваться классическим алгоритмом БПФ (быстрого преобразования Фурье, или FFT - Fast Fourier transform), для которого тоже существует библиотека. Правда, она до сих пор жила в треде форума, но кто его знает - может быть к моменту прочтения этой статьи она уже будет в составе ArduinoIDE.
Берем стандартный 3,5мм аудио-разъем (обрабатывать будем только один канал, так что можно и монофонический) и подключаем центр к свободному analog5, а землю к GND - все эти линии очень удачно выведены на LoL-Shield.
Теперь надо написать скетч, как я уже говорил - понадобится две библиотеки:
- для LoL-Shield
- для FFT
Для установки надо распаковать их в каталог sketchbook/libraries и перезапустить ArduinoIDE.
Библиотека FFT разбивает аудио-диапазон на 64 спектральные полосы, поэтому нам надо как-то трансформировать это в 14 столбцов и 9 уровней по размеру матрицы LoL-Sheild. В целом это выглядит приблизительно так:
/* FFT for LoL Shield v0.9 by Andy Doro http://andydoro.com/ based on FFT library and code from the Arduino forums and the Charlieplexing library for the LoL Shield. */ #include <Charliplexing.h> #include <fix_fft.h> #define AUDIOPIN 5 char im[128], data[128]; char data_avgs[14]; int i=0,val; void setup() { LedSign::Init(); //Инициализировать LoL-Shield } void loop() { for (i=0; i < 128; i++){ val = analogRead(AUDIOPIN); data[i] = val; im[i] = 0; }; fix_fft(data,im,7,0); for (i=0; i< 64;i++){ data[i] = sqrt(data[i] * data[i] + im[i] * im[i]); // получаем абсолютное значение от значения из массива, дальше имеем дело только с положительными числами }; for (i=0; i<14; i++) { data_avgs[i] = data[i*4] + data[i*4 + 1] + data[i*4 + 2] + data[i*4 + 3]; // усредняем соседние значения data_avgs[i] = map(data_avgs[i], 0, 30, 0, 9); // масштабируем значения под LoL } // вывод в LoLShield for (int x=0; x < 14; x++) { for (int y=0; y < 9; y++) { if (y < data_avgs[13-x]) { // 13-x меняет порядок строк так, чтобы частоты от низких к высоким показывались слева направо. LedSign::Set(x,y,1); // set the LED on } else { LedSign::Set(x,y,0); // set the LED off } } } }
На мой вкус, обновлять гистограмму уровней можно было бы чуть реже, уж больно мельтишит. Но тут, как говорится, дело вкуса:
При подготовке статьи использованы материалы c сайтов forum.arduino.cc, jimmieprodgers.com, instructables.com.