Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added Lib/test/audiodata/pluck-float32.wav
Binary file not shown.
12 changes: 9 additions & 3 deletions Lib/test/audiotests.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,14 @@ def tearDown(self):
unlink(TESTFN)

def check_params(self, f, nchannels, sampwidth, framerate, nframes,
comptype, compname):
comptype, compname, encoding):
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.getencoding(), encoding)

params = f.getparams()
self.assertEqual(params,
Expand All @@ -51,13 +52,17 @@ 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.setencoding(self.encoding)
return f

def check_file(self, testfile, nframes, frames):
Expand All @@ -67,13 +72,14 @@ 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.getencoding(), self.encoding)

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.encoding)
f.close()

def test_write_context_manager_calls_close(self):
Expand Down Expand Up @@ -257,7 +263,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.sndfilenframes, self.comptype, self.compname, self.encoding)

def test_close(self):
with open(self.sndfilepath, 'rb') as testfile:
Expand Down
33 changes: 32 additions & 1 deletion Lib/test/test_wave.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class WavePCM8Test(WaveTest, unittest.TestCase):
sampwidth = 1
framerate = 11025
nframes = 48
encoding = wave.WAVE_FORMAT_PCM
comptype = 'NONE'
compname = 'not compressed'
frames = bytes.fromhex("""\
Expand All @@ -39,6 +40,7 @@ class WavePCM16Test(WaveTest, unittest.TestCase):
sampwidth = 2
framerate = 11025
nframes = 48
encoding = wave.WAVE_FORMAT_PCM
comptype = 'NONE'
compname = 'not compressed'
frames = bytes.fromhex("""\
Expand All @@ -60,6 +62,7 @@ class WavePCM24Test(WaveTest, unittest.TestCase):
sampwidth = 3
framerate = 11025
nframes = 48
encoding = wave.WAVE_FORMAT_PCM
comptype = 'NONE'
compname = 'not compressed'
frames = bytes.fromhex("""\
Expand Down Expand Up @@ -87,6 +90,8 @@ class WavePCM24ExtTest(WaveTest, unittest.TestCase):
sampwidth = 3
framerate = 11025
nframes = 48
encoding = wave.WAVE_FORMAT_EXTENSIBLE
readonly = True # Writing EXTENSIBLE wave format is not supported.
comptype = 'NONE'
compname = 'not compressed'
frames = bytes.fromhex("""\
Expand Down Expand Up @@ -114,6 +119,7 @@ class WavePCM32Test(WaveTest, unittest.TestCase):
sampwidth = 4
framerate = 11025
nframes = 48
encoding = wave.WAVE_FORMAT_PCM
comptype = 'NONE'
compname = 'not compressed'
frames = bytes.fromhex("""\
Expand All @@ -134,9 +140,34 @@ 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
encoding = 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 = {'WAVE_FORMAT_PCM', 'WAVE_FORMAT_IEEE_FLOAT', 'WAVE_FORMAT_EXTENSIBLE', 'KSDATAFORMAT_SUBTYPE_PCM'}
support.check__all__(self, wave, not_exported=not_exported)


Expand Down
36 changes: 31 additions & 5 deletions Lib/wave.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
getsampwidth() -- returns sample width in bytes
getframerate() -- returns sampling frequency
getnframes() -- returns number of audio frames
getencoding() -- 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)
Expand Down Expand Up @@ -42,6 +44,9 @@
setsampwidth(n) -- set the sample width
setframerate(n) -- set the frame rate
setnframes(n) -- set the number of frames
setencoding(encoding)
-- set the frame encoding. Only WAVE_FORMAT_PCM,
WAVE_FORMAT_IEEE_FLOAT are supported.
setcomptype(type, name)
-- set the compression type and the
human-readable compression type
Expand Down Expand Up @@ -80,6 +85,7 @@ 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'
Expand Down Expand Up @@ -226,6 +232,10 @@ class Wave_read:
available through the getsampwidth() method
_framerate -- the sampling frequency
available through the getframerate() method
_encoding -- frame encoding
One of WAVE_FORMAT_PCM, WAVE_FORMAT_IEEE_FLOAT
or WAVE_FORMAT_EXTENSIBLE available through
getencoding() method
_comptype -- the AIFF-C compression type ('NONE' if AIFF)
available through the getcomptype() method
_compname -- the human-readable AIFF-C compression type
Expand Down Expand Up @@ -338,6 +348,9 @@ def getparams(self):
self.getframerate(), self.getnframes(),
self.getcomptype(), self.getcompname())

def getencoding(self):
return self._encoding

def setpos(self, pos):
if pos < 0 or pos > self._nframes:
raise Error('position not in range')
Expand Down Expand Up @@ -367,16 +380,16 @@ def readframes(self, nframes):

def _read_fmt_chunk(self, chunk):
try:
wFormatTag, self._nchannels, self._framerate, dwAvgBytesPerSec, wBlockAlign = struct.unpack_from('<HHLLH', chunk.read(14))
self._encoding, 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._encoding not in (WAVE_FORMAT_PCM, WAVE_FORMAT_IEEE_FLOAT, WAVE_FORMAT_EXTENSIBLE):
raise Error('unknown format: %r' % (self._encoding,))
try:
sampwidth = struct.unpack_from('<H', chunk.read(2))[0]
except struct.error:
raise EOFError from None
if wFormatTag == WAVE_FORMAT_EXTENSIBLE:
if self._encoding == WAVE_FORMAT_EXTENSIBLE:
try:
cbSize, wValidBitsPerSample, dwChannelMask = struct.unpack_from('<HHL', chunk.read(8))
# Read the entire UUID from the chunk
Expand Down Expand Up @@ -419,6 +432,8 @@ class Wave_write:
set through the setsampwidth() or setparams() method
_framerate -- the sampling frequency
set through the setframerate() or setparams() method
_encoding -- frame encoding
set through setencoding() method
_nframes -- the number of audio frames written to the header
set through the setnframes() or setparams() method

Expand Down Expand Up @@ -446,6 +461,7 @@ def initfp(self, file):
self._file = file
self._convert = None
self._nchannels = 0
self._encoding = WAVE_FORMAT_PCM
self._sampwidth = 0
self._framerate = 0
self._nframes = 0
Expand Down Expand Up @@ -518,6 +534,16 @@ def setcomptype(self, comptype, compname):
self._comptype = comptype
self._compname = compname

def setencoding(self, encoding):
if self._datawritten:
raise Error('cannot change parameters after starting to write')
if encoding not in (WAVE_FORMAT_IEEE_FLOAT, WAVE_FORMAT_PCM):
raise Error('unsupported wave format')
self._encoding = encoding

def getencoding(self):
return self._encoding

def getcomptype(self):
return self._comptype

Expand Down Expand Up @@ -601,7 +627,7 @@ def _write_header(self, initlength):
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,
self._encoding, self._nchannels, self._framerate,
self._nchannels * self._framerate * self._sampwidth,
self._nchannels * self._sampwidth,
self._sampwidth * 8, b'data'))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add support for floating point audio wave files.
Loading