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!
This commit is contained in:
Tim Peters 2001-02-19 07:06:36 +00:00
parent c761fc87d2
commit 25a9ce371c
3 changed files with 112 additions and 42 deletions

View 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}

View File

@ -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?
=================================

View 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);
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;
}