Thank you Marcus and Max,
Long story short I need to calculate the amplitude of each frequency bin of the input wav. I'm starting with a 1024 bin fft, sample rate 48k. So 0-24khz each bin being 24000/1024 Hz wide. I want to know the amplitude or loudness of sounds each frequency bin.
The goal being to monitor a wav stream and periodically record to a database the average amplitude within each frequency band.
Then I can later query the database for information about what harmonics were spiking at different times. This will be useful I believe in correlating various harmonics with changes taking place at the same time.
If we listen to a dial tone recording for example, we can clearly tell that two and only two tones are present. Is FFT able to tell us the same thing (exactly two spikes, at expected frequencies) if given floating point samples rather than complex? (Tried this using wx gui FFT plot and I don't see mirroring, does the WX GUI FFT suppress the mirroring?)
Taking a step back, maybe the mirroring is a product of the method I'm using to interpret the output of the FFT. I'm feeding the stream into a vector sink and reading from the vector sink one "item" at a time with a for loop. Code snippets below:
self.fft_vxx_0 = fft.fft_vfc(myVectorLength, True, (window.blackmanharris(myVectorLength)), 1)
self.blocks_wavfile_source_0 = blocks.wavfile_source('test.wav', False)
self.blocks_vector_sink_x_0 = blocks.vector_sink_f(myVectorLength)
self.blocks_stream_to_vector_0 = blocks.stream_to_vector(gr.sizeof_float*1, myVectorLength)
self.blocks_null_sink_0 = blocks.null_sink(gr.sizeof_float*1)
self.blocks_keep_one_in_n_0 = blocks.keep_one_in_n(gr.sizeof_float*1, self.n_keep)
self.blocks_complex_to_mag_squared_0 = blocks.complex_to_mag_squared(myVectorLength)
self.blocks_throttle = blocks.throttle(gr.sizeof_float * 1, self.samp_rate, True)
# Throw away unneeded channel. input is mono but two channels.
self.connect((self.blocks_wavfile_source_0, 0), (self.blocks_null_sink_0, 0))
# Throttle the input so the flow graph runs long enough to sample it
self.connect((self.blocks_wavfile_source_0, 1), (self.blocks_throttle, 0))
# Down-sample / decimate the input
self.connect((self.blocks_throttle, 0), (self.blocks_keep_one_in_n_0, 0))
# Convert the stream to a vector in preparation for FFT
self.connect((self.blocks_keep_one_in_n_0, 0), (self.blocks_stream_to_vector_0, 0))
# Perform FFT analysis of the stream.
self.connect((self.blocks_stream_to_vector_0, 0), (self.fft_vxx_0, 0))
# Send FFT data (phase and magnitude) into a mag^2 block to get strength of each bin
self.connect((self.fft_vxx_0, 0), (self.blocks_complex_to_mag_squared_0, 0))
# Send the final result (magnitudes in each fft bin) into vector sink.
self.connect((self.blocks_complex_to_mag_squared_0, 0), (self.blocks_vector_sink_x_0, 0))
# Probe thread, processes each item out of the vector probe:
def probeFftDataThread():
# Sleep while the flow graph starts up. Without waiting we would get attribute errors.
time.sleep(5)
bigaverage = 0
multiplier = 10000
while True:
# dataVal = self.probeFft.data()
# try:
dataVal = self.blocks_vector_sink_x_0.data()
if (len(dataVal) == 0):
time.sleep(0.1)
continue;
for thisDataElement in dataVal:
thisDataElement = thisDataElement * multiplier
bigaverage = (bigaverage + thisDataElement) / 2
thresh_min = bigaverage * 8
thresh_med = bigaverage * 16
thresh_max = bigaverage * 64
elemnum = 0
avgLevel = 0
# sys.stdout.write("len=" + str(len(dataVal)) + "\n")
for thisDataElement in dataVal:
elemnum = elemnum + 1
thisDataElement = thisDataElement * multiplier
thisDataElement = int(round(thisDataElement))
avgLevel = (avgLevel + thisDataElement)/2
if (thisDataElement < thresh_min):
sys.stdout.write(" ")
else:
if (thisDataElement >= thresh_min and thisDataElement < thresh_med):
sys.stdout.write(" ")
else:
if (thisDataElement >= thresh_med and thisDataElement < thresh_max):
sys.stdout.write("o ")
else:
if (thisDataElement >= thresh_max):
sys.stdout.write("* ")
if (elemnum % myVectorLength == 0):
sys.stdout.write(" avg: " + str(avgLevel) + " bigavg=" + str(bigaverage) + "\n")
# if (elemnum >= 512):
# break
# print("")
self.blocks_vector_sink_x_0.reset()
# Reset the contents of the vector sink?
# --> reset() caused it to go to 0 length and stay there.
# self.blocks_vector_sink_x_0.reset()
# dataVal = dataVal * 1000
# dataVal = round(dataVal)
# print ("DATAlength: " + str(len(dataVal)))
# print ("the datatype for the vector sink data is " + str(type(dataVal)))
# self.set_variable_function_probe_0(dataVal)
# except AttributeError:
# print ("No vector sink data attribute yet")
# pass
time.sleep(0.1)
Application output looks like this (very wide terminal required. Noote the mirroring):
o o o o avg: 1875 bigavg=426.111233193
avg: 153 bigavg=426.111233193
o o o o o o o o avg: 155 bigavg=426.111233193
o o avg: 611 bigavg=426.111233193
avg: 1292 bigavg=426.111233193
avg: 449 bigavg=426.111233193
avg: 490 bigavg=426.111233193
avg: 1342 bigavg=426.111233193
o o o o avg: 267 bigavg=426.111233193
avg: 1428 bigavg=426.111233193
o o o o o o o o o o o o o o o o avg: 3781 bigavg=426.111233193
o o o o o o o o avg: 829 bigavg=426.111233193
o o avg: 469 bigavg=426.111233193
o o o o o o o o o o avg: 634 bigavg=426.111233193
o o o o avg: 3076 bigavg=426.111233193
o o avg: 671 bigavg=426.111233193
o o o o avg: 1135 bigavg=426.111233193
o * o o * o avg: 430 bigavg=426.111233193
o o o o o o o o o o avg: 765 bigavg=426.111233193
* * o o * o o o * o o * * avg: 942 bigavg=426.111233193
avg: 586 bigavg=426.111233193
o o o o o o o o avg: 736 bigavg=426.111233193
avg: 425 bigavg=426.111233193
avg: 1448 bigavg=1448.26562964
o o avg: 741 bigavg=740.994978869