Skip to content

Commit ca064d9

Browse files
authored
gh-117716: Fix wave RIFF padding for data chunks (GH-145237)
wave.Wave_write now writes the required RIFF pad byte when the data chunk size is odd. Update RIFF chunk size calculations in both header writing and header patching so they include the alignment pad byte when present. Add a regression test in test_wave.py that verifies odd-sized writes are padded, RIFF size is correct, and roundtrip reads preserve frame data.
1 parent 69e0a78 commit ca064d9

File tree

3 files changed

+29
-2
lines changed

3 files changed

+29
-2
lines changed

Lib/test/test_wave.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,30 @@ def test_setframerate_rounds(self, arg, expected):
430430
f.setframerate(arg)
431431
self.assertEqual(f.getframerate(), expected)
432432

433+
def test_write_odd_data_chunk_pads_and_updates_riff_size(self):
434+
# gh-117716: odd-sized data chunks must be padded with one zero byte.
435+
with io.BytesIO() as output:
436+
with wave.open(output, mode='wb') as w:
437+
w.setnchannels(1)
438+
w.setsampwidth(1)
439+
w.setframerate(48000)
440+
w.writeframes(b'\x80')
441+
442+
value = output.getvalue()
443+
444+
self.assertEqual(value[-1], 0)
445+
self.assertEqual(
446+
int.from_bytes(value[4:8], byteorder='little'),
447+
38,
448+
)
449+
450+
with wave.open(io.BytesIO(value), mode='rb') as r:
451+
self.assertEqual(r.getnchannels(), 1)
452+
self.assertEqual(r.getsampwidth(), 1)
453+
self.assertEqual(r.getframerate(), 48000)
454+
self.assertEqual(r.getnframes(), 1)
455+
self.assertEqual(r.readframes(-1), b'\x80')
456+
433457

434458
class WaveOpen(unittest.TestCase):
435459
def test_open_pathlike(self):

Lib/wave.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,8 @@ def close(self):
611611
try:
612612
if self._file:
613613
self._ensure_header_written(0)
614+
if self._datawritten & 1:
615+
self._file.write(b'\x00')
614616
if self._datalength != self._datawritten:
615617
self._patchheader()
616618
self._file.flush()
@@ -651,7 +653,7 @@ def _write_header(self, initlength):
651653
has_fact = self._needs_fact_chunk()
652654
header_overhead = 36 + (12 if has_fact else 0)
653655
self._file.write(struct.pack('<L4s4sLHHLLHH',
654-
header_overhead + self._datalength, b'WAVE', b'fmt ', 16,
656+
header_overhead + self._datalength + (self._datalength & 1), b'WAVE', b'fmt ', 16,
655657
self._format, self._nchannels, self._framerate,
656658
self._nchannels * self._framerate * self._sampwidth,
657659
self._nchannels * self._sampwidth,
@@ -677,7 +679,7 @@ def _patchheader(self):
677679
curpos = self._file.tell()
678680
header_overhead = 36 + (12 if self._needs_fact_chunk() else 0)
679681
self._file.seek(self._form_length_pos, 0)
680-
self._file.write(struct.pack('<L', header_overhead + self._datawritten))
682+
self._file.write(struct.pack('<L', header_overhead + self._datawritten + (self._datawritten & 1)))
681683
if self._fact_sample_count_pos is not None:
682684
self._file.seek(self._fact_sample_count_pos, 0)
683685
nframes = self._datawritten // (self._nchannels * self._sampwidth)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix :mod:`wave` writing of odd-sized ``data`` chunks by appending the required RIFF pad byte and correcting the RIFF chunk size field accordingly.

0 commit comments

Comments
 (0)