Python Sound

One-liner Guitar Tuner in Python


On windows, assuming imports are free:

import winsound
winsound.Beep(220*((2**(1/12.0))**7), 2000)

But that’s just because I like to tune to E. If you prefer a more “natural looking” note, you can use A:

winsound.Beep(110, 1000)

But why choose at all when you can go for all of them?

[winsound.Beep(220*((2**(1/12.0))**i), 500) for i in [7, 2, -2, -7, -12, -17]]

Image by Keela84

Programming Projects Python Sound

Preparing PyImprov for GeekCon on Friday

A long long time ago, I wrote Pytuner. It was one of the first projects I published on this website.
For a long time it just sat there, doing nothing, while the library it’s based on – PyMedia, wasn’t being maintained anymore, and PyTuner could only work on Python 2.4.

Enter GeekCon – GeekCon is a get together of people looking to work on their wildest fantasy projects – things that they don’t get do because of their regular work. Last yet I worked on a real life 3d lunar lander, and this year I thought I’d take the opportunity to work on PyImprov – my wild fantasy project.
The idea is simple – I’ll play some simple chords, and the script will improvise some blues solo. To that purpose, I wrote (also a long time ago) a chord recognizer. Now I’m missing a beat recognizer, and a simple improviser, which I plan to complete during the GeekCon weekend.

In preparation for the event, I’ve opened up a subversion repository for PyImprov on Assembla, and patched the scripts to work with PyAudio instead of PyMedia.

So, onwards to GeekCon, see you on the other side, with a guitar and a Python script in hand!

Algorithms Programming Python Sound

How PyTuner works

PyTuner is really quite simple. Here is the outline of the algorithm behind it:

  1. Record audio data with pymedia.
  2. Compute the FFT of the audio data – now we have the strength of each frequency in the sample.
  3. Find peaks in the result of the FFT. This means looking for the dominant frequencies.
  4. Find the lowest frequency that has a peak besides the zero frequency.
  5. Find the nearest ‘desired note’ (the notes we should tune to) to that frequency.
  6. Mark the difference between the actual frequency and the desired note frequency.

There is more to the algorithm. First, to find peaks, I used a simple algorithm – I computed the average strength of the frequencies in the sample, and looked at all the frequencies above two standard deviations above that average. This yields an array, where each frequency is marked by 0, or 1. (Instead of 1 you can mark it with its original strength). For each consecutive sequence of frequencies with 1 – I computed the average of the run, and that was the peak location. So for example, if the data was:
0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0,
The peak would be in the frequency matching index 5.
(If you used the original strength instead of 0 and 1, you can apply a weighted average to get a more accurate result)

To find the actual note of a given frequency, I used the fact that notes (half-tones) are separated by a ratio of 2^(1/12) . (the twelfth root of 2). So, if our base note is A, at 110 Hz, to compute the note we would use the following code snippet:

note_names = "A A# B C C# D D# E F F# G G#".split()
note_index = math.log(note_freq/base_freq, 2**(1.0/12))
note_name = note_names[note_index%12]

While writing PyTuner, at first I implemented the FFT on my own, just to know how to do it once. I found it helped me to better understand the nature of the algorithm, and its output. I find the practice of implementing various algorithms on your own, for educational purposes, quite healthy. I always like to know how things work ‘behind the scenes’.