Using a microphone as a sensor

From XPUB & Lens-Based wiki

Uses Python and Gstreamer, requires "gstreamer python bindings" (sudo apt-get install python-gst0.10).

Reading the mike level

The following program uses GStreamer & Python to read the microphone, and prints out numbers of the current microphone "level" (the "loudness" / volume). It also maintains the minimum and maximum values. The output is simply lines of data to the screen (stdout); so the next step is to feed these numbers (with a shell pipe) into a PyGame program to visualise it.

import gst, pygst, struct

(pmin, pmax) = (None, None)

# gboolean message_handler (GstBus * bus, GstMessage * message, gpointer data)
def listener_on_message (bus, message, data):
	global pmin, pmax
	s = message.structure
	if s and s.get_name() == "level":
		rms = s['rms']
		# also available, but not used here
		# peak = s['peak']; decay = s['decay']
		# numbers are in pairs (left, right) -- we use just the first one (left?)
		p = rms[0]
		# check against/update min and max
		if (pmin==None or p<pmin): pmin = p
		if (pmax==None or p>pmax): pmax = p
		print "level", p, pmin, pmax

	return True

listener_desc = 'alsasrc ! level ! fakesink'
listener = gst.parse_launch(listener_desc)
listener.get_bus().add_watch(listener_on_message, None)

import gobject
mainloop = gobject.MainLoop()

except: # an interruption from Ctrl-C
	print "stopping"

# cleanup

Displaying the data with PyGame

A PyGame program that reads numbers from stdin (sent by the level program above), and makes a simple visualisation.


import sys, pygame, thread

def reader ():
    while True:
        line = sys.stdin.readline()
        evt = pygame.event.Event(pygame.USEREVENT, {"msg" : line.strip()})

screen = pygame.display.set_mode((640, 480), 0, 32)
clock = pygame.time.Clock()
level, lo, hi = (0, 0, 1)    
thread.start_new_thread(reader, ())

while True:
    for event in pygame.event.get():
        if event.type==pygame.QUIT or \
        (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
        elif event.type == pygame.USEREVENT:
            # print event
            level = event.msg
            msg = event.msg.split()
            cmd = msg[0]
            level = float(msg[1])
            lo = float(msg[2])
            hi = float(msg[3])
    s = (level - lo) / (hi - lo)
    pos = int(640 * s)
    screen.fill((0, 0, 0))
    pygame.draw.rect(screen, (255, 255, 255), (pos, 0, 10, 480), 0)

Putting the two together

To run this, you pipe the output of the first program into the second. Note that the "-u" option is very important -- it tells Python to not buffer (unbufferd) stdin/out. This is important to do "realtime" processing in this case.

python -u | python -u