Последние статьи из блога
- MegaD-485-TTL - изолированный конвертер индустриального класса
- MegaD-4VC - подключаем сигналы 0-10В и 4-20мА к контроллеру
- MegaD-4CM - высокоточный 4-х канальный измеритель тока
- MegaD-4US - 4-х канальный датчик напряжения на DIN-рейку
- Как я боролся с собаками с помощью нейросети
- Интеграция ультразвукового датчика скорости и направления ветра
- Установка WiFi-камеры на робота-газонокосилку
- Твердотельный датчик направления и скорости ветра. Эксперимент
- iPhone на стене в качестве панели управления домом
- MegaD-16M-XT - подсветка выключателей и не только
- Переделка выключателей в кнопки и мини-обзор текущего рынка
- RadSens - модульный счетчик Гейгера с интерфейсом I2C
- "U" - значит универсальный. Обзор модуля MegaD-16U-XT
- SCD4x - современная альтернатива для измерения концентрации CO2
- HTU31D - новый датчик температуры и влажности с нагревательным элементом
- Измерение коэффициента пульсации ламп с помощью MegaD-2561
Highway to Hell или цветомузыка по Ethernet
22/12/2013 03:07:27
No stop signs, speed limit
Nobody's gonna slow me down
Like a wheel, gonna spin it
Nobody's gonna mess me round...
Приближались новогодние праздники и я подумал, что пора бы уже поднять автоматизацию гирлянд и прочей праздничной иллюминации на новый уровень. В прошлой статье "Елочная гирлянда с управлением по 1-wire" я моргал светодиодной гирляндой посредством технологии 1-wire. Все хорошо, но как-то скучновато. Душа требовала чего-то более возвышенного. А что если... а что если моргать лампочками под музыку?
У нас же есть такая замечательная штука, как "Мегадевайс" (он же Жора, он же Гога... то есть MegaD-328). Устройство, которое управляется по сети Ethernet. А почему бы не сделать так, чтобы сервер или просто любой домашний компьютер, проигрывая музыку через медиа-систему (мультирум), отправлял в такт мелодии команды MegaD-328 на включение и выключение определенных ламп? Действительно! Но, хватит ли скорости Ethernet и производительности МК для обработки столь большого количества команд? Ведь каждая команда - это целый TCP-пакет. Микроконтроллеру нужно установить соединение, принять сообщение, распарсить его... А попробуем!
Для решений этой задачи я воспользовался языком программирования python. Во-первых, потому что в нем есть ряд уже готовых к использованию библиотек для работы со звуком, а во-вторых, ниже описанный код должен работать не только под Linux (Unix), но и под Windows и Mac OS.
Итак, поехали.
Задача первая. Делаем из MegaD-328 волюметер (VU-meter или поросту измеритель уровня звука)
Но сперва поймем как с помощью python'а организовать отправку команд Мегадевайсу. А очень все просто
import httplib
conn = httplib.HTTPConnection("192.168.0.14")
conn.request("GET", "/sec?cmd=7:2")
conn.close()
Работает!
Теперь мы подключим библиотеки alsaaudio и audioop
По умолчанию в Debian Linux библиотека alsaaudio не установлена, но решается вопрос просто.
apt-get install python-alsaaudio
Далее пишем простой скрипт, который анализирует громкость музыки и приводит значение к диапазону от 0 до 6 (количество выходных портов стандартного комплекта). К сожалению готовых средств для работы с mp3 в python нет, зато есть возможность работать с wav. А поскольку wav из mp3 делается за 5 секунд (ну хотя бы командой lame --decode), но для начала сойдет.
#!/usr/bin/env python
import alsaaudio as aa
import audioop
from time import sleep
import struct
import numpy as np
import wave
import httplib
# Set up audio
wavfile = wave.open('music/03.wav','r')
sample_rate = wavfile.getframerate()
no_channels = wavfile.getnchannels()
chunk = 2048
output = aa.PCM(aa.PCM_PLAYBACK, aa.PCM_NORMAL)
output.setchannels(no_channels)
output.setrate(sample_rate)
output.setformat(aa.PCM_FORMAT_S16_LE)
output.setperiodsize(chunk)
cmd_off_old = "";
print "Processing....."
data = wavfile.readframes(chunk)
while data!='':
output.write(data)
max_vol_factor =4000
max_vol = audioop.max(data,2)/max_vol_factor
cmd = ""
cmd_off = ""
last_val = 0
for i in range (0,max_vol):
port = 7 + i
cmd = cmd + str(port) + ":1;"
last_val = max_vol
for j in range (last_val, 8):
port = 7 + j
cmd_off = cmd_off + str(port) + ":0;"
print cmd
if ( cmd != "" ):
conn = httplib.HTTPConnection("192.168.0.14")
conn.request("GET", "/sec?cmd=" + cmd)
conn.close()
data = wavfile.readframes(chunk)
if ( cmd_off_old == cmd_off and last_val == 0 ):
cmd_off = ""
if ( cmd_off != "" ):
conn = httplib.HTTPConnection("192.168.0.14")
conn.request("GET", "/sec?cmd=" + cmd_off)
conn.close()
cmd_off_old = cmd_off
cmd_old = cmd
И вот, что я получил в результате.
ПРИМЕЧАНИЕ! В ролях участвовал бесшумный симисторный исполнительный модуль MegaD-7I7O-S на который каким-то таинственным, необъяснимым и загадочным образом попала наклейка от релейного модуля. Но я специально провел подобный опыт и с релейным модулем, который работал абсолютно аналогично с той лишь разницей, что в такт морганию светодиодов щелкали еще и релюшки.
Ну как? Результат данного эксперимента показал, что MegaD-328 без труда справляется с задачей визуализации музыки без какой-либо видимой рассинхронизации. Теперь достаточно к портам подключить лампы накаливания Ватт по 150 вряд, да зарядить что-нибудь погромче на всю Ивановскую. Знатный получится волюметр!
Но постойте, отплясывать гопака под волюметр нынче не в моде. Я прошел пешком пол Европы и нигде не видел, чтобы танцевали под VU-meter. Сейчас у них в моде форменное безобразие - называется FFT (Fast Fourier transform). А по нашему, значит, "Быстрое преобразование Фурье". С применением этого алгоритма можно разложить музыку на отдельные частоты, а из MegaD-328 сделать что-то вроде спектрометра. К сожалению по математике у меня всегда была твердая тройка, поэтому я не стал изобретать велосипед, а взял уже готовую реализацию FFT для данной задачи, написанную для python'а. И вот такой получился скрипт
#!/usr/bin/env python
# 8 band Audio equaliser from wav file
import alsaaudio as aa
from struct import unpack
import numpy as np
import wave
import httplib
matrix = [0,0,0,0,0,0,0,0]
power = []
weighting = [2,2,8,8,16,32,64,64] # Change these according to taste
# Set up audio
wavfile = wave.open('music/06.wav','r')
sample_rate = wavfile.getframerate()
no_channels = wavfile.getnchannels()
chunk = 4096 # Use a multiple of 8
output = aa.PCM(aa.PCM_PLAYBACK, aa.PCM_NORMAL)
output.setchannels(no_channels)
output.setrate(sample_rate)
output.setformat(aa.PCM_FORMAT_S16_LE)
output.setperiodsize(chunk)
# Return power array index corresponding to a particular frequency
def piff(val):
return int(2*chunk*val/sample_rate)
def calculate_levels(data, chunk,sample_rate):
global matrix
# Convert raw data (ASCII string) to numpy array
data = unpack("%dh"%(len(data)/2),data)
data = np.array(data, dtype='h')
# Apply FFT - real data
fourier=np.fft.rfft(data)
# Remove last element in array to make it the same size as chunk
fourier=np.delete(fourier,len(fourier)-1)
# Find average 'amplitude' for specific frequency ranges in Hz
power = np.abs(fourier)
matrix[0]= int(np.mean(power[piff(0) :piff(156):1]))
matrix[1]= int(np.mean(power[piff(156) :piff(313):1]))
matrix[2]= int(np.mean(power[piff(313) :piff(625):1]))
matrix[3]= int(np.mean(power[piff(625) :piff(1250):1]))
matrix[4]= int(np.mean(power[piff(1250) :piff(2500):1]))
matrix[5]= int(np.mean(power[piff(2500) :piff(5000):1]))
matrix[6]= int(np.mean(power[piff(5000) :piff(10000):1]))
matrix[7]= int(np.mean(power[piff(10000):piff(20000):1]))
# Tidy up column values for the LED matrix
matrix=np.divide(np.multiply(matrix,weighting),1000000)
# Set floor at 0 and ceiling at 8 for LED matrix
matrix=matrix.clip(0,8)
return matrix
cmd_off_old = "";
# Process audio file
print "Processing....."
data = wavfile.readframes(chunk)
while data!='':
output.write(data)
matrix=calculate_levels(data, chunk,sample_rate)
cmd = "";
cmd_off = "";
for i in range (0,7):
port = 7 + i
if ( matrix[i] > 3 ):
cmd = cmd + str(port) + ":1;"
else:
cmd_off = cmd_off + str(port) + ":0;"
if ( cmd_off_old == cmd_off and cmd == "" ):
cmd_off = "";
print cmd
if ( cmd != "" ):
conn = httplib.HTTPConnection("192.168.0.14")
conn.request("GET", "/sec?cmd=" + cmd)
conn.close()
data = wavfile.readframes(chunk)
if ( cmd_off != "" ):
conn = httplib.HTTPConnection("192.168.0.14")
conn.request("GET", "/sec?cmd=" + cmd_off)
conn.close()
cmd_off_old = cmd_off
Вот теперь можно смело подключать к выходам разноцветные лампочки и плясать "у ту степь", джампстайл или даже стрип-дэнс. Результат в студию.
Ну а как же Новый Год и елочные гирлянды? Ведь визуализация в виде спектрометра для такой задачи подойдет слабо. Ха! Когда у нас есть FFT, можно реализовать любые алгоритмы, насколько только хватит фантазии. На скорую руку я немного изменил уже существующий скрипт таким образом, чтобы лампы переключались случайным образом в такт музыке. Под "тактом" я тут понимаю не уровень громкости музыки, а всплеск по любой из частот выше определенного порога.
#!/usr/bin/env python
# 8 band Audio equaliser from wav file
import alsaaudio as aa
from struct import unpack
import numpy as np
import wave
import httplib
import random
matrix = [0,0,0,0,0,0,0,0]
power = []
weighting = [2,2,8,8,16,32,64,64] # Change these according to taste
# Set up audio
wavfile = wave.open('music/01.wav','r')
sample_rate = wavfile.getframerate()
no_channels = wavfile.getnchannels()
chunk = 4096 # Use a multiple of 8
output = aa.PCM(aa.PCM_PLAYBACK, aa.PCM_NORMAL)
output.setchannels(no_channels)
output.setrate(sample_rate)
output.setformat(aa.PCM_FORMAT_S16_LE)
output.setperiodsize(chunk)
# Return power array index corresponding to a particular frequency
def piff(val):
return int(2*chunk*val/sample_rate)
def calculate_levels(data, chunk,sample_rate):
global matrix
# Convert raw data (ASCII string) to numpy array
data = unpack("%dh"%(len(data)/2),data)
data = np.array(data, dtype='h')
# Apply FFT - real data
fourier=np.fft.rfft(data)
# Remove last element in array to make it the same size as chunk
fourier=np.delete(fourier,len(fourier)-1)
# Find average 'amplitude' for specific frequency ranges in Hz
power = np.abs(fourier)
matrix[0]= int(np.mean(power[piff(0) :piff(156):1]))
matrix[1]= int(np.mean(power[piff(156) :piff(313):1]))
matrix[2]= int(np.mean(power[piff(313) :piff(625):1]))
matrix[3]= int(np.mean(power[piff(625) :piff(1250):1]))
matrix[4]= int(np.mean(power[piff(1250) :piff(2500):1]))
matrix[5]= int(np.mean(power[piff(2500) :piff(5000):1]))
matrix[6]= int(np.mean(power[piff(5000) :piff(10000):1]))
matrix[7]= int(np.mean(power[piff(10000):piff(20000):1]))
# Tidy up column values for the LED matrix
matrix=np.divide(np.multiply(matrix,weighting),1000000)
# Set floor at 0 and ceiling at 8 for LED matrix
matrix=matrix.clip(0,8)
return matrix
turn = 0;
# Process audio file
print "Processing....."
data = wavfile.readframes(chunk)
while data!='':
output.write(data)
matrix=calculate_levels(data, chunk,sample_rate)
cmd = "";
cmd_off = "";
onoff = 0;
for i in range (0,7):
port = 7 + i
if ( matrix[i] > 3 ):
turn = 1
if turn == 1:
for i in range (0,4):
port = 7 + i * 2
port2 = port + 1
rand = random.randint(0, 1)
if rand == 1:
cmd = cmd + str(port) + ":1;" + str(port2) + ":1;"
else:
cmd = cmd + str(port) + ":0;" + str(port2) + ":0;"
turn = 0
print cmd
if ( cmd != "" ):
conn = httplib.HTTPConnection("192.168.0.14")
conn.request("GET", "/sec?cmd=" + cmd)
conn.close()
data = wavfile.readframes(chunk)
Нельзя сказать, что этот тип визуализации очень уж впечатляет, но для управления гирляндами, по-моему, вполне... Сначала я хотел использовать какую-нибудь новогоднюю песенку навроде "В лесу родилась елочка" или "Happy New Year", но потом решил - НЕТ! Только консоль, только Ethernet, только синяя изолента, только ХАРДКОР!
Вы, конечно, можете придумать любые собственные алгоритмы. Ну и... С Новым 2014 годом!
Запуск скриптов в ОС Windows (XP, Vista, Win7 и т.д.)
1. Устанавливаем python 2.7
Идем на сайт http://python.org/download/, выбираем и скачиваем подходящий дистрибутив python. Важно! Версия должна быть 2.7 (не 3.3) и 32 битная!
2. Устанавливаем библиотеку PyAudio
Идем на сайт http://people.csail.mit.edu/hubert/pyaudio/, скачиваем и устанавливаем PyAudio for Python 2.7 для Microsoft Windows
3. Устанавливаем библиотеку Numphy
Идем на сайт http://sourceforge.net/projects/numpy/files/NumPy/, скачиваем и устанавливаем последнюю версию Numphy для python 2.7
4. Скачиваем поправленные python-скрипты ab-log.ru/files/File/megad-lights-win.zip
5. Распаковываем скрипты и правим путь к проигрываемому WAV файлу.
6. Запускаем скрипт примерно так
C:python27python.exe megad-music1-win.py
Автор: Andrey_B
Любое использование материалов сайта возможно только с разрешения автора и с обязательным указанием источника.
Добавить комментарий:
Сортировка комментариев: Последние сверху | Первые сверху
2014-01-07 04:42:03 | Александр
Как раз во время статья.. Нужно попробовать у себя
2014-01-12 01:21:21 | Дмитрий
Насколько я помню статус каждого выхода сохраняется в энергонезависимой памяти атмеги? если так, то сколько часов музыки она переживет до 1го сбоя?
2014-01-12 10:57:05 | Andrey_B
Состояние порта в EEPROM не записывается, поэтому работать будет вечно.
2014-01-15 13:31:44 | Андриан
Спасибо за статью Андрей,новый уровень:-) песня просто не в бровь а в глаз!
Осталось только купить програматор починить своего гогу:-) А то гирлянды уличные простаивают в коробках:-)
2014-01-15 17:35:46 | Andrey_B
Андриан, а что с вашим "гогой"?
2014-05-19 21:42:09 | Марат
Извините, не вчитывался в код, а на сколько реально это сделать для 2-3 MegaD-328
2014-05-20 13:10:36 | Andrey_B
Марат, вы имеете ввиду сделать так, чтобы использовалось не 7 выходов и одно устройство, а разложить цветомузыку на 14-21 порт и 2-3 устройства? Теоретически сделать это можно. Думаю, должно работать. Скрипт отправляет в одном пакете команды сразу всем 7 портам. Поэтому они изменяют свои состояния почти мгновенно. Если мы имеем несколько устройств, нужно будет отправлять несколько пакетов разным устройствам. Вот в этом месте может в теории возникнуть рассинхронизация. Но учитывая скорость работы Ethernet, я думаю, вряд ли это будет заметно на глаз.
2015-01-26 21:24:26 | Олег
Как я понял в связи с изменением в прошивках, в коде нужно исправить conn.request("GET", "/sec?cmd=" + cmd) на conn.request("GET", "/sec/?cmd=" + cmd) иначе в первом варианте переключения не происходят.
2023-06-22 21:15:33 | Kef
Интересно, сколько в таком режиме проживут релюхи
2023-06-22 22:39:09 | Andrey_B
Kef, в статье указано, что использовался не релейный, а симисторный модуль. А он в таком режиме может прожить очень долго.