As I blogged earlier I am writing a game (and yes, it's pretty much
playable already).
One thing I didn't mention is that I never wrote a game before. Yeah, I
know everyone does it as one of his first projects, but I never did.
So, there are some things I really have no clue about , like sound
and moving graphics around.
For the graphics stuff, QCanvas is just fine and dandy, but to make
things bloop and warble and squeak when the time is right, I found Qt's
sound support somewhat depressing.
Come on, NAS? Who uses that? And what about music? I had no idea.
So, I started trying to follow one of my leading principles of
development: find a way to make it Someone Else's Problem (TM).
The usual way to do that is finding a library that handles the problem,
write minimal glue, stick it to the side of the program, tell the
program that's his new arm, and forget about it quickly.
Here's what I found.
Mi Dios!
I thought I should start by adding one of those annoying little
tunes every game has. It's just a game tune, I don't want to have to
include a 3MB OGG file for it, so I wanted an instrument-based format.
I remembered MIDI tunes. You may know them as ringtones nowadays, but
they used to be just cheesy tunes generated by your SBPro's FM
generator, not your phone.
In fact, I remember having a little proggie called playmidi, that would
do that in Linux.
Well, it seems that in the past few years, either sound cards have
forgotten how to play them, they fell out of fashion, or something,
because the only things I found that could play MIDI are monstrosities
that require a 9MB digital instrument set. And how was I to include
that along with my 25KB game???
So, what's next? I had a C64, so...
MOD me up!
MOD files are like MIDI files, only the MOD includes it's own
instrument set, called samples, and instructions on how to repeat and
alter those samples to make a tune.
Good news: there are nice-sounding, funny MOD files that are about 30KB
in size.
Better news: There is a popular library to play them! It's called
Mikmod, and your distro has it (and it's a dependency for KDE's
multimedia packages too).
Even better news: It has support for playing simple sounds (samples in
mod lingo) by calling a couple of functions.
Awesome news: It includes a software mixer so you can just tell it to
play this, then play that, then that, and a tune in the background, and
everything sounds at the same time.
So, we have a winner. This baby can handle everything I need for the
game!
But... is that a snake in your pocket?
I can't find a Python binding for it. I am sure as soon as I post this
article someone is going to come up and tell me, here they are, moron!
But I just can't find any.
So, I decided to do something I wanted to do already and learn to use
Pyrex. Pyrex is a tool to write python extensions, with almost-free
access to C libraries, in an almost-python language (only minor syntax
differences).
That way, I could write a Python module to use Mikmod.
You know what? It was almost scarily simple . I didn't wrap all of
Mikmod because I don't need it, but now I can do stuff for games
and apps almost trivially.
Even more: Pyrex has awesome distutils support, so building the
extensions, usually a pain in the rear, is trivial (mostly you just
copy and delete stuff, with some search and replace).
One thing I found I did nicely is this: Mikmod requires you to call
Mikmod_Update every once in a while so it fills the soundcard's buffer
with stuff to play. If you don't, it skips.
So, I just started a thread that loops and takes care of it. You don't
even have to know about it to use the extension. Oh, sure, if your
Mikmod is not threadsafe, it breaks. Well, get a decent Mikmod
package, then.
How does it look?
Here's a whole noisy proggie
#Load the modules
import mikmod, time
#Init the library
mikmod.init()
#40 voices, 20 for music, 20 for random sounds (overkill)
mikmod.setNumVoices(20,20)
#Enable sound, starts the thread that pushes sound, too
mikmod.enableOutput()
#Create a module, that is, a music track
module=mikmod.Module("BasicInstinct.mod")
#Load two samples, just a couple of noises
s1=mikmod.Sample("lost.wav")
s2=mikmod.Sample("swap.wav")
#Start playing the song
module.play()
#For the duration of the song, each second, make some noise
while module.active():
s1.play()
time.sleep(0.5)
s2.play()
time.sleep(0.5)
#Close the mikmod library, stop the thread, etc.
mikmod.exit()