Skip to content

Instantly share code, notes, and snippets.

@vectorijk
Last active December 14, 2015 08:38
Show Gist options
  • Save vectorijk/d8bd361d0f5b3572dcd0 to your computer and use it in GitHub Desktop.
Save vectorijk/d8bd361d0f5b3572dcd0 to your computer and use it in GitHub Desktop.
fft
#!/usr/bin/env python
import wave
import struct
import sys
import numpy as np
from math import sqrt
import matplotlib
matplotlib.use('Agg')
from matplotlib import pylab
import matplotlib.pyplot as plt
filename = sys.argv[1]
if __name__ == '__main__':
# Open the wave file and get info
wave_file = wave.open(filename, 'r')
data_size = wave_file.getnframes()
sample_rate = wave_file.getframerate()
# print "Sample rate: %s" % sample_rate
sample_width = wave_file.getsampwidth()
duration = data_size / float(sample_rate)
# Read in sample data
sound_data = wave_file.readframes(data_size)
# Close the file, as we don't need it any more
wave_file.close()
# Unpack the binary data into an array
unpack_fmt = '%dh' % (data_size)
sound_data = struct.unpack(unpack_fmt, sound_data)
# Process many samples
fouriers_per_second = 5 # Frames per second
fourier_spread = 1.0/fouriers_per_second
fourier_width = fourier_spread
fourier_width_index = fourier_width * float(sample_rate)
if len(sys.argv) < 3:
length_to_process = int(duration)-1
else:
length_to_process = float(sys.argv[2])
# print "Fourier width: %s" % str(fourier_width)
total_transforms = int(round(length_to_process * fouriers_per_second))
fourier_spacing = round(fourier_spread * float(sample_rate))
# print "Duration: %s" % duration
# print "For Fourier width of "+str(fourier_width)+" need "+str(fourier_width_index)+" samples each FFT"
# print "Doing "+str(fouriers_per_second)+" Fouriers per second"
# print "Total " + str(total_transforms * fourier_spread)
# print "Spacing: "+str(fourier_spacing)
# print "Total transforms "+str(total_transforms)
lastpoint=int(round(length_to_process*float(sample_rate)+fourier_width_index))-1
sample_size = fourier_width_index
freq = sample_rate / sample_size * np.arange(sample_size)
x_axis = range(0, 12)
def getBandWidth():
return (2.0/sample_size) * (sample_rate / 2.0)
def freqToIndex(f):
# If f (frequency is lower than the bandwidth of spectrum[0]
if f < getBandWidth()/2:
return 0
if f > (sample_rate / 2) - (getBandWidth() / 2):
return sample_size -1
fraction = float(f) / float(sample_rate)
index = round(sample_size * fraction)
return index
fft_averages = []
def average_fft_bands(fft_array):
num_bands = 12 # The number of frequency bands (12 = 1 octave)
del fft_averages[:]
for band in range(0, num_bands):
avg = 0.0
if band == 0:
lowFreq = int(0)
else:
lowFreq = int(int(sample_rate / 2) / float(2 ** (num_bands - band)))
hiFreq = int((sample_rate / 2) / float(2 ** ((num_bands-1) - band)))
lowBound = int(freqToIndex(lowFreq))
hiBound = int(freqToIndex(hiFreq))
for j in range(lowBound, hiBound):
avg += fft_array[j]
avg /= (hiBound - lowBound + 1)
fft_averages.append(avg)
# MOUTH UPPER
LOWER_MOUTH_MIN = 768 * 4
LOWER_MOUTH_MAX = 2304 * 4
UPPER_MOUTH_MIN = 1008 * 4
UPPER_MOUTH_MAX = 2304 * 4
result = []
for offset in range(0, total_transforms):
start = int(offset * sample_size)
end = int((offset * sample_size) + sample_size -1)
#print "Processing sample %i of %i (%d seconds)" % (offset + 1, total_transforms, end/float(sample_rate))
sample_range = sound_data[start:end]
## FFT the data
fft_data = abs(np.fft.fft(sample_range))
# Normalise the data a second time, to make numbers sensible
fft_data *= ((2**.5)/sample_size)
plt.ylim(0, 1000)
average_fft_bands(fft_data)
f_a = fft_averages
f_a = [x**2 for x in f_a]
mean = sqrt(sum(f_a))
result.append(mean)
# y_axis = fft_averages
# """Stuff for bar graph"""
# width = 0.35
# p1 = plt.bar(x_axis, y_axis, width, color='r')
# plt.title(mean)
# """End bar graph stuff"""
# filename = str('frame_%05d' % offset) + '.png'
# plt.savefig(filename, dpi=100)
# plt.close()
MX = max(result)
# print MX
# print 'res'
# print result
for r in result:
percent = int(r/MX*100)
# print percent
print '\tchannel_array[0] = %d; channel_array[1] = %d;' % \
(int(LOWER_MOUTH_MIN + percent*(LOWER_MOUTH_MAX - LOWER_MOUTH_MIN)/100),
int(UPPER_MOUTH_MIN + percent*(UPPER_MOUTH_MAX - UPPER_MOUTH_MIN)/100))
print '\tmaestro.setMultiTarget(2, 22, channel_array);'
print '\tdelay(250);'
# print "DONE!"
//generated_code from 9-Cat\ Excited_01.wav
// 5 frame per second
channel_array[0] = 4669; channel_array[1] = 5379;
maestro.setMultiTarget(2, 22, channel_array);
delay(250);
channel_array[0] = 3993; channel_array[1] = 4809;
maestro.setMultiTarget(2, 22, channel_array);
delay(250);
channel_array[0] = 8048; channel_array[1] = 8231;
maestro.setMultiTarget(2, 22, channel_array);
delay(250);
channel_array[0] = 4976; channel_array[1] = 5639;
maestro.setMultiTarget(2, 22, channel_array);
delay(250);
channel_array[0] = 3932; channel_array[1] = 4757;
maestro.setMultiTarget(2, 22, channel_array);
delay(250);
channel_array[0] = 3072; channel_array[1] = 4032;
maestro.setMultiTarget(2, 22, channel_array);
delay(250);
channel_array[0] = 4239; channel_array[1] = 5016;
maestro.setMultiTarget(2, 22, channel_array);
delay(250);
channel_array[0] = 6512; channel_array[1] = 6935;
maestro.setMultiTarget(2, 22, channel_array);
delay(250);
channel_array[0] = 3686; channel_array[1] = 4550;
maestro.setMultiTarget(2, 22, channel_array);
delay(250);
channel_array[0] = 3256; channel_array[1] = 4187;
maestro.setMultiTarget(2, 22, channel_array);
delay(250);
channel_array[0] = 3194; channel_array[1] = 4135;
maestro.setMultiTarget(2, 22, channel_array);
delay(250);
channel_array[0] = 3440; channel_array[1] = 4343;
maestro.setMultiTarget(2, 22, channel_array);
delay(250);
channel_array[0] = 5959; channel_array[1] = 6468;
maestro.setMultiTarget(2, 22, channel_array);
delay(250);
channel_array[0] = 6635; channel_array[1] = 7038;
maestro.setMultiTarget(2, 22, channel_array);
delay(250);
channel_array[0] = 3317; channel_array[1] = 4239;
maestro.setMultiTarget(2, 22, channel_array);
delay(250);
channel_array[0] = 4300; channel_array[1] = 5068;
maestro.setMultiTarget(2, 22, channel_array);
//generated_code from 9-Cat\ Excited_01.wav
//delay 250ms and
delay(250);
channel_array[0] = 4915; channel_array[1] = 5587;
maestro.setMultiTarget(2, 22, channel_array);
delay(250);
channel_array[0] = 3379; channel_array[1] = 4291;
maestro.setMultiTarget(2, 22, channel_array);
delay(250);
channel_array[0] = 3379; channel_array[1] = 4291;
maestro.setMultiTarget(2, 22, channel_array);
delay(250);
channel_array[0] = 5898; channel_array[1] = 6416;
maestro.setMultiTarget(2, 22, channel_array);
delay(250);
# need root privilige (sudo sh run.sh)
./arduino-serial/arduino-serial -b 9600 -p /dev/ttyACM0 -s s
aplay '9-Cat Excited_01.wav';

Get and compile arduino-serial for send command via command line

git clone git@github.com:todbot/arduino-serial.git
cd arduino-serial
make
cd ..

Handle upload through arduino ide on ubuntu may cause error

avrdude: ser_open(): can't open device "/dev/ttyACM0": Permission denied
ioctl("TIOCMGET"): Inappropriate ioctl for device

solution:

sudo usermod -a -G dialout <replace_with_your_username>`
sudo chmod a+rw /dev/ttyACM0

Generate data

python fft.py 9-Cat\ Excited_01.wav > output

Paste into .ino file

run bash script to start

sudo sh run.sh
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment