Help with Optimization of Python software: real-time audio controller

craiglewiston at gmail.com craiglewiston at gmail.com
Sun Feb 11 21:40:03 EST 2007


I've been working on a Python-based music training application for a
few months, and have recently run into what I believe are some basic
limitations (or at least constraints) of Python with regards to
timing.  I'll try and give a decently thorough description, and
hopefully some of you can process this with your knowledge and help
steer me in the right direction.

As mentioned above, my application deals with music training. I have a
Tkinter GUI, which communicates via pyserial with an external device I
built myself.  The code reads in a MIDI file and converts the song to
"output data " for the external device and the GUI, all adjusted to
the user-selected tempo.  While the MIDI file is playing, there are 4
main actions the code must take:
1) Send "output data" signals out to the external hardware.
2) Poll the input from the external hardware device for incoming
keypresses.  If detected, then the play the sound corresponding to
that key.
3) Start and keep a metronome running for the durations of the song.
4) Update GUI

I'm able to get all the above "accomplished" through the use of
threads and a top-level loop (for updating the Tkinter GUI  - if
you've worked with threads and Tkinter, then you know that you can't
update a Tkinter GUI from a thread, but instead have to do it on the
top level loop).

While running, I have  the following threads implemented :
1) song processing thread - this thread steps incrementally through a
data matrix (the "output data"), and updates two variables:  GUI_vars
and  Device_vars
2) metronome thread  - this thread starts at the same time as thread
#1, and outputs a "beep" to the audio chain
3) poll input thread  - this thread loops continually, looking for
data on the serial Input.  When data is found, it starts playing the
sound corresponding to that key until a "note off" signal is received
4) top-level recursive loop (technically not a thread) for updating
GUI and calling Serial Write subfunction.  This loop monitors GUI_vars
and Device_vars, updating the GUI and writing out to the device when
either variable has changed

Currently, I have all of the above "working", although I'm running
into some serious timing issues.  When I run the program, I get
irregular timing for my metronome (if it sounds at all), as well as
irregular timing in writing to the external device.  It's extremely
crucial that threads #1 & #2 are executed as close to real-time as
possible, as they form the "core" of the song, and their elements
can't be delayed without "messing" the song up considerably.


I've read up quite a bit on different optimization methods in Python,
but am not sure which direction to head.  I've checked out profile,
Psyco, Pyrex, as well as just porting everything over to C.  Since I'm
on a Mac (Power PC), I can't use Psyco.  And doing any of the others
seemed like a big enough project that I should really ask someone else
before I embark.

So, for a music-based application where it's crucial to have real-time
execution of serial writeouts and audio, as well as keeping a
continual poll on the input from the same port....can this be done
successfully in Python?  Does using Tkinter have anything to do with
my timing issues?  Would it benefit me to move over to wxPython
(something I've been considering doing)?  As for the metronome, should
I incorporate the metronome thread into the "song processing" thread,
since both are dealing with events whose timing is crucial?

I'm a relative newbie (this is my first Python project) to Python, so
any help is GREATLY appreciated!

Also, just for reference, here's a list of the modules I'm using and
my system info:
Audio:  SndObj
Ser com: pyserial
GUI: Tkinter
System: Apple PowerBook G4 (PowerPC), Mac OS 10.4, Python 2.4.4

Thanks,
Craig Lewiston




More information about the Python-list mailing list