[Tutor] Python Access to Computer speaker

Tim Peters tim.one@home.com
Mon, 19 Feb 2001 02:21:06 -0500

[Tim (this Tim -- that's me) bemoaned]
> This is a case where you really need to use the development docs:
>     http://python.sourceforge.net/devel-docs/lib/module-winsound.html
> They point out that-- alas --Beep()'s arguments are ignored except under
> Windows NT and 2000.  Under 95 and 98, Windows doesn't supply
> anything "that works".  Unsure about Windows ME.
> ...
> no-good-news-here!-ly y'rs  - tim

OK, I confess I got really irritated with this.  What good is having a
computer if you can't play stupid one-voice tunes on it laboriously poking
each note in by hand <wink>?  So I added Win9X support for winsound.Beep(),
and that will be released in Python 2.1.

Here's the heart of the Win9X code.  This is educational, because it
demonstrates why you want to stick with coding in Python just as long as you
can <snarl>:

static PyObject *
sound_beep(PyObject *self, PyObject *args)
	int freq;
	int dur;

	if (!PyArg_ParseTuple(args, "ii:Beep", &freq,  &dur))
		return NULL;

	if (freq < 37 || freq > 32767) {
				"frequency must be in 37 thru 32767");
		return NULL;

	/* On NT and 2000, the SDK Beep() function does the whole job.
	 * But while Beep() exists before NT, it ignores its arguments and
	 * plays the system default sound.  Sheesh ...
	 * The Win9X code is mondo bizarre.  I (Tim) pieced it together from
	 * crap all over the web.  The original IBM PC used some particular
	 * pieces of hardware (Intel 8255 and 8254 chips) hardwired to
	 * particular port addresses and running at particular clock speeds,
	 * and the poor sound card folks have been forced to emulate that in
	 * all particulars ever since.  But NT and 2000 don't support port
	 * manipulation,   Don't know about WinME; guessing it's like 98.

	if (whichOS == WinNT2000) {
		BOOL ok;
		ok = Beep(freq, dur);
		if (!ok) {
			PyErr_SetString(PyExc_RuntimeError,"Failed to beep");
			return NULL;
	else if (whichOS == Win9X) {
		int speaker_state;
		/* Force timer into oscillator mode via timer control port. */
		_outp(0x43, 0xb6);
		/* Compute ratio of ancient hardcoded timer frequency to
		 * frequency we want.  Then feed that ratio (lowest byte
		 * first) into timer data port.
		freq = 1193180 / freq;
		_outp(0x42, freq & 0xff);
		_outp(0x42, (freq >> 8) & 0xff);
		/* Get speaker control state. */
		speaker_state = _inp(0x61);
		/* Turn the speaker on (bit 1)
		 * and drive speaker from timer (bit 0).
		_outp(0x61, speaker_state | 0x3);
		/* Let it blast in peace for the duration. */
		/* Restore speaker control to original state. */
		_outp(0x61, speaker_state);
	else {
		assert(!"winsound's whichOS has insane value");
	return Py_None;

    it-until-you've-suffered-with-everything-else-ly y'rs  - tim