35
votes

I have a handful of wav files. I'd like to use SciPy FFT to plot the frequency spectrum of these wav files. How would I go about doing this?

2
Try googling each step (reading in a wav file, using FFT on the data). It should not be hard at all, come back here if you get stuck. - MattG

2 Answers

73
votes

Python provides several api to do this fairly quickly. I download the sheep-bleats wav file from this link. You can save it on the desktop and cd there within terminal. These lines in the python prompt should be enough: (omit >>>)

import matplotlib.pyplot as plt
from scipy.fftpack import fft
from scipy.io import wavfile # get the api
fs, data = wavfile.read('test.wav') # load the data
a = data.T[0] # this is a two channel soundtrack, I get the first track
b=[(ele/2**8.)*2-1 for ele in a] # this is 8-bit track, b is now normalized on [-1,1)
c = fft(b) # calculate fourier transform (complex numbers list)
d = len(c)/2  # you only need half of the fft list (real signal symmetry)
plt.plot(abs(c[:(d-1)]),'r') 
plt.show()

Here is a plot for the input signal:
signal

Here is the spectrum spectrum

For the correct output, you will have to convert the xlabelto the frequency for the spectrum plot.

k = arange(len(data))
T = len(data)/fs  # where fs is the sampling frequency
frqLabel = k/T  

If you are have to deal with a bunch of files, you can implement this as a function: put these lines in the test2.py:

import matplotlib.pyplot as plt
from scipy.io import wavfile # get the api
from scipy.fftpack import fft
from pylab import *

def f(filename):
    fs, data = wavfile.read(filename) # load the data
    a = data.T[0] # this is a two channel soundtrack, I get the first track
    b=[(ele/2**8.)*2-1 for ele in a] # this is 8-bit track, b is now normalized on [-1,1)
    c = fft(b) # create a list of complex number
    d = len(c)/2  # you only need half of the fft list
    plt.plot(abs(c[:(d-1)]),'r')
    savefig(filename+'.png',bbox_inches='tight')

Say, I have test.wav and test2.wav in the current working dir, the following command in python prompt interface is sufficient: import test2 map(test2.f, ['test.wav','test2.wav'])

Assuming you have 100 such files and you do not want to type their names individually, you need the glob package:

import glob
import test2
files = glob.glob('./*.wav')
for ele in files:
    f(ele)
quit()

You will need to add getparams in the test2.f if your .wav files are not of the same bit.

12
votes

You could use the following code to do the transform:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from __future__ import print_function
import scipy.io.wavfile as wavfile
import scipy
import scipy.fftpack
import numpy as np
from matplotlib import pyplot as plt

fs_rate, signal = wavfile.read("output.wav")
print ("Frequency sampling", fs_rate)
l_audio = len(signal.shape)
print ("Channels", l_audio)
if l_audio == 2:
    signal = signal.sum(axis=1) / 2
N = signal.shape[0]
print ("Complete Samplings N", N)
secs = N / float(fs_rate)
print ("secs", secs)
Ts = 1.0/fs_rate # sampling interval in time
print ("Timestep between samples Ts", Ts)
t = scipy.arange(0, secs, Ts) # time vector as scipy arange field / numpy.ndarray
FFT = abs(scipy.fft(signal))
FFT_side = FFT[range(N/2)] # one side FFT range
freqs = scipy.fftpack.fftfreq(signal.size, t[1]-t[0])
fft_freqs = np.array(freqs)
freqs_side = freqs[range(N/2)] # one side frequency range
fft_freqs_side = np.array(freqs_side)
plt.subplot(311)
p1 = plt.plot(t, signal, "g") # plotting the signal
plt.xlabel('Time')
plt.ylabel('Amplitude')
plt.subplot(312)
p2 = plt.plot(freqs, FFT, "r") # plotting the complete fft spectrum
plt.xlabel('Frequency (Hz)')
plt.ylabel('Count dbl-sided')
plt.subplot(313)
p3 = plt.plot(freqs_side, abs(FFT_side), "b") # plotting the positive fft spectrum
plt.xlabel('Frequency (Hz)')
plt.ylabel('Count single-sided')
plt.show()