Skip to content

Instantly share code, notes, and snippets.

@gabrielelanaro
Created February 23, 2011 19:50
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gabrielelanaro/841028 to your computer and use it in GitHub Desktop.
Save gabrielelanaro/841028 to your computer and use it in GitHub Desktop.
import pyaudio
import wave
import numpy as np
from contextlib import contextmanager
import math
p = pyaudio.PyAudio()
chunk = 1024*4
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
@contextmanager
def audio_open():
"""Context to be used with the with statement, it yields the audio stream
"""
stream = p.open(format = FORMAT,
channels = CHANNELS,
rate = RATE,
input = True,
#input_device_index=0,
#output = True,
frames_per_buffer = chunk)
try:
yield stream
except:
stream.close()
p.terminate()
raise
else:
stream.close()
p.terminate()
def listen(seconds, callback):
"""Listen for tot *seconds* from the default input for frequencies.
For each frequency it calls *callback* with the frequency as the only argument.
"""
with audio_open() as stream:
for i in range(0, RATE / chunk * seconds):
data = stream.read(chunk)
# use a Blackman window
window = np.blackman(chunk)
# find the frequency of each chunk
swidth = p.get_sample_size(FORMAT)
# unpack the data and times by the hamming window
indata = np.array(wave.struct.unpack("%dh"%(len(data)/swidth),\
data))*window
# Take the fft and square each value
fftData=abs(np.fft.rfft(indata))**2
# find the maximum
which = fftData[1:].argmax() + 1
# use quadratic interpolation around the max
if which != len(fftData)-1:
y0,y1,y2 = np.log(fftData[which-1:which+2:])
x1 = (y2 - y0) * .5 / (2 * y1 - y2 - y0)
# find the frequency and output it
thefreq = (which+x1)*RATE/chunk
callback(thefreq)
else:
thefreq = which*RATE/chunk
callback(thefreq)
# read some more data
data = stream.read(chunk)
def freq_to_notename_cent(freq):
e = 440.0
if e > freq:
while e > freq:
e = e / 2
else:
while e < freq/2:
e = e * 2
d = freq / e
v = 12 * math.log(d) / math.log(2)
i = int(v)
cent = (v-i) * 100
n = ('a', 'ais', 'b', 'c', 'cis', 'd', 'dis', 'e', 'f', 'fis', 'g', 'gis')
if cent > 50:
return n[(i+1) % 12], cent-100
return n[int(v)], (v-int(v)) * 100
def test():
def cb(freq):
if 80<freq<1100:
print freq_to_notename_cent(freq), freq, 'Hz'
listen(10, cb)
if __name__ == '__main__':
import threading
threading.Thread(target=test).start()
import gtk,cairo,glib
import tellnote
import threading
gtk.gdk.threads_init()
class TunerThread(threading.Thread):
def __init__(self, widg):
super(TunerThread,self).__init__()
self.widg = widg
self.note = widg.note
def run(self):
tellnote.listen(20, self.on_freq)
def on_freq(self, freq):
if 80<freq<1200:
name,cent = tellnote.freq_to_notename_cent(freq)
if name == self.note:
glib.idle_add(self.update_gui, cent)
def update_gui(self, cent):
self.widg.cent = cent
print "Draw", cent
self.widg.queue_draw()
class Tuner(gtk.DrawingArea):
def __init__(self, label, note):
"""label: which label to display in the tuner
"""
super(Tuner, self).__init__()
self.set_size_request(200, 30)
self.label=label
self.connect('expose-event', self.expose)
self.cent=0
self.note = note
TunerThread(self).start()
def expose(self, widget, event):
cr = widget.window.cairo_create()
cr.set_line_width(0.8)
cr.select_font_face('Sans', cairo.FONT_SLANT_NORMAL,
cairo.FONT_WEIGHT_NORMAL)
cr.set_font_size(20)
self._draw_bg(cr)
self._draw_line(cr, self.cent)
self._draw_text(cr)
def _draw_bg(self, cr):
cr.set_source_rgb(1.0, 1.0, 1.0)
cr.rectangle(0,0,self.allocation.width,30)
cr.paint()
cr.clip()
def _draw_text(self,cr):
(x, y, width, height, dx, dy) = cr.text_extents(self.label)
cr.set_source_rgb(0,0,0)
cr.move_to(100-(width/2 + x),15 - (height/2 + y))
cr.show_text(self.label)
cr.stroke()
def _draw_line(self, cr, cents):
width = self.allocation.width
x = width/2 + cents/100*width
cr.set_source_rgb(0.81640625,0.81640625,0.81640625)
cr.set_line_width(10)
cr.set_line_cap(cairo.LINE_CAP_ROUND)
cr.move_to(x, 4)
cr.line_to(x, 26)
cr.stroke()
def test():
win = gtk.Window()
win.add(Tuner('A', 'a'))
win.show_all()
win.connect('delete-event', lambda w,e: gtk.main_quit())
gtk.main()
if __name__ == '__main__':
test()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment