1*cda5da8dSAndroid Build Coastguard Worker"""Stuff to parse Sun and NeXT audio files. 2*cda5da8dSAndroid Build Coastguard Worker 3*cda5da8dSAndroid Build Coastguard WorkerAn audio file consists of a header followed by the data. The structure 4*cda5da8dSAndroid Build Coastguard Workerof the header is as follows. 5*cda5da8dSAndroid Build Coastguard Worker 6*cda5da8dSAndroid Build Coastguard Worker +---------------+ 7*cda5da8dSAndroid Build Coastguard Worker | magic word | 8*cda5da8dSAndroid Build Coastguard Worker +---------------+ 9*cda5da8dSAndroid Build Coastguard Worker | header size | 10*cda5da8dSAndroid Build Coastguard Worker +---------------+ 11*cda5da8dSAndroid Build Coastguard Worker | data size | 12*cda5da8dSAndroid Build Coastguard Worker +---------------+ 13*cda5da8dSAndroid Build Coastguard Worker | encoding | 14*cda5da8dSAndroid Build Coastguard Worker +---------------+ 15*cda5da8dSAndroid Build Coastguard Worker | sample rate | 16*cda5da8dSAndroid Build Coastguard Worker +---------------+ 17*cda5da8dSAndroid Build Coastguard Worker | # of channels | 18*cda5da8dSAndroid Build Coastguard Worker +---------------+ 19*cda5da8dSAndroid Build Coastguard Worker | info | 20*cda5da8dSAndroid Build Coastguard Worker | | 21*cda5da8dSAndroid Build Coastguard Worker +---------------+ 22*cda5da8dSAndroid Build Coastguard Worker 23*cda5da8dSAndroid Build Coastguard WorkerThe magic word consists of the 4 characters '.snd'. Apart from the 24*cda5da8dSAndroid Build Coastguard Workerinfo field, all header fields are 4 bytes in size. They are all 25*cda5da8dSAndroid Build Coastguard Worker32-bit unsigned integers encoded in big-endian byte order. 26*cda5da8dSAndroid Build Coastguard Worker 27*cda5da8dSAndroid Build Coastguard WorkerThe header size really gives the start of the data. 28*cda5da8dSAndroid Build Coastguard WorkerThe data size is the physical size of the data. From the other 29*cda5da8dSAndroid Build Coastguard Workerparameters the number of frames can be calculated. 30*cda5da8dSAndroid Build Coastguard WorkerThe encoding gives the way in which audio samples are encoded. 31*cda5da8dSAndroid Build Coastguard WorkerPossible values are listed below. 32*cda5da8dSAndroid Build Coastguard WorkerThe info field currently consists of an ASCII string giving a 33*cda5da8dSAndroid Build Coastguard Workerhuman-readable description of the audio file. The info field is 34*cda5da8dSAndroid Build Coastguard Workerpadded with NUL bytes to the header size. 35*cda5da8dSAndroid Build Coastguard Worker 36*cda5da8dSAndroid Build Coastguard WorkerUsage. 37*cda5da8dSAndroid Build Coastguard Worker 38*cda5da8dSAndroid Build Coastguard WorkerReading audio files: 39*cda5da8dSAndroid Build Coastguard Worker f = sunau.open(file, 'r') 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 read(), seek(), and close(). 42*cda5da8dSAndroid Build Coastguard WorkerWhen the setpos() and rewind() methods are not used, the seek() 43*cda5da8dSAndroid Build Coastguard Workermethod is not necessary. 44*cda5da8dSAndroid Build Coastguard Worker 45*cda5da8dSAndroid Build Coastguard WorkerThis returns an instance of a class with the following public methods: 46*cda5da8dSAndroid Build Coastguard Worker getnchannels() -- returns number of audio channels (1 for 47*cda5da8dSAndroid Build Coastguard Worker mono, 2 for stereo) 48*cda5da8dSAndroid Build Coastguard Worker getsampwidth() -- returns sample width in bytes 49*cda5da8dSAndroid Build Coastguard Worker getframerate() -- returns sampling frequency 50*cda5da8dSAndroid Build Coastguard Worker getnframes() -- returns number of audio frames 51*cda5da8dSAndroid Build Coastguard Worker getcomptype() -- returns compression type ('NONE' or 'ULAW') 52*cda5da8dSAndroid Build Coastguard Worker getcompname() -- returns human-readable version of 53*cda5da8dSAndroid Build Coastguard Worker compression type ('not compressed' matches 'NONE') 54*cda5da8dSAndroid Build Coastguard Worker getparams() -- returns a namedtuple consisting of all of the 55*cda5da8dSAndroid Build Coastguard Worker above in the above order 56*cda5da8dSAndroid Build Coastguard Worker getmarkers() -- returns None (for compatibility with the 57*cda5da8dSAndroid Build Coastguard Worker aifc module) 58*cda5da8dSAndroid Build Coastguard Worker getmark(id) -- raises an error since the mark does not 59*cda5da8dSAndroid Build Coastguard Worker exist (for compatibility with the aifc module) 60*cda5da8dSAndroid Build Coastguard Worker readframes(n) -- returns at most n frames of audio 61*cda5da8dSAndroid Build Coastguard Worker rewind() -- rewind to the beginning of the audio stream 62*cda5da8dSAndroid Build Coastguard Worker setpos(pos) -- seek to the specified position 63*cda5da8dSAndroid Build Coastguard Worker tell() -- return the current position 64*cda5da8dSAndroid Build Coastguard Worker close() -- close the instance (make it unusable) 65*cda5da8dSAndroid Build Coastguard WorkerThe position returned by tell() and the position given to setpos() 66*cda5da8dSAndroid Build Coastguard Workerare compatible and have nothing to do with the actual position in the 67*cda5da8dSAndroid Build Coastguard Workerfile. 68*cda5da8dSAndroid Build Coastguard WorkerThe close() method is called automatically when the class instance 69*cda5da8dSAndroid Build Coastguard Workeris destroyed. 70*cda5da8dSAndroid Build Coastguard Worker 71*cda5da8dSAndroid Build Coastguard WorkerWriting audio files: 72*cda5da8dSAndroid Build Coastguard Worker f = sunau.open(file, 'w') 73*cda5da8dSAndroid Build Coastguard Workerwhere file is either the name of a file or an open file pointer. 74*cda5da8dSAndroid Build Coastguard WorkerThe open file pointer must have methods write(), tell(), seek(), and 75*cda5da8dSAndroid Build Coastguard Workerclose(). 76*cda5da8dSAndroid Build Coastguard Worker 77*cda5da8dSAndroid Build Coastguard WorkerThis returns an instance of a class with the following public methods: 78*cda5da8dSAndroid Build Coastguard Worker setnchannels(n) -- set the number of channels 79*cda5da8dSAndroid Build Coastguard Worker setsampwidth(n) -- set the sample width 80*cda5da8dSAndroid Build Coastguard Worker setframerate(n) -- set the frame rate 81*cda5da8dSAndroid Build Coastguard Worker setnframes(n) -- set the number of frames 82*cda5da8dSAndroid Build Coastguard Worker setcomptype(type, name) 83*cda5da8dSAndroid Build Coastguard Worker -- set the compression type and the 84*cda5da8dSAndroid Build Coastguard Worker human-readable compression type 85*cda5da8dSAndroid Build Coastguard Worker setparams(tuple)-- set all parameters at once 86*cda5da8dSAndroid Build Coastguard Worker tell() -- return current position in output file 87*cda5da8dSAndroid Build Coastguard Worker writeframesraw(data) 88*cda5da8dSAndroid Build Coastguard Worker -- write audio frames without pathing up the 89*cda5da8dSAndroid Build Coastguard Worker file header 90*cda5da8dSAndroid Build Coastguard Worker writeframes(data) 91*cda5da8dSAndroid Build Coastguard Worker -- write audio frames and patch up the file header 92*cda5da8dSAndroid Build Coastguard Worker close() -- patch up the file header and close the 93*cda5da8dSAndroid Build Coastguard Worker output file 94*cda5da8dSAndroid Build Coastguard WorkerYou should set the parameters before the first writeframesraw or 95*cda5da8dSAndroid Build Coastguard Workerwriteframes. The total number of frames does not need to be set, 96*cda5da8dSAndroid Build Coastguard Workerbut when it is set to the correct value, the header does not have to 97*cda5da8dSAndroid Build Coastguard Workerbe patched up. 98*cda5da8dSAndroid Build Coastguard WorkerIt is best to first set all parameters, perhaps possibly the 99*cda5da8dSAndroid Build Coastguard Workercompression type, and then write audio frames using writeframesraw. 100*cda5da8dSAndroid Build Coastguard WorkerWhen all frames have been written, either call writeframes(b'') or 101*cda5da8dSAndroid Build Coastguard Workerclose() to patch up the sizes in the header. 102*cda5da8dSAndroid Build Coastguard WorkerThe close() method is called automatically when the class instance 103*cda5da8dSAndroid Build Coastguard Workeris destroyed. 104*cda5da8dSAndroid Build Coastguard Worker""" 105*cda5da8dSAndroid Build Coastguard Worker 106*cda5da8dSAndroid Build Coastguard Workerfrom collections import namedtuple 107*cda5da8dSAndroid Build Coastguard Workerimport warnings 108*cda5da8dSAndroid Build Coastguard Worker 109*cda5da8dSAndroid Build Coastguard Workerwarnings._deprecated(__name__, remove=(3, 13)) 110*cda5da8dSAndroid Build Coastguard Worker 111*cda5da8dSAndroid Build Coastguard Worker 112*cda5da8dSAndroid Build Coastguard Worker_sunau_params = namedtuple('_sunau_params', 113*cda5da8dSAndroid Build Coastguard Worker 'nchannels sampwidth framerate nframes comptype compname') 114*cda5da8dSAndroid Build Coastguard Worker 115*cda5da8dSAndroid Build Coastguard Worker# from <multimedia/audio_filehdr.h> 116*cda5da8dSAndroid Build Coastguard WorkerAUDIO_FILE_MAGIC = 0x2e736e64 117*cda5da8dSAndroid Build Coastguard WorkerAUDIO_FILE_ENCODING_MULAW_8 = 1 118*cda5da8dSAndroid Build Coastguard WorkerAUDIO_FILE_ENCODING_LINEAR_8 = 2 119*cda5da8dSAndroid Build Coastguard WorkerAUDIO_FILE_ENCODING_LINEAR_16 = 3 120*cda5da8dSAndroid Build Coastguard WorkerAUDIO_FILE_ENCODING_LINEAR_24 = 4 121*cda5da8dSAndroid Build Coastguard WorkerAUDIO_FILE_ENCODING_LINEAR_32 = 5 122*cda5da8dSAndroid Build Coastguard WorkerAUDIO_FILE_ENCODING_FLOAT = 6 123*cda5da8dSAndroid Build Coastguard WorkerAUDIO_FILE_ENCODING_DOUBLE = 7 124*cda5da8dSAndroid Build Coastguard WorkerAUDIO_FILE_ENCODING_ADPCM_G721 = 23 125*cda5da8dSAndroid Build Coastguard WorkerAUDIO_FILE_ENCODING_ADPCM_G722 = 24 126*cda5da8dSAndroid Build Coastguard WorkerAUDIO_FILE_ENCODING_ADPCM_G723_3 = 25 127*cda5da8dSAndroid Build Coastguard WorkerAUDIO_FILE_ENCODING_ADPCM_G723_5 = 26 128*cda5da8dSAndroid Build Coastguard WorkerAUDIO_FILE_ENCODING_ALAW_8 = 27 129*cda5da8dSAndroid Build Coastguard Worker 130*cda5da8dSAndroid Build Coastguard Worker# from <multimedia/audio_hdr.h> 131*cda5da8dSAndroid Build Coastguard WorkerAUDIO_UNKNOWN_SIZE = 0xFFFFFFFF # ((unsigned)(~0)) 132*cda5da8dSAndroid Build Coastguard Worker 133*cda5da8dSAndroid Build Coastguard Worker_simple_encodings = [AUDIO_FILE_ENCODING_MULAW_8, 134*cda5da8dSAndroid Build Coastguard Worker AUDIO_FILE_ENCODING_LINEAR_8, 135*cda5da8dSAndroid Build Coastguard Worker AUDIO_FILE_ENCODING_LINEAR_16, 136*cda5da8dSAndroid Build Coastguard Worker AUDIO_FILE_ENCODING_LINEAR_24, 137*cda5da8dSAndroid Build Coastguard Worker AUDIO_FILE_ENCODING_LINEAR_32, 138*cda5da8dSAndroid Build Coastguard Worker AUDIO_FILE_ENCODING_ALAW_8] 139*cda5da8dSAndroid Build Coastguard Worker 140*cda5da8dSAndroid Build Coastguard Workerclass Error(Exception): 141*cda5da8dSAndroid Build Coastguard Worker pass 142*cda5da8dSAndroid Build Coastguard Worker 143*cda5da8dSAndroid Build Coastguard Workerdef _read_u32(file): 144*cda5da8dSAndroid Build Coastguard Worker x = 0 145*cda5da8dSAndroid Build Coastguard Worker for i in range(4): 146*cda5da8dSAndroid Build Coastguard Worker byte = file.read(1) 147*cda5da8dSAndroid Build Coastguard Worker if not byte: 148*cda5da8dSAndroid Build Coastguard Worker raise EOFError 149*cda5da8dSAndroid Build Coastguard Worker x = x*256 + ord(byte) 150*cda5da8dSAndroid Build Coastguard Worker return x 151*cda5da8dSAndroid Build Coastguard Worker 152*cda5da8dSAndroid Build Coastguard Workerdef _write_u32(file, x): 153*cda5da8dSAndroid Build Coastguard Worker data = [] 154*cda5da8dSAndroid Build Coastguard Worker for i in range(4): 155*cda5da8dSAndroid Build Coastguard Worker d, m = divmod(x, 256) 156*cda5da8dSAndroid Build Coastguard Worker data.insert(0, int(m)) 157*cda5da8dSAndroid Build Coastguard Worker x = d 158*cda5da8dSAndroid Build Coastguard Worker file.write(bytes(data)) 159*cda5da8dSAndroid Build Coastguard Worker 160*cda5da8dSAndroid Build Coastguard Workerclass Au_read: 161*cda5da8dSAndroid Build Coastguard Worker 162*cda5da8dSAndroid Build Coastguard Worker def __init__(self, f): 163*cda5da8dSAndroid Build Coastguard Worker if type(f) == type(''): 164*cda5da8dSAndroid Build Coastguard Worker import builtins 165*cda5da8dSAndroid Build Coastguard Worker f = builtins.open(f, 'rb') 166*cda5da8dSAndroid Build Coastguard Worker self._opened = True 167*cda5da8dSAndroid Build Coastguard Worker else: 168*cda5da8dSAndroid Build Coastguard Worker self._opened = False 169*cda5da8dSAndroid Build Coastguard Worker self.initfp(f) 170*cda5da8dSAndroid Build Coastguard Worker 171*cda5da8dSAndroid Build Coastguard Worker def __del__(self): 172*cda5da8dSAndroid Build Coastguard Worker if self._file: 173*cda5da8dSAndroid Build Coastguard Worker self.close() 174*cda5da8dSAndroid Build Coastguard Worker 175*cda5da8dSAndroid Build Coastguard Worker def __enter__(self): 176*cda5da8dSAndroid Build Coastguard Worker return self 177*cda5da8dSAndroid Build Coastguard Worker 178*cda5da8dSAndroid Build Coastguard Worker def __exit__(self, *args): 179*cda5da8dSAndroid Build Coastguard Worker self.close() 180*cda5da8dSAndroid Build Coastguard Worker 181*cda5da8dSAndroid Build Coastguard Worker def initfp(self, file): 182*cda5da8dSAndroid Build Coastguard Worker self._file = file 183*cda5da8dSAndroid Build Coastguard Worker self._soundpos = 0 184*cda5da8dSAndroid Build Coastguard Worker magic = int(_read_u32(file)) 185*cda5da8dSAndroid Build Coastguard Worker if magic != AUDIO_FILE_MAGIC: 186*cda5da8dSAndroid Build Coastguard Worker raise Error('bad magic number') 187*cda5da8dSAndroid Build Coastguard Worker self._hdr_size = int(_read_u32(file)) 188*cda5da8dSAndroid Build Coastguard Worker if self._hdr_size < 24: 189*cda5da8dSAndroid Build Coastguard Worker raise Error('header size too small') 190*cda5da8dSAndroid Build Coastguard Worker if self._hdr_size > 100: 191*cda5da8dSAndroid Build Coastguard Worker raise Error('header size ridiculously large') 192*cda5da8dSAndroid Build Coastguard Worker self._data_size = _read_u32(file) 193*cda5da8dSAndroid Build Coastguard Worker if self._data_size != AUDIO_UNKNOWN_SIZE: 194*cda5da8dSAndroid Build Coastguard Worker self._data_size = int(self._data_size) 195*cda5da8dSAndroid Build Coastguard Worker self._encoding = int(_read_u32(file)) 196*cda5da8dSAndroid Build Coastguard Worker if self._encoding not in _simple_encodings: 197*cda5da8dSAndroid Build Coastguard Worker raise Error('encoding not (yet) supported') 198*cda5da8dSAndroid Build Coastguard Worker if self._encoding in (AUDIO_FILE_ENCODING_MULAW_8, 199*cda5da8dSAndroid Build Coastguard Worker AUDIO_FILE_ENCODING_ALAW_8): 200*cda5da8dSAndroid Build Coastguard Worker self._sampwidth = 2 201*cda5da8dSAndroid Build Coastguard Worker self._framesize = 1 202*cda5da8dSAndroid Build Coastguard Worker elif self._encoding == AUDIO_FILE_ENCODING_LINEAR_8: 203*cda5da8dSAndroid Build Coastguard Worker self._framesize = self._sampwidth = 1 204*cda5da8dSAndroid Build Coastguard Worker elif self._encoding == AUDIO_FILE_ENCODING_LINEAR_16: 205*cda5da8dSAndroid Build Coastguard Worker self._framesize = self._sampwidth = 2 206*cda5da8dSAndroid Build Coastguard Worker elif self._encoding == AUDIO_FILE_ENCODING_LINEAR_24: 207*cda5da8dSAndroid Build Coastguard Worker self._framesize = self._sampwidth = 3 208*cda5da8dSAndroid Build Coastguard Worker elif self._encoding == AUDIO_FILE_ENCODING_LINEAR_32: 209*cda5da8dSAndroid Build Coastguard Worker self._framesize = self._sampwidth = 4 210*cda5da8dSAndroid Build Coastguard Worker else: 211*cda5da8dSAndroid Build Coastguard Worker raise Error('unknown encoding') 212*cda5da8dSAndroid Build Coastguard Worker self._framerate = int(_read_u32(file)) 213*cda5da8dSAndroid Build Coastguard Worker self._nchannels = int(_read_u32(file)) 214*cda5da8dSAndroid Build Coastguard Worker if not self._nchannels: 215*cda5da8dSAndroid Build Coastguard Worker raise Error('bad # of channels') 216*cda5da8dSAndroid Build Coastguard Worker self._framesize = self._framesize * self._nchannels 217*cda5da8dSAndroid Build Coastguard Worker if self._hdr_size > 24: 218*cda5da8dSAndroid Build Coastguard Worker self._info = file.read(self._hdr_size - 24) 219*cda5da8dSAndroid Build Coastguard Worker self._info, _, _ = self._info.partition(b'\0') 220*cda5da8dSAndroid Build Coastguard Worker else: 221*cda5da8dSAndroid Build Coastguard Worker self._info = b'' 222*cda5da8dSAndroid Build Coastguard Worker try: 223*cda5da8dSAndroid Build Coastguard Worker self._data_pos = file.tell() 224*cda5da8dSAndroid Build Coastguard Worker except (AttributeError, OSError): 225*cda5da8dSAndroid Build Coastguard Worker self._data_pos = None 226*cda5da8dSAndroid Build Coastguard Worker 227*cda5da8dSAndroid Build Coastguard Worker def getfp(self): 228*cda5da8dSAndroid Build Coastguard Worker return self._file 229*cda5da8dSAndroid Build Coastguard Worker 230*cda5da8dSAndroid Build Coastguard Worker def getnchannels(self): 231*cda5da8dSAndroid Build Coastguard Worker return self._nchannels 232*cda5da8dSAndroid Build Coastguard Worker 233*cda5da8dSAndroid Build Coastguard Worker def getsampwidth(self): 234*cda5da8dSAndroid Build Coastguard Worker return self._sampwidth 235*cda5da8dSAndroid Build Coastguard Worker 236*cda5da8dSAndroid Build Coastguard Worker def getframerate(self): 237*cda5da8dSAndroid Build Coastguard Worker return self._framerate 238*cda5da8dSAndroid Build Coastguard Worker 239*cda5da8dSAndroid Build Coastguard Worker def getnframes(self): 240*cda5da8dSAndroid Build Coastguard Worker if self._data_size == AUDIO_UNKNOWN_SIZE: 241*cda5da8dSAndroid Build Coastguard Worker return AUDIO_UNKNOWN_SIZE 242*cda5da8dSAndroid Build Coastguard Worker if self._encoding in _simple_encodings: 243*cda5da8dSAndroid Build Coastguard Worker return self._data_size // self._framesize 244*cda5da8dSAndroid Build Coastguard Worker return 0 # XXX--must do some arithmetic here 245*cda5da8dSAndroid Build Coastguard Worker 246*cda5da8dSAndroid Build Coastguard Worker def getcomptype(self): 247*cda5da8dSAndroid Build Coastguard Worker if self._encoding == AUDIO_FILE_ENCODING_MULAW_8: 248*cda5da8dSAndroid Build Coastguard Worker return 'ULAW' 249*cda5da8dSAndroid Build Coastguard Worker elif self._encoding == AUDIO_FILE_ENCODING_ALAW_8: 250*cda5da8dSAndroid Build Coastguard Worker return 'ALAW' 251*cda5da8dSAndroid Build Coastguard Worker else: 252*cda5da8dSAndroid Build Coastguard Worker return 'NONE' 253*cda5da8dSAndroid Build Coastguard Worker 254*cda5da8dSAndroid Build Coastguard Worker def getcompname(self): 255*cda5da8dSAndroid Build Coastguard Worker if self._encoding == AUDIO_FILE_ENCODING_MULAW_8: 256*cda5da8dSAndroid Build Coastguard Worker return 'CCITT G.711 u-law' 257*cda5da8dSAndroid Build Coastguard Worker elif self._encoding == AUDIO_FILE_ENCODING_ALAW_8: 258*cda5da8dSAndroid Build Coastguard Worker return 'CCITT G.711 A-law' 259*cda5da8dSAndroid Build Coastguard Worker else: 260*cda5da8dSAndroid Build Coastguard Worker return 'not compressed' 261*cda5da8dSAndroid Build Coastguard Worker 262*cda5da8dSAndroid Build Coastguard Worker def getparams(self): 263*cda5da8dSAndroid Build Coastguard Worker return _sunau_params(self.getnchannels(), self.getsampwidth(), 264*cda5da8dSAndroid Build Coastguard Worker self.getframerate(), self.getnframes(), 265*cda5da8dSAndroid Build Coastguard Worker self.getcomptype(), self.getcompname()) 266*cda5da8dSAndroid Build Coastguard Worker 267*cda5da8dSAndroid Build Coastguard Worker def getmarkers(self): 268*cda5da8dSAndroid Build Coastguard Worker return None 269*cda5da8dSAndroid Build Coastguard Worker 270*cda5da8dSAndroid Build Coastguard Worker def getmark(self, id): 271*cda5da8dSAndroid Build Coastguard Worker raise Error('no marks') 272*cda5da8dSAndroid Build Coastguard Worker 273*cda5da8dSAndroid Build Coastguard Worker def readframes(self, nframes): 274*cda5da8dSAndroid Build Coastguard Worker if self._encoding in _simple_encodings: 275*cda5da8dSAndroid Build Coastguard Worker if nframes == AUDIO_UNKNOWN_SIZE: 276*cda5da8dSAndroid Build Coastguard Worker data = self._file.read() 277*cda5da8dSAndroid Build Coastguard Worker else: 278*cda5da8dSAndroid Build Coastguard Worker data = self._file.read(nframes * self._framesize) 279*cda5da8dSAndroid Build Coastguard Worker self._soundpos += len(data) // self._framesize 280*cda5da8dSAndroid Build Coastguard Worker if self._encoding == AUDIO_FILE_ENCODING_MULAW_8: 281*cda5da8dSAndroid Build Coastguard Worker with warnings.catch_warnings(): 282*cda5da8dSAndroid Build Coastguard Worker warnings.simplefilter('ignore', category=DeprecationWarning) 283*cda5da8dSAndroid Build Coastguard Worker import audioop 284*cda5da8dSAndroid Build Coastguard Worker data = audioop.ulaw2lin(data, self._sampwidth) 285*cda5da8dSAndroid Build Coastguard Worker return data 286*cda5da8dSAndroid Build Coastguard Worker return None # XXX--not implemented yet 287*cda5da8dSAndroid Build Coastguard Worker 288*cda5da8dSAndroid Build Coastguard Worker def rewind(self): 289*cda5da8dSAndroid Build Coastguard Worker if self._data_pos is None: 290*cda5da8dSAndroid Build Coastguard Worker raise OSError('cannot seek') 291*cda5da8dSAndroid Build Coastguard Worker self._file.seek(self._data_pos) 292*cda5da8dSAndroid Build Coastguard Worker self._soundpos = 0 293*cda5da8dSAndroid Build Coastguard Worker 294*cda5da8dSAndroid Build Coastguard Worker def tell(self): 295*cda5da8dSAndroid Build Coastguard Worker return self._soundpos 296*cda5da8dSAndroid Build Coastguard Worker 297*cda5da8dSAndroid Build Coastguard Worker def setpos(self, pos): 298*cda5da8dSAndroid Build Coastguard Worker if pos < 0 or pos > self.getnframes(): 299*cda5da8dSAndroid Build Coastguard Worker raise Error('position not in range') 300*cda5da8dSAndroid Build Coastguard Worker if self._data_pos is None: 301*cda5da8dSAndroid Build Coastguard Worker raise OSError('cannot seek') 302*cda5da8dSAndroid Build Coastguard Worker self._file.seek(self._data_pos + pos * self._framesize) 303*cda5da8dSAndroid Build Coastguard Worker self._soundpos = pos 304*cda5da8dSAndroid Build Coastguard Worker 305*cda5da8dSAndroid Build Coastguard Worker def close(self): 306*cda5da8dSAndroid Build Coastguard Worker file = self._file 307*cda5da8dSAndroid Build Coastguard Worker if file: 308*cda5da8dSAndroid Build Coastguard Worker self._file = None 309*cda5da8dSAndroid Build Coastguard Worker if self._opened: 310*cda5da8dSAndroid Build Coastguard Worker file.close() 311*cda5da8dSAndroid Build Coastguard Worker 312*cda5da8dSAndroid Build Coastguard Workerclass Au_write: 313*cda5da8dSAndroid Build Coastguard Worker 314*cda5da8dSAndroid Build Coastguard Worker def __init__(self, f): 315*cda5da8dSAndroid Build Coastguard Worker if type(f) == type(''): 316*cda5da8dSAndroid Build Coastguard Worker import builtins 317*cda5da8dSAndroid Build Coastguard Worker f = builtins.open(f, 'wb') 318*cda5da8dSAndroid Build Coastguard Worker self._opened = True 319*cda5da8dSAndroid Build Coastguard Worker else: 320*cda5da8dSAndroid Build Coastguard Worker self._opened = False 321*cda5da8dSAndroid Build Coastguard Worker self.initfp(f) 322*cda5da8dSAndroid Build Coastguard Worker 323*cda5da8dSAndroid Build Coastguard Worker def __del__(self): 324*cda5da8dSAndroid Build Coastguard Worker if self._file: 325*cda5da8dSAndroid Build Coastguard Worker self.close() 326*cda5da8dSAndroid Build Coastguard Worker self._file = None 327*cda5da8dSAndroid Build Coastguard Worker 328*cda5da8dSAndroid Build Coastguard Worker def __enter__(self): 329*cda5da8dSAndroid Build Coastguard Worker return self 330*cda5da8dSAndroid Build Coastguard Worker 331*cda5da8dSAndroid Build Coastguard Worker def __exit__(self, *args): 332*cda5da8dSAndroid Build Coastguard Worker self.close() 333*cda5da8dSAndroid Build Coastguard Worker 334*cda5da8dSAndroid Build Coastguard Worker def initfp(self, file): 335*cda5da8dSAndroid Build Coastguard Worker self._file = file 336*cda5da8dSAndroid Build Coastguard Worker self._framerate = 0 337*cda5da8dSAndroid Build Coastguard Worker self._nchannels = 0 338*cda5da8dSAndroid Build Coastguard Worker self._sampwidth = 0 339*cda5da8dSAndroid Build Coastguard Worker self._framesize = 0 340*cda5da8dSAndroid Build Coastguard Worker self._nframes = AUDIO_UNKNOWN_SIZE 341*cda5da8dSAndroid Build Coastguard Worker self._nframeswritten = 0 342*cda5da8dSAndroid Build Coastguard Worker self._datawritten = 0 343*cda5da8dSAndroid Build Coastguard Worker self._datalength = 0 344*cda5da8dSAndroid Build Coastguard Worker self._info = b'' 345*cda5da8dSAndroid Build Coastguard Worker self._comptype = 'ULAW' # default is U-law 346*cda5da8dSAndroid Build Coastguard Worker 347*cda5da8dSAndroid Build Coastguard Worker def setnchannels(self, nchannels): 348*cda5da8dSAndroid Build Coastguard Worker if self._nframeswritten: 349*cda5da8dSAndroid Build Coastguard Worker raise Error('cannot change parameters after starting to write') 350*cda5da8dSAndroid Build Coastguard Worker if nchannels not in (1, 2, 4): 351*cda5da8dSAndroid Build Coastguard Worker raise Error('only 1, 2, or 4 channels supported') 352*cda5da8dSAndroid Build Coastguard Worker self._nchannels = nchannels 353*cda5da8dSAndroid Build Coastguard Worker 354*cda5da8dSAndroid Build Coastguard Worker def getnchannels(self): 355*cda5da8dSAndroid Build Coastguard Worker if not self._nchannels: 356*cda5da8dSAndroid Build Coastguard Worker raise Error('number of channels not set') 357*cda5da8dSAndroid Build Coastguard Worker return self._nchannels 358*cda5da8dSAndroid Build Coastguard Worker 359*cda5da8dSAndroid Build Coastguard Worker def setsampwidth(self, sampwidth): 360*cda5da8dSAndroid Build Coastguard Worker if self._nframeswritten: 361*cda5da8dSAndroid Build Coastguard Worker raise Error('cannot change parameters after starting to write') 362*cda5da8dSAndroid Build Coastguard Worker if sampwidth not in (1, 2, 3, 4): 363*cda5da8dSAndroid Build Coastguard Worker raise Error('bad sample width') 364*cda5da8dSAndroid Build Coastguard Worker self._sampwidth = sampwidth 365*cda5da8dSAndroid Build Coastguard Worker 366*cda5da8dSAndroid Build Coastguard Worker def getsampwidth(self): 367*cda5da8dSAndroid Build Coastguard Worker if not self._framerate: 368*cda5da8dSAndroid Build Coastguard Worker raise Error('sample width not specified') 369*cda5da8dSAndroid Build Coastguard Worker return self._sampwidth 370*cda5da8dSAndroid Build Coastguard Worker 371*cda5da8dSAndroid Build Coastguard Worker def setframerate(self, framerate): 372*cda5da8dSAndroid Build Coastguard Worker if self._nframeswritten: 373*cda5da8dSAndroid Build Coastguard Worker raise Error('cannot change parameters after starting to write') 374*cda5da8dSAndroid Build Coastguard Worker self._framerate = framerate 375*cda5da8dSAndroid Build Coastguard Worker 376*cda5da8dSAndroid Build Coastguard Worker def getframerate(self): 377*cda5da8dSAndroid Build Coastguard Worker if not self._framerate: 378*cda5da8dSAndroid Build Coastguard Worker raise Error('frame rate not set') 379*cda5da8dSAndroid Build Coastguard Worker return self._framerate 380*cda5da8dSAndroid Build Coastguard Worker 381*cda5da8dSAndroid Build Coastguard Worker def setnframes(self, nframes): 382*cda5da8dSAndroid Build Coastguard Worker if self._nframeswritten: 383*cda5da8dSAndroid Build Coastguard Worker raise Error('cannot change parameters after starting to write') 384*cda5da8dSAndroid Build Coastguard Worker if nframes < 0: 385*cda5da8dSAndroid Build Coastguard Worker raise Error('# of frames cannot be negative') 386*cda5da8dSAndroid Build Coastguard Worker self._nframes = nframes 387*cda5da8dSAndroid Build Coastguard Worker 388*cda5da8dSAndroid Build Coastguard Worker def getnframes(self): 389*cda5da8dSAndroid Build Coastguard Worker return self._nframeswritten 390*cda5da8dSAndroid Build Coastguard Worker 391*cda5da8dSAndroid Build Coastguard Worker def setcomptype(self, type, name): 392*cda5da8dSAndroid Build Coastguard Worker if type in ('NONE', 'ULAW'): 393*cda5da8dSAndroid Build Coastguard Worker self._comptype = type 394*cda5da8dSAndroid Build Coastguard Worker else: 395*cda5da8dSAndroid Build Coastguard Worker raise Error('unknown compression type') 396*cda5da8dSAndroid Build Coastguard Worker 397*cda5da8dSAndroid Build Coastguard Worker def getcomptype(self): 398*cda5da8dSAndroid Build Coastguard Worker return self._comptype 399*cda5da8dSAndroid Build Coastguard Worker 400*cda5da8dSAndroid Build Coastguard Worker def getcompname(self): 401*cda5da8dSAndroid Build Coastguard Worker if self._comptype == 'ULAW': 402*cda5da8dSAndroid Build Coastguard Worker return 'CCITT G.711 u-law' 403*cda5da8dSAndroid Build Coastguard Worker elif self._comptype == 'ALAW': 404*cda5da8dSAndroid Build Coastguard Worker return 'CCITT G.711 A-law' 405*cda5da8dSAndroid Build Coastguard Worker else: 406*cda5da8dSAndroid Build Coastguard Worker return 'not compressed' 407*cda5da8dSAndroid Build Coastguard Worker 408*cda5da8dSAndroid Build Coastguard Worker def setparams(self, params): 409*cda5da8dSAndroid Build Coastguard Worker nchannels, sampwidth, framerate, nframes, comptype, compname = params 410*cda5da8dSAndroid Build Coastguard Worker self.setnchannels(nchannels) 411*cda5da8dSAndroid Build Coastguard Worker self.setsampwidth(sampwidth) 412*cda5da8dSAndroid Build Coastguard Worker self.setframerate(framerate) 413*cda5da8dSAndroid Build Coastguard Worker self.setnframes(nframes) 414*cda5da8dSAndroid Build Coastguard Worker self.setcomptype(comptype, compname) 415*cda5da8dSAndroid Build Coastguard Worker 416*cda5da8dSAndroid Build Coastguard Worker def getparams(self): 417*cda5da8dSAndroid Build Coastguard Worker return _sunau_params(self.getnchannels(), self.getsampwidth(), 418*cda5da8dSAndroid Build Coastguard Worker self.getframerate(), self.getnframes(), 419*cda5da8dSAndroid Build Coastguard Worker self.getcomptype(), self.getcompname()) 420*cda5da8dSAndroid Build Coastguard Worker 421*cda5da8dSAndroid Build Coastguard Worker def tell(self): 422*cda5da8dSAndroid Build Coastguard Worker return self._nframeswritten 423*cda5da8dSAndroid Build Coastguard Worker 424*cda5da8dSAndroid Build Coastguard Worker def writeframesraw(self, data): 425*cda5da8dSAndroid Build Coastguard Worker if not isinstance(data, (bytes, bytearray)): 426*cda5da8dSAndroid Build Coastguard Worker data = memoryview(data).cast('B') 427*cda5da8dSAndroid Build Coastguard Worker self._ensure_header_written() 428*cda5da8dSAndroid Build Coastguard Worker if self._comptype == 'ULAW': 429*cda5da8dSAndroid Build Coastguard Worker with warnings.catch_warnings(): 430*cda5da8dSAndroid Build Coastguard Worker warnings.simplefilter('ignore', category=DeprecationWarning) 431*cda5da8dSAndroid Build Coastguard Worker import audioop 432*cda5da8dSAndroid Build Coastguard Worker data = audioop.lin2ulaw(data, self._sampwidth) 433*cda5da8dSAndroid Build Coastguard Worker nframes = len(data) // self._framesize 434*cda5da8dSAndroid Build Coastguard Worker self._file.write(data) 435*cda5da8dSAndroid Build Coastguard Worker self._nframeswritten = self._nframeswritten + nframes 436*cda5da8dSAndroid Build Coastguard Worker self._datawritten = self._datawritten + len(data) 437*cda5da8dSAndroid Build Coastguard Worker 438*cda5da8dSAndroid Build Coastguard Worker def writeframes(self, data): 439*cda5da8dSAndroid Build Coastguard Worker self.writeframesraw(data) 440*cda5da8dSAndroid Build Coastguard Worker if self._nframeswritten != self._nframes or \ 441*cda5da8dSAndroid Build Coastguard Worker self._datalength != self._datawritten: 442*cda5da8dSAndroid Build Coastguard Worker self._patchheader() 443*cda5da8dSAndroid Build Coastguard Worker 444*cda5da8dSAndroid Build Coastguard Worker def close(self): 445*cda5da8dSAndroid Build Coastguard Worker if self._file: 446*cda5da8dSAndroid Build Coastguard Worker try: 447*cda5da8dSAndroid Build Coastguard Worker self._ensure_header_written() 448*cda5da8dSAndroid Build Coastguard Worker if self._nframeswritten != self._nframes or \ 449*cda5da8dSAndroid Build Coastguard Worker self._datalength != self._datawritten: 450*cda5da8dSAndroid Build Coastguard Worker self._patchheader() 451*cda5da8dSAndroid Build Coastguard Worker self._file.flush() 452*cda5da8dSAndroid Build Coastguard Worker finally: 453*cda5da8dSAndroid Build Coastguard Worker file = self._file 454*cda5da8dSAndroid Build Coastguard Worker self._file = None 455*cda5da8dSAndroid Build Coastguard Worker if self._opened: 456*cda5da8dSAndroid Build Coastguard Worker file.close() 457*cda5da8dSAndroid Build Coastguard Worker 458*cda5da8dSAndroid Build Coastguard Worker # 459*cda5da8dSAndroid Build Coastguard Worker # private methods 460*cda5da8dSAndroid Build Coastguard Worker # 461*cda5da8dSAndroid Build Coastguard Worker 462*cda5da8dSAndroid Build Coastguard Worker def _ensure_header_written(self): 463*cda5da8dSAndroid Build Coastguard Worker if not self._nframeswritten: 464*cda5da8dSAndroid Build Coastguard Worker if not self._nchannels: 465*cda5da8dSAndroid Build Coastguard Worker raise Error('# of channels not specified') 466*cda5da8dSAndroid Build Coastguard Worker if not self._sampwidth: 467*cda5da8dSAndroid Build Coastguard Worker raise Error('sample width not specified') 468*cda5da8dSAndroid Build Coastguard Worker if not self._framerate: 469*cda5da8dSAndroid Build Coastguard Worker raise Error('frame rate not specified') 470*cda5da8dSAndroid Build Coastguard Worker self._write_header() 471*cda5da8dSAndroid Build Coastguard Worker 472*cda5da8dSAndroid Build Coastguard Worker def _write_header(self): 473*cda5da8dSAndroid Build Coastguard Worker if self._comptype == 'NONE': 474*cda5da8dSAndroid Build Coastguard Worker if self._sampwidth == 1: 475*cda5da8dSAndroid Build Coastguard Worker encoding = AUDIO_FILE_ENCODING_LINEAR_8 476*cda5da8dSAndroid Build Coastguard Worker self._framesize = 1 477*cda5da8dSAndroid Build Coastguard Worker elif self._sampwidth == 2: 478*cda5da8dSAndroid Build Coastguard Worker encoding = AUDIO_FILE_ENCODING_LINEAR_16 479*cda5da8dSAndroid Build Coastguard Worker self._framesize = 2 480*cda5da8dSAndroid Build Coastguard Worker elif self._sampwidth == 3: 481*cda5da8dSAndroid Build Coastguard Worker encoding = AUDIO_FILE_ENCODING_LINEAR_24 482*cda5da8dSAndroid Build Coastguard Worker self._framesize = 3 483*cda5da8dSAndroid Build Coastguard Worker elif self._sampwidth == 4: 484*cda5da8dSAndroid Build Coastguard Worker encoding = AUDIO_FILE_ENCODING_LINEAR_32 485*cda5da8dSAndroid Build Coastguard Worker self._framesize = 4 486*cda5da8dSAndroid Build Coastguard Worker else: 487*cda5da8dSAndroid Build Coastguard Worker raise Error('internal error') 488*cda5da8dSAndroid Build Coastguard Worker elif self._comptype == 'ULAW': 489*cda5da8dSAndroid Build Coastguard Worker encoding = AUDIO_FILE_ENCODING_MULAW_8 490*cda5da8dSAndroid Build Coastguard Worker self._framesize = 1 491*cda5da8dSAndroid Build Coastguard Worker else: 492*cda5da8dSAndroid Build Coastguard Worker raise Error('internal error') 493*cda5da8dSAndroid Build Coastguard Worker self._framesize = self._framesize * self._nchannels 494*cda5da8dSAndroid Build Coastguard Worker _write_u32(self._file, AUDIO_FILE_MAGIC) 495*cda5da8dSAndroid Build Coastguard Worker header_size = 25 + len(self._info) 496*cda5da8dSAndroid Build Coastguard Worker header_size = (header_size + 7) & ~7 497*cda5da8dSAndroid Build Coastguard Worker _write_u32(self._file, header_size) 498*cda5da8dSAndroid Build Coastguard Worker if self._nframes == AUDIO_UNKNOWN_SIZE: 499*cda5da8dSAndroid Build Coastguard Worker length = AUDIO_UNKNOWN_SIZE 500*cda5da8dSAndroid Build Coastguard Worker else: 501*cda5da8dSAndroid Build Coastguard Worker length = self._nframes * self._framesize 502*cda5da8dSAndroid Build Coastguard Worker try: 503*cda5da8dSAndroid Build Coastguard Worker self._form_length_pos = self._file.tell() 504*cda5da8dSAndroid Build Coastguard Worker except (AttributeError, OSError): 505*cda5da8dSAndroid Build Coastguard Worker self._form_length_pos = None 506*cda5da8dSAndroid Build Coastguard Worker _write_u32(self._file, length) 507*cda5da8dSAndroid Build Coastguard Worker self._datalength = length 508*cda5da8dSAndroid Build Coastguard Worker _write_u32(self._file, encoding) 509*cda5da8dSAndroid Build Coastguard Worker _write_u32(self._file, self._framerate) 510*cda5da8dSAndroid Build Coastguard Worker _write_u32(self._file, self._nchannels) 511*cda5da8dSAndroid Build Coastguard Worker self._file.write(self._info) 512*cda5da8dSAndroid Build Coastguard Worker self._file.write(b'\0'*(header_size - len(self._info) - 24)) 513*cda5da8dSAndroid Build Coastguard Worker 514*cda5da8dSAndroid Build Coastguard Worker def _patchheader(self): 515*cda5da8dSAndroid Build Coastguard Worker if self._form_length_pos is None: 516*cda5da8dSAndroid Build Coastguard Worker raise OSError('cannot seek') 517*cda5da8dSAndroid Build Coastguard Worker self._file.seek(self._form_length_pos) 518*cda5da8dSAndroid Build Coastguard Worker _write_u32(self._file, self._datawritten) 519*cda5da8dSAndroid Build Coastguard Worker self._datalength = self._datawritten 520*cda5da8dSAndroid Build Coastguard Worker self._file.seek(0, 2) 521*cda5da8dSAndroid Build Coastguard Worker 522*cda5da8dSAndroid Build Coastguard Workerdef open(f, mode=None): 523*cda5da8dSAndroid Build Coastguard Worker if mode is None: 524*cda5da8dSAndroid Build Coastguard Worker if hasattr(f, 'mode'): 525*cda5da8dSAndroid Build Coastguard Worker mode = f.mode 526*cda5da8dSAndroid Build Coastguard Worker else: 527*cda5da8dSAndroid Build Coastguard Worker mode = 'rb' 528*cda5da8dSAndroid Build Coastguard Worker if mode in ('r', 'rb'): 529*cda5da8dSAndroid Build Coastguard Worker return Au_read(f) 530*cda5da8dSAndroid Build Coastguard Worker elif mode in ('w', 'wb'): 531*cda5da8dSAndroid Build Coastguard Worker return Au_write(f) 532*cda5da8dSAndroid Build Coastguard Worker else: 533*cda5da8dSAndroid Build Coastguard Worker raise Error("mode must be 'r', 'rb', 'w', or 'wb'") 534