diff --git a/Doc/c-api/bytes.rst b/Doc/c-api/bytes.rst index 82c2557368371f..b3cd26a8504715 100644 --- a/Doc/c-api/bytes.rst +++ b/Doc/c-api/bytes.rst @@ -371,6 +371,8 @@ Getters Get the writer size. + The function cannot fail. + .. c:function:: void* PyBytesWriter_GetData(PyBytesWriter *writer) Get the writer data: start of the internal buffer. @@ -378,6 +380,8 @@ Getters The pointer is valid until :c:func:`PyBytesWriter_Finish` or :c:func:`PyBytesWriter_Discard` is called on *writer*. + The function cannot fail. + Low-level API ^^^^^^^^^^^^^ diff --git a/Doc/library/wave.rst b/Doc/library/wave.rst index 9d30a14f112937..ff020b52da3f23 100644 --- a/Doc/library/wave.rst +++ b/Doc/library/wave.rst @@ -9,19 +9,14 @@ -------------- The :mod:`!wave` module provides a convenient interface to the Waveform Audio -"WAVE" (or "WAV") file format. - -The module supports uncompressed PCM and IEEE floating-point WAV formats. +"WAVE" (or "WAV") file format. Only uncompressed PCM encoded wave files are +supported. .. versionchanged:: 3.12 Support for ``WAVE_FORMAT_EXTENSIBLE`` headers was added, provided that the extended format is ``KSDATAFORMAT_SUBTYPE_PCM``. -.. versionchanged:: next - - Support for reading and writing ``WAVE_FORMAT_IEEE_FLOAT`` files was added. - The :mod:`!wave` module defines the following function and exception: @@ -65,21 +60,6 @@ The :mod:`!wave` module defines the following function and exception: specification or hits an implementation deficiency. -.. data:: WAVE_FORMAT_PCM - - Format code for uncompressed PCM audio. - - -.. data:: WAVE_FORMAT_IEEE_FLOAT - - Format code for IEEE floating-point audio. - - -.. data:: WAVE_FORMAT_EXTENSIBLE - - Format code for WAVE extensible headers. - - .. _wave-read-objects: Wave_read Objects @@ -118,14 +98,6 @@ Wave_read Objects Returns number of audio frames. - .. method:: getformat() - - Returns the frame format code. - - This is one of :data:`WAVE_FORMAT_PCM`, - :data:`WAVE_FORMAT_IEEE_FLOAT`, or :data:`WAVE_FORMAT_EXTENSIBLE`. - - .. method:: getcomptype() Returns compression type (``'NONE'`` is the only supported type). @@ -140,8 +112,8 @@ Wave_read Objects .. method:: getparams() Returns a :func:`~collections.namedtuple` ``(nchannels, sampwidth, - framerate, nframes, comptype, compname)``, equivalent to output - of the ``get*()`` methods. + framerate, nframes, comptype, compname)``, equivalent to output of the + ``get*()`` methods. .. method:: readframes(n) @@ -218,9 +190,6 @@ Wave_write Objects Set the sample width to *n* bytes. - For :data:`WAVE_FORMAT_IEEE_FLOAT`, only 4-byte (32-bit) and - 8-byte (64-bit) sample widths are supported. - .. method:: getsampwidth() @@ -269,32 +238,11 @@ Wave_write Objects Return the human-readable compression type name. - .. method:: setformat(format) - - Set the frame format code. - - Supported values are :data:`WAVE_FORMAT_PCM` and - :data:`WAVE_FORMAT_IEEE_FLOAT`. - - When setting :data:`WAVE_FORMAT_IEEE_FLOAT`, the sample width must be - 4 or 8 bytes. - - - .. method:: getformat() - - Return the current frame format code. - - .. method:: setparams(tuple) - The *tuple* should be - ``(nchannels, sampwidth, framerate, nframes, comptype, compname, format)``, - with values valid for the ``set*()`` methods. Sets all parameters. - - For backwards compatibility, a 6-item tuple without *format* is also - accepted and defaults to :data:`WAVE_FORMAT_PCM`. - - For ``format=WAVE_FORMAT_IEEE_FLOAT``, *sampwidth* must be 4 or 8. + The *tuple* should be ``(nchannels, sampwidth, framerate, nframes, comptype, + compname)``, with values valid for the ``set*()`` methods. Sets all + parameters. .. method:: getparams() @@ -331,6 +279,3 @@ Wave_write Objects Note that it is invalid to set any parameters after calling :meth:`writeframes` or :meth:`writeframesraw`, and any attempt to do so will raise :exc:`wave.Error`. - - For :data:`WAVE_FORMAT_IEEE_FLOAT` output, a ``fact`` chunk is written as - required by the WAVE specification for non-PCM formats. diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index d5b14216770906..459846e55ccf70 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -1518,21 +1518,6 @@ typing wave ---- -* Added support for IEEE floating-point WAVE audio - (``WAVE_FORMAT_IEEE_FLOAT``) in :mod:`wave`. - -* Added :meth:`wave.Wave_read.getformat`, :meth:`wave.Wave_write.getformat`, - and :meth:`wave.Wave_write.setformat` for explicit frame format handling. - -* :meth:`wave.Wave_write.setparams` accepts both 7-item tuples including - ``format`` and 6-item tuples for backwards compatibility (defaulting to - ``WAVE_FORMAT_PCM``). - -* ``WAVE_FORMAT_IEEE_FLOAT`` output now includes a ``fact`` chunk, - as required for non-PCM WAVE formats. - -(Contributed by Lionel Koenig and Michiel W. Beijen in :gh:`60729`.) - * Removed the ``getmark()``, ``setmark()`` and ``getmarkers()`` methods of the :class:`~wave.Wave_read` and :class:`~wave.Wave_write` classes, which were deprecated since Python 3.13. diff --git a/Lib/test/audiodata/pluck-float32.wav b/Lib/test/audiodata/pluck-float32.wav deleted file mode 100644 index 2030fb16d6e3bd..00000000000000 Binary files a/Lib/test/audiodata/pluck-float32.wav and /dev/null differ diff --git a/Lib/test/audiotests.py b/Lib/test/audiotests.py index 394097df17dca9..9d6c4cc2b4b02c 100644 --- a/Lib/test/audiotests.py +++ b/Lib/test/audiotests.py @@ -27,18 +27,17 @@ def tearDown(self): unlink(TESTFN) def check_params(self, f, nchannels, sampwidth, framerate, nframes, - comptype, compname, format): + comptype, compname): self.assertEqual(f.getnchannels(), nchannels) self.assertEqual(f.getsampwidth(), sampwidth) self.assertEqual(f.getframerate(), framerate) self.assertEqual(f.getnframes(), nframes) self.assertEqual(f.getcomptype(), comptype) self.assertEqual(f.getcompname(), compname) - self.assertEqual(f.getformat(), format) params = f.getparams() self.assertEqual(params, - (nchannels, sampwidth, framerate, nframes, comptype, compname)) + (nchannels, sampwidth, framerate, nframes, comptype, compname)) self.assertEqual(params.nchannels, nchannels) self.assertEqual(params.sampwidth, sampwidth) self.assertEqual(params.framerate, framerate) @@ -52,17 +51,13 @@ def check_params(self, f, nchannels, sampwidth, framerate, nframes, class AudioWriteTests(AudioTests): - readonly = False def create_file(self, testfile): - if self.readonly: - self.skipTest('Read only file format') f = self.fout = self.module.open(testfile, 'wb') f.setnchannels(self.nchannels) f.setsampwidth(self.sampwidth) f.setframerate(self.framerate) f.setcomptype(self.comptype, self.compname) - f.setformat(self.format) return f def check_file(self, testfile, nframes, frames): @@ -72,14 +67,13 @@ def check_file(self, testfile, nframes, frames): self.assertEqual(f.getframerate(), self.framerate) self.assertEqual(f.getnframes(), nframes) self.assertEqual(f.readframes(nframes), frames) - self.assertEqual(f.getformat(), self.format) def test_write_params(self): f = self.create_file(TESTFN) f.setnframes(self.nframes) f.writeframes(self.frames) self.check_params(f, self.nchannels, self.sampwidth, self.framerate, - self.nframes, self.comptype, self.compname, self.format) + self.nframes, self.comptype, self.compname) f.close() def test_write_context_manager_calls_close(self): @@ -263,7 +257,7 @@ def test_read_params(self): f = self.f = self.module.open(self.sndfilepath) #self.assertEqual(f.getfp().name, self.sndfilepath) self.check_params(f, self.nchannels, self.sampwidth, self.framerate, - self.sndfilenframes, self.comptype, self.compname, self.format) + self.sndfilenframes, self.comptype, self.compname) def test_close(self): with open(self.sndfilepath, 'rb') as testfile: @@ -304,8 +298,6 @@ def test_read(self): f.setpos(f.getnframes() + 1) def test_copy(self): - if self.readonly: - self.skipTest('Read only file format') f = self.f = self.module.open(self.sndfilepath) fout = self.fout = self.module.open(TESTFN, 'wb') fout.setparams(f.getparams()) diff --git a/Lib/test/test_wave.py b/Lib/test/test_wave.py index a1afe91e3774b9..4c21f16553775c 100644 --- a/Lib/test/test_wave.py +++ b/Lib/test/test_wave.py @@ -1,7 +1,7 @@ import unittest from test import audiotests from test import support -from test.support.os_helper import FakePath, unlink +from test.support.os_helper import FakePath import io import os import struct @@ -22,7 +22,6 @@ class WavePCM8Test(WaveTest, unittest.TestCase): sampwidth = 1 framerate = 11025 nframes = 48 - format = wave.WAVE_FORMAT_PCM comptype = 'NONE' compname = 'not compressed' frames = bytes.fromhex("""\ @@ -40,7 +39,6 @@ class WavePCM16Test(WaveTest, unittest.TestCase): sampwidth = 2 framerate = 11025 nframes = 48 - format = wave.WAVE_FORMAT_PCM comptype = 'NONE' compname = 'not compressed' frames = bytes.fromhex("""\ @@ -62,7 +60,6 @@ class WavePCM24Test(WaveTest, unittest.TestCase): sampwidth = 3 framerate = 11025 nframes = 48 - format = wave.WAVE_FORMAT_PCM comptype = 'NONE' compname = 'not compressed' frames = bytes.fromhex("""\ @@ -90,8 +87,6 @@ class WavePCM24ExtTest(WaveTest, unittest.TestCase): sampwidth = 3 framerate = 11025 nframes = 48 - format = wave.WAVE_FORMAT_EXTENSIBLE - readonly = True # Writing EXTENSIBLE wave format is not supported. comptype = 'NONE' compname = 'not compressed' frames = bytes.fromhex("""\ @@ -119,7 +114,6 @@ class WavePCM32Test(WaveTest, unittest.TestCase): sampwidth = 4 framerate = 11025 nframes = 48 - format = wave.WAVE_FORMAT_PCM comptype = 'NONE' compname = 'not compressed' frames = bytes.fromhex("""\ @@ -140,140 +134,14 @@ class WavePCM32Test(WaveTest, unittest.TestCase): frames = wave._byteswap(frames, 4) -class WaveIeeeFloatingPointTest(WaveTest, unittest.TestCase): - sndfilename = 'pluck-float32.wav' - sndfilenframes = 3307 - nchannels = 2 - sampwidth = 4 - framerate = 11025 - nframes = 48 - format = wave.WAVE_FORMAT_IEEE_FLOAT - comptype = 'NONE' - compname = 'not compressed' - frames = bytes.fromhex("""\ - 60598B3C001423BA 1FB4163F8054FA3B 0E4FC43E80C51D3D 53467EBF4030843D \ - FC84D0BE304C563D 3053113F40BEFC3C B72F00BFC03E583C E0FEDA3C805142BC \ - 54510FBFE02638BD 569F16BF40FDCABD C060A63EECA421BE 3CE5523E2C3349BE \ - 0C2E10BE14725BBE 5268E7BEDC3B6CBE 985AE03D80497ABE B4B606BEECB67EBE \ - B0B12E3FC87C6CBE 005519BD4C0F3EBE F8BD1B3EECDF03BE 924E9FBE588D8DBD \ - D4E150BF501711BD B079A0BD20FBFBBC 5863863D40760CBD 0E3C83BE40E217BD \ - 04FF0B3EF07839BD E29AFB3E80A714BD B91007BFE042D3BC B5AD4D3F80CDA0BB \ - 1AB1C3BEB04E023D D33A063FC0A8973D 8012F9BEE074EC3D 7341223FD415153E \ - D80409BE04A63A3E 00F27BBFBC25333E 0000803FFC29223E 000080BF38A7143E \ - 3638133F283BEB3D 7C6E253F00CADB3D 686A02BE88FDF53D 920CC7BE28E1FB3D \ - 185B5ABED8A2CE3D 5189463FC8A7A53D E88F8C3DF0FFA13D 1CE6AE3EE0A0B03D \ - DF90223F184EE43D 376768BF2CD8093E 281612BF60B3EE3D 2F26083F88B4A53D \ - """) - class MiscTestCase(unittest.TestCase): def test__all__(self): - not_exported = {'KSDATAFORMAT_SUBTYPE_PCM'} + not_exported = {'WAVE_FORMAT_PCM', 'WAVE_FORMAT_EXTENSIBLE', 'KSDATAFORMAT_SUBTYPE_PCM'} support.check__all__(self, wave, not_exported=not_exported) class WaveLowLevelTest(unittest.TestCase): - def test_setparams_6_tuple_defaults_to_pcm(self): - with tempfile.NamedTemporaryFile(delete_on_close=False) as fp: - filename = fp.name - self.addCleanup(unlink, filename) - - with wave.open(filename, 'wb') as w: - w.setformat(wave.WAVE_FORMAT_IEEE_FLOAT) - w.setparams((1, 2, 22050, 0, 'NONE', 'not compressed')) - self.assertEqual(w.getformat(), wave.WAVE_FORMAT_PCM) - - def test_setparams_7_tuple_uses_format(self): - with tempfile.NamedTemporaryFile(delete_on_close=False) as fp: - filename = fp.name - self.addCleanup(unlink, filename) - - with wave.open(filename, 'wb') as w: - w.setparams((1, 4, 22050, 0, 'NONE', 'not compressed', - wave.WAVE_FORMAT_IEEE_FLOAT)) - self.assertEqual(w.getformat(), wave.WAVE_FORMAT_IEEE_FLOAT) - - def test_setparams_7_tuple_ieee_64bit_sampwidth(self): - with tempfile.NamedTemporaryFile(delete_on_close=False) as fp: - filename = fp.name - self.addCleanup(unlink, filename) - - with wave.open(filename, 'wb') as w: - w.setparams((1, 8, 22050, 0, 'NONE', 'not compressed', - wave.WAVE_FORMAT_IEEE_FLOAT)) - self.assertEqual(w.getformat(), wave.WAVE_FORMAT_IEEE_FLOAT) - self.assertEqual(w.getsampwidth(), 8) - - def test_getparams_backward_compatible_shape(self): - with tempfile.NamedTemporaryFile(delete_on_close=False) as fp: - filename = fp.name - self.addCleanup(unlink, filename) - - with wave.open(filename, 'wb') as w: - w.setparams((1, 4, 22050, 0, 'NONE', 'not compressed', - wave.WAVE_FORMAT_IEEE_FLOAT)) - params = w.getparams() - self.assertEqual(params, (1, 4, 22050, 0, 'NONE', 'not compressed')) - - def test_getformat_setformat(self): - with tempfile.NamedTemporaryFile(delete_on_close=False) as fp: - filename = fp.name - self.addCleanup(unlink, filename) - - with wave.open(filename, 'wb') as w: - w.setnchannels(1) - w.setsampwidth(4) - w.setframerate(22050) - self.assertEqual(w.getformat(), wave.WAVE_FORMAT_PCM) - w.setformat(wave.WAVE_FORMAT_IEEE_FLOAT) - self.assertEqual(w.getformat(), wave.WAVE_FORMAT_IEEE_FLOAT) - - def test_setformat_ieee_requires_32_or_64_bit_sampwidth(self): - with tempfile.NamedTemporaryFile(delete_on_close=False) as fp: - filename = fp.name - self.addCleanup(unlink, filename) - - with wave.open(filename, 'wb') as w: - w.setnchannels(1) - w.setsampwidth(2) - w.setframerate(22050) - with self.assertRaisesRegex(wave.Error, - 'unsupported sample width for IEEE float format'): - w.setformat(wave.WAVE_FORMAT_IEEE_FLOAT) - - def test_setsampwidth_ieee_requires_32_or_64_bit(self): - with tempfile.NamedTemporaryFile(delete_on_close=False) as fp: - filename = fp.name - self.addCleanup(unlink, filename) - - with wave.open(filename, 'wb') as w: - w.setnchannels(1) - w.setframerate(22050) - w.setformat(wave.WAVE_FORMAT_IEEE_FLOAT) - with self.assertRaisesRegex(wave.Error, - 'unsupported sample width for IEEE float format'): - w.setsampwidth(2) - w.setsampwidth(4) - - def test_setsampwidth_ieee_accepts_64_bit(self): - with tempfile.NamedTemporaryFile(delete_on_close=False) as fp: - filename = fp.name - self.addCleanup(unlink, filename) - - with wave.open(filename, 'wb') as w: - w.setnchannels(1) - w.setframerate(22050) - w.setformat(wave.WAVE_FORMAT_IEEE_FLOAT) - w.setsampwidth(8) - self.assertEqual(w.getsampwidth(), 8) - - def test_read_getformat(self): - b = b'RIFF' + struct.pack(' 4: + if sampwidth < 1 or sampwidth > 4: raise Error('bad sample width') self._sampwidth = sampwidth @@ -546,18 +518,6 @@ def setcomptype(self, comptype, compname): self._comptype = comptype self._compname = compname - def setformat(self, format): - if self._datawritten: - raise Error('cannot change parameters after starting to write') - if format not in (WAVE_FORMAT_IEEE_FLOAT, WAVE_FORMAT_PCM): - raise Error('unsupported wave format') - if format == WAVE_FORMAT_IEEE_FLOAT and self._sampwidth and self._sampwidth not in (4, 8): - raise Error('unsupported sample width for IEEE float format') - self._format = format - - def getformat(self): - return self._format - def getcomptype(self): return self._comptype @@ -565,15 +525,10 @@ def getcompname(self): return self._compname def setparams(self, params): + nchannels, sampwidth, framerate, nframes, comptype, compname = params if self._datawritten: raise Error('cannot change parameters after starting to write') - if len(params) == 6: - nchannels, sampwidth, framerate, nframes, comptype, compname = params - format = WAVE_FORMAT_PCM - else: - nchannels, sampwidth, framerate, nframes, comptype, compname, format = params self.setnchannels(nchannels) - self.setformat(format) self.setsampwidth(sampwidth) self.setframerate(framerate) self.setnframes(nframes) @@ -634,9 +589,6 @@ def _ensure_header_written(self, datasize): raise Error('sampling rate not specified') self._write_header(datasize) - def _needs_fact_chunk(self): - return self._format == WAVE_FORMAT_IEEE_FLOAT - def _write_header(self, initlength): assert not self._headerwritten self._file.write(b'RIFF') @@ -647,23 +599,12 @@ def _write_header(self, initlength): self._form_length_pos = self._file.tell() except (AttributeError, OSError): self._form_length_pos = None - has_fact = self._needs_fact_chunk() - header_overhead = 36 + (12 if has_fact else 0) - self._file.write(struct.pack('