--------------
The :mod:`!wave` module provides a convenient interface to the Waveform Audio
-"WAVE" (or "WAV") file format. Only uncompressed PCM encoded wave files are
-supported.
+"WAVE" (or "WAV") file format.
+
+The module supports uncompressed PCM and IEEE floating-point WAV formats.
.. 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:
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
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).
.. 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)
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()
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)``, with values valid for the ``set*()`` methods. Sets all
- parameters.
+ 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.
.. method:: getparams()
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.
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.
unlink(TESTFN)
def check_params(self, f, nchannels, sampwidth, framerate, nframes,
- comptype, compname):
+ comptype, compname, format):
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)
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):
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.nframes, self.comptype, self.compname, self.format)
f.close()
def test_write_context_manager_calls_close(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.sndfilenframes, self.comptype, self.compname, self.format)
def test_close(self):
with open(self.sndfilepath, 'rb') as testfile:
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())
import unittest
from test import audiotests
from test import support
-from test.support.os_helper import FakePath
+from test.support.os_helper import FakePath, unlink
import io
import os
import struct
sampwidth = 1
framerate = 11025
nframes = 48
+ format = wave.WAVE_FORMAT_PCM
comptype = 'NONE'
compname = 'not compressed'
frames = bytes.fromhex("""\
sampwidth = 2
framerate = 11025
nframes = 48
+ format = wave.WAVE_FORMAT_PCM
comptype = 'NONE'
compname = 'not compressed'
frames = bytes.fromhex("""\
sampwidth = 3
framerate = 11025
nframes = 48
+ format = wave.WAVE_FORMAT_PCM
comptype = 'NONE'
compname = 'not compressed'
frames = bytes.fromhex("""\
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("""\
sampwidth = 4
framerate = 11025
nframes = 48
+ format = wave.WAVE_FORMAT_PCM
comptype = 'NONE'
compname = 'not compressed'
frames = bytes.fromhex("""\
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 = {'WAVE_FORMAT_PCM', 'WAVE_FORMAT_EXTENSIBLE', 'KSDATAFORMAT_SUBTYPE_PCM'}
+ not_exported = {'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('<L', 36) + b'WAVE'
+ b += b'fmt ' + struct.pack('<LHHLLHH', 16, 1, 1, 11025, 11025, 1, 8)
+ b += b'data' + struct.pack('<L', 0)
+ with wave.open(io.BytesIO(b), 'rb') as r:
+ self.assertEqual(r.getformat(), wave.WAVE_FORMAT_PCM)
+
def test_read_no_chunks(self):
b = b'SPAM'
with self.assertRaises(EOFError):
support.gc_collect()
self.assertIsNone(cm.unraisable)
+ def test_ieee_float_has_fact_chunk(self):
+ nframes = 100
+ 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)
+ w.setformat(wave.WAVE_FORMAT_IEEE_FLOAT)
+ w.writeframes(b'\x00\x00\x00\x00' * nframes)
+
+ with open(filename, 'rb') as f:
+ f.read(12)
+ fact_found = False
+ fact_samples = None
+ while True:
+ chunk_id = f.read(4)
+ if len(chunk_id) < 4:
+ break
+ chunk_size = struct.unpack('<L', f.read(4))[0]
+ if chunk_id == b'fact':
+ fact_found = True
+ fact_samples = struct.unpack('<L', f.read(4))[0]
+ break
+ f.seek(chunk_size + (chunk_size & 1), 1)
+
+ self.assertTrue(fact_found)
+ self.assertEqual(fact_samples, nframes)
+
+ def test_pcm_has_no_fact_chunk(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)
+ w.writeframes(b'\x00\x00' * 100)
+
+ with open(filename, 'rb') as f:
+ f.read(12)
+ while True:
+ chunk_id = f.read(4)
+ if len(chunk_id) < 4:
+ break
+ chunk_size = struct.unpack('<L', f.read(4))[0]
+ self.assertNotEqual(chunk_id, b'fact')
+ f.seek(chunk_size + (chunk_size & 1), 1)
+
class WaveOpen(unittest.TestCase):
def test_open_pathlike(self):
getsampwidth() -- returns sample width in bytes
getframerate() -- returns sampling frequency
getnframes() -- returns number of audio frames
+ getformat() -- returns frame encoding (WAVE_FORMAT_PCM, WAVE_FORMAT_IEEE_FLOAT
+ or WAVE_FORMAT_EXTENSIBLE)
getcomptype() -- returns compression type ('NONE' for linear samples)
getcompname() -- returns human-readable version of
compression type ('not compressed' linear samples)
setsampwidth(n) -- set the sample width
setframerate(n) -- set the frame rate
setnframes(n) -- set the number of frames
+ setformat(format)
+ -- set the frame format. Only WAVE_FORMAT_PCM and
+ WAVE_FORMAT_IEEE_FLOAT are supported.
setcomptype(type, name)
-- set the compression type and the
human-readable compression type
import sys
-__all__ = ["open", "Error", "Wave_read", "Wave_write"]
+__all__ = [
+ "open",
+ "Error",
+ "Wave_read",
+ "Wave_write",
+ "WAVE_FORMAT_PCM",
+ "WAVE_FORMAT_IEEE_FLOAT",
+ "WAVE_FORMAT_EXTENSIBLE",
+]
class Error(Exception):
pass
WAVE_FORMAT_PCM = 0x0001
+WAVE_FORMAT_IEEE_FLOAT = 0x0003
WAVE_FORMAT_EXTENSIBLE = 0xFFFE
# Derived from uuid.UUID("00000001-0000-0010-8000-00aa00389b71").bytes_le
KSDATAFORMAT_SUBTYPE_PCM = b'\x01\x00\x00\x00\x00\x00\x10\x00\x80\x00\x00\xaa\x008\x9bq'
available through the getsampwidth() method
_framerate -- the sampling frequency
available through the getframerate() method
+ _format -- frame format
+ One of WAVE_FORMAT_PCM, WAVE_FORMAT_IEEE_FLOAT
+ or WAVE_FORMAT_EXTENSIBLE available through
+ getformat() method
_comptype -- the AIFF-C compression type ('NONE' if AIFF)
available through the getcomptype() method
_compname -- the human-readable AIFF-C compression type
def getframerate(self):
return self._framerate
+ def getformat(self):
+ return self._format
+
def getcomptype(self):
return self._comptype
def _read_fmt_chunk(self, chunk):
try:
- wFormatTag, self._nchannels, self._framerate, dwAvgBytesPerSec, wBlockAlign = struct.unpack_from('<HHLLH', chunk.read(14))
+ self._format, self._nchannels, self._framerate, dwAvgBytesPerSec, wBlockAlign = struct.unpack_from('<HHLLH', chunk.read(14))
except struct.error:
raise EOFError from None
- if wFormatTag != WAVE_FORMAT_PCM and wFormatTag != WAVE_FORMAT_EXTENSIBLE:
- raise Error('unknown format: %r' % (wFormatTag,))
+ if self._format not in (WAVE_FORMAT_PCM, WAVE_FORMAT_IEEE_FLOAT, WAVE_FORMAT_EXTENSIBLE):
+ raise Error('unknown format: %r' % (self._format,))
try:
sampwidth = struct.unpack_from('<H', chunk.read(2))[0]
except struct.error:
raise EOFError from None
- if wFormatTag == WAVE_FORMAT_EXTENSIBLE:
+ if self._format == WAVE_FORMAT_EXTENSIBLE:
try:
cbSize, wValidBitsPerSample, dwChannelMask = struct.unpack_from('<HHL', chunk.read(8))
# Read the entire UUID from the chunk
set through the setsampwidth() or setparams() method
_framerate -- the sampling frequency
set through the setframerate() or setparams() method
+ _format -- frame format
+ set through setformat() method
_nframes -- the number of audio frames written to the header
set through the setnframes() or setparams() method
self._file = file
self._convert = None
self._nchannels = 0
+ self._format = WAVE_FORMAT_PCM
self._sampwidth = 0
self._framerate = 0
self._nframes = 0
self._nframeswritten = 0
self._datawritten = 0
self._datalength = 0
+ self._fact_sample_count_pos = None
self._headerwritten = False
def __del__(self):
def setsampwidth(self, sampwidth):
if self._datawritten:
raise Error('cannot change parameters after starting to write')
- if sampwidth < 1 or sampwidth > 4:
+ if self._format == WAVE_FORMAT_IEEE_FLOAT:
+ if sampwidth not in (4, 8):
+ raise Error('unsupported sample width for IEEE float format')
+ elif sampwidth < 1 or sampwidth > 4:
raise Error('bad sample width')
self._sampwidth = sampwidth
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
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)
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')
self._form_length_pos = self._file.tell()
except (AttributeError, OSError):
self._form_length_pos = None
- self._file.write(struct.pack('<L4s4sLHHLLHH4s',
- 36 + self._datalength, b'WAVE', b'fmt ', 16,
- WAVE_FORMAT_PCM, self._nchannels, self._framerate,
+ has_fact = self._needs_fact_chunk()
+ header_overhead = 36 + (12 if has_fact else 0)
+ self._file.write(struct.pack('<L4s4sLHHLLHH',
+ header_overhead + self._datalength, b'WAVE', b'fmt ', 16,
+ self._format, self._nchannels, self._framerate,
self._nchannels * self._framerate * self._sampwidth,
self._nchannels * self._sampwidth,
- self._sampwidth * 8, b'data'))
+ self._sampwidth * 8))
+ if has_fact:
+ self._file.write(b'fact')
+ self._file.write(struct.pack('<L', 4))
+ try:
+ self._fact_sample_count_pos = self._file.tell()
+ except (AttributeError, OSError):
+ self._fact_sample_count_pos = None
+ self._file.write(struct.pack('<L', self._nframes))
+ self._file.write(b'data')
if self._form_length_pos is not None:
self._data_length_pos = self._file.tell()
self._file.write(struct.pack('<L', self._datalength))
if self._datawritten == self._datalength:
return
curpos = self._file.tell()
+ header_overhead = 36 + (12 if self._needs_fact_chunk() else 0)
self._file.seek(self._form_length_pos, 0)
- self._file.write(struct.pack('<L', 36 + self._datawritten))
+ self._file.write(struct.pack('<L', header_overhead + self._datawritten))
+ if self._fact_sample_count_pos is not None:
+ self._file.seek(self._fact_sample_count_pos, 0)
+ nframes = self._datawritten // (self._nchannels * self._sampwidth)
+ self._file.write(struct.pack('<L', nframes))
self._file.seek(self._data_length_pos, 0)
self._file.write(struct.pack('<L', self._datawritten))
self._file.seek(curpos, 0)
--- /dev/null
+Add support for floating point audio wave files in :mod:`wave`.