Последние статьи
- Твердотельный датчик направления и скорости ветра. Эксперимент
- iPhone на стене в качестве панели управления домом
- MegaD-16M-XT - подсветка выключателей и не только
- Переделка выключателей в кнопки и мини-обзор текущего рынка
- RadSens - модульный счетчик Гейгера с интерфейсом I2C
- "U" - значит универсальный. Обзор модуля MegaD-16U-XT
- SCD4x - современная альтернатива для измерения концентрации CO2
- HTU31D - новый датчик температуры и влажности с нагревательным элементом
- Измерение коэффициента пульсации ламп с помощью MegaD-2561
- Использование солнечных панелей в качестве датчика освещенности
- Согласование датчиков с выходом типа TTL со стандартными входами контроллера
- DPS368 - датчик атмосферного давления индустриального класса повышенной точности
- DS18B20 Waterpoof - импортзамещение
- TMP117 - высокоточный датчик температуры с интерфейсом I2C
- MegaD-16R-XT - расширитель на 16 релейных выходов
- MegaD-2561-RTC V3 - больше портов, зуммер и ИОН
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, в статье указано, что использовался не релейный, а симисторный модуль. А он в таком режиме может прожить очень долго.