mirror of
https://github.com/python/cpython.git
synced 2025-09-26 10:19:53 +00:00
aifc.py: added missing tell() method in AIFC write class;
use audioop module as backup for cl module when reading or writing u-law compressed files. sunau.py: interface with the same methods as aifc for Sun and NeXT audio files
This commit is contained in:
parent
66bca326cb
commit
43bf0bc857
2 changed files with 569 additions and 47 deletions
145
Lib/aifc.py
145
Lib/aifc.py
|
@ -40,7 +40,7 @@
|
||||||
# <size of the samples> (2 bytes)
|
# <size of the samples> (2 bytes)
|
||||||
# <sampling frequency> (10 bytes, IEEE 80-bit extended
|
# <sampling frequency> (10 bytes, IEEE 80-bit extended
|
||||||
# floating point)
|
# floating point)
|
||||||
# if AIFF-C files only:
|
# in AIFF-C files only:
|
||||||
# <compression type> (4 bytes)
|
# <compression type> (4 bytes)
|
||||||
# <human-readable version of compression type> ("pstring")
|
# <human-readable version of compression type> ("pstring")
|
||||||
# SSND
|
# SSND
|
||||||
|
@ -58,9 +58,9 @@
|
||||||
# or
|
# or
|
||||||
# f = aifc.openfp(filep, 'r')
|
# f = aifc.openfp(filep, 'r')
|
||||||
# where file is the name of a file and filep is an open file pointer.
|
# 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
|
# The open file pointer must have methods read(), seek(), and close().
|
||||||
# close(). In some types of audio files, if the setpos() method is
|
# In some types of audio files, if the setpos() method is not used,
|
||||||
# not used, the seek() method is not necessary.
|
# the seek() method is not necessary.
|
||||||
#
|
#
|
||||||
# This returns an instance of a class with the following public methods:
|
# This returns an instance of a class with the following public methods:
|
||||||
# getnchannels() -- returns number of audio channels (1 for
|
# 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 returned by tell(), the position given to setpos() and
|
||||||
# the position of marks are all compatible and have nothing to do with
|
# the position of marks are all compatible and have nothing to do with
|
||||||
# the actual postion in the file.
|
# the actual postion in the file.
|
||||||
|
# The close() method is called automatically when the class instance
|
||||||
|
# is destroyed.
|
||||||
#
|
#
|
||||||
# Writing AIFF files:
|
# Writing AIFF files:
|
||||||
# f = aifc.open(file, 'w')
|
# f = aifc.open(file, 'w')
|
||||||
|
@ -122,11 +124,13 @@
|
||||||
# but when it is set to the correct value, the header does not have to
|
# but when it is set to the correct value, the header does not have to
|
||||||
# be patched up.
|
# be patched up.
|
||||||
# It is best to first set all parameters, perhaps possibly the
|
# 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
|
# When all frames have been written, either call writeframes('') or
|
||||||
# close() to patch up the sizes in the header.
|
# close() to patch up the sizes in the header.
|
||||||
# Marks can be added anytime. If there are any marks, ypu must call
|
# Marks can be added anytime. If there are any marks, ypu must call
|
||||||
# close() after all frames have been written.
|
# 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
|
# 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
|
# written, otherwise an AIFF-C file is written. This default can be
|
||||||
|
@ -347,7 +351,7 @@ class Aifc_read:
|
||||||
# methods
|
# methods
|
||||||
# _soundpos -- the position in the audio stream
|
# _soundpos -- the position in the audio stream
|
||||||
# available through the tell() method, set through the
|
# available through the tell() method, set through the
|
||||||
# tell() method
|
# setpos() method
|
||||||
#
|
#
|
||||||
# These variables are used internally only:
|
# These variables are used internally only:
|
||||||
# _version -- the AIFF-C version number
|
# _version -- the AIFF-C version number
|
||||||
|
@ -362,6 +366,7 @@ class Aifc_read:
|
||||||
self._file = file
|
self._file = file
|
||||||
self._version = 0
|
self._version = 0
|
||||||
self._decomp = None
|
self._decomp = None
|
||||||
|
self._convert = None
|
||||||
self._markers = []
|
self._markers = []
|
||||||
self._soundpos = 0
|
self._soundpos = 0
|
||||||
form = self._file.read(4)
|
form = self._file.read(4)
|
||||||
|
@ -433,6 +438,10 @@ class Aifc_read:
|
||||||
def init(self, filename):
|
def init(self, filename):
|
||||||
return self.initfp(builtin.open(filename, 'r'))
|
return self.initfp(builtin.open(filename, 'r'))
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
if self._file:
|
||||||
|
self.close()
|
||||||
|
|
||||||
#
|
#
|
||||||
# User visible methods.
|
# User visible methods.
|
||||||
#
|
#
|
||||||
|
@ -475,8 +484,9 @@ class Aifc_read:
|
||||||
## return self._version
|
## return self._version
|
||||||
|
|
||||||
def getparams(self):
|
def getparams(self):
|
||||||
return self._nchannels, self._sampwidth, self._framerate, \
|
return self.getnchannels(), self.getsampwidth(), \
|
||||||
self._nframes, self._comptype, self._compname
|
self.getframerate(), self.getnframes(), \
|
||||||
|
self.getcomptype(), self.getcompname()
|
||||||
|
|
||||||
def getmarkers(self):
|
def getmarkers(self):
|
||||||
if len(self._markers) == 0:
|
if len(self._markers) == 0:
|
||||||
|
@ -506,16 +516,24 @@ class Aifc_read:
|
||||||
if nframes == 0:
|
if nframes == 0:
|
||||||
return ''
|
return ''
|
||||||
data = self._ssnd_chunk.read(nframes * self._framesize)
|
data = self._ssnd_chunk.read(nframes * self._framesize)
|
||||||
if self._decomp and data:
|
if self._convert and data:
|
||||||
dummy = self._decomp.SetParam(CL.FRAME_BUFFER_SIZE, \
|
data = self._convert(data)
|
||||||
len(data) * 2)
|
|
||||||
data = self._decomp.Decompress(len(data) / self._nchannels, data)
|
|
||||||
self._soundpos = self._soundpos + len(data) / (self._nchannels * self._sampwidth)
|
self._soundpos = self._soundpos + len(data) / (self._nchannels * self._sampwidth)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
#
|
#
|
||||||
# Internal methods.
|
# 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):
|
def _read_comm_chunk(self, chunk):
|
||||||
nchannels = _read_short(chunk)
|
nchannels = _read_short(chunk)
|
||||||
self._nchannels = _convert1(nchannels, _nchannelslist)
|
self._nchannels = _convert1(nchannels, _nchannelslist)
|
||||||
|
@ -547,6 +565,14 @@ class Aifc_read:
|
||||||
try:
|
try:
|
||||||
import cl, CL
|
import cl, CL
|
||||||
except ImportError:
|
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'
|
raise Error, 'cannot read compressed AIFF-C files'
|
||||||
if self._comptype == 'ULAW':
|
if self._comptype == 'ULAW':
|
||||||
scheme = CL.G711_ULAW
|
scheme = CL.G711_ULAW
|
||||||
|
@ -557,6 +583,7 @@ class Aifc_read:
|
||||||
else:
|
else:
|
||||||
raise Error, 'unsupported compression type'
|
raise Error, 'unsupported compression type'
|
||||||
self._decomp = cl.OpenDecompressor(scheme)
|
self._decomp = cl.OpenDecompressor(scheme)
|
||||||
|
self._convert = self._decomp_data
|
||||||
else:
|
else:
|
||||||
self._comptype = 'NONE'
|
self._comptype = 'NONE'
|
||||||
self._compname = 'not compressed'
|
self._compname = 'not compressed'
|
||||||
|
@ -622,6 +649,7 @@ class Aifc_write:
|
||||||
self._comptype = 'NONE'
|
self._comptype = 'NONE'
|
||||||
self._compname = 'not compressed'
|
self._compname = 'not compressed'
|
||||||
self._comp = None
|
self._comp = None
|
||||||
|
self._convert = None
|
||||||
self._nchannels = 0
|
self._nchannels = 0
|
||||||
self._sampwidth = 0
|
self._sampwidth = 0
|
||||||
self._framerate = 0
|
self._framerate = 0
|
||||||
|
@ -634,6 +662,10 @@ class Aifc_write:
|
||||||
self._aifc = 1 # AIFF-C is default
|
self._aifc = 1 # AIFF-C is default
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
if self._file:
|
||||||
|
self.close()
|
||||||
|
|
||||||
#
|
#
|
||||||
# User visible methods.
|
# User visible methods.
|
||||||
#
|
#
|
||||||
|
@ -752,15 +784,14 @@ class Aifc_write:
|
||||||
return None
|
return None
|
||||||
return self._markers
|
return self._markers
|
||||||
|
|
||||||
|
def tell(self):
|
||||||
|
return self._nframeswritten
|
||||||
|
|
||||||
def writeframesraw(self, data):
|
def writeframesraw(self, data):
|
||||||
self._ensure_header_written(len(data))
|
self._ensure_header_written(len(data))
|
||||||
nframes = len(data) / (self._sampwidth * self._nchannels)
|
nframes = len(data) / (self._sampwidth * self._nchannels)
|
||||||
if self._comp:
|
if self._convert:
|
||||||
dummy = self._comp.SetParam(CL.FRAME_BUFFER_SIZE, \
|
data = self._convert(data)
|
||||||
len(data))
|
|
||||||
dummy = self._comp.SetParam(CL.COMPRESSED_BUFFER_SIZE,\
|
|
||||||
len(data))
|
|
||||||
data = self._comp.Compress(nframes, data)
|
|
||||||
self._file.write(data)
|
self._file.write(data)
|
||||||
self._nframeswritten = self._nframeswritten + nframes
|
self._nframeswritten = self._nframeswritten + nframes
|
||||||
self._datawritten = self._datawritten + len(data)
|
self._datawritten = self._datawritten + len(data)
|
||||||
|
@ -791,6 +822,15 @@ class Aifc_write:
|
||||||
#
|
#
|
||||||
# Internal methods.
|
# 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):
|
def _ensure_header_written(self, datasize):
|
||||||
if not self._nframeswritten:
|
if not self._nframeswritten:
|
||||||
if self._comptype in ('ULAW', 'ALAW'):
|
if self._comptype in ('ULAW', 'ALAW'):
|
||||||
|
@ -806,37 +846,48 @@ class Aifc_write:
|
||||||
raise Error, 'sampling rate not specified'
|
raise Error, 'sampling rate not specified'
|
||||||
self._write_header(datasize)
|
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):
|
def _write_header(self, initlength):
|
||||||
if self._aifc and self._comptype != 'NONE':
|
if self._aifc and self._comptype != 'NONE':
|
||||||
try:
|
self._init_compression()
|
||||||
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._file.write('FORM')
|
self._file.write('FORM')
|
||||||
if not self._nframes:
|
if not self._nframes:
|
||||||
self._nframes = initlength / (self._nchannels * self._sampwidth)
|
self._nframes = initlength / (self._nchannels * self._sampwidth)
|
||||||
|
|
471
Lib/sunau.py
Normal file
471
Lib/sunau.py
Normal file
|
@ -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'"
|
Loading…
Add table
Add a link
Reference in a new issue