While thinking of ways to improve my QRSS VD high-definitions spectrograph software, I often wish I had a better way to display large spectrographs. Currently I'm using PIL (the Python Imaging Library) with TK and it's slow as heck. I looked into the PyGame project, and it seems to be designed with speed in mind. I whipped-up this quick demo, and it's a simple case audio spectrograph which takes in audio from your sound card and graphs it time vs. frequency. This method is far superior to the method I was using previously to display the data, because while QRSS VD can only update the entire GUI (500px by 8,000 px) every 3 seconds, early tests with PyGame suggests it can do it about 20 times a second (wow!). With less time/CPU going into the GUI, the program can be more responsivle and my software can be less of a drain.
import pygame import numpy import threading import pyaudio import scipy import scipy.fftpack import scipy.io.wavfile import wave rate = 12000 # try 5000 for HD data, 48000 for realtime soundcard = 2 windowWidth = 500 fftsize = 512 currentCol = 0 scooter =  overlap = 5 # 1 for raw, realtime - 8 or 16 for high-definition def graphFFT(pcm): global currentCol, data ffty = scipy.fftpack.fft(pcm) # convert WAV to FFT ffty = abs(ffty[0:len(ffty)/2])/500 # FFT is mirror-imaged # ffty=(scipy.log(ffty))*30-50 # if you want uniform data print "MIN:t%stMAX:t%s" % (min(ffty), max(ffty)) for i in range(len(ffty)): if ffty[i] < 0: ffty[i] = 0 if ffty[i] > 255: ffty[i] = 255 scooter.append(ffty) if len(scooter) < 6: return scooter.pop(0) ffty = (scooter+scooter*2+scooter*3+scooter*2+scooter)/9 data = numpy.roll(data, -1, 0) data[-1] = ffty[::-1] currentCol += 1 if currentCol == windowWidth: currentCol = 0 def record(): p = pyaudio.PyAudio() inStream = p.open(format=pyaudio.paInt16, channels=1, rate=rate, input_device_index=soundcard, input=True) linear = *fftsize while True: linear = linear[fftsize/overlap:] pcm = numpy.fromstring(inStream.read( fftsize/overlap), dtype=numpy.int16) linear = numpy.append(linear, pcm) graphFFT(linear) pal = [(max((x-128)*2, 0), x, min(x*2, 255)) for x in xrange(256)] print max(pal), min(pal) data = numpy.array(numpy.zeros((windowWidth, fftsize/2)), dtype=int) # data=Numeric.array(data) # for older PyGame that requires Numeric pygame.init() # crank up PyGame pygame.display.set_caption("Simple Spectrograph") screen = pygame.display.set_mode((windowWidth, fftsize/2)) world = pygame.Surface((windowWidth, fftsize/2), depth=8) # MAIN SURFACE world.set_palette(pal) t_rec = threading.Thread(target=record) # make thread for record() t_rec.daemon = True # daemon mode forces thread to quit with program t_rec.start() # launch thread clk = pygame.time.Clock() while 1: for event in pygame.event.get(): # check if we need to exit if event.type == pygame.QUIT: pygame.quit() sys.exit() pygame.surfarray.blit_array(world, data) # place data in window screen.blit(world, (0, 0)) pygame.display.flip() # RENDER WINDOW clk.tick(30) # limit to 30FPS