#!/usr/bin/env python3 import hyperion import array import colorsys import numpy from past.builtins import map DEBUG_MODE = False FIFO = '/tmp/mpd.fifo' class IIR: def __init__(self, alpha): self.alpha = alpha self.prev = 0 def update(self, value): self.prev = (1-self.alpha)*value + self.alpha*self.prev return self.prev class SoundToColorProcessor: def __init__(self): self.HSV_VALUE = 1 self.PEAK_THRESHOLD = 3e6 self.BAND_LOWER = 40 self.BAND_UPPER = 500 self.MAX_LOUDNESS = 10240 self.SATURATION_FIR_DEQUE_SIZE = 20 self.HUE_IIR_ALPHA = 0.9 self.VAL_IIR_ALPHA = 0.5 self.CHUNK = 2048 self.CHANNELS = 1 self.RATE = 48000 self.OCTAVES = 1 def calculateMagnitude(self, real, imaginary): ''' calculate the magnitude of the fourier tranform parts ''' magnitudes = [] x = 0 while x < len(real) and x < len(imaginary): magnitudes.append( numpy.sqrt(real[x]*real[x] + imaginary[x]*imaginary[x])) x += 1 return magnitudes def getFrequencyIndex(self, freq): return int(freq/(self.RATE/self.CHUNK)) def convertPercentToColorValue(self, percent): return int(round(percent*255)) def mapFrequencyToHue(self, freq): if freq < self.BAND_LOWER: freq = self.BAND_LOWER elif freq > self.BAND_UPPER: freq = self.BAND_UPPER freq_log = numpy.log(freq)/numpy.log(2**self.OCTAVES) lower_log = numpy.log(self.BAND_LOWER)/numpy.log(2**self.OCTAVES) upper_log = numpy.log(self.BAND_UPPER)/numpy.log(2**self.OCTAVES) hue = (freq_log - lower_log) / (1.0*upper_log - 1.0*lower_log) return hue def startProcessing(self): ''' Performs the processing of the audio data into color values. ''' if DEBUG_MODE: import matplotlib.pyplot as plt # open the audio stream on the sound card stream = open(FIFO, 'rb') if DEBUG_MODE: # Plots and canvas initialization plt.ion() fig, ax = plt.subplots() line, = ax.plot(numpy.random.randn(100)) plt.axis([20, 5000, 0, 1.1e7]) plt.show(block=False) # initialize the queues for the filtering techniques. hue_iir = IIR(self.HUE_IIR_ALPHA) val_iir = IIR(self.VAL_IIR_ALPHA) saturation_fir_deque = [] while not hyperion.abort(): data = stream.read(self.CHUNK) nums = array.array('h', data) results = numpy.fft.fft(nums) freq_bins = numpy.fft.fftfreq(len(nums), 1.0/self.RATE) results = results[0:(len(results)//2 - 1)] freq_bins = 2 * freq_bins[0:(len(freq_bins)//2 - 1)] mags = self.calculateMagnitude(results.real, results.imag) lower_band_index = self.getFrequencyIndex(self.BAND_LOWER) upper_band_index = self.getFrequencyIndex(self.BAND_UPPER) max_mag = max(mags[lower_band_index:upper_band_index]) max_freq_index = mags.index(max_mag) max_freq = freq_bins[max_freq_index] new_frequency = self.BAND_LOWER if max_mag > self.PEAK_THRESHOLD: new_frequency = max_freq rms = numpy.sqrt(numpy.mean(numpy.square(nums))) value = val_iir.update(rms) / self.MAX_LOUDNESS if value > 1.0 or numpy.isnan(value): value = 1.0 averaged_frequency = hue_iir.update(new_frequency) hue = self.mapFrequencyToHue(averaged_frequency) mag_sum = numpy.sum(mags) saturation_fir_deque.append(mag_sum) if len(saturation_fir_deque) > self.SATURATION_FIR_DEQUE_SIZE: saturation_fir_deque.pop(0) average_sum = numpy.mean(saturation_fir_deque) max_sum = max(saturation_fir_deque) if max_sum == 0: max_sum = 1 saturation = 0.85 + (0.15)*average_sum/max_sum rgb = colorsys.hsv_to_rgb(hue, saturation, value) rgb = map(self.convertPercentToColorValue, rgb) # send to hyperion hyperion.setColor(bytearray(rgb * hyperion.ledCount)) if DEBUG_MODE: # graph the fourier stuff line.set_data(freq_bins, mags) fig.canvas.draw() fig.canvas.flush_events() if DEBUG_MODE: plt.close() stream.stop_stream() stream.close() def run(): SoundToColorProcessor().startProcessing() run()