summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/aifc.py145
-rw-r--r--Lib/sunau.py471
2 files changed, 569 insertions, 47 deletions
diff --git a/Lib/aifc.py b/Lib/aifc.py
index 2d897be..ee1f8f0 100644
--- a/Lib/aifc.py
+++ b/Lib/aifc.py
@@ -40,7 +40,7 @@
# <size of the samples> (2 bytes)
# <sampling frequency> (10 bytes, IEEE 80-bit extended
# floating point)
-# if AIFF-C files only:
+# in AIFF-C files only:
# <compression type> (4 bytes)
# <human-readable version of compression type> ("pstring")
# SSND
@@ -58,9 +58,9 @@
# or
# f = aifc.openfp(filep, 'r')
# where file is the name of a file and filep is an open file pointer.
-# The open file pointer must have methods read(), seek(), and
-# close(). In some types of audio files, if the setpos() method is
-# not used, the seek() method is not necessary.
+# The open file pointer must have methods read(), seek(), and close().
+# In some types of audio files, if the setpos() method is not used,
+# the seek() method is not necessary.
#
# This returns an instance of a class with the following public methods:
# getnchannels() -- returns number of audio channels (1 for
@@ -85,6 +85,8 @@
# The position returned by tell(), the position given to setpos() and
# the position of marks are all compatible and have nothing to do with
# the actual postion in the file.
+# The close() method is called automatically when the class instance
+# is destroyed.
#
# Writing AIFF files:
# f = aifc.open(file, 'w')
@@ -122,11 +124,13 @@
# 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 the write audio frames using writeframesraw.
+# compression type, and then write audio frames using writeframesraw.
# When all frames have been written, either call writeframes('') or
# close() to patch up the sizes in the header.
# Marks can be added anytime. If there are any marks, ypu must call
# close() after all frames have been written.
+# The close() method is called automatically when the class instance
+# is destroyed.
#
# When a file is opened with the extension '.aiff', an AIFF file is
# written, otherwise an AIFF-C file is written. This default can be
@@ -347,7 +351,7 @@ class Aifc_read:
# methods
# _soundpos -- the position in the audio stream
# available through the tell() method, set through the
- # tell() method
+ # setpos() method
#
# These variables are used internally only:
# _version -- the AIFF-C version number
@@ -362,6 +366,7 @@ class Aifc_read:
self._file = file
self._version = 0
self._decomp = None
+ self._convert = None
self._markers = []
self._soundpos = 0
form = self._file.read(4)
@@ -433,6 +438,10 @@ class Aifc_read:
def init(self, filename):
return self.initfp(builtin.open(filename, 'r'))
+ def __del__(self):
+ if self._file:
+ self.close()
+
#
# User visible methods.
#
@@ -475,8 +484,9 @@ class Aifc_read:
## return self._version
def getparams(self):
- return self._nchannels, self._sampwidth, self._framerate, \
- self._nframes, self._comptype, self._compname
+ return self.getnchannels(), self.getsampwidth(), \
+ self.getframerate(), self.getnframes(), \
+ self.getcomptype(), self.getcompname()
def getmarkers(self):
if len(self._markers) == 0:
@@ -506,16 +516,24 @@ class Aifc_read:
if nframes == 0:
return ''
data = self._ssnd_chunk.read(nframes * self._framesize)
- if self._decomp and data:
- dummy = self._decomp.SetParam(CL.FRAME_BUFFER_SIZE, \
- len(data) * 2)
- data = self._decomp.Decompress(len(data) / self._nchannels, data)
+ if self._convert and data:
+ data = self._convert(data)
self._soundpos = self._soundpos + len(data) / (self._nchannels * self._sampwidth)
return data
#
# Internal methods.
#
+ def _decomp_data(self, data):
+ dummy = self._decomp.SetParam(CL.FRAME_BUFFER_SIZE,
+ len(data) * 2)
+ return self._decomp.Decompress(len(data) / self._nchannels,
+ data)
+
+ def _ulaw2lin(self, data):
+ import audioop
+ return audioop.ulaw2lin(data, 2)
+
def _read_comm_chunk(self, chunk):
nchannels = _read_short(chunk)
self._nchannels = _convert1(nchannels, _nchannelslist)
@@ -547,6 +565,14 @@ class Aifc_read:
try:
import cl, CL
except ImportError:
+ if self._comptype == 'ULAW':
+ try:
+ import audioop
+ self._convert = self._ulaw2lin
+ self._framesize = self._framesize / 2
+ return
+ except ImportError:
+ pass
raise Error, 'cannot read compressed AIFF-C files'
if self._comptype == 'ULAW':
scheme = CL.G711_ULAW
@@ -557,6 +583,7 @@ class Aifc_read:
else:
raise Error, 'unsupported compression type'
self._decomp = cl.OpenDecompressor(scheme)
+ self._convert = self._decomp_data
else:
self._comptype = 'NONE'
self._compname = 'not compressed'
@@ -622,6 +649,7 @@ class Aifc_write:
self._comptype = 'NONE'
self._compname = 'not compressed'
self._comp = None
+ self._convert = None
self._nchannels = 0
self._sampwidth = 0
self._framerate = 0
@@ -634,6 +662,10 @@ class Aifc_write:
self._aifc = 1 # AIFF-C is default
return self
+ def __del__(self):
+ if self._file:
+ self.close()
+
#
# User visible methods.
#
@@ -752,15 +784,14 @@ class Aifc_write:
return None
return self._markers
+ def tell(self):
+ return self._nframeswritten
+
def writeframesraw(self, data):
self._ensure_header_written(len(data))
nframes = len(data) / (self._sampwidth * self._nchannels)
- if self._comp:
- dummy = self._comp.SetParam(CL.FRAME_BUFFER_SIZE, \
- len(data))
- dummy = self._comp.SetParam(CL.COMPRESSED_BUFFER_SIZE,\
- len(data))
- data = self._comp.Compress(nframes, data)
+ if self._convert:
+ data = self._convert(data)
self._file.write(data)
self._nframeswritten = self._nframeswritten + nframes
self._datawritten = self._datawritten + len(data)
@@ -791,6 +822,15 @@ class Aifc_write:
#
# Internal methods.
#
+ def _comp_data(self, data):
+ dum = self._comp.SetParam(CL.FRAME_BUFFER_SIZE, len(data))
+ dum = self._comp.SetParam(CL.COMPRESSED_BUFFER_SIZE, len(data))
+ return self._comp.Compress(nframes, data)
+
+ def _lin2ulaw(self, data):
+ import audioop
+ return audioop.lin2ulaw(data, 2)
+
def _ensure_header_written(self, datasize):
if not self._nframeswritten:
if self._comptype in ('ULAW', 'ALAW'):
@@ -806,37 +846,48 @@ class Aifc_write:
raise Error, 'sampling rate not specified'
self._write_header(datasize)
+ def _init_compression(self):
+ try:
+ import cl, CL
+ except ImportError:
+ if self._comptype == 'ULAW':
+ try:
+ import audioop
+ self._convert = self._lin2ulaw
+ return
+ except ImportError:
+ pass
+ raise Error, 'cannot write compressed AIFF-C files'
+ if self._comptype == 'ULAW':
+ scheme = CL.G711_ULAW
+ elif self._comptype == 'ALAW':
+ scheme = CL.G711_ALAW
+ else:
+ raise Error, 'unsupported compression type'
+ self._comp = cl.OpenCompressor(scheme)
+ params = [CL.ORIGINAL_FORMAT, 0, \
+ CL.BITS_PER_COMPONENT, 0, \
+ CL.FRAME_RATE, self._framerate, \
+ CL.FRAME_BUFFER_SIZE, 100, \
+ CL.COMPRESSED_BUFFER_SIZE, 100]
+ if self._nchannels == AL.MONO:
+ params[1] = CL.MONO
+ else:
+ params[1] = CL.STEREO_INTERLEAVED
+ if self._sampwidth == AL.SAMPLE_8:
+ params[3] = 8
+ elif self._sampwidth == AL.SAMPLE_16:
+ params[3] = 16
+ else:
+ params[3] = 24
+ self._comp.SetParams(params)
+ # the compressor produces a header which we ignore
+ dummy = self._comp.Compress(0, '')
+ self._convert = self._comp_data
+
def _write_header(self, initlength):
if self._aifc and self._comptype != 'NONE':
- try:
- import cl, CL
- except ImportError:
- raise Error, 'cannot write compressed AIFF-C files'
- if self._comptype == 'ULAW':
- scheme = CL.G711_ULAW
- elif self._comptype == 'ALAW':
- scheme = CL.G711_ALAW
- else:
- raise Error, 'unsupported compression type'
- self._comp = cl.OpenCompressor(scheme)
- params = [CL.ORIGINAL_FORMAT, 0, \
- CL.BITS_PER_COMPONENT, 0, \
- CL.FRAME_RATE, self._framerate, \
- CL.FRAME_BUFFER_SIZE, 100, \
- CL.COMPRESSED_BUFFER_SIZE, 100]
- if self._nchannels == AL.MONO:
- params[1] = CL.MONO
- else:
- params[1] = CL.STEREO_INTERLEAVED
- if self._sampwidth == AL.SAMPLE_8:
- params[3] = 8
- elif self._sampwidth == AL.SAMPLE_16:
- params[3] = 16
- else:
- params[3] = 24
- self._comp.SetParams(params)
- # the compressor produces a header which we ignore
- dummy = self._comp.Compress(0, '')
+ self._init_compression()
self._file.write('FORM')
if not self._nframes:
self._nframes = initlength / (self._nchannels * self._sampwidth)
diff --git a/Lib/sunau.py b/Lib/sunau.py
new file mode 100644
index 0000000..1acebd0
--- /dev/null
+++ b/Lib/sunau.py
@@ -0,0 +1,471 @@
+# Stuff to parse Sun and NeXT audio files.
+#
+# An audio consists of a header followed by the data. The structure
+# of the header is as follows.
+#
+# +---------------+
+# | magic word |
+# +---------------+
+# | header size |
+# +---------------+
+# | data size |
+# +---------------+
+# | encoding |
+# +---------------+
+# | sample rate |
+# +---------------+
+# | # of channels |
+# +---------------+
+# | info |
+# | |
+# +---------------+
+#
+# The magic word consists of the 4 characters '.snd'. Apart from the
+# info field, all header fields are 4 bytes in size. They are all
+# 32-bit unsigned integers encoded in big-endian byte order.
+#
+# The header size really gives the start of the data.
+# The data size is the physical size of the data. From the other
+# parameter the number of frames can be calculated.
+# The encoding gives the way in which audio samples are encoded.
+# Possible values are listed below.
+# The info field currently consists of an ASCII string giving a
+# human-readable description of the audio file. The info field is
+# padded with NUL bytes to the header size.
+#
+# Usage.
+#
+# Reading audio files:
+# f = au.open(file, 'r')
+# or
+# f = au.openfp(filep, 'r')
+# where file is the name of a file and filep is an open file pointer.
+# The open file pointer must have methods read(), seek(), and close().
+# When the setpos() and rewind() methods are not used, the seek()
+# method is not necessary.
+#
+# This returns an instance of a class with the following public methods:
+# getnchannels() -- returns number of audio channels (1 for
+# mono, 2 for stereo)
+# getsampwidth() -- returns sample width in bytes
+# getframerate() -- returns sampling frequency
+# getnframes() -- returns number of audio frames
+# getcomptype() -- returns compression type ('NONE' for AIFF files)
+# getcompname() -- returns human-readable version of
+# compression type ('not compressed' for AIFF files)
+# getparams() -- returns a tuple consisting of all of the
+# above in the above order
+# getmarkers() -- returns None (for compatibility with the
+# aifc module)
+# getmark(id) -- raises an error since the mark does not
+# exist (for compatibility with the aifc module)
+# readframes(n) -- returns at most n frames of audio
+# rewind() -- rewind to the beginning of the audio stream
+# setpos(pos) -- seek to the specified position
+# tell() -- return the current position
+# close() -- close the instance (make it unusable)
+# The position returned by tell() and the position given to setpos()
+# are compatible and have nothing to do with the actual postion in the
+# file.
+# The close() method is called automatically when the class instance
+# is destroyed.
+#
+# Writing audio files:
+# f = au.open(file, 'w')
+# or
+# f = au.openfp(filep, 'w')
+# where file is the name of a file and filep is an open file pointer.
+# The open file pointer must have methods write(), tell(), seek(), and
+# close().
+#
+# This returns an instance of a class with the following public methods:
+# setnchannels(n) -- set the number of channels
+# setsampwidth(n) -- set the sample width
+# setframerate(n) -- set the frame rate
+# setnframes(n) -- set the number of frames
+# setcomptype(type, name)
+# -- set the compression type and the
+# human-readable compression type
+# setparams(nchannels, sampwidth, framerate, nframes, comptype, compname)
+# -- set all parameters at once
+# tell() -- return current position in output file
+# writeframesraw(data)
+# -- write audio frames without pathing up the
+# file header
+# writeframes(data)
+# -- write audio frames and patch up the file header
+# close() -- patch up the file header and close the
+# output file
+# You should set the parameters before the first writeframesraw or
+# writeframes. The total number of frames does not need to be set,
+# 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
+# close() to patch up the sizes in the header.
+# The close() method is called automatically when the class instance
+# is destroyed.
+
+# from <multimedia/audio_filehdr.h>
+AUDIO_FILE_MAGIC = 0x2e736e64
+AUDIO_FILE_ENCODING_MULAW_8 = 1
+AUDIO_FILE_ENCODING_LINEAR_8 = 2
+AUDIO_FILE_ENCODING_LINEAR_16 = 3
+AUDIO_FILE_ENCODING_LINEAR_24 = 4
+AUDIO_FILE_ENCODING_LINEAR_32 = 5
+AUDIO_FILE_ENCODING_FLOAT = 6
+AUDIO_FILE_ENCODING_DOUBLE = 7
+AUDIO_FILE_ENCODING_ADPCM_G721 = 23
+AUDIO_FILE_ENCODING_ADPCM_G722 = 24
+AUDIO_FILE_ENCODING_ADPCM_G723_3 = 25
+AUDIO_FILE_ENCODING_ADPCM_G723_5 = 26
+AUDIO_FILE_ENCODING_ALAW_8 = 27
+
+# from <multimedia/audio_hdr.h>
+AUDIO_UNKNOWN_SIZE = 0xFFFFFFFFL # ((unsigned)(~0))
+
+_simple_encodings = [AUDIO_FILE_ENCODING_MULAW_8,
+ AUDIO_FILE_ENCODING_LINEAR_8,
+ AUDIO_FILE_ENCODING_LINEAR_16,
+ AUDIO_FILE_ENCODING_LINEAR_24,
+ AUDIO_FILE_ENCODING_LINEAR_32,
+ AUDIO_FILE_ENCODING_ALAW_8]
+
+def _read_u32(file):
+ x = 0L
+ for i in range(4):
+ byte = file.read(1)
+ if byte == '':
+ raise EOFError
+ x = x*256 + ord(byte)
+ return x
+
+def _write_u32(file, x):
+ data = []
+ for i in range(4):
+ d, m = divmod(x, 256)
+ data.insert(0, m)
+ x = d
+ for i in range(4):
+ file.write(chr(int(data[i])))
+
+class Au_read:
+ def initfp(self, file):
+ self._file = file
+ self._soundpos = 0
+ magic = int(_read_u32(file))
+ if magic != AUDIO_FILE_MAGIC:
+ raise Error, 'bad magic number'
+ self._hdr_size = int(_read_u32(file))
+ if self._hdr_size < 24:
+ raise Error, 'header size too small'
+ if self._hdr_size > 100:
+ raise Error, 'header size rediculously large'
+ self._data_size = _read_u32(file)
+ if self._data_size != AUDIO_UNKNOWN_SIZE:
+ self._data_size = int(self._data_size)
+ self._encoding = int(_read_u32(file))
+ if self._encoding not in _simple_encodings:
+ raise Error, 'encoding not (yet) supported'
+ if self._encoding in (AUDIO_FILE_ENCODING_MULAW_8,
+ AUDIO_FILE_ENCODING_LINEAR_8,
+ AUDIO_FILE_ENCODING_ALAW_8):
+ self._sampwidth = 2
+ self._framesize = 1
+ elif self._encoding == AUDIO_FILE_ENCODING_LINEAR_16:
+ self._framesize = self._sampwidth = 2
+ elif self._encoding == AUDIO_FILE_ENCODING_LINEAR_24:
+ self._framesize = self._sampwidth = 3
+ elif self._encoding == AUDIO_FILE_ENCODING_LINEAR_32:
+ self._framesize = self._sampwidth = 4
+ else:
+ raise Error, 'unknown encoding'
+ self._framerate = int(_read_u32(file))
+ self._nchannels = int(_read_u32(file))
+ self._framesize = self._framesize * self._nchannels
+ if self._hdr_size > 24:
+ self._info = file.read(self._hdr_size - 24)
+ for i in range(len(self._info)):
+ if self._info[i] == '\0':
+ self._info = self._info[:i]
+ break
+ else:
+ self._info = ''
+ return self
+
+ def init(self, filename):
+ import builtin
+ return self.initfp(builtin.open(filename, 'r'))
+
+ def __del__(self):
+ if self._file:
+ self.close()
+
+ def getfp(self):
+ return self._file
+
+ def getnchannels(self):
+ return self._nchannels
+
+ def getsampwidth(self):
+ return self._sampwidth
+
+ def getframerate(self):
+ return self._framerate
+
+ def getnframes(self):
+ if self._data_size == AUDIO_UNKNOWN_SIZE:
+ return AUDIO_UNKNOWN_SIZE
+ if self._encoding in _simple_encodings:
+ return self._data_size / self._framesize
+ return 0 # XXX--must do some arithmetic here
+
+ def getcomptype(self):
+ if self._encoding == AUDIO_FILE_ENCODING_MULAW_8:
+ return 'ULAW'
+ elif self._encoding == AUDIO_FILE_ENCODING_ALAW_8:
+ return 'ALAW'
+ else:
+ return 'NONE'
+
+ def getcompname(self):
+ if self._encoding == AUDIO_FILE_ENCODING_MULAW_8:
+ return 'CCITT G.711 u-law'
+ elif self._encoding == AUDIO_FILE_ENCODING_ALAW_8:
+ return 'CCITT G.711 A-law'
+ else:
+ return 'not compressed'
+
+ def getparams(self):
+ return self.getnchannels(), self.getsampwidth(), \
+ self.getframerate(), self.getnframes(), \
+ self.getcomptype(), self.getcompname()
+
+ def getmarkers(self):
+ return None
+
+ def getmark(self, id):
+ raise Error, 'no marks'
+
+ def readframes(self, nframes):
+ if self._encoding in _simple_encodings:
+ if nframes == AUDIO_UNKNOWN_SIZE:
+ data = self._file.read()
+ else:
+ data = self._file.read(nframes * self._sampwidth * self._nchannels)
+ if self._encoding == AUDIO_FILE_ENCODING_MULAW_8:
+ import audioop
+ data = audioop.ulaw2lin(data, self._sampwidth)
+ return data
+ return None # XXX--not implemented yet
+
+ def rewind(self):
+ self._soundpos = 0
+ self._file.seek(self._hdr_size)
+
+ def tell(self):
+ return self._soundpos
+
+ def setpos(self, pos):
+ if pos < 0 or pos > self.getnframes():
+ raise Error, 'position not in range'
+ self._file.seek(pos * self._framesize + self._hdr_size)
+ self._soundpos = pos
+
+ def close(self):
+ self._file.close()
+ self._file = None
+
+class Au_write:
+ def init(self, filename):
+ import builtin
+ return self.initfp(builtin.open(filename, 'w'))
+
+ def initfp(self, file):
+ self._file = file
+ self._framerate = 0
+ self._nchannels = 0
+ self._sampwidth = 0
+ self._framesize = 0
+ self._nframes = AUDIO_UNKNOWN_SIZE
+ self._nframeswritten = 0
+ self._datawritten = 0
+ self._datalength = 0
+ self._info = ''
+ self._comptype = 'ULAW' # default is U-law
+ return self
+
+ def __del__(self):
+ if self._file:
+ self.close()
+
+ def setnchannels(self, nchannels):
+ if self._nframeswritten:
+ raise Error, 'cannot change parameters after starting to write'
+ if nchannels not in (1, 2, 4):
+ raise Error, 'only 1, 2, or 4 channels supported'
+ self._nchannels = nchannels
+
+ def getnchannels(self):
+ if not self._nchannels:
+ raise Error, 'number of channels not set'
+ return self._nchannels
+
+ def setsampwidth(self, sampwidth):
+ if self._nframeswritten:
+ raise Error, 'cannot change parameters after starting to write'
+ if sampwidth not in (1, 2, 4):
+ raise Error, 'bad sample width'
+ self._sampwidth = sampwidth
+
+ def getsampwidth(self):
+ if not self._framerate:
+ raise Error, 'sample width not specified'
+ return self._sampwidth
+
+ def setframerate(self, framerate):
+ if self._nframeswritten:
+ raise Error, 'cannot change parameters after starting to write'
+ self._framerate = framerate
+
+ def getframerate(self):
+ if not self._framerate:
+ raise Error, 'frame rate not set'
+ return self._framerate
+
+ def setnframes(self, nframes):
+ if self._nframeswritten:
+ raise Error, 'cannot change parameters after starting to write'
+ if nframes < 0:
+ raise Error, '# of frames cannot be negative'
+ self._nframes = nframes
+
+ def getnframes(self):
+ return self._nframeswritten
+
+ def setcomptype(self, type, name):
+ if type in ('NONE', 'ULAW'):
+ self._comptype = type
+ else:
+ raise Error, 'unknown compression type'
+
+ def getcomptype(self):
+ return self._comptype
+
+ def getcompname(self):
+ if self._comptype == 'ULAW':
+ return 'CCITT G.711 u-law'
+ elif self._comptype == 'ALAW':
+ return 'CCITT G.711 A-law'
+ else:
+ return 'not compressed'
+
+ def setparams(self, (nchannels, sampwidth, framerate, nframes, comptype, compname)):
+ self.setnchannels(nchannels)
+ self.setsampwidth(sampwidth)
+ self.setframerate(framerate)
+ self.setnframes(nframes)
+ self.setcomptype(comptype, compname)
+
+ def getparams(self):
+ return self.getnchannels(), self.getsampwidth(), \
+ self.getframerate(), self.getnframes(), \
+ self.getcomptype(), self.getcompname()
+
+ def tell(self):
+ return self._nframeswritten
+
+ def writeframesraw(self, data):
+ self._ensure_header_written()
+ nframes = len(data) / self._framesize
+ if self._comptype == 'ULAW':
+ import audioop
+ data = audioop.lin2ulaw(data, self._sampwidth)
+ self._file.write(data)
+ self._nframeswritten = self._nframeswritten + nframes
+ self._datawritten = self._datawritten + len(data)
+
+ def writeframes(self, data):
+ self.writeframesraw(data)
+ if self._nframeswritten != self._nframes or \
+ self._datalength != self._datawritten:
+ self._patchheader()
+
+ def close(self):
+ self._ensure_header_written()
+ if self._nframeswritten != self._nframes or \
+ self._datalength != self._datawritten:
+ self._patchheader()
+ self._file.close()
+ self._file = None
+
+ #
+ # private methods
+ #
+ def _ensure_header_written(self):
+ if not self._nframeswritten:
+ if not self._nchannels:
+ raise Error, '# of channels not specified'
+ if not self._sampwidth:
+ raise Error, 'sample width not specified'
+ if not self._framerate:
+ raise Error, 'frame rate not specified'
+ self._write_header()
+
+ def _write_header(self):
+ if self._comptype == 'NONE':
+ if self._sampwidth == 1:
+ encoding = AUDIO_FILE_ENCODING_LINEAR_8
+ self._framesize = 1
+ elif self._sampwidth == 2:
+ encoding = AUDIO_FILE_ENCODING_LINEAR_16
+ self._framesize = 2
+ elif self._sampwidth == 4:
+ encoding = AUDIO_FILE_ENCODING_LINEAR_32
+ self._framesize = 4
+ else:
+ raise Error, 'internal error'
+ elif self._comptype == 'ULAW':
+ encoding = AUDIO_FILE_ENCODING_MULAW_8
+ self._framesize = 1
+ else:
+ raise Error, 'internal error'
+ self._framesize = self._framesize * self._nchannels
+ _write_u32(self._file, AUDIO_FILE_MAGIC)
+ header_size = 25 + len(self._info)
+ header_size = (header_size + 7) & ~7
+ _write_u32(self._file, header_size)
+ if self._nframes == AUDIO_UNKNOWN_SIZE:
+ length = AUDIO_UNKNOWN_SIZE
+ else:
+ length = self._nframes * self._framesize
+ _write_u32(self._file, length)
+ self._datalength = length
+ _write_u32(self._file, encoding)
+ _write_u32(self._file, self._framerate)
+ _write_u32(self._file, self._nchannels)
+ self._file.write(self._info)
+ self._file.write('\0'*(header_size - len(self._info) - 24))
+
+ def _patchheader(self):
+ self._file.seek(8)
+ _write_u32(self._file, self._datawritten)
+ self._datalength = self._datawritten
+ self._file.seek(0, 2)
+
+def open(filename, mode):
+ if mode == 'r':
+ return Au_read().init(filename)
+ elif mode == 'w':
+ return Au_write().init(filename)
+ else:
+ raise Error, "mode must be 'r' or 'w'"
+
+def openfp(filep, mode):
+ if mode == 'r':
+ return Au_read().initfp(filep)
+ elif mode == 'w':
+ return Au_write().initfp(filep)
+ else:
+ raise Error, "mode must be 'r' or 'w'"