]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Take a tour of hell's seedier neighborhoods to try to make winsound.Beep()
authorTim Peters <tim.peters@gmail.com>
Mon, 19 Feb 2001 07:06:36 +0000 (07:06 +0000)
committerTim Peters <tim.peters@gmail.com>
Mon, 19 Feb 2001 07:06:36 +0000 (07:06 +0000)
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
Misc/NEWS
PC/winsound.c

index 589658f3bcc2897bdd9c758c1f97d6b5ae08112c..317e206b3627e206335a740f83e33b8c44c58c96 100644 (file)
@@ -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}
 
index 5bc906af8550b9e076d0f9013b339ca2d78dc824..73327d19032a3f8255f0722f1da3e00de65b5d5c 100644 (file)
--- 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?
 =================================
 
index bc96387a6b8bf62748d92bad8ad9343d3a877b71..77572abac28d9b5bdc3cc491e41b6c0597bb1fee 100644 (file)
@@ -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 <windows.h>
 #include <mmsystem.h>
+#include <conio.h>     /* port functions on Win9x */
 #include <Python.h>
 
 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);
-
-    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);
+       OSVERSIONINFO version;
+
+       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;
 }