1*cda5da8dSAndroid Build Coastguard Worker"""Stuff to parse WAVE files. 2*cda5da8dSAndroid Build Coastguard Worker 3*cda5da8dSAndroid Build Coastguard WorkerUsage. 4*cda5da8dSAndroid Build Coastguard Worker 5*cda5da8dSAndroid Build Coastguard WorkerReading WAVE files: 6*cda5da8dSAndroid Build Coastguard Worker f = wave.open(file, 'r') 7*cda5da8dSAndroid Build Coastguard Workerwhere file is either the name of a file or an open file pointer. 8*cda5da8dSAndroid Build Coastguard WorkerThe open file pointer must have methods read(), seek(), and close(). 9*cda5da8dSAndroid Build Coastguard WorkerWhen the setpos() and rewind() methods are not used, the seek() 10*cda5da8dSAndroid Build Coastguard Workermethod is not necessary. 11*cda5da8dSAndroid Build Coastguard Worker 12*cda5da8dSAndroid Build Coastguard WorkerThis returns an instance of a class with the following public methods: 13*cda5da8dSAndroid Build Coastguard Worker getnchannels() -- returns number of audio channels (1 for 14*cda5da8dSAndroid Build Coastguard Worker mono, 2 for stereo) 15*cda5da8dSAndroid Build Coastguard Worker getsampwidth() -- returns sample width in bytes 16*cda5da8dSAndroid Build Coastguard Worker getframerate() -- returns sampling frequency 17*cda5da8dSAndroid Build Coastguard Worker getnframes() -- returns number of audio frames 18*cda5da8dSAndroid Build Coastguard Worker getcomptype() -- returns compression type ('NONE' for linear samples) 19*cda5da8dSAndroid Build Coastguard Worker getcompname() -- returns human-readable version of 20*cda5da8dSAndroid Build Coastguard Worker compression type ('not compressed' linear samples) 21*cda5da8dSAndroid Build Coastguard Worker getparams() -- returns a namedtuple consisting of all of the 22*cda5da8dSAndroid Build Coastguard Worker above in the above order 23*cda5da8dSAndroid Build Coastguard Worker getmarkers() -- returns None (for compatibility with the 24*cda5da8dSAndroid Build Coastguard Worker aifc module) 25*cda5da8dSAndroid Build Coastguard Worker getmark(id) -- raises an error since the mark does not 26*cda5da8dSAndroid Build Coastguard Worker exist (for compatibility with the aifc module) 27*cda5da8dSAndroid Build Coastguard Worker readframes(n) -- returns at most n frames of audio 28*cda5da8dSAndroid Build Coastguard Worker rewind() -- rewind to the beginning of the audio stream 29*cda5da8dSAndroid Build Coastguard Worker setpos(pos) -- seek to the specified position 30*cda5da8dSAndroid Build Coastguard Worker tell() -- return the current position 31*cda5da8dSAndroid Build Coastguard Worker close() -- close the instance (make it unusable) 32*cda5da8dSAndroid Build Coastguard WorkerThe position returned by tell() and the position given to setpos() 33*cda5da8dSAndroid Build Coastguard Workerare compatible and have nothing to do with the actual position in the 34*cda5da8dSAndroid Build Coastguard Workerfile. 35*cda5da8dSAndroid Build Coastguard WorkerThe close() method is called automatically when the class instance 36*cda5da8dSAndroid Build Coastguard Workeris destroyed. 37*cda5da8dSAndroid Build Coastguard Worker 38*cda5da8dSAndroid Build Coastguard WorkerWriting WAVE files: 39*cda5da8dSAndroid Build Coastguard Worker f = wave.open(file, 'w') 40*cda5da8dSAndroid Build Coastguard Workerwhere file is either the name of a file or an open file pointer. 41*cda5da8dSAndroid Build Coastguard WorkerThe open file pointer must have methods write(), tell(), seek(), and 42*cda5da8dSAndroid Build Coastguard Workerclose(). 43*cda5da8dSAndroid Build Coastguard Worker 44*cda5da8dSAndroid Build Coastguard WorkerThis returns an instance of a class with the following public methods: 45*cda5da8dSAndroid Build Coastguard Worker setnchannels(n) -- set the number of channels 46*cda5da8dSAndroid Build Coastguard Worker setsampwidth(n) -- set the sample width 47*cda5da8dSAndroid Build Coastguard Worker setframerate(n) -- set the frame rate 48*cda5da8dSAndroid Build Coastguard Worker setnframes(n) -- set the number of frames 49*cda5da8dSAndroid Build Coastguard Worker setcomptype(type, name) 50*cda5da8dSAndroid Build Coastguard Worker -- set the compression type and the 51*cda5da8dSAndroid Build Coastguard Worker human-readable compression type 52*cda5da8dSAndroid Build Coastguard Worker setparams(tuple) 53*cda5da8dSAndroid Build Coastguard Worker -- set all parameters at once 54*cda5da8dSAndroid Build Coastguard Worker tell() -- return current position in output file 55*cda5da8dSAndroid Build Coastguard Worker writeframesraw(data) 56*cda5da8dSAndroid Build Coastguard Worker -- write audio frames without patching up the 57*cda5da8dSAndroid Build Coastguard Worker file header 58*cda5da8dSAndroid Build Coastguard Worker writeframes(data) 59*cda5da8dSAndroid Build Coastguard Worker -- write audio frames and patch up the file header 60*cda5da8dSAndroid Build Coastguard Worker close() -- patch up the file header and close the 61*cda5da8dSAndroid Build Coastguard Worker output file 62*cda5da8dSAndroid Build Coastguard WorkerYou should set the parameters before the first writeframesraw or 63*cda5da8dSAndroid Build Coastguard Workerwriteframes. The total number of frames does not need to be set, 64*cda5da8dSAndroid Build Coastguard Workerbut when it is set to the correct value, the header does not have to 65*cda5da8dSAndroid Build Coastguard Workerbe patched up. 66*cda5da8dSAndroid Build Coastguard WorkerIt is best to first set all parameters, perhaps possibly the 67*cda5da8dSAndroid Build Coastguard Workercompression type, and then write audio frames using writeframesraw. 68*cda5da8dSAndroid Build Coastguard WorkerWhen all frames have been written, either call writeframes(b'') or 69*cda5da8dSAndroid Build Coastguard Workerclose() to patch up the sizes in the header. 70*cda5da8dSAndroid Build Coastguard WorkerThe close() method is called automatically when the class instance 71*cda5da8dSAndroid Build Coastguard Workeris destroyed. 72*cda5da8dSAndroid Build Coastguard Worker""" 73*cda5da8dSAndroid Build Coastguard Worker 74*cda5da8dSAndroid Build Coastguard Workerfrom collections import namedtuple 75*cda5da8dSAndroid Build Coastguard Workerimport builtins 76*cda5da8dSAndroid Build Coastguard Workerimport struct 77*cda5da8dSAndroid Build Coastguard Workerimport sys 78*cda5da8dSAndroid Build Coastguard Worker 79*cda5da8dSAndroid Build Coastguard Worker 80*cda5da8dSAndroid Build Coastguard Worker__all__ = ["open", "Error", "Wave_read", "Wave_write"] 81*cda5da8dSAndroid Build Coastguard Worker 82*cda5da8dSAndroid Build Coastguard Workerclass Error(Exception): 83*cda5da8dSAndroid Build Coastguard Worker pass 84*cda5da8dSAndroid Build Coastguard Worker 85*cda5da8dSAndroid Build Coastguard WorkerWAVE_FORMAT_PCM = 0x0001 86*cda5da8dSAndroid Build Coastguard Worker 87*cda5da8dSAndroid Build Coastguard Worker_array_fmts = None, 'b', 'h', None, 'i' 88*cda5da8dSAndroid Build Coastguard Worker 89*cda5da8dSAndroid Build Coastguard Worker_wave_params = namedtuple('_wave_params', 90*cda5da8dSAndroid Build Coastguard Worker 'nchannels sampwidth framerate nframes comptype compname') 91*cda5da8dSAndroid Build Coastguard Worker 92*cda5da8dSAndroid Build Coastguard Worker 93*cda5da8dSAndroid Build Coastguard Workerdef _byteswap(data, width): 94*cda5da8dSAndroid Build Coastguard Worker swapped_data = bytearray(len(data)) 95*cda5da8dSAndroid Build Coastguard Worker 96*cda5da8dSAndroid Build Coastguard Worker for i in range(0, len(data), width): 97*cda5da8dSAndroid Build Coastguard Worker for j in range(width): 98*cda5da8dSAndroid Build Coastguard Worker swapped_data[i + width - 1 - j] = data[i + j] 99*cda5da8dSAndroid Build Coastguard Worker 100*cda5da8dSAndroid Build Coastguard Worker return bytes(swapped_data) 101*cda5da8dSAndroid Build Coastguard Worker 102*cda5da8dSAndroid Build Coastguard Worker 103*cda5da8dSAndroid Build Coastguard Workerclass _Chunk: 104*cda5da8dSAndroid Build Coastguard Worker def __init__(self, file, align=True, bigendian=True, inclheader=False): 105*cda5da8dSAndroid Build Coastguard Worker self.closed = False 106*cda5da8dSAndroid Build Coastguard Worker self.align = align # whether to align to word (2-byte) boundaries 107*cda5da8dSAndroid Build Coastguard Worker if bigendian: 108*cda5da8dSAndroid Build Coastguard Worker strflag = '>' 109*cda5da8dSAndroid Build Coastguard Worker else: 110*cda5da8dSAndroid Build Coastguard Worker strflag = '<' 111*cda5da8dSAndroid Build Coastguard Worker self.file = file 112*cda5da8dSAndroid Build Coastguard Worker self.chunkname = file.read(4) 113*cda5da8dSAndroid Build Coastguard Worker if len(self.chunkname) < 4: 114*cda5da8dSAndroid Build Coastguard Worker raise EOFError 115*cda5da8dSAndroid Build Coastguard Worker try: 116*cda5da8dSAndroid Build Coastguard Worker self.chunksize = struct.unpack_from(strflag+'L', file.read(4))[0] 117*cda5da8dSAndroid Build Coastguard Worker except struct.error: 118*cda5da8dSAndroid Build Coastguard Worker raise EOFError from None 119*cda5da8dSAndroid Build Coastguard Worker if inclheader: 120*cda5da8dSAndroid Build Coastguard Worker self.chunksize = self.chunksize - 8 # subtract header 121*cda5da8dSAndroid Build Coastguard Worker self.size_read = 0 122*cda5da8dSAndroid Build Coastguard Worker try: 123*cda5da8dSAndroid Build Coastguard Worker self.offset = self.file.tell() 124*cda5da8dSAndroid Build Coastguard Worker except (AttributeError, OSError): 125*cda5da8dSAndroid Build Coastguard Worker self.seekable = False 126*cda5da8dSAndroid Build Coastguard Worker else: 127*cda5da8dSAndroid Build Coastguard Worker self.seekable = True 128*cda5da8dSAndroid Build Coastguard Worker 129*cda5da8dSAndroid Build Coastguard Worker def getname(self): 130*cda5da8dSAndroid Build Coastguard Worker """Return the name (ID) of the current chunk.""" 131*cda5da8dSAndroid Build Coastguard Worker return self.chunkname 132*cda5da8dSAndroid Build Coastguard Worker 133*cda5da8dSAndroid Build Coastguard Worker def close(self): 134*cda5da8dSAndroid Build Coastguard Worker if not self.closed: 135*cda5da8dSAndroid Build Coastguard Worker try: 136*cda5da8dSAndroid Build Coastguard Worker self.skip() 137*cda5da8dSAndroid Build Coastguard Worker finally: 138*cda5da8dSAndroid Build Coastguard Worker self.closed = True 139*cda5da8dSAndroid Build Coastguard Worker 140*cda5da8dSAndroid Build Coastguard Worker def seek(self, pos, whence=0): 141*cda5da8dSAndroid Build Coastguard Worker """Seek to specified position into the chunk. 142*cda5da8dSAndroid Build Coastguard Worker Default position is 0 (start of chunk). 143*cda5da8dSAndroid Build Coastguard Worker If the file is not seekable, this will result in an error. 144*cda5da8dSAndroid Build Coastguard Worker """ 145*cda5da8dSAndroid Build Coastguard Worker 146*cda5da8dSAndroid Build Coastguard Worker if self.closed: 147*cda5da8dSAndroid Build Coastguard Worker raise ValueError("I/O operation on closed file") 148*cda5da8dSAndroid Build Coastguard Worker if not self.seekable: 149*cda5da8dSAndroid Build Coastguard Worker raise OSError("cannot seek") 150*cda5da8dSAndroid Build Coastguard Worker if whence == 1: 151*cda5da8dSAndroid Build Coastguard Worker pos = pos + self.size_read 152*cda5da8dSAndroid Build Coastguard Worker elif whence == 2: 153*cda5da8dSAndroid Build Coastguard Worker pos = pos + self.chunksize 154*cda5da8dSAndroid Build Coastguard Worker if pos < 0 or pos > self.chunksize: 155*cda5da8dSAndroid Build Coastguard Worker raise RuntimeError 156*cda5da8dSAndroid Build Coastguard Worker self.file.seek(self.offset + pos, 0) 157*cda5da8dSAndroid Build Coastguard Worker self.size_read = pos 158*cda5da8dSAndroid Build Coastguard Worker 159*cda5da8dSAndroid Build Coastguard Worker def tell(self): 160*cda5da8dSAndroid Build Coastguard Worker if self.closed: 161*cda5da8dSAndroid Build Coastguard Worker raise ValueError("I/O operation on closed file") 162*cda5da8dSAndroid Build Coastguard Worker return self.size_read 163*cda5da8dSAndroid Build Coastguard Worker 164*cda5da8dSAndroid Build Coastguard Worker def read(self, size=-1): 165*cda5da8dSAndroid Build Coastguard Worker """Read at most size bytes from the chunk. 166*cda5da8dSAndroid Build Coastguard Worker If size is omitted or negative, read until the end 167*cda5da8dSAndroid Build Coastguard Worker of the chunk. 168*cda5da8dSAndroid Build Coastguard Worker """ 169*cda5da8dSAndroid Build Coastguard Worker 170*cda5da8dSAndroid Build Coastguard Worker if self.closed: 171*cda5da8dSAndroid Build Coastguard Worker raise ValueError("I/O operation on closed file") 172*cda5da8dSAndroid Build Coastguard Worker if self.size_read >= self.chunksize: 173*cda5da8dSAndroid Build Coastguard Worker return b'' 174*cda5da8dSAndroid Build Coastguard Worker if size < 0: 175*cda5da8dSAndroid Build Coastguard Worker size = self.chunksize - self.size_read 176*cda5da8dSAndroid Build Coastguard Worker if size > self.chunksize - self.size_read: 177*cda5da8dSAndroid Build Coastguard Worker size = self.chunksize - self.size_read 178*cda5da8dSAndroid Build Coastguard Worker data = self.file.read(size) 179*cda5da8dSAndroid Build Coastguard Worker self.size_read = self.size_read + len(data) 180*cda5da8dSAndroid Build Coastguard Worker if self.size_read == self.chunksize and \ 181*cda5da8dSAndroid Build Coastguard Worker self.align and \ 182*cda5da8dSAndroid Build Coastguard Worker (self.chunksize & 1): 183*cda5da8dSAndroid Build Coastguard Worker dummy = self.file.read(1) 184*cda5da8dSAndroid Build Coastguard Worker self.size_read = self.size_read + len(dummy) 185*cda5da8dSAndroid Build Coastguard Worker return data 186*cda5da8dSAndroid Build Coastguard Worker 187*cda5da8dSAndroid Build Coastguard Worker def skip(self): 188*cda5da8dSAndroid Build Coastguard Worker """Skip the rest of the chunk. 189*cda5da8dSAndroid Build Coastguard Worker If you are not interested in the contents of the chunk, 190*cda5da8dSAndroid Build Coastguard Worker this method should be called so that the file points to 191*cda5da8dSAndroid Build Coastguard Worker the start of the next chunk. 192*cda5da8dSAndroid Build Coastguard Worker """ 193*cda5da8dSAndroid Build Coastguard Worker 194*cda5da8dSAndroid Build Coastguard Worker if self.closed: 195*cda5da8dSAndroid Build Coastguard Worker raise ValueError("I/O operation on closed file") 196*cda5da8dSAndroid Build Coastguard Worker if self.seekable: 197*cda5da8dSAndroid Build Coastguard Worker try: 198*cda5da8dSAndroid Build Coastguard Worker n = self.chunksize - self.size_read 199*cda5da8dSAndroid Build Coastguard Worker # maybe fix alignment 200*cda5da8dSAndroid Build Coastguard Worker if self.align and (self.chunksize & 1): 201*cda5da8dSAndroid Build Coastguard Worker n = n + 1 202*cda5da8dSAndroid Build Coastguard Worker self.file.seek(n, 1) 203*cda5da8dSAndroid Build Coastguard Worker self.size_read = self.size_read + n 204*cda5da8dSAndroid Build Coastguard Worker return 205*cda5da8dSAndroid Build Coastguard Worker except OSError: 206*cda5da8dSAndroid Build Coastguard Worker pass 207*cda5da8dSAndroid Build Coastguard Worker while self.size_read < self.chunksize: 208*cda5da8dSAndroid Build Coastguard Worker n = min(8192, self.chunksize - self.size_read) 209*cda5da8dSAndroid Build Coastguard Worker dummy = self.read(n) 210*cda5da8dSAndroid Build Coastguard Worker if not dummy: 211*cda5da8dSAndroid Build Coastguard Worker raise EOFError 212*cda5da8dSAndroid Build Coastguard Worker 213*cda5da8dSAndroid Build Coastguard Worker 214*cda5da8dSAndroid Build Coastguard Workerclass Wave_read: 215*cda5da8dSAndroid Build Coastguard Worker """Variables used in this class: 216*cda5da8dSAndroid Build Coastguard Worker 217*cda5da8dSAndroid Build Coastguard Worker These variables are available to the user though appropriate 218*cda5da8dSAndroid Build Coastguard Worker methods of this class: 219*cda5da8dSAndroid Build Coastguard Worker _file -- the open file with methods read(), close(), and seek() 220*cda5da8dSAndroid Build Coastguard Worker set through the __init__() method 221*cda5da8dSAndroid Build Coastguard Worker _nchannels -- the number of audio channels 222*cda5da8dSAndroid Build Coastguard Worker available through the getnchannels() method 223*cda5da8dSAndroid Build Coastguard Worker _nframes -- the number of audio frames 224*cda5da8dSAndroid Build Coastguard Worker available through the getnframes() method 225*cda5da8dSAndroid Build Coastguard Worker _sampwidth -- the number of bytes per audio sample 226*cda5da8dSAndroid Build Coastguard Worker available through the getsampwidth() method 227*cda5da8dSAndroid Build Coastguard Worker _framerate -- the sampling frequency 228*cda5da8dSAndroid Build Coastguard Worker available through the getframerate() method 229*cda5da8dSAndroid Build Coastguard Worker _comptype -- the AIFF-C compression type ('NONE' if AIFF) 230*cda5da8dSAndroid Build Coastguard Worker available through the getcomptype() method 231*cda5da8dSAndroid Build Coastguard Worker _compname -- the human-readable AIFF-C compression type 232*cda5da8dSAndroid Build Coastguard Worker available through the getcomptype() method 233*cda5da8dSAndroid Build Coastguard Worker _soundpos -- the position in the audio stream 234*cda5da8dSAndroid Build Coastguard Worker available through the tell() method, set through the 235*cda5da8dSAndroid Build Coastguard Worker setpos() method 236*cda5da8dSAndroid Build Coastguard Worker 237*cda5da8dSAndroid Build Coastguard Worker These variables are used internally only: 238*cda5da8dSAndroid Build Coastguard Worker _fmt_chunk_read -- 1 iff the FMT chunk has been read 239*cda5da8dSAndroid Build Coastguard Worker _data_seek_needed -- 1 iff positioned correctly in audio 240*cda5da8dSAndroid Build Coastguard Worker file for readframes() 241*cda5da8dSAndroid Build Coastguard Worker _data_chunk -- instantiation of a chunk class for the DATA chunk 242*cda5da8dSAndroid Build Coastguard Worker _framesize -- size of one frame in the file 243*cda5da8dSAndroid Build Coastguard Worker """ 244*cda5da8dSAndroid Build Coastguard Worker 245*cda5da8dSAndroid Build Coastguard Worker def initfp(self, file): 246*cda5da8dSAndroid Build Coastguard Worker self._convert = None 247*cda5da8dSAndroid Build Coastguard Worker self._soundpos = 0 248*cda5da8dSAndroid Build Coastguard Worker self._file = _Chunk(file, bigendian = 0) 249*cda5da8dSAndroid Build Coastguard Worker if self._file.getname() != b'RIFF': 250*cda5da8dSAndroid Build Coastguard Worker raise Error('file does not start with RIFF id') 251*cda5da8dSAndroid Build Coastguard Worker if self._file.read(4) != b'WAVE': 252*cda5da8dSAndroid Build Coastguard Worker raise Error('not a WAVE file') 253*cda5da8dSAndroid Build Coastguard Worker self._fmt_chunk_read = 0 254*cda5da8dSAndroid Build Coastguard Worker self._data_chunk = None 255*cda5da8dSAndroid Build Coastguard Worker while 1: 256*cda5da8dSAndroid Build Coastguard Worker self._data_seek_needed = 1 257*cda5da8dSAndroid Build Coastguard Worker try: 258*cda5da8dSAndroid Build Coastguard Worker chunk = _Chunk(self._file, bigendian = 0) 259*cda5da8dSAndroid Build Coastguard Worker except EOFError: 260*cda5da8dSAndroid Build Coastguard Worker break 261*cda5da8dSAndroid Build Coastguard Worker chunkname = chunk.getname() 262*cda5da8dSAndroid Build Coastguard Worker if chunkname == b'fmt ': 263*cda5da8dSAndroid Build Coastguard Worker self._read_fmt_chunk(chunk) 264*cda5da8dSAndroid Build Coastguard Worker self._fmt_chunk_read = 1 265*cda5da8dSAndroid Build Coastguard Worker elif chunkname == b'data': 266*cda5da8dSAndroid Build Coastguard Worker if not self._fmt_chunk_read: 267*cda5da8dSAndroid Build Coastguard Worker raise Error('data chunk before fmt chunk') 268*cda5da8dSAndroid Build Coastguard Worker self._data_chunk = chunk 269*cda5da8dSAndroid Build Coastguard Worker self._nframes = chunk.chunksize // self._framesize 270*cda5da8dSAndroid Build Coastguard Worker self._data_seek_needed = 0 271*cda5da8dSAndroid Build Coastguard Worker break 272*cda5da8dSAndroid Build Coastguard Worker chunk.skip() 273*cda5da8dSAndroid Build Coastguard Worker if not self._fmt_chunk_read or not self._data_chunk: 274*cda5da8dSAndroid Build Coastguard Worker raise Error('fmt chunk and/or data chunk missing') 275*cda5da8dSAndroid Build Coastguard Worker 276*cda5da8dSAndroid Build Coastguard Worker def __init__(self, f): 277*cda5da8dSAndroid Build Coastguard Worker self._i_opened_the_file = None 278*cda5da8dSAndroid Build Coastguard Worker if isinstance(f, str): 279*cda5da8dSAndroid Build Coastguard Worker f = builtins.open(f, 'rb') 280*cda5da8dSAndroid Build Coastguard Worker self._i_opened_the_file = f 281*cda5da8dSAndroid Build Coastguard Worker # else, assume it is an open file object already 282*cda5da8dSAndroid Build Coastguard Worker try: 283*cda5da8dSAndroid Build Coastguard Worker self.initfp(f) 284*cda5da8dSAndroid Build Coastguard Worker except: 285*cda5da8dSAndroid Build Coastguard Worker if self._i_opened_the_file: 286*cda5da8dSAndroid Build Coastguard Worker f.close() 287*cda5da8dSAndroid Build Coastguard Worker raise 288*cda5da8dSAndroid Build Coastguard Worker 289*cda5da8dSAndroid Build Coastguard Worker def __del__(self): 290*cda5da8dSAndroid Build Coastguard Worker self.close() 291*cda5da8dSAndroid Build Coastguard Worker 292*cda5da8dSAndroid Build Coastguard Worker def __enter__(self): 293*cda5da8dSAndroid Build Coastguard Worker return self 294*cda5da8dSAndroid Build Coastguard Worker 295*cda5da8dSAndroid Build Coastguard Worker def __exit__(self, *args): 296*cda5da8dSAndroid Build Coastguard Worker self.close() 297*cda5da8dSAndroid Build Coastguard Worker 298*cda5da8dSAndroid Build Coastguard Worker # 299*cda5da8dSAndroid Build Coastguard Worker # User visible methods. 300*cda5da8dSAndroid Build Coastguard Worker # 301*cda5da8dSAndroid Build Coastguard Worker def getfp(self): 302*cda5da8dSAndroid Build Coastguard Worker return self._file 303*cda5da8dSAndroid Build Coastguard Worker 304*cda5da8dSAndroid Build Coastguard Worker def rewind(self): 305*cda5da8dSAndroid Build Coastguard Worker self._data_seek_needed = 1 306*cda5da8dSAndroid Build Coastguard Worker self._soundpos = 0 307*cda5da8dSAndroid Build Coastguard Worker 308*cda5da8dSAndroid Build Coastguard Worker def close(self): 309*cda5da8dSAndroid Build Coastguard Worker self._file = None 310*cda5da8dSAndroid Build Coastguard Worker file = self._i_opened_the_file 311*cda5da8dSAndroid Build Coastguard Worker if file: 312*cda5da8dSAndroid Build Coastguard Worker self._i_opened_the_file = None 313*cda5da8dSAndroid Build Coastguard Worker file.close() 314*cda5da8dSAndroid Build Coastguard Worker 315*cda5da8dSAndroid Build Coastguard Worker def tell(self): 316*cda5da8dSAndroid Build Coastguard Worker return self._soundpos 317*cda5da8dSAndroid Build Coastguard Worker 318*cda5da8dSAndroid Build Coastguard Worker def getnchannels(self): 319*cda5da8dSAndroid Build Coastguard Worker return self._nchannels 320*cda5da8dSAndroid Build Coastguard Worker 321*cda5da8dSAndroid Build Coastguard Worker def getnframes(self): 322*cda5da8dSAndroid Build Coastguard Worker return self._nframes 323*cda5da8dSAndroid Build Coastguard Worker 324*cda5da8dSAndroid Build Coastguard Worker def getsampwidth(self): 325*cda5da8dSAndroid Build Coastguard Worker return self._sampwidth 326*cda5da8dSAndroid Build Coastguard Worker 327*cda5da8dSAndroid Build Coastguard Worker def getframerate(self): 328*cda5da8dSAndroid Build Coastguard Worker return self._framerate 329*cda5da8dSAndroid Build Coastguard Worker 330*cda5da8dSAndroid Build Coastguard Worker def getcomptype(self): 331*cda5da8dSAndroid Build Coastguard Worker return self._comptype 332*cda5da8dSAndroid Build Coastguard Worker 333*cda5da8dSAndroid Build Coastguard Worker def getcompname(self): 334*cda5da8dSAndroid Build Coastguard Worker return self._compname 335*cda5da8dSAndroid Build Coastguard Worker 336*cda5da8dSAndroid Build Coastguard Worker def getparams(self): 337*cda5da8dSAndroid Build Coastguard Worker return _wave_params(self.getnchannels(), self.getsampwidth(), 338*cda5da8dSAndroid Build Coastguard Worker self.getframerate(), self.getnframes(), 339*cda5da8dSAndroid Build Coastguard Worker self.getcomptype(), self.getcompname()) 340*cda5da8dSAndroid Build Coastguard Worker 341*cda5da8dSAndroid Build Coastguard Worker def getmarkers(self): 342*cda5da8dSAndroid Build Coastguard Worker return None 343*cda5da8dSAndroid Build Coastguard Worker 344*cda5da8dSAndroid Build Coastguard Worker def getmark(self, id): 345*cda5da8dSAndroid Build Coastguard Worker raise Error('no marks') 346*cda5da8dSAndroid Build Coastguard Worker 347*cda5da8dSAndroid Build Coastguard Worker def setpos(self, pos): 348*cda5da8dSAndroid Build Coastguard Worker if pos < 0 or pos > self._nframes: 349*cda5da8dSAndroid Build Coastguard Worker raise Error('position not in range') 350*cda5da8dSAndroid Build Coastguard Worker self._soundpos = pos 351*cda5da8dSAndroid Build Coastguard Worker self._data_seek_needed = 1 352*cda5da8dSAndroid Build Coastguard Worker 353*cda5da8dSAndroid Build Coastguard Worker def readframes(self, nframes): 354*cda5da8dSAndroid Build Coastguard Worker if self._data_seek_needed: 355*cda5da8dSAndroid Build Coastguard Worker self._data_chunk.seek(0, 0) 356*cda5da8dSAndroid Build Coastguard Worker pos = self._soundpos * self._framesize 357*cda5da8dSAndroid Build Coastguard Worker if pos: 358*cda5da8dSAndroid Build Coastguard Worker self._data_chunk.seek(pos, 0) 359*cda5da8dSAndroid Build Coastguard Worker self._data_seek_needed = 0 360*cda5da8dSAndroid Build Coastguard Worker if nframes == 0: 361*cda5da8dSAndroid Build Coastguard Worker return b'' 362*cda5da8dSAndroid Build Coastguard Worker data = self._data_chunk.read(nframes * self._framesize) 363*cda5da8dSAndroid Build Coastguard Worker if self._sampwidth != 1 and sys.byteorder == 'big': 364*cda5da8dSAndroid Build Coastguard Worker data = _byteswap(data, self._sampwidth) 365*cda5da8dSAndroid Build Coastguard Worker if self._convert and data: 366*cda5da8dSAndroid Build Coastguard Worker data = self._convert(data) 367*cda5da8dSAndroid Build Coastguard Worker self._soundpos = self._soundpos + len(data) // (self._nchannels * self._sampwidth) 368*cda5da8dSAndroid Build Coastguard Worker return data 369*cda5da8dSAndroid Build Coastguard Worker 370*cda5da8dSAndroid Build Coastguard Worker # 371*cda5da8dSAndroid Build Coastguard Worker # Internal methods. 372*cda5da8dSAndroid Build Coastguard Worker # 373*cda5da8dSAndroid Build Coastguard Worker 374*cda5da8dSAndroid Build Coastguard Worker def _read_fmt_chunk(self, chunk): 375*cda5da8dSAndroid Build Coastguard Worker try: 376*cda5da8dSAndroid Build Coastguard Worker wFormatTag, self._nchannels, self._framerate, dwAvgBytesPerSec, wBlockAlign = struct.unpack_from('<HHLLH', chunk.read(14)) 377*cda5da8dSAndroid Build Coastguard Worker except struct.error: 378*cda5da8dSAndroid Build Coastguard Worker raise EOFError from None 379*cda5da8dSAndroid Build Coastguard Worker if wFormatTag == WAVE_FORMAT_PCM: 380*cda5da8dSAndroid Build Coastguard Worker try: 381*cda5da8dSAndroid Build Coastguard Worker sampwidth = struct.unpack_from('<H', chunk.read(2))[0] 382*cda5da8dSAndroid Build Coastguard Worker except struct.error: 383*cda5da8dSAndroid Build Coastguard Worker raise EOFError from None 384*cda5da8dSAndroid Build Coastguard Worker self._sampwidth = (sampwidth + 7) // 8 385*cda5da8dSAndroid Build Coastguard Worker if not self._sampwidth: 386*cda5da8dSAndroid Build Coastguard Worker raise Error('bad sample width') 387*cda5da8dSAndroid Build Coastguard Worker else: 388*cda5da8dSAndroid Build Coastguard Worker raise Error('unknown format: %r' % (wFormatTag,)) 389*cda5da8dSAndroid Build Coastguard Worker if not self._nchannels: 390*cda5da8dSAndroid Build Coastguard Worker raise Error('bad # of channels') 391*cda5da8dSAndroid Build Coastguard Worker self._framesize = self._nchannels * self._sampwidth 392*cda5da8dSAndroid Build Coastguard Worker self._comptype = 'NONE' 393*cda5da8dSAndroid Build Coastguard Worker self._compname = 'not compressed' 394*cda5da8dSAndroid Build Coastguard Worker 395*cda5da8dSAndroid Build Coastguard Worker 396*cda5da8dSAndroid Build Coastguard Workerclass Wave_write: 397*cda5da8dSAndroid Build Coastguard Worker """Variables used in this class: 398*cda5da8dSAndroid Build Coastguard Worker 399*cda5da8dSAndroid Build Coastguard Worker These variables are user settable through appropriate methods 400*cda5da8dSAndroid Build Coastguard Worker of this class: 401*cda5da8dSAndroid Build Coastguard Worker _file -- the open file with methods write(), close(), tell(), seek() 402*cda5da8dSAndroid Build Coastguard Worker set through the __init__() method 403*cda5da8dSAndroid Build Coastguard Worker _comptype -- the AIFF-C compression type ('NONE' in AIFF) 404*cda5da8dSAndroid Build Coastguard Worker set through the setcomptype() or setparams() method 405*cda5da8dSAndroid Build Coastguard Worker _compname -- the human-readable AIFF-C compression type 406*cda5da8dSAndroid Build Coastguard Worker set through the setcomptype() or setparams() method 407*cda5da8dSAndroid Build Coastguard Worker _nchannels -- the number of audio channels 408*cda5da8dSAndroid Build Coastguard Worker set through the setnchannels() or setparams() method 409*cda5da8dSAndroid Build Coastguard Worker _sampwidth -- the number of bytes per audio sample 410*cda5da8dSAndroid Build Coastguard Worker set through the setsampwidth() or setparams() method 411*cda5da8dSAndroid Build Coastguard Worker _framerate -- the sampling frequency 412*cda5da8dSAndroid Build Coastguard Worker set through the setframerate() or setparams() method 413*cda5da8dSAndroid Build Coastguard Worker _nframes -- the number of audio frames written to the header 414*cda5da8dSAndroid Build Coastguard Worker set through the setnframes() or setparams() method 415*cda5da8dSAndroid Build Coastguard Worker 416*cda5da8dSAndroid Build Coastguard Worker These variables are used internally only: 417*cda5da8dSAndroid Build Coastguard Worker _datalength -- the size of the audio samples written to the header 418*cda5da8dSAndroid Build Coastguard Worker _nframeswritten -- the number of frames actually written 419*cda5da8dSAndroid Build Coastguard Worker _datawritten -- the size of the audio samples actually written 420*cda5da8dSAndroid Build Coastguard Worker """ 421*cda5da8dSAndroid Build Coastguard Worker 422*cda5da8dSAndroid Build Coastguard Worker def __init__(self, f): 423*cda5da8dSAndroid Build Coastguard Worker self._i_opened_the_file = None 424*cda5da8dSAndroid Build Coastguard Worker if isinstance(f, str): 425*cda5da8dSAndroid Build Coastguard Worker f = builtins.open(f, 'wb') 426*cda5da8dSAndroid Build Coastguard Worker self._i_opened_the_file = f 427*cda5da8dSAndroid Build Coastguard Worker try: 428*cda5da8dSAndroid Build Coastguard Worker self.initfp(f) 429*cda5da8dSAndroid Build Coastguard Worker except: 430*cda5da8dSAndroid Build Coastguard Worker if self._i_opened_the_file: 431*cda5da8dSAndroid Build Coastguard Worker f.close() 432*cda5da8dSAndroid Build Coastguard Worker raise 433*cda5da8dSAndroid Build Coastguard Worker 434*cda5da8dSAndroid Build Coastguard Worker def initfp(self, file): 435*cda5da8dSAndroid Build Coastguard Worker self._file = file 436*cda5da8dSAndroid Build Coastguard Worker self._convert = None 437*cda5da8dSAndroid Build Coastguard Worker self._nchannels = 0 438*cda5da8dSAndroid Build Coastguard Worker self._sampwidth = 0 439*cda5da8dSAndroid Build Coastguard Worker self._framerate = 0 440*cda5da8dSAndroid Build Coastguard Worker self._nframes = 0 441*cda5da8dSAndroid Build Coastguard Worker self._nframeswritten = 0 442*cda5da8dSAndroid Build Coastguard Worker self._datawritten = 0 443*cda5da8dSAndroid Build Coastguard Worker self._datalength = 0 444*cda5da8dSAndroid Build Coastguard Worker self._headerwritten = False 445*cda5da8dSAndroid Build Coastguard Worker 446*cda5da8dSAndroid Build Coastguard Worker def __del__(self): 447*cda5da8dSAndroid Build Coastguard Worker self.close() 448*cda5da8dSAndroid Build Coastguard Worker 449*cda5da8dSAndroid Build Coastguard Worker def __enter__(self): 450*cda5da8dSAndroid Build Coastguard Worker return self 451*cda5da8dSAndroid Build Coastguard Worker 452*cda5da8dSAndroid Build Coastguard Worker def __exit__(self, *args): 453*cda5da8dSAndroid Build Coastguard Worker self.close() 454*cda5da8dSAndroid Build Coastguard Worker 455*cda5da8dSAndroid Build Coastguard Worker # 456*cda5da8dSAndroid Build Coastguard Worker # User visible methods. 457*cda5da8dSAndroid Build Coastguard Worker # 458*cda5da8dSAndroid Build Coastguard Worker def setnchannels(self, nchannels): 459*cda5da8dSAndroid Build Coastguard Worker if self._datawritten: 460*cda5da8dSAndroid Build Coastguard Worker raise Error('cannot change parameters after starting to write') 461*cda5da8dSAndroid Build Coastguard Worker if nchannels < 1: 462*cda5da8dSAndroid Build Coastguard Worker raise Error('bad # of channels') 463*cda5da8dSAndroid Build Coastguard Worker self._nchannels = nchannels 464*cda5da8dSAndroid Build Coastguard Worker 465*cda5da8dSAndroid Build Coastguard Worker def getnchannels(self): 466*cda5da8dSAndroid Build Coastguard Worker if not self._nchannels: 467*cda5da8dSAndroid Build Coastguard Worker raise Error('number of channels not set') 468*cda5da8dSAndroid Build Coastguard Worker return self._nchannels 469*cda5da8dSAndroid Build Coastguard Worker 470*cda5da8dSAndroid Build Coastguard Worker def setsampwidth(self, sampwidth): 471*cda5da8dSAndroid Build Coastguard Worker if self._datawritten: 472*cda5da8dSAndroid Build Coastguard Worker raise Error('cannot change parameters after starting to write') 473*cda5da8dSAndroid Build Coastguard Worker if sampwidth < 1 or sampwidth > 4: 474*cda5da8dSAndroid Build Coastguard Worker raise Error('bad sample width') 475*cda5da8dSAndroid Build Coastguard Worker self._sampwidth = sampwidth 476*cda5da8dSAndroid Build Coastguard Worker 477*cda5da8dSAndroid Build Coastguard Worker def getsampwidth(self): 478*cda5da8dSAndroid Build Coastguard Worker if not self._sampwidth: 479*cda5da8dSAndroid Build Coastguard Worker raise Error('sample width not set') 480*cda5da8dSAndroid Build Coastguard Worker return self._sampwidth 481*cda5da8dSAndroid Build Coastguard Worker 482*cda5da8dSAndroid Build Coastguard Worker def setframerate(self, framerate): 483*cda5da8dSAndroid Build Coastguard Worker if self._datawritten: 484*cda5da8dSAndroid Build Coastguard Worker raise Error('cannot change parameters after starting to write') 485*cda5da8dSAndroid Build Coastguard Worker if framerate <= 0: 486*cda5da8dSAndroid Build Coastguard Worker raise Error('bad frame rate') 487*cda5da8dSAndroid Build Coastguard Worker self._framerate = int(round(framerate)) 488*cda5da8dSAndroid Build Coastguard Worker 489*cda5da8dSAndroid Build Coastguard Worker def getframerate(self): 490*cda5da8dSAndroid Build Coastguard Worker if not self._framerate: 491*cda5da8dSAndroid Build Coastguard Worker raise Error('frame rate not set') 492*cda5da8dSAndroid Build Coastguard Worker return self._framerate 493*cda5da8dSAndroid Build Coastguard Worker 494*cda5da8dSAndroid Build Coastguard Worker def setnframes(self, nframes): 495*cda5da8dSAndroid Build Coastguard Worker if self._datawritten: 496*cda5da8dSAndroid Build Coastguard Worker raise Error('cannot change parameters after starting to write') 497*cda5da8dSAndroid Build Coastguard Worker self._nframes = nframes 498*cda5da8dSAndroid Build Coastguard Worker 499*cda5da8dSAndroid Build Coastguard Worker def getnframes(self): 500*cda5da8dSAndroid Build Coastguard Worker return self._nframeswritten 501*cda5da8dSAndroid Build Coastguard Worker 502*cda5da8dSAndroid Build Coastguard Worker def setcomptype(self, comptype, compname): 503*cda5da8dSAndroid Build Coastguard Worker if self._datawritten: 504*cda5da8dSAndroid Build Coastguard Worker raise Error('cannot change parameters after starting to write') 505*cda5da8dSAndroid Build Coastguard Worker if comptype not in ('NONE',): 506*cda5da8dSAndroid Build Coastguard Worker raise Error('unsupported compression type') 507*cda5da8dSAndroid Build Coastguard Worker self._comptype = comptype 508*cda5da8dSAndroid Build Coastguard Worker self._compname = compname 509*cda5da8dSAndroid Build Coastguard Worker 510*cda5da8dSAndroid Build Coastguard Worker def getcomptype(self): 511*cda5da8dSAndroid Build Coastguard Worker return self._comptype 512*cda5da8dSAndroid Build Coastguard Worker 513*cda5da8dSAndroid Build Coastguard Worker def getcompname(self): 514*cda5da8dSAndroid Build Coastguard Worker return self._compname 515*cda5da8dSAndroid Build Coastguard Worker 516*cda5da8dSAndroid Build Coastguard Worker def setparams(self, params): 517*cda5da8dSAndroid Build Coastguard Worker nchannels, sampwidth, framerate, nframes, comptype, compname = params 518*cda5da8dSAndroid Build Coastguard Worker if self._datawritten: 519*cda5da8dSAndroid Build Coastguard Worker raise Error('cannot change parameters after starting to write') 520*cda5da8dSAndroid Build Coastguard Worker self.setnchannels(nchannels) 521*cda5da8dSAndroid Build Coastguard Worker self.setsampwidth(sampwidth) 522*cda5da8dSAndroid Build Coastguard Worker self.setframerate(framerate) 523*cda5da8dSAndroid Build Coastguard Worker self.setnframes(nframes) 524*cda5da8dSAndroid Build Coastguard Worker self.setcomptype(comptype, compname) 525*cda5da8dSAndroid Build Coastguard Worker 526*cda5da8dSAndroid Build Coastguard Worker def getparams(self): 527*cda5da8dSAndroid Build Coastguard Worker if not self._nchannels or not self._sampwidth or not self._framerate: 528*cda5da8dSAndroid Build Coastguard Worker raise Error('not all parameters set') 529*cda5da8dSAndroid Build Coastguard Worker return _wave_params(self._nchannels, self._sampwidth, self._framerate, 530*cda5da8dSAndroid Build Coastguard Worker self._nframes, self._comptype, self._compname) 531*cda5da8dSAndroid Build Coastguard Worker 532*cda5da8dSAndroid Build Coastguard Worker def setmark(self, id, pos, name): 533*cda5da8dSAndroid Build Coastguard Worker raise Error('setmark() not supported') 534*cda5da8dSAndroid Build Coastguard Worker 535*cda5da8dSAndroid Build Coastguard Worker def getmark(self, id): 536*cda5da8dSAndroid Build Coastguard Worker raise Error('no marks') 537*cda5da8dSAndroid Build Coastguard Worker 538*cda5da8dSAndroid Build Coastguard Worker def getmarkers(self): 539*cda5da8dSAndroid Build Coastguard Worker return None 540*cda5da8dSAndroid Build Coastguard Worker 541*cda5da8dSAndroid Build Coastguard Worker def tell(self): 542*cda5da8dSAndroid Build Coastguard Worker return self._nframeswritten 543*cda5da8dSAndroid Build Coastguard Worker 544*cda5da8dSAndroid Build Coastguard Worker def writeframesraw(self, data): 545*cda5da8dSAndroid Build Coastguard Worker if not isinstance(data, (bytes, bytearray)): 546*cda5da8dSAndroid Build Coastguard Worker data = memoryview(data).cast('B') 547*cda5da8dSAndroid Build Coastguard Worker self._ensure_header_written(len(data)) 548*cda5da8dSAndroid Build Coastguard Worker nframes = len(data) // (self._sampwidth * self._nchannels) 549*cda5da8dSAndroid Build Coastguard Worker if self._convert: 550*cda5da8dSAndroid Build Coastguard Worker data = self._convert(data) 551*cda5da8dSAndroid Build Coastguard Worker if self._sampwidth != 1 and sys.byteorder == 'big': 552*cda5da8dSAndroid Build Coastguard Worker data = _byteswap(data, self._sampwidth) 553*cda5da8dSAndroid Build Coastguard Worker self._file.write(data) 554*cda5da8dSAndroid Build Coastguard Worker self._datawritten += len(data) 555*cda5da8dSAndroid Build Coastguard Worker self._nframeswritten = self._nframeswritten + nframes 556*cda5da8dSAndroid Build Coastguard Worker 557*cda5da8dSAndroid Build Coastguard Worker def writeframes(self, data): 558*cda5da8dSAndroid Build Coastguard Worker self.writeframesraw(data) 559*cda5da8dSAndroid Build Coastguard Worker if self._datalength != self._datawritten: 560*cda5da8dSAndroid Build Coastguard Worker self._patchheader() 561*cda5da8dSAndroid Build Coastguard Worker 562*cda5da8dSAndroid Build Coastguard Worker def close(self): 563*cda5da8dSAndroid Build Coastguard Worker try: 564*cda5da8dSAndroid Build Coastguard Worker if self._file: 565*cda5da8dSAndroid Build Coastguard Worker self._ensure_header_written(0) 566*cda5da8dSAndroid Build Coastguard Worker if self._datalength != self._datawritten: 567*cda5da8dSAndroid Build Coastguard Worker self._patchheader() 568*cda5da8dSAndroid Build Coastguard Worker self._file.flush() 569*cda5da8dSAndroid Build Coastguard Worker finally: 570*cda5da8dSAndroid Build Coastguard Worker self._file = None 571*cda5da8dSAndroid Build Coastguard Worker file = self._i_opened_the_file 572*cda5da8dSAndroid Build Coastguard Worker if file: 573*cda5da8dSAndroid Build Coastguard Worker self._i_opened_the_file = None 574*cda5da8dSAndroid Build Coastguard Worker file.close() 575*cda5da8dSAndroid Build Coastguard Worker 576*cda5da8dSAndroid Build Coastguard Worker # 577*cda5da8dSAndroid Build Coastguard Worker # Internal methods. 578*cda5da8dSAndroid Build Coastguard Worker # 579*cda5da8dSAndroid Build Coastguard Worker 580*cda5da8dSAndroid Build Coastguard Worker def _ensure_header_written(self, datasize): 581*cda5da8dSAndroid Build Coastguard Worker if not self._headerwritten: 582*cda5da8dSAndroid Build Coastguard Worker if not self._nchannels: 583*cda5da8dSAndroid Build Coastguard Worker raise Error('# channels not specified') 584*cda5da8dSAndroid Build Coastguard Worker if not self._sampwidth: 585*cda5da8dSAndroid Build Coastguard Worker raise Error('sample width not specified') 586*cda5da8dSAndroid Build Coastguard Worker if not self._framerate: 587*cda5da8dSAndroid Build Coastguard Worker raise Error('sampling rate not specified') 588*cda5da8dSAndroid Build Coastguard Worker self._write_header(datasize) 589*cda5da8dSAndroid Build Coastguard Worker 590*cda5da8dSAndroid Build Coastguard Worker def _write_header(self, initlength): 591*cda5da8dSAndroid Build Coastguard Worker assert not self._headerwritten 592*cda5da8dSAndroid Build Coastguard Worker self._file.write(b'RIFF') 593*cda5da8dSAndroid Build Coastguard Worker if not self._nframes: 594*cda5da8dSAndroid Build Coastguard Worker self._nframes = initlength // (self._nchannels * self._sampwidth) 595*cda5da8dSAndroid Build Coastguard Worker self._datalength = self._nframes * self._nchannels * self._sampwidth 596*cda5da8dSAndroid Build Coastguard Worker try: 597*cda5da8dSAndroid Build Coastguard Worker self._form_length_pos = self._file.tell() 598*cda5da8dSAndroid Build Coastguard Worker except (AttributeError, OSError): 599*cda5da8dSAndroid Build Coastguard Worker self._form_length_pos = None 600*cda5da8dSAndroid Build Coastguard Worker self._file.write(struct.pack('<L4s4sLHHLLHH4s', 601*cda5da8dSAndroid Build Coastguard Worker 36 + self._datalength, b'WAVE', b'fmt ', 16, 602*cda5da8dSAndroid Build Coastguard Worker WAVE_FORMAT_PCM, self._nchannels, self._framerate, 603*cda5da8dSAndroid Build Coastguard Worker self._nchannels * self._framerate * self._sampwidth, 604*cda5da8dSAndroid Build Coastguard Worker self._nchannels * self._sampwidth, 605*cda5da8dSAndroid Build Coastguard Worker self._sampwidth * 8, b'data')) 606*cda5da8dSAndroid Build Coastguard Worker if self._form_length_pos is not None: 607*cda5da8dSAndroid Build Coastguard Worker self._data_length_pos = self._file.tell() 608*cda5da8dSAndroid Build Coastguard Worker self._file.write(struct.pack('<L', self._datalength)) 609*cda5da8dSAndroid Build Coastguard Worker self._headerwritten = True 610*cda5da8dSAndroid Build Coastguard Worker 611*cda5da8dSAndroid Build Coastguard Worker def _patchheader(self): 612*cda5da8dSAndroid Build Coastguard Worker assert self._headerwritten 613*cda5da8dSAndroid Build Coastguard Worker if self._datawritten == self._datalength: 614*cda5da8dSAndroid Build Coastguard Worker return 615*cda5da8dSAndroid Build Coastguard Worker curpos = self._file.tell() 616*cda5da8dSAndroid Build Coastguard Worker self._file.seek(self._form_length_pos, 0) 617*cda5da8dSAndroid Build Coastguard Worker self._file.write(struct.pack('<L', 36 + self._datawritten)) 618*cda5da8dSAndroid Build Coastguard Worker self._file.seek(self._data_length_pos, 0) 619*cda5da8dSAndroid Build Coastguard Worker self._file.write(struct.pack('<L', self._datawritten)) 620*cda5da8dSAndroid Build Coastguard Worker self._file.seek(curpos, 0) 621*cda5da8dSAndroid Build Coastguard Worker self._datalength = self._datawritten 622*cda5da8dSAndroid Build Coastguard Worker 623*cda5da8dSAndroid Build Coastguard Worker 624*cda5da8dSAndroid Build Coastguard Workerdef open(f, mode=None): 625*cda5da8dSAndroid Build Coastguard Worker if mode is None: 626*cda5da8dSAndroid Build Coastguard Worker if hasattr(f, 'mode'): 627*cda5da8dSAndroid Build Coastguard Worker mode = f.mode 628*cda5da8dSAndroid Build Coastguard Worker else: 629*cda5da8dSAndroid Build Coastguard Worker mode = 'rb' 630*cda5da8dSAndroid Build Coastguard Worker if mode in ('r', 'rb'): 631*cda5da8dSAndroid Build Coastguard Worker return Wave_read(f) 632*cda5da8dSAndroid Build Coastguard Worker elif mode in ('w', 'wb'): 633*cda5da8dSAndroid Build Coastguard Worker return Wave_write(f) 634*cda5da8dSAndroid Build Coastguard Worker else: 635*cda5da8dSAndroid Build Coastguard Worker raise Error("mode must be 'r', 'rb', 'w', or 'wb'") 636