From 25a9ce371c1b43094f5e108c3fff834833c68342 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Mon, 19 Feb 2001 07:06:36 +0000 Subject: [PATCH] Take a tour of hell's seedier neighborhoods to try to make winsound.Beep() do something non-useless on Win9X boxes. WinME unknown to me. Someone with NT/2000 make sure it still works there! --- Doc/lib/libwinsound.tex | 15 +++-- Misc/NEWS | 7 +++ PC/winsound.c | 132 +++++++++++++++++++++++++++++----------- 3 files changed, 112 insertions(+), 42 deletions(-) diff --git a/Doc/lib/libwinsound.tex b/Doc/lib/libwinsound.tex index 589658f3bcc..317e206b362 100644 --- a/Doc/lib/libwinsound.tex +++ b/Doc/lib/libwinsound.tex @@ -17,15 +17,14 @@ two functions and several constants. \begin{funcdesc}{Beep}{frequency, duration} Beep the PC's speaker. The \var{frequency} parameter specifies frequency, in hertz, of the - sound, and must be in the range 37 through 32,767 (\code{0x25} - through \code{0x7fff}). The \var{duration} parameter specifies the - number of milliseconds the sound should last. If the system is not + sound, and must be in the range 37 through 32,767. + The \var{duration} parameter specifies the number of milliseconds the + sound should last. If the system is not able to beep the speaker, \exception{RuntimeError} is raised. - \strong{Note:} Under Windows 95 and 98, the arguments are ignored; - if the system has a sound card, the system default sound is played - (typically \file{ding.wav}, or whatever is registered as the default - sound via Control Panel -> Sounds); else (no sound card) the - standard system beep. + \strong{Note:} Under Windows 95 and 98, the Windows \cfunction{Beep()} + function exists but is useless (it ignores its arguments). In rhat + case Python simulates it via direct port manipulation (added in version + 2.1). It's unknown whether that will work on all systems. \versionadded{1.6} \end{funcdesc} diff --git a/Misc/NEWS b/Misc/NEWS index 5bc906af855..73327d19032 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -9,6 +9,13 @@ Windows changes - Build: Subproject _test (effectively) renamed to _testcapi. +- winsound module: Under Win9x, winsound.Beep() now attempts to simulate + what it's supposed to do (and does do under NT and 2000) via direct + port manipulation. It's unknown whether this will work on all systems, + but it does work on my Win98SE system now and was known to be useless on + all Win9x systems before. + + What's New in Python 2.1 alpha 2? ================================= diff --git a/PC/winsound.c b/PC/winsound.c index bc96387a6b8..77572abac28 100644 --- a/PC/winsound.c +++ b/PC/winsound.c @@ -9,13 +9,14 @@ /* Modified by Guido van Rossum */ /* Beep added by Mark Hammond */ +/* Win9X Beep and platform identification added by Uncle Timmy */ /* Example: import winsound import time - # Play wav file + # Play wav file winsound.PlaySound('c:/windows/media/Chord.wav', winsound.SND_FILENAME) # Play sound from control panel settings @@ -36,6 +37,7 @@ #include #include +#include /* port functions on Win9x */ #include static char sound_playsound_doc[] = @@ -48,11 +50,11 @@ static char sound_beep_doc[] = "Beep(frequency, duration) - a wrapper around the Windows Beep API\n" "\n" "The frequency argument specifies frequency, in hertz, of the sound.\n" -"This parameter must be in the range 37 through 32,767 (0x25 through 0x7FFF).\n" -"The duration argument specifies the number of milli-seconds.\n" -"Note: Under Windows 95 and 98, the arguments are ignored; if the system\n" -"has a sound card, the system default sound is played; else (no sound card)\n" -"the standard system beep.\n"; +"This parameter must be in the range 37 through 32,767.\n" +"The duration argument specifies the number of milliseconds.\n" +"On WinNT and 2000, the platform Beep API is used directly. Else funky\n" +"code doing direct port manipulation is used; it's unknown whether that\n" +"will work on all systems.\n"; static char sound_module_doc[] = "PlaySound(sound, flags) - play a sound\n" @@ -68,20 +70,19 @@ static char sound_module_doc[] = "\n" "Beep(frequency, duration) - Make a beep through the PC speaker.\n"; -PyObject *sound_playsound(PyObject *s, PyObject *args) +PyObject * +sound_playsound(PyObject *s, PyObject *args) { const char *sound; int flags; int length; int ok; - if(!PyArg_ParseTuple(args,"z#i:PlaySound",&sound,&length,&flags)) - { + if(!PyArg_ParseTuple(args,"z#i:PlaySound",&sound,&length,&flags)) { return NULL; } - if(flags&SND_ASYNC && flags &SND_MEMORY) - { + if(flags&SND_ASYNC && flags &SND_MEMORY) { /* Sidestep reference counting headache; unfortunately this also prevent SND_LOOP from memory. */ PyErr_SetString(PyExc_RuntimeError,"Cannot play asynchronously from memory"); @@ -101,22 +102,73 @@ PyObject *sound_playsound(PyObject *s, PyObject *args) return Py_None; } -static PyObject *sound_beep( PyObject *self, PyObject *args ) +enum OSType {Win9X, WinNT2000}; +static enum OSType whichOS; /* set by module init */ + +static PyObject * +sound_beep(PyObject *self, PyObject *args) { int freq; int dur; - BOOL ok; if (!PyArg_ParseTuple(args, "ii:Beep", &freq, &dur)) return NULL; - Py_BEGIN_ALLOW_THREADS - ok = Beep(freq,dur); - Py_END_ALLOW_THREADS - if(!ok) - { - PyErr_SetString(PyExc_RuntimeError,"Failed to beep"); - return NULL; - } + + if (freq < 37 || freq > 32767) { + PyErr_SetString(PyExc_ValueError, + "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; + Py_BEGIN_ALLOW_THREADS + ok = Beep(freq, dur); + Py_END_ALLOW_THREADS + 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. */ + Py_BEGIN_ALLOW_THREADS + Sleep(dur); + Py_END_ALLOW_THREADS + /* Restore speaker control to original state. */ + _outp(0x61, speaker_state); + } + else { + assert(!"winsound's whichOS has insane value"); + } Py_INCREF(Py_None); return Py_None; } @@ -128,7 +180,8 @@ static struct PyMethodDef sound_methods[] = {NULL, NULL} }; -static void add_define(PyObject *dict, const char *key, long value) +static void +add_define(PyObject *dict, const char *key, long value) { PyObject *k=PyString_FromString(key); PyObject *v=PyLong_FromLong(value); @@ -145,17 +198,28 @@ static void add_define(PyObject *dict, const char *key, long value) DL_EXPORT(void) initwinsound(void) { - PyObject *module=Py_InitModule3("winsound", sound_methods, sound_module_doc); - PyObject *dict=PyModule_GetDict(module); + OSVERSIONINFO version; - ADD_DEFINE(SND_ASYNC); - ADD_DEFINE(SND_NODEFAULT); - ADD_DEFINE(SND_NOSTOP); - ADD_DEFINE(SND_NOWAIT); - ADD_DEFINE(SND_ALIAS); - ADD_DEFINE(SND_FILENAME); - ADD_DEFINE(SND_MEMORY); - ADD_DEFINE(SND_PURGE); - ADD_DEFINE(SND_LOOP); - ADD_DEFINE(SND_APPLICATION); + PyObject *module = Py_InitModule3("winsound", + sound_methods, + sound_module_doc); + PyObject *dict = PyModule_GetDict(module); + + ADD_DEFINE(SND_ASYNC); + ADD_DEFINE(SND_NODEFAULT); + ADD_DEFINE(SND_NOSTOP); + ADD_DEFINE(SND_NOWAIT); + ADD_DEFINE(SND_ALIAS); + ADD_DEFINE(SND_FILENAME); + ADD_DEFINE(SND_MEMORY); + ADD_DEFINE(SND_PURGE); + ADD_DEFINE(SND_LOOP); + ADD_DEFINE(SND_APPLICATION); + + version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&version); + whichOS = Win9X; + if (version.dwPlatformId != VER_PLATFORM_WIN32s && + version.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS) + whichOS = WinNT2000; }