summaryrefslogtreecommitdiff
path: root/Lib/wave.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/wave.py')
-rw-r--r--Lib/wave.py112
1 files changed, 51 insertions, 61 deletions
diff --git a/Lib/wave.py b/Lib/wave.py
index 4a223451bf..8a101e320b 100644
--- a/Lib/wave.py
+++ b/Lib/wave.py
@@ -18,7 +18,7 @@ This returns an instance of a class with the following public methods:
getcomptype() -- returns compression type ('NONE' for linear samples)
getcompname() -- returns human-readable version of
compression type ('not compressed' linear samples)
- getparams() -- returns a tuple consisting of all of the
+ getparams() -- returns a namedtuple consisting of all of the
above in the above order
getmarkers() -- returns None (for compatibility with the
aifc module)
@@ -65,7 +65,7 @@ but when it is set to the correct value, the header does not have to
be patched up.
It is best to first set all parameters, perhaps possibly the
compression type, and then write audio frames using writeframesraw.
-When all frames have been written, either call writeframes('') or
+When all frames have been written, either call writeframes(b'') or
close() to patch up the sizes in the header.
The close() method is called automatically when the class instance
is destroyed.
@@ -82,15 +82,14 @@ WAVE_FORMAT_PCM = 0x0001
_array_fmts = None, 'b', 'h', None, 'i'
+import audioop
import struct
import sys
from chunk import Chunk
+from collections import namedtuple
-def _byteswap3(data):
- ba = bytearray(data)
- ba[::3] = data[2::3]
- ba[2::3] = data[::3]
- return bytes(ba)
+_wave_params = namedtuple('_wave_params',
+ 'nchannels sampwidth framerate nframes comptype compname')
class Wave_read:
"""Variables used in this class:
@@ -169,6 +168,13 @@ class Wave_read:
def __del__(self):
self.close()
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *args):
+ self.close()
+
#
# User visible methods.
#
@@ -180,10 +186,11 @@ class Wave_read:
self._soundpos = 0
def close(self):
- if self._i_opened_the_file:
- self._i_opened_the_file.close()
- self._i_opened_the_file = None
self._file = None
+ file = self._i_opened_the_file
+ if file:
+ self._i_opened_the_file = None
+ file.close()
def tell(self):
return self._soundpos
@@ -207,9 +214,9 @@ class Wave_read:
return self._compname
def getparams(self):
- return self.getnchannels(), self.getsampwidth(), \
- self.getframerate(), self.getnframes(), \
- self.getcomptype(), self.getcompname()
+ return _wave_params(self.getnchannels(), self.getsampwidth(),
+ self.getframerate(), self.getnframes(),
+ self.getcomptype(), self.getcompname())
def getmarkers(self):
return None
@@ -232,29 +239,9 @@ class Wave_read:
self._data_seek_needed = 0
if nframes == 0:
return b''
- if self._sampwidth in (2, 4) and sys.byteorder == 'big':
- # unfortunately the fromfile() method does not take
- # something that only looks like a file object, so
- # we have to reach into the innards of the chunk object
- import array
- chunk = self._data_chunk
- data = array.array(_array_fmts[self._sampwidth])
- assert data.itemsize == self._sampwidth
- nitems = nframes * self._nchannels
- if nitems * self._sampwidth > chunk.chunksize - chunk.size_read:
- nitems = (chunk.chunksize - chunk.size_read) // self._sampwidth
- data.fromfile(chunk.file.file, nitems)
- # "tell" data chunk how much was read
- chunk.size_read = chunk.size_read + nitems * self._sampwidth
- # do the same for the outermost chunk
- chunk = chunk.file
- chunk.size_read = chunk.size_read + nitems * self._sampwidth
- data.byteswap()
- data = data.tobytes()
- else:
- data = self._data_chunk.read(nframes * self._framesize)
- if self._sampwidth == 3 and sys.byteorder == 'big':
- data = _byteswap3(data)
+ data = self._data_chunk.read(nframes * self._framesize)
+ if self._sampwidth != 1 and sys.byteorder == 'big':
+ data = audioop.byteswap(data, self._sampwidth)
if self._convert and data:
data = self._convert(data)
self._soundpos = self._soundpos + len(data) // (self._nchannels * self._sampwidth)
@@ -328,6 +315,12 @@ class Wave_write:
def __del__(self):
self.close()
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *args):
+ self.close()
+
#
# User visible methods.
#
@@ -402,8 +395,8 @@ class Wave_write:
def getparams(self):
if not self._nchannels or not self._sampwidth or not self._framerate:
raise Error('not all parameters set')
- return self._nchannels, self._sampwidth, self._framerate, \
- self._nframes, self._comptype, self._compname
+ return _wave_params(self._nchannels, self._sampwidth, self._framerate,
+ self._nframes, self._comptype, self._compname)
def setmark(self, id, pos, name):
raise Error('setmark() not supported')
@@ -418,24 +411,16 @@ class Wave_write:
return self._nframeswritten
def writeframesraw(self, data):
+ if not isinstance(data, (bytes, bytearray)):
+ data = memoryview(data).cast('B')
self._ensure_header_written(len(data))
nframes = len(data) // (self._sampwidth * self._nchannels)
if self._convert:
data = self._convert(data)
- if self._sampwidth in (2, 4) and sys.byteorder == 'big':
- import array
- a = array.array(_array_fmts[self._sampwidth])
- a.frombytes(data)
- data = a
- assert data.itemsize == self._sampwidth
- data.byteswap()
- data.tofile(self._file)
- self._datawritten = self._datawritten + len(data) * self._sampwidth
- else:
- if self._sampwidth == 3 and sys.byteorder == 'big':
- data = _byteswap3(data)
- self._file.write(data)
- self._datawritten = self._datawritten + len(data)
+ if self._sampwidth != 1 and sys.byteorder == 'big':
+ data = audioop.byteswap(data, self._sampwidth)
+ self._file.write(data)
+ self._datawritten += len(data)
self._nframeswritten = self._nframeswritten + nframes
def writeframes(self, data):
@@ -444,17 +429,18 @@ class Wave_write:
self._patchheader()
def close(self):
- if self._file:
- try:
+ try:
+ if self._file:
self._ensure_header_written(0)
if self._datalength != self._datawritten:
self._patchheader()
self._file.flush()
- finally:
- self._file = None
- if self._i_opened_the_file:
- self._i_opened_the_file.close()
- self._i_opened_the_file = None
+ finally:
+ self._file = None
+ file = self._i_opened_the_file
+ if file:
+ self._i_opened_the_file = None
+ file.close()
#
# Internal methods.
@@ -476,14 +462,18 @@ class Wave_write:
if not self._nframes:
self._nframes = initlength // (self._nchannels * self._sampwidth)
self._datalength = self._nframes * self._nchannels * self._sampwidth
- self._form_length_pos = self._file.tell()
+ try:
+ 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,
self._nchannels * self._framerate * self._sampwidth,
self._nchannels * self._sampwidth,
self._sampwidth * 8, b'data'))
- self._data_length_pos = self._file.tell()
+ if self._form_length_pos is not None:
+ self._data_length_pos = self._file.tell()
self._file.write(struct.pack('<L', self._datalength))
self._headerwritten = True