togiles
Posts: 10
Joined: Fri Oct 11, 2013 2:11 am

Re: Reading Audio stream for FFT

Sun Jan 05, 2014 6:29 am

Update, my code is now at (changed name of repository):

https://bitbucket.org/togiles/lightshowpi

CuriousInventor
Posts: 1
Joined: Tue Jan 07, 2014 3:18 pm

Re: Reading Audio stream for FFT

Tue Jan 07, 2014 3:27 pm

I just did a project displaying a spectrum analyzer on a RGB LED strip:

http://www.instructables.com/id/Raspber ... ED-Strip-/

I used code from the LightShow Pi project, which I believe got their FFT code from this post.

I made a few changes to the FFT processing part, more details are on the instructable.

For larger arrays (2048pts), I found the np.array([]) is incredibly slow. I used np.frombuffer instead. I also added windowing to prevent the FFT from thinking the edges of the chunks were part of the signal. And instead of averaging the bins, sum them and log10 the result. My signals knowledge is a bit rough, so there still may be issues.

togiles
Posts: 10
Joined: Fri Oct 11, 2013 2:11 am

Re: Reading Audio stream for FFT

Tue Jan 07, 2014 4:20 pm

CuriousInventor wrote:I used code from the LightShow Pi project, which I believe got their FFT code from this post.
Indeed we did get the original FFT code from this post (see my previous posts above). Thanks again for your enhancements, they're great!

beniot
Posts: 2
Joined: Thu Jan 15, 2015 9:40 am

Re: Reading Audio stream for FFT

Thu Jan 15, 2015 10:16 am

stulevine wrote:Oh, regarding that link, that's where I originally started with the MCP3008 IC and decided to pursue using the IC with the hardware SPI of the Raspberry Pi.

So, with that said, my problem is not reading the analog input as I am already able to do that via spidev (Installed now under Raspian Wheezy) using a much simpler approach with less GPIO pins utilized. Here's the python code if you are interested.

So, I want to take what I read from analog pin 4 and process it with FTT and graph it on the 8x8 matrix like they do on the Arduino Music visualizer tutorial :) I'm just not quite understanding how to take that voltage and process it via FTT like you do in your scripts.
Stulevine, did you already managed to work out your little project? I am trying to do something similar... I have 1 led strip and every time the MAX4466 mic outputs a signal (if he captures noise) the color of my led strip should change.

I already used this code (http://pastebin.com/5DGg7UWS) to convert the analog signal that is being outputted by the MAX4466 chip into a digital signal (using the MCP3008). But i just get 'random' numbers as output... even when I yell into the mic I don't realy see a big change in output. I guess this output still needs o be processed by this Fast Fourier Transformation... Does somebody has some experience with this ?

G4UFS
Posts: 10
Joined: Tue Jan 08, 2013 1:24 am

Re: Reading Audio stream for FFT

Sun May 31, 2015 2:42 pm

Hi,

I am trying to modify the code to simply get numbers from the FFT and print them to the console.

I removed the code that send the data to the gpio but I get an error :-

data_in.pause(1) # Pause capture whilst RPi processes data
alsaaudio.ALSAAudioError: Input/output error

I did the same thing with the volume analyser and all is well but I desperately need to get audio from mic input and do an FFT and be able to get the FFT data into a stream of number I can manipulate and then display. For now, just reading the mic input and getting an FFT would be very useful. Even with only 8 columns

Any help would be very much appreciated.

BTW, I know little about Python but am learning. I know even less about C and am learning sloooooowly

Regards
Dave

cristobal.aguila
Posts: 2
Joined: Wed Jul 08, 2015 2:09 am

Re: Reading Audio stream for FFT

Wed Jul 08, 2015 2:48 am

Hi
I hope there is no problem reviving an old thread :D
I've been using SpaceGerbil code to work with the FFT in 8 channels.
I'm working on an automatic Karaoke.
My script downloads the songs and the lyrics, then converts the song file to wav, and then uses the SpaceGerbil to generate a matrix.
This is a sample of the matrix generated

Code: Select all

0 1 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 
7 1 1 1 1 1 0 3 
3 0 1 1 0 0 0 1 
3 1 1 1 0 0 0 0 
3 1 1 1 0 0 0 0 
3 1 0 1 0 0 0 0 
3 1 1 1 1 1 0 3
Then, using pygame i'm making a real time fft spectrum image.
What I want to achieve is to get a timestamp for every word in the song. This way I could synchronize the lyrics with the song and make an automatic karaoke.
So far I think that the lyrics are being displayed randomly. It's hard to clearly differentiate between the voice and the instruments.

Also I didn't understood what weighting is used for.

This code generates the matrix (My test song was Bon Jovi's Living on a prayer)

Code: Select all

#!/usr/bin/env python

# 8 bar Audio equaliser

import alsaaudio as aa
from time import sleep
from struct import unpack
import numpy as np
import wave

matrix    = [0,0,0,0,0,0,0,0]
power     = []
weighting = [2,4,8,16,32,64,128,256] # Change these according to taste


# Set up audio
wavfile = wave.open('songs/livingonaprayer.wav','r')
sample_rate = wavfile.getframerate()
print "Sample_rate : " + str(sample_rate)
no_channels = wavfile.getnchannels()
chunk = 4096
output = aa.PCM(aa.PCM_PLAYBACK, aa.PCM_NORMAL)
output.setchannels(no_channels)
output.setrate(sample_rate)
output.setformat(aa.PCM_FORMAT_S16_LE)
output.setperiodsize(chunk)

# Return power array index corresponding to a particular frequency
def piff(val):
   return int(2*chunk*val/sample_rate)

def calculate_levels(data, chunk,sample_rate):
   global matrix
   # Convert raw data to numpy array
   data = unpack("%dh"%(len(data)/2),data)
   data = np.array(data, dtype='h')
   # Apply FFT - real data so rfft used
   fourier=np.fft.rfft(data)
   # Remove last element in array to make it the same size as chunk
   fourier=np.delete(fourier,len(fourier)-1)
   # Find amplitude
   power = np.abs(fourier)
   matrix[0]= int(np.mean(power[piff(0)    :piff(156):1]))
   matrix[1]= int(np.mean(power[piff(156)  :piff(313):1]))
   matrix[2]= int(np.mean(power[piff(313)  :piff(625):1]))
   matrix[3]= int(np.mean(power[piff(625)  :piff(1250):1]))
   matrix[4]= int(np.mean(power[piff(1250) :piff(2500):1]))
   matrix[5]= int(np.mean(power[piff(2500) :piff(5000):1]))
   matrix[6]= int(np.mean(power[piff(5000) :piff(10000):1]))
   matrix[7]= int(np.mean(power[piff(10000):piff(20000):1]))
   # Tidy up column values for the LED matrix
   matrix=np.divide(np.multiply(matrix,weighting),1000000)
   # Set floor at 0 and ceiling at 8 for LED matrix
   matrix=matrix.clip(0,8)
   return matrix

print "Processing....."

data = wavfile.readframes(chunk)
file = open("livingonaprayer.txt","w")

while data!='':
   output.write(data)
   matrix=calculate_levels(data, chunk,sample_rate)
   for i in range (0,8):
      file.write(str((1<<matrix[i])-1)+" ")
   file.write( "\n")
   data = wavfile.readframes(chunk)
file.close()
If you want to get only the numbers from the FFT, this code will work for you. Instead of saving the lines to a text file, just print the lines.

I hope you can help me.

Greetings,
Cristóbal.

andig2
Posts: 51
Joined: Wed Oct 31, 2012 9:34 pm

Re: Reading Audio stream for FFT

Wed Oct 21, 2015 1:54 pm

Hi there,

I've read the whole thread and the attached links but couldn't come to a conclusion: is there sample code anywhere that allows to read what the pi's audio is playing via ALSA and feed it into the FFT? I'm working part-time on Moode Audio and would sure love to add an spectrum analyzer to the capabilities...

Cheers,
Andi

User avatar
PeterO
Posts: 4942
Joined: Sun Jul 22, 2012 4:14 pm

Re: Reading Audio stream for FFT

Wed Oct 21, 2015 2:13 pm

There used to be an ALSA loopback device that would to do this, but ISTR it added a significant delay and needed to be built and installed as a kernel module. I've not used it for several years.
google has some links for "ALSA loopback"
PeterO
Discoverer of the PI2 XENON DEATH FLASH!
Interests: C,Python,PIC,Electronics,Ham Radio (G0DZB),1960s British Computers.
"The primary requirement (as we've always seen in your examples) is that the code is readable. " Dougie Lawson

User avatar
Douglas6
Posts: 4739
Joined: Sat Mar 16, 2013 5:34 am
Location: Chicago, IL

Re: Reading Audio stream for FFT

Wed Oct 21, 2015 2:47 pm

snd_loopback was included in the last Wheezy image; not sure of Jessie. There's some ALSA config code in this thread to add a capture device to the default sound card. Performed quite well. viewtopic.php?p=609780#p609780

Reading and FFTing it are beyond me. This works only with ALSA sources, I.E. not omxplayer.

andig2
Posts: 51
Joined: Wed Oct 31, 2012 9:34 pm

Re: Reading Audio stream for FFT

Thu Oct 22, 2015 6:29 am

snd_loopback was included in the last Wheezy image; not sure of Jessie.


I'm following the instructions here https://github.com/karlstav/cava. I got the loopback device just fine via modprobe and have setup an asound.conf as in https://github.com/karlstav/cava/blob/m ... sound.conf.

Playing to default now does not give errors but also no sound. The author has experienced the same problems on the raspberry, but not on his PC.

funfrancis
Posts: 14
Joined: Mon Jul 20, 2015 4:29 am

Re: Reading Audio stream for FFT

Sun Nov 22, 2015 4:26 am

Im using a mic-pre amp setup.....connected to Cirrus Logic card (http://www.element14.com/community/comm ... audio_card) on Pi2...I would like to see FFT numbers rather than lighting LEDs
Could u modify ur codes

interweber
Posts: 1
Joined: Tue Mar 01, 2016 11:51 pm

Re: Reading Audio stream for FFT

Tue Mar 01, 2016 11:54 pm

Which sound card did you use?

funfrancis
Posts: 14
Joined: Mon Jul 20, 2015 4:29 am

Re: Reading Audio stream for FFT

Wed Mar 02, 2016 2:02 am

I am using Wolfson audio card with RPi .1

Algislos
Posts: 2
Joined: Fri Dec 18, 2015 9:39 pm

Re: Reading Audio stream for FFT

Wed May 25, 2016 8:59 am

SpaceGerbil wrote:All done.

Image

Video of it in action below:
http://www.youtube.com/watch?v=Du0zp7AjlBY

The code:

Code: Select all

#!/usr/bin/env python

# 8 bar Audio equaliser using MCP2307
 
import alsaaudio as aa
import smbus
from time import sleep
from struct import unpack
import numpy as np

bus=smbus.SMBus(0)  	#Use '1' for newer Pi boards;

ADDR   = 0x20			#The I2C address of MCP23017
DIRA   = 0x00			#PortA I/O direction, by pin. 0=output, 1=input
DIRB   = 0x01			#PortB I/O direction, by pin. 0=output, 1=input
BANKA  = 0x12			#Register address for Bank A
BANKB  = 0x13			#Register address for Bank B

#Set up the 23017 for 16 output pins
bus.write_byte_data(ADDR, DIRA, 0);  #all zeros = all outputs on Bank A
bus.write_byte_data(ADDR, DIRB, 0);  #all zeros = all outputs on Bank B

def TurnOffLEDS ():
	bus.write_byte_data(ADDR, BANKA, 0xFF)  #set all columns high
	bus.write_byte_data(ADDR, BANKB, 0x00)  #set all rows low

def Set_Column(row, col):
	TurnOffLEDS()
	bus.write_byte_data(ADDR, BANKA, col)
	bus.write_byte_data(ADDR, BANKB, row)
			
# Initialise matrix
TurnOffLEDS()

# Set up audio
sample_rate = 44100
no_channels = 2
chunk = 512 # Use a multiple of 8
data_in = aa.PCM(aa.PCM_CAPTURE, aa.PCM_NORMAL)
data_in.setchannels(no_channels)
data_in.setrate(sample_rate)
data_in.setformat(aa.PCM_FORMAT_S16_LE)
data_in.setperiodsize(chunk)

def calculate_levels(data, chunk,sample_rate):
	# Convert raw data to numpy array
	data = unpack("%dh"%(len(data)/2),data)
	data = np.array(data, dtype='h')
	# Apply FFT - real data so rfft used
	fourier=np.fft.rfft(data)
	# Remove last element in array to make it the same size as chunk
	fourier=np.delete(fourier,len(fourier)-1)
	# Find amplitude
	power = np.log10(np.abs(fourier))**2
	# Araange array into 8 rows for the 8 bars on LED matrix
	power = np.reshape(power,(8,chunk/8))
	matrix= np.int_(np.average(power,axis=1)/4)
	return matrix

print "Processing....."

while True:
	TurnOffLEDS()
	# Read data from device	
	l,data = data_in.read()
	data_in.pause(1) # Pause capture whilst RPi processes data
	if l:
		# catch frame error
		try:
			matrix=calculate_levels(data, chunk,sample_rate)
			for i in range (0,8):
				Set_Column((1<<matrix[i])-1,0xFF^(1<<i))

		except audioop.error, e:
			if e.message !="not a whole number of frames":
				raise e
	sleep(0.001)
	data_in.pause(0) # Resume capture
I got over the remaining problems for use with numpy. It is important that you capture in NORMAL (block) mode to always have the same set size of data. It has been running for 5 hours today with no lock ups/underruns/overflows.

The i2c part of the code is well documented so I'll just explain a few bits I didn't comment in the code. The original code referenced by yamanoorsai did not make good use of the powerful numpy routines. For starters the audio data is real (integers) and rfft is approriate for these arrays.
As I was using 8 columns for the equaliser, I arranged the array into 8 rows (each with 64 elements (chunk/8)). These numbers represent the 'amplitudes' for the first 64 frequencies in jumps of (0.5*sample_rate/chunk) (0.5*44100/512= 43 Hz) i.e row 1 is the amplitudes for the following frequencies:

0, 43, 86,.......,2713
2756, 2799..............
.
.
19294,...........,22007 (row 8)

I then took the mean for each row (very crude I know) to return 8 values for the LED matrix.

There are too many areas for improvement to talk about. The most obvious one is to focus on the frequencies more applicable to music/speech etc. (maybe just 60-5000 Hz) and ignore the rest.
I hope some of you have a laugh with this - it is very cool when hooked up to an iPod or the radio.

If some audiophiles could post their successes or suggestions for improvement, I would be grateful (so that I can use it in my lessons!)
Hello,

I am trying to implement alsaaudio.PCM_Capture function as it is in this code, however if I run your code on Raspberry Pi3 with Jessie and kernel for Cirrus audio card I am getting error: data_in.pause(1) alsaaudio.ALSAAudioError: Function not implemented. Have you had anything like this? And how could this be resolved maybe without data_in.pause(1)?

Return to “Python”