2/11/2004

CoreAudio from Python

Well, I've only been talking about doing it for 4 years now, but I finally got Python playing some buffers of audio using CoreAudio. It was about a day's worth of hacking, with a few hours on the side nailing down threading issues.

I put the code up here:

http://soundfarmer.com/content/code/coreaudio/

I also put a short description of what it is on the pythonmac wiki:

http://pythonmac.org/wiki/CoreAudio

CoreAudio calls you back in a thread, you see. A thread Python hasn't seen before. Python doesn't like that very much. This isn't the first time I have tackled this issue. CarbonEvents also call you back in a new thread you haven't seen yet. Two years ago when I wrote the CarbonEvent wrappers for Python, there were no convenient ways to deal with this situation. Before you are allowed to request the global interpreter lock, you must have a PyThreadState. You can create a new PyThreadState, but only if you have a PyInterpreterState. And if you are getting called back from a new thread that has never seen Python before, you are pretty much out of luck.
The solution I came up with for CarbonEvents, later on Windows talking to a stupid Asset Management system, and finally for CoreAudio, is to make an initialization call into my module which will eventually be generating callbacks into Python. In this call, I stash the current PyInterpreterState away in a C global. Then, when the new thread comes in and needs to make a PyThreadState you can use the stashed PyInterpreterState.
This time I was trying to use Pyrex to wrap the C calls I needed, partially because I thought it would be fast and easy and partially because I wanted to learn Pyrex. During the build-crash-debug cycle I kept thinking to myself there was an easier way that I had read about a while ago, and I was right: http://python.org/peps/pep-0311.html describes an implementation of an API specifically designed for this situation which is included in Python 2.3.
I'm happy to say it works quite well, and I was able to generate my sine wave in python code. I even went on later and used numarray to wrap the incoming and outgoing buffer for a speed boost.
My favorite thing about Python is that it is trivial for those who aren't afraid of C to get access to literally everything they need, if it's not possible already.

No comments: