From ae8298bfb78eebc6c1bf914c89ec397121640a94 Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Mon, 5 Sep 2016 16:31:21 -0500 Subject: Closes #11620: Fix support for SND_MEMORY in winsound.PlaySound. Based on a patch by Tim Lesher. --- Doc/library/winsound.rst | 5 +++-- Lib/test/test_winsound.py | 16 ++++++++++++++++ Misc/NEWS | 3 +++ PC/clinic/winsound.c.h | 8 ++++---- PC/winsound.c | 49 ++++++++++++++++++++++++++++++++++++----------- 5 files changed, 64 insertions(+), 17 deletions(-) diff --git a/Doc/library/winsound.rst b/Doc/library/winsound.rst index d2c4210..e72d025 100644 --- a/Doc/library/winsound.rst +++ b/Doc/library/winsound.rst @@ -25,7 +25,8 @@ provided by Windows platforms. It includes functions and several constants. .. function:: PlaySound(sound, flags) Call the underlying :c:func:`PlaySound` function from the Platform API. The - *sound* parameter may be a filename, audio data as a string, or ``None``. Its + *sound* parameter may be a filename, a system sound alias, audio data as a + :term:`bytes-like object`, or ``None``. Its interpretation depends on the value of *flags*, which can be a bitwise ORed combination of the constants described below. If the *sound* parameter is ``None``, any currently playing waveform sound is stopped. If the system @@ -92,7 +93,7 @@ provided by Windows platforms. It includes functions and several constants. .. data:: SND_MEMORY The *sound* parameter to :func:`PlaySound` is a memory image of a WAV file, as a - string. + :term:`bytes-like object`. .. note:: diff --git a/Lib/test/test_winsound.py b/Lib/test/test_winsound.py index 4a8ab7d..1cfef77 100644 --- a/Lib/test/test_winsound.py +++ b/Lib/test/test_winsound.py @@ -87,6 +87,22 @@ class PlaySoundTest(unittest.TestCase): winsound.PlaySound, "none", winsound.SND_ASYNC | winsound.SND_MEMORY ) + self.assertRaises(TypeError, winsound.PlaySound, b"bad", 0) + self.assertRaises(TypeError, winsound.PlaySound, "bad", + winsound.SND_MEMORY) + self.assertRaises(TypeError, winsound.PlaySound, 1, 0) + + def test_snd_memory(self): + with open(support.findfile('pluck-pcm8.wav', + subdir='audiodata'), 'rb') as f: + audio_data = f.read() + safe_PlaySound(audio_data, winsound.SND_MEMORY) + audio_data = bytearray(audio_data) + safe_PlaySound(audio_data, winsound.SND_MEMORY) + + def test_snd_filename(self): + fn = support.findfile('pluck-pcm8.wav', subdir='audiodata') + safe_PlaySound(fn, winsound.SND_FILENAME | winsound.SND_NODEFAULT) def test_aliases(self): aliases = [ diff --git a/Misc/NEWS b/Misc/NEWS index e503547..5fdabe0 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -75,6 +75,9 @@ Core and Builtins Library ------- +- Issue #11620: Fix support for SND_MEMORY in winsound.PlaySound. Based on a + patch by Tim Lesher. + - Issue #11734: Add support for IEEE 754 half-precision floats to the struct module. Based on a patch by Eli Stevens. diff --git a/PC/clinic/winsound.c.h b/PC/clinic/winsound.c.h index cdb2045..a4c3938 100644 --- a/PC/clinic/winsound.c.h +++ b/PC/clinic/winsound.c.h @@ -17,16 +17,16 @@ PyDoc_STRVAR(winsound_PlaySound__doc__, {"PlaySound", (PyCFunction)winsound_PlaySound, METH_VARARGS, winsound_PlaySound__doc__}, static PyObject * -winsound_PlaySound_impl(PyObject *module, Py_UNICODE *sound, int flags); +winsound_PlaySound_impl(PyObject *module, PyObject *sound, int flags); static PyObject * winsound_PlaySound(PyObject *module, PyObject *args) { PyObject *return_value = NULL; - Py_UNICODE *sound; + PyObject *sound; int flags; - if (!PyArg_ParseTuple(args, "Zi:PlaySound", + if (!PyArg_ParseTuple(args, "Oi:PlaySound", &sound, &flags)) { goto exit; } @@ -100,4 +100,4 @@ winsound_MessageBeep(PyObject *module, PyObject *args) exit: return return_value; } -/*[clinic end generated code: output=1044b2adf3c67014 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=b999334e2e444ad2 input=a9049054013a1b77]*/ diff --git a/PC/winsound.c b/PC/winsound.c index 6b79d23..000ddd8 100644 --- a/PC/winsound.c +++ b/PC/winsound.c @@ -64,7 +64,7 @@ module winsound /*[clinic input] winsound.PlaySound - sound: Py_UNICODE(accept={str, NoneType}) + sound: object The sound to play; a filename, data, or None. flags: int Flag values, ored together. See module documentation. @@ -74,22 +74,49 @@ A wrapper around the Windows PlaySound API. [clinic start generated code]*/ static PyObject * -winsound_PlaySound_impl(PyObject *module, Py_UNICODE *sound, int flags) -/*[clinic end generated code: output=ec24b3a2b4368378 input=3411b1b7c1f36d93]*/ +winsound_PlaySound_impl(PyObject *module, PyObject *sound, int flags) +/*[clinic end generated code: output=49a0fd16a372ebeb input=7bdf637f10201d37]*/ { int ok; - - 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"); - return NULL; + wchar_t *wsound; + Py_buffer view = {NULL, NULL}; + + if (sound == Py_None) { + wsound = NULL; + } else if (flags & SND_MEMORY) { + if (flags & SND_ASYNC) { + /* Sidestep reference counting headache; unfortunately this also + prevent SND_LOOP from memory. */ + PyErr_SetString(PyExc_RuntimeError, + "Cannot play asynchronously from memory"); + return NULL; + } + if (PyObject_GetBuffer(sound, &view, PyBUF_SIMPLE) < 0) { + return NULL; + } + wsound = (wchar_t *)view.buf; + } else { + if (!PyUnicode_Check(sound)) { + PyErr_Format(PyExc_TypeError, + "'sound' must be str or None, not '%s'", + Py_TYPE(sound)->tp_name); + return NULL; + } + wsound = PyUnicode_AsWideCharString(sound, NULL); + if (wsound == NULL) { + return NULL; + } } + Py_BEGIN_ALLOW_THREADS - ok = PlaySoundW(sound, NULL, flags); + ok = PlaySoundW(wsound, NULL, flags); Py_END_ALLOW_THREADS + if (view.obj) { + PyBuffer_Release(&view); + } else if (sound != Py_None) { + PyMem_Free(wsound); + } if (!ok) { PyErr_SetString(PyExc_RuntimeError, "Failed to play sound"); return NULL; -- cgit v0.12