From 21d9a3d4a4959dc8c51b451459f7751a31329671 Mon Sep 17 00:00:00 2001 From: Jakub Zika Date: Fri, 23 Sep 2011 09:18:55 +0200 Subject: [PATCH] Added Python equalizer effect generator * It uses input from microphone and displays colors based on the frequencies present in the input --- host_python/README | 1 + host_python/equalizer.py | 84 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100755 host_python/equalizer.py diff --git a/host_python/README b/host_python/README index ff3639b..5b85627 100644 --- a/host_python/README +++ b/host_python/README @@ -24,3 +24,4 @@ DEPENDENCIES: demo.py requires PyGame, Python wrapper around SDL send_to_serial.py requires PySerial, Python library for work with serial ports +equalizer.py requires PyAudio (wrapper around PortAudio) and a microphone diff --git a/host_python/equalizer.py b/host_python/equalizer.py new file mode 100755 index 0000000..e1a816d --- /dev/null +++ b/host_python/equalizer.py @@ -0,0 +1,84 @@ +#!/usr/bin/python +# vim:et:sw=4:ts=4:sts=4 + +import pyaudio +import struct +import math +import numpy as np + +import ledbar + +CHUNK_SIZE = 1024 +FORMAT = pyaudio.paInt16 +CHANNELS = 1 +RATE = 44100 + +PIXELS = 10 + +HISTORY_SIZE = 4 +SAMPLE_SIZE = CHUNK_SIZE*HISTORY_SIZE +MIN_FREQ = 20 +MAX_FREQ = 12000 +FREQ_STEP = float(RATE) / (CHUNK_SIZE * HISTORY_SIZE) +PIXEL_FREQ_RANGE = math.pow(float(MAX_FREQ) / MIN_FREQ, 1.0/PIXELS) + +p = pyaudio.PyAudio() + +stream = p.open(format = FORMAT, + channels = CHANNELS, + rate = RATE, + input = True, + frames_per_buffer = CHUNK_SIZE) + +def get_color(volume): + p = 1-15/volume + if p <= 0: return (0, 0, 0) + p *= p + if p <= 0.4: return (0, 0, p*2.5) + elif p <= 0.7: return (0, (p-0.4)*3.33, 1.0-(p-0.4)*3.33) + elif p <= 0.9: return ((p-0.7)*5.0, 1.0-(p-0.7)*5.0, 0.0) + else: return (1.0, (p-0.9)*10.0, (p-0.9)*10.0) + +l = ledbar.Ledbar(PIXELS) +history = [] +window = np.array([0.5*(1-math.cos(2*math.pi*i/(SAMPLE_SIZE-1))) for i in xrange(SAMPLE_SIZE)]) +work = True + +try: + while work: + try: data = stream.read(CHUNK_SIZE) + except IOError: continue + if len(data) == 0: break + indata = np.array(struct.unpack('%dh'%CHUNK_SIZE,data)) + history.append(indata) + if len(history) > HISTORY_SIZE: history.pop(0) + elif len(history) < HISTORY_SIZE: continue + fft = np.fft.rfft(np.concatenate(history)*window) + freq_limit = MIN_FREQ + freq = 0 + i = 0 + while freq < freq_limit: + i += 1 + freq += FREQ_STEP + freq_limit *= PIXEL_FREQ_RANGE + pixel = 0 + count = 0 + volumes = [] + while pixel < PIXELS: + total = 0.0 + while freq < freq_limit: + total += abs(fft[i])**2 + i += 1; count += 1 + freq += FREQ_STEP + volume = (total/count)**0.5/SAMPLE_SIZE + volumes.append(volume) + freq_limit *= PIXEL_FREQ_RANGE + pixel += 1 + count = 0 + for pixel in xrange(PIXELS): + c = get_color(volumes[pixel]) + l.set_pixel(pixel, c[0], c[1], c[2]) + work = l.update() +finally: + stream.close() + p.terminate()