31.05.2011

Индикатор уровня на Arduino и LoL-shield

Если вы уже знакомы с LoL-shield - до этой идеи просто невозможно не додуматься. Сначала она была озвучена мне Юрием Соловьевым, а затем показана в реализации Andy Doro.

Итак, аудио-сигнал поступает с выхода проигрывающего устройства (плеер, телефон или даже выход звуковой карты на компе) и подается на аналоговый вход Arduino / Freeduino. Далее, он оцифровывается и выводится в виде гистограммки на LoL-Shield.

Библиотека LoL-Shield имеет  базовую функцию, которая позволяет зажигать и гасить один светодиод прямым заданием координат, так что здесь все просто.

А вот для преобразования аналоговых отсчетов в спектрограмму можно воспользоваться классическим алгоритмом БПФ (быстрого преобразования Фурье, или FFT - Fast Fourier transform), для которого тоже существует библиотека. Правда, она до сих пор жила в треде форума, но кто его знает - может быть к моменту прочтения этой статьи она уже будет в составе ArduinoIDE.

Берем стандартный 3,5мм аудио-разъем (обрабатывать будем только один канал, так что можно и монофонический) и подключаем центр к свободному analog5, а землю к GND - все эти линии очень удачно выведены на LoL-Shield.



Теперь надо написать скетч, как я уже говорил - понадобится две библиотеки:
Для установки надо распаковать их в каталог 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.

1 комментарий:

  1. Все хорошо, но на вход мы подаем сигнал, из которого Ардуино берет только положительные составляющие. Как это отразится на последующих преобраованиях?

    ОтветитьУдалить