17db96d56Sopenharmony_ci"""Stuff to parse AIFF-C and AIFF files. 27db96d56Sopenharmony_ci 37db96d56Sopenharmony_ciUnless explicitly stated otherwise, the description below is true 47db96d56Sopenharmony_ciboth for AIFF-C files and AIFF files. 57db96d56Sopenharmony_ci 67db96d56Sopenharmony_ciAn AIFF-C file has the following structure. 77db96d56Sopenharmony_ci 87db96d56Sopenharmony_ci +-----------------+ 97db96d56Sopenharmony_ci | FORM | 107db96d56Sopenharmony_ci +-----------------+ 117db96d56Sopenharmony_ci | <size> | 127db96d56Sopenharmony_ci +----+------------+ 137db96d56Sopenharmony_ci | | AIFC | 147db96d56Sopenharmony_ci | +------------+ 157db96d56Sopenharmony_ci | | <chunks> | 167db96d56Sopenharmony_ci | | . | 177db96d56Sopenharmony_ci | | . | 187db96d56Sopenharmony_ci | | . | 197db96d56Sopenharmony_ci +----+------------+ 207db96d56Sopenharmony_ci 217db96d56Sopenharmony_ciAn AIFF file has the string "AIFF" instead of "AIFC". 227db96d56Sopenharmony_ci 237db96d56Sopenharmony_ciA chunk consists of an identifier (4 bytes) followed by a size (4 bytes, 247db96d56Sopenharmony_cibig endian order), followed by the data. The size field does not include 257db96d56Sopenharmony_cithe size of the 8 byte header. 267db96d56Sopenharmony_ci 277db96d56Sopenharmony_ciThe following chunk types are recognized. 287db96d56Sopenharmony_ci 297db96d56Sopenharmony_ci FVER 307db96d56Sopenharmony_ci <version number of AIFF-C defining document> (AIFF-C only). 317db96d56Sopenharmony_ci MARK 327db96d56Sopenharmony_ci <# of markers> (2 bytes) 337db96d56Sopenharmony_ci list of markers: 347db96d56Sopenharmony_ci <marker ID> (2 bytes, must be > 0) 357db96d56Sopenharmony_ci <position> (4 bytes) 367db96d56Sopenharmony_ci <marker name> ("pstring") 377db96d56Sopenharmony_ci COMM 387db96d56Sopenharmony_ci <# of channels> (2 bytes) 397db96d56Sopenharmony_ci <# of sound frames> (4 bytes) 407db96d56Sopenharmony_ci <size of the samples> (2 bytes) 417db96d56Sopenharmony_ci <sampling frequency> (10 bytes, IEEE 80-bit extended 427db96d56Sopenharmony_ci floating point) 437db96d56Sopenharmony_ci in AIFF-C files only: 447db96d56Sopenharmony_ci <compression type> (4 bytes) 457db96d56Sopenharmony_ci <human-readable version of compression type> ("pstring") 467db96d56Sopenharmony_ci SSND 477db96d56Sopenharmony_ci <offset> (4 bytes, not used by this program) 487db96d56Sopenharmony_ci <blocksize> (4 bytes, not used by this program) 497db96d56Sopenharmony_ci <sound data> 507db96d56Sopenharmony_ci 517db96d56Sopenharmony_ciA pstring consists of 1 byte length, a string of characters, and 0 or 1 527db96d56Sopenharmony_cibyte pad to make the total length even. 537db96d56Sopenharmony_ci 547db96d56Sopenharmony_ciUsage. 557db96d56Sopenharmony_ci 567db96d56Sopenharmony_ciReading AIFF files: 577db96d56Sopenharmony_ci f = aifc.open(file, 'r') 587db96d56Sopenharmony_ciwhere file is either the name of a file or an open file pointer. 597db96d56Sopenharmony_ciThe open file pointer must have methods read(), seek(), and close(). 607db96d56Sopenharmony_ciIn some types of audio files, if the setpos() method is not used, 617db96d56Sopenharmony_cithe seek() method is not necessary. 627db96d56Sopenharmony_ci 637db96d56Sopenharmony_ciThis returns an instance of a class with the following public methods: 647db96d56Sopenharmony_ci getnchannels() -- returns number of audio channels (1 for 657db96d56Sopenharmony_ci mono, 2 for stereo) 667db96d56Sopenharmony_ci getsampwidth() -- returns sample width in bytes 677db96d56Sopenharmony_ci getframerate() -- returns sampling frequency 687db96d56Sopenharmony_ci getnframes() -- returns number of audio frames 697db96d56Sopenharmony_ci getcomptype() -- returns compression type ('NONE' for AIFF files) 707db96d56Sopenharmony_ci getcompname() -- returns human-readable version of 717db96d56Sopenharmony_ci compression type ('not compressed' for AIFF files) 727db96d56Sopenharmony_ci getparams() -- returns a namedtuple consisting of all of the 737db96d56Sopenharmony_ci above in the above order 747db96d56Sopenharmony_ci getmarkers() -- get the list of marks in the audio file or None 757db96d56Sopenharmony_ci if there are no marks 767db96d56Sopenharmony_ci getmark(id) -- get mark with the specified id (raises an error 777db96d56Sopenharmony_ci if the mark does not exist) 787db96d56Sopenharmony_ci readframes(n) -- returns at most n frames of audio 797db96d56Sopenharmony_ci rewind() -- rewind to the beginning of the audio stream 807db96d56Sopenharmony_ci setpos(pos) -- seek to the specified position 817db96d56Sopenharmony_ci tell() -- return the current position 827db96d56Sopenharmony_ci close() -- close the instance (make it unusable) 837db96d56Sopenharmony_ciThe position returned by tell(), the position given to setpos() and 847db96d56Sopenharmony_cithe position of marks are all compatible and have nothing to do with 857db96d56Sopenharmony_cithe actual position in the file. 867db96d56Sopenharmony_ciThe close() method is called automatically when the class instance 877db96d56Sopenharmony_ciis destroyed. 887db96d56Sopenharmony_ci 897db96d56Sopenharmony_ciWriting AIFF files: 907db96d56Sopenharmony_ci f = aifc.open(file, 'w') 917db96d56Sopenharmony_ciwhere file is either the name of a file or an open file pointer. 927db96d56Sopenharmony_ciThe open file pointer must have methods write(), tell(), seek(), and 937db96d56Sopenharmony_ciclose(). 947db96d56Sopenharmony_ci 957db96d56Sopenharmony_ciThis returns an instance of a class with the following public methods: 967db96d56Sopenharmony_ci aiff() -- create an AIFF file (AIFF-C default) 977db96d56Sopenharmony_ci aifc() -- create an AIFF-C file 987db96d56Sopenharmony_ci setnchannels(n) -- set the number of channels 997db96d56Sopenharmony_ci setsampwidth(n) -- set the sample width 1007db96d56Sopenharmony_ci setframerate(n) -- set the frame rate 1017db96d56Sopenharmony_ci setnframes(n) -- set the number of frames 1027db96d56Sopenharmony_ci setcomptype(type, name) 1037db96d56Sopenharmony_ci -- set the compression type and the 1047db96d56Sopenharmony_ci human-readable compression type 1057db96d56Sopenharmony_ci setparams(tuple) 1067db96d56Sopenharmony_ci -- set all parameters at once 1077db96d56Sopenharmony_ci setmark(id, pos, name) 1087db96d56Sopenharmony_ci -- add specified mark to the list of marks 1097db96d56Sopenharmony_ci tell() -- return current position in output file (useful 1107db96d56Sopenharmony_ci in combination with setmark()) 1117db96d56Sopenharmony_ci writeframesraw(data) 1127db96d56Sopenharmony_ci -- write audio frames without pathing up the 1137db96d56Sopenharmony_ci file header 1147db96d56Sopenharmony_ci writeframes(data) 1157db96d56Sopenharmony_ci -- write audio frames and patch up the file header 1167db96d56Sopenharmony_ci close() -- patch up the file header and close the 1177db96d56Sopenharmony_ci output file 1187db96d56Sopenharmony_ciYou should set the parameters before the first writeframesraw or 1197db96d56Sopenharmony_ciwriteframes. The total number of frames does not need to be set, 1207db96d56Sopenharmony_cibut when it is set to the correct value, the header does not have to 1217db96d56Sopenharmony_cibe patched up. 1227db96d56Sopenharmony_ciIt is best to first set all parameters, perhaps possibly the 1237db96d56Sopenharmony_cicompression type, and then write audio frames using writeframesraw. 1247db96d56Sopenharmony_ciWhen all frames have been written, either call writeframes(b'') or 1257db96d56Sopenharmony_ciclose() to patch up the sizes in the header. 1267db96d56Sopenharmony_ciMarks can be added anytime. If there are any marks, you must call 1277db96d56Sopenharmony_ciclose() after all frames have been written. 1287db96d56Sopenharmony_ciThe close() method is called automatically when the class instance 1297db96d56Sopenharmony_ciis destroyed. 1307db96d56Sopenharmony_ci 1317db96d56Sopenharmony_ciWhen a file is opened with the extension '.aiff', an AIFF file is 1327db96d56Sopenharmony_ciwritten, otherwise an AIFF-C file is written. This default can be 1337db96d56Sopenharmony_cichanged by calling aiff() or aifc() before the first writeframes or 1347db96d56Sopenharmony_ciwriteframesraw. 1357db96d56Sopenharmony_ci""" 1367db96d56Sopenharmony_ci 1377db96d56Sopenharmony_ciimport struct 1387db96d56Sopenharmony_ciimport builtins 1397db96d56Sopenharmony_ciimport warnings 1407db96d56Sopenharmony_ci 1417db96d56Sopenharmony_ci__all__ = ["Error", "open"] 1427db96d56Sopenharmony_ci 1437db96d56Sopenharmony_ci 1447db96d56Sopenharmony_ciwarnings._deprecated(__name__, remove=(3, 13)) 1457db96d56Sopenharmony_ci 1467db96d56Sopenharmony_ci 1477db96d56Sopenharmony_ciclass Error(Exception): 1487db96d56Sopenharmony_ci pass 1497db96d56Sopenharmony_ci 1507db96d56Sopenharmony_ci_AIFC_version = 0xA2805140 # Version 1 of AIFF-C 1517db96d56Sopenharmony_ci 1527db96d56Sopenharmony_cidef _read_long(file): 1537db96d56Sopenharmony_ci try: 1547db96d56Sopenharmony_ci return struct.unpack('>l', file.read(4))[0] 1557db96d56Sopenharmony_ci except struct.error: 1567db96d56Sopenharmony_ci raise EOFError from None 1577db96d56Sopenharmony_ci 1587db96d56Sopenharmony_cidef _read_ulong(file): 1597db96d56Sopenharmony_ci try: 1607db96d56Sopenharmony_ci return struct.unpack('>L', file.read(4))[0] 1617db96d56Sopenharmony_ci except struct.error: 1627db96d56Sopenharmony_ci raise EOFError from None 1637db96d56Sopenharmony_ci 1647db96d56Sopenharmony_cidef _read_short(file): 1657db96d56Sopenharmony_ci try: 1667db96d56Sopenharmony_ci return struct.unpack('>h', file.read(2))[0] 1677db96d56Sopenharmony_ci except struct.error: 1687db96d56Sopenharmony_ci raise EOFError from None 1697db96d56Sopenharmony_ci 1707db96d56Sopenharmony_cidef _read_ushort(file): 1717db96d56Sopenharmony_ci try: 1727db96d56Sopenharmony_ci return struct.unpack('>H', file.read(2))[0] 1737db96d56Sopenharmony_ci except struct.error: 1747db96d56Sopenharmony_ci raise EOFError from None 1757db96d56Sopenharmony_ci 1767db96d56Sopenharmony_cidef _read_string(file): 1777db96d56Sopenharmony_ci length = ord(file.read(1)) 1787db96d56Sopenharmony_ci if length == 0: 1797db96d56Sopenharmony_ci data = b'' 1807db96d56Sopenharmony_ci else: 1817db96d56Sopenharmony_ci data = file.read(length) 1827db96d56Sopenharmony_ci if length & 1 == 0: 1837db96d56Sopenharmony_ci dummy = file.read(1) 1847db96d56Sopenharmony_ci return data 1857db96d56Sopenharmony_ci 1867db96d56Sopenharmony_ci_HUGE_VAL = 1.79769313486231e+308 # See <limits.h> 1877db96d56Sopenharmony_ci 1887db96d56Sopenharmony_cidef _read_float(f): # 10 bytes 1897db96d56Sopenharmony_ci expon = _read_short(f) # 2 bytes 1907db96d56Sopenharmony_ci sign = 1 1917db96d56Sopenharmony_ci if expon < 0: 1927db96d56Sopenharmony_ci sign = -1 1937db96d56Sopenharmony_ci expon = expon + 0x8000 1947db96d56Sopenharmony_ci himant = _read_ulong(f) # 4 bytes 1957db96d56Sopenharmony_ci lomant = _read_ulong(f) # 4 bytes 1967db96d56Sopenharmony_ci if expon == himant == lomant == 0: 1977db96d56Sopenharmony_ci f = 0.0 1987db96d56Sopenharmony_ci elif expon == 0x7FFF: 1997db96d56Sopenharmony_ci f = _HUGE_VAL 2007db96d56Sopenharmony_ci else: 2017db96d56Sopenharmony_ci expon = expon - 16383 2027db96d56Sopenharmony_ci f = (himant * 0x100000000 + lomant) * pow(2.0, expon - 63) 2037db96d56Sopenharmony_ci return sign * f 2047db96d56Sopenharmony_ci 2057db96d56Sopenharmony_cidef _write_short(f, x): 2067db96d56Sopenharmony_ci f.write(struct.pack('>h', x)) 2077db96d56Sopenharmony_ci 2087db96d56Sopenharmony_cidef _write_ushort(f, x): 2097db96d56Sopenharmony_ci f.write(struct.pack('>H', x)) 2107db96d56Sopenharmony_ci 2117db96d56Sopenharmony_cidef _write_long(f, x): 2127db96d56Sopenharmony_ci f.write(struct.pack('>l', x)) 2137db96d56Sopenharmony_ci 2147db96d56Sopenharmony_cidef _write_ulong(f, x): 2157db96d56Sopenharmony_ci f.write(struct.pack('>L', x)) 2167db96d56Sopenharmony_ci 2177db96d56Sopenharmony_cidef _write_string(f, s): 2187db96d56Sopenharmony_ci if len(s) > 255: 2197db96d56Sopenharmony_ci raise ValueError("string exceeds maximum pstring length") 2207db96d56Sopenharmony_ci f.write(struct.pack('B', len(s))) 2217db96d56Sopenharmony_ci f.write(s) 2227db96d56Sopenharmony_ci if len(s) & 1 == 0: 2237db96d56Sopenharmony_ci f.write(b'\x00') 2247db96d56Sopenharmony_ci 2257db96d56Sopenharmony_cidef _write_float(f, x): 2267db96d56Sopenharmony_ci import math 2277db96d56Sopenharmony_ci if x < 0: 2287db96d56Sopenharmony_ci sign = 0x8000 2297db96d56Sopenharmony_ci x = x * -1 2307db96d56Sopenharmony_ci else: 2317db96d56Sopenharmony_ci sign = 0 2327db96d56Sopenharmony_ci if x == 0: 2337db96d56Sopenharmony_ci expon = 0 2347db96d56Sopenharmony_ci himant = 0 2357db96d56Sopenharmony_ci lomant = 0 2367db96d56Sopenharmony_ci else: 2377db96d56Sopenharmony_ci fmant, expon = math.frexp(x) 2387db96d56Sopenharmony_ci if expon > 16384 or fmant >= 1 or fmant != fmant: # Infinity or NaN 2397db96d56Sopenharmony_ci expon = sign|0x7FFF 2407db96d56Sopenharmony_ci himant = 0 2417db96d56Sopenharmony_ci lomant = 0 2427db96d56Sopenharmony_ci else: # Finite 2437db96d56Sopenharmony_ci expon = expon + 16382 2447db96d56Sopenharmony_ci if expon < 0: # denormalized 2457db96d56Sopenharmony_ci fmant = math.ldexp(fmant, expon) 2467db96d56Sopenharmony_ci expon = 0 2477db96d56Sopenharmony_ci expon = expon | sign 2487db96d56Sopenharmony_ci fmant = math.ldexp(fmant, 32) 2497db96d56Sopenharmony_ci fsmant = math.floor(fmant) 2507db96d56Sopenharmony_ci himant = int(fsmant) 2517db96d56Sopenharmony_ci fmant = math.ldexp(fmant - fsmant, 32) 2527db96d56Sopenharmony_ci fsmant = math.floor(fmant) 2537db96d56Sopenharmony_ci lomant = int(fsmant) 2547db96d56Sopenharmony_ci _write_ushort(f, expon) 2557db96d56Sopenharmony_ci _write_ulong(f, himant) 2567db96d56Sopenharmony_ci _write_ulong(f, lomant) 2577db96d56Sopenharmony_ci 2587db96d56Sopenharmony_ciwith warnings.catch_warnings(): 2597db96d56Sopenharmony_ci warnings.simplefilter("ignore", DeprecationWarning) 2607db96d56Sopenharmony_ci from chunk import Chunk 2617db96d56Sopenharmony_cifrom collections import namedtuple 2627db96d56Sopenharmony_ci 2637db96d56Sopenharmony_ci_aifc_params = namedtuple('_aifc_params', 2647db96d56Sopenharmony_ci 'nchannels sampwidth framerate nframes comptype compname') 2657db96d56Sopenharmony_ci 2667db96d56Sopenharmony_ci_aifc_params.nchannels.__doc__ = 'Number of audio channels (1 for mono, 2 for stereo)' 2677db96d56Sopenharmony_ci_aifc_params.sampwidth.__doc__ = 'Sample width in bytes' 2687db96d56Sopenharmony_ci_aifc_params.framerate.__doc__ = 'Sampling frequency' 2697db96d56Sopenharmony_ci_aifc_params.nframes.__doc__ = 'Number of audio frames' 2707db96d56Sopenharmony_ci_aifc_params.comptype.__doc__ = 'Compression type ("NONE" for AIFF files)' 2717db96d56Sopenharmony_ci_aifc_params.compname.__doc__ = ("""\ 2727db96d56Sopenharmony_ciA human-readable version of the compression type 2737db96d56Sopenharmony_ci('not compressed' for AIFF files)""") 2747db96d56Sopenharmony_ci 2757db96d56Sopenharmony_ci 2767db96d56Sopenharmony_ciclass Aifc_read: 2777db96d56Sopenharmony_ci # Variables used in this class: 2787db96d56Sopenharmony_ci # 2797db96d56Sopenharmony_ci # These variables are available to the user though appropriate 2807db96d56Sopenharmony_ci # methods of this class: 2817db96d56Sopenharmony_ci # _file -- the open file with methods read(), close(), and seek() 2827db96d56Sopenharmony_ci # set through the __init__() method 2837db96d56Sopenharmony_ci # _nchannels -- the number of audio channels 2847db96d56Sopenharmony_ci # available through the getnchannels() method 2857db96d56Sopenharmony_ci # _nframes -- the number of audio frames 2867db96d56Sopenharmony_ci # available through the getnframes() method 2877db96d56Sopenharmony_ci # _sampwidth -- the number of bytes per audio sample 2887db96d56Sopenharmony_ci # available through the getsampwidth() method 2897db96d56Sopenharmony_ci # _framerate -- the sampling frequency 2907db96d56Sopenharmony_ci # available through the getframerate() method 2917db96d56Sopenharmony_ci # _comptype -- the AIFF-C compression type ('NONE' if AIFF) 2927db96d56Sopenharmony_ci # available through the getcomptype() method 2937db96d56Sopenharmony_ci # _compname -- the human-readable AIFF-C compression type 2947db96d56Sopenharmony_ci # available through the getcomptype() method 2957db96d56Sopenharmony_ci # _markers -- the marks in the audio file 2967db96d56Sopenharmony_ci # available through the getmarkers() and getmark() 2977db96d56Sopenharmony_ci # methods 2987db96d56Sopenharmony_ci # _soundpos -- the position in the audio stream 2997db96d56Sopenharmony_ci # available through the tell() method, set through the 3007db96d56Sopenharmony_ci # setpos() method 3017db96d56Sopenharmony_ci # 3027db96d56Sopenharmony_ci # These variables are used internally only: 3037db96d56Sopenharmony_ci # _version -- the AIFF-C version number 3047db96d56Sopenharmony_ci # _decomp -- the decompressor from builtin module cl 3057db96d56Sopenharmony_ci # _comm_chunk_read -- 1 iff the COMM chunk has been read 3067db96d56Sopenharmony_ci # _aifc -- 1 iff reading an AIFF-C file 3077db96d56Sopenharmony_ci # _ssnd_seek_needed -- 1 iff positioned correctly in audio 3087db96d56Sopenharmony_ci # file for readframes() 3097db96d56Sopenharmony_ci # _ssnd_chunk -- instantiation of a chunk class for the SSND chunk 3107db96d56Sopenharmony_ci # _framesize -- size of one frame in the file 3117db96d56Sopenharmony_ci 3127db96d56Sopenharmony_ci _file = None # Set here since __del__ checks it 3137db96d56Sopenharmony_ci 3147db96d56Sopenharmony_ci def initfp(self, file): 3157db96d56Sopenharmony_ci self._version = 0 3167db96d56Sopenharmony_ci self._convert = None 3177db96d56Sopenharmony_ci self._markers = [] 3187db96d56Sopenharmony_ci self._soundpos = 0 3197db96d56Sopenharmony_ci self._file = file 3207db96d56Sopenharmony_ci chunk = Chunk(file) 3217db96d56Sopenharmony_ci if chunk.getname() != b'FORM': 3227db96d56Sopenharmony_ci raise Error('file does not start with FORM id') 3237db96d56Sopenharmony_ci formdata = chunk.read(4) 3247db96d56Sopenharmony_ci if formdata == b'AIFF': 3257db96d56Sopenharmony_ci self._aifc = 0 3267db96d56Sopenharmony_ci elif formdata == b'AIFC': 3277db96d56Sopenharmony_ci self._aifc = 1 3287db96d56Sopenharmony_ci else: 3297db96d56Sopenharmony_ci raise Error('not an AIFF or AIFF-C file') 3307db96d56Sopenharmony_ci self._comm_chunk_read = 0 3317db96d56Sopenharmony_ci self._ssnd_chunk = None 3327db96d56Sopenharmony_ci while 1: 3337db96d56Sopenharmony_ci self._ssnd_seek_needed = 1 3347db96d56Sopenharmony_ci try: 3357db96d56Sopenharmony_ci chunk = Chunk(self._file) 3367db96d56Sopenharmony_ci except EOFError: 3377db96d56Sopenharmony_ci break 3387db96d56Sopenharmony_ci chunkname = chunk.getname() 3397db96d56Sopenharmony_ci if chunkname == b'COMM': 3407db96d56Sopenharmony_ci self._read_comm_chunk(chunk) 3417db96d56Sopenharmony_ci self._comm_chunk_read = 1 3427db96d56Sopenharmony_ci elif chunkname == b'SSND': 3437db96d56Sopenharmony_ci self._ssnd_chunk = chunk 3447db96d56Sopenharmony_ci dummy = chunk.read(8) 3457db96d56Sopenharmony_ci self._ssnd_seek_needed = 0 3467db96d56Sopenharmony_ci elif chunkname == b'FVER': 3477db96d56Sopenharmony_ci self._version = _read_ulong(chunk) 3487db96d56Sopenharmony_ci elif chunkname == b'MARK': 3497db96d56Sopenharmony_ci self._readmark(chunk) 3507db96d56Sopenharmony_ci chunk.skip() 3517db96d56Sopenharmony_ci if not self._comm_chunk_read or not self._ssnd_chunk: 3527db96d56Sopenharmony_ci raise Error('COMM chunk and/or SSND chunk missing') 3537db96d56Sopenharmony_ci 3547db96d56Sopenharmony_ci def __init__(self, f): 3557db96d56Sopenharmony_ci if isinstance(f, str): 3567db96d56Sopenharmony_ci file_object = builtins.open(f, 'rb') 3577db96d56Sopenharmony_ci try: 3587db96d56Sopenharmony_ci self.initfp(file_object) 3597db96d56Sopenharmony_ci except: 3607db96d56Sopenharmony_ci file_object.close() 3617db96d56Sopenharmony_ci raise 3627db96d56Sopenharmony_ci else: 3637db96d56Sopenharmony_ci # assume it is an open file object already 3647db96d56Sopenharmony_ci self.initfp(f) 3657db96d56Sopenharmony_ci 3667db96d56Sopenharmony_ci def __enter__(self): 3677db96d56Sopenharmony_ci return self 3687db96d56Sopenharmony_ci 3697db96d56Sopenharmony_ci def __exit__(self, *args): 3707db96d56Sopenharmony_ci self.close() 3717db96d56Sopenharmony_ci 3727db96d56Sopenharmony_ci # 3737db96d56Sopenharmony_ci # User visible methods. 3747db96d56Sopenharmony_ci # 3757db96d56Sopenharmony_ci def getfp(self): 3767db96d56Sopenharmony_ci return self._file 3777db96d56Sopenharmony_ci 3787db96d56Sopenharmony_ci def rewind(self): 3797db96d56Sopenharmony_ci self._ssnd_seek_needed = 1 3807db96d56Sopenharmony_ci self._soundpos = 0 3817db96d56Sopenharmony_ci 3827db96d56Sopenharmony_ci def close(self): 3837db96d56Sopenharmony_ci file = self._file 3847db96d56Sopenharmony_ci if file is not None: 3857db96d56Sopenharmony_ci self._file = None 3867db96d56Sopenharmony_ci file.close() 3877db96d56Sopenharmony_ci 3887db96d56Sopenharmony_ci def tell(self): 3897db96d56Sopenharmony_ci return self._soundpos 3907db96d56Sopenharmony_ci 3917db96d56Sopenharmony_ci def getnchannels(self): 3927db96d56Sopenharmony_ci return self._nchannels 3937db96d56Sopenharmony_ci 3947db96d56Sopenharmony_ci def getnframes(self): 3957db96d56Sopenharmony_ci return self._nframes 3967db96d56Sopenharmony_ci 3977db96d56Sopenharmony_ci def getsampwidth(self): 3987db96d56Sopenharmony_ci return self._sampwidth 3997db96d56Sopenharmony_ci 4007db96d56Sopenharmony_ci def getframerate(self): 4017db96d56Sopenharmony_ci return self._framerate 4027db96d56Sopenharmony_ci 4037db96d56Sopenharmony_ci def getcomptype(self): 4047db96d56Sopenharmony_ci return self._comptype 4057db96d56Sopenharmony_ci 4067db96d56Sopenharmony_ci def getcompname(self): 4077db96d56Sopenharmony_ci return self._compname 4087db96d56Sopenharmony_ci 4097db96d56Sopenharmony_ci## def getversion(self): 4107db96d56Sopenharmony_ci## return self._version 4117db96d56Sopenharmony_ci 4127db96d56Sopenharmony_ci def getparams(self): 4137db96d56Sopenharmony_ci return _aifc_params(self.getnchannels(), self.getsampwidth(), 4147db96d56Sopenharmony_ci self.getframerate(), self.getnframes(), 4157db96d56Sopenharmony_ci self.getcomptype(), self.getcompname()) 4167db96d56Sopenharmony_ci 4177db96d56Sopenharmony_ci def getmarkers(self): 4187db96d56Sopenharmony_ci if len(self._markers) == 0: 4197db96d56Sopenharmony_ci return None 4207db96d56Sopenharmony_ci return self._markers 4217db96d56Sopenharmony_ci 4227db96d56Sopenharmony_ci def getmark(self, id): 4237db96d56Sopenharmony_ci for marker in self._markers: 4247db96d56Sopenharmony_ci if id == marker[0]: 4257db96d56Sopenharmony_ci return marker 4267db96d56Sopenharmony_ci raise Error('marker {0!r} does not exist'.format(id)) 4277db96d56Sopenharmony_ci 4287db96d56Sopenharmony_ci def setpos(self, pos): 4297db96d56Sopenharmony_ci if pos < 0 or pos > self._nframes: 4307db96d56Sopenharmony_ci raise Error('position not in range') 4317db96d56Sopenharmony_ci self._soundpos = pos 4327db96d56Sopenharmony_ci self._ssnd_seek_needed = 1 4337db96d56Sopenharmony_ci 4347db96d56Sopenharmony_ci def readframes(self, nframes): 4357db96d56Sopenharmony_ci if self._ssnd_seek_needed: 4367db96d56Sopenharmony_ci self._ssnd_chunk.seek(0) 4377db96d56Sopenharmony_ci dummy = self._ssnd_chunk.read(8) 4387db96d56Sopenharmony_ci pos = self._soundpos * self._framesize 4397db96d56Sopenharmony_ci if pos: 4407db96d56Sopenharmony_ci self._ssnd_chunk.seek(pos + 8) 4417db96d56Sopenharmony_ci self._ssnd_seek_needed = 0 4427db96d56Sopenharmony_ci if nframes == 0: 4437db96d56Sopenharmony_ci return b'' 4447db96d56Sopenharmony_ci data = self._ssnd_chunk.read(nframes * self._framesize) 4457db96d56Sopenharmony_ci if self._convert and data: 4467db96d56Sopenharmony_ci data = self._convert(data) 4477db96d56Sopenharmony_ci self._soundpos = self._soundpos + len(data) // (self._nchannels 4487db96d56Sopenharmony_ci * self._sampwidth) 4497db96d56Sopenharmony_ci return data 4507db96d56Sopenharmony_ci 4517db96d56Sopenharmony_ci # 4527db96d56Sopenharmony_ci # Internal methods. 4537db96d56Sopenharmony_ci # 4547db96d56Sopenharmony_ci 4557db96d56Sopenharmony_ci def _alaw2lin(self, data): 4567db96d56Sopenharmony_ci with warnings.catch_warnings(): 4577db96d56Sopenharmony_ci warnings.simplefilter('ignore', category=DeprecationWarning) 4587db96d56Sopenharmony_ci import audioop 4597db96d56Sopenharmony_ci return audioop.alaw2lin(data, 2) 4607db96d56Sopenharmony_ci 4617db96d56Sopenharmony_ci def _ulaw2lin(self, data): 4627db96d56Sopenharmony_ci with warnings.catch_warnings(): 4637db96d56Sopenharmony_ci warnings.simplefilter('ignore', category=DeprecationWarning) 4647db96d56Sopenharmony_ci import audioop 4657db96d56Sopenharmony_ci return audioop.ulaw2lin(data, 2) 4667db96d56Sopenharmony_ci 4677db96d56Sopenharmony_ci def _adpcm2lin(self, data): 4687db96d56Sopenharmony_ci with warnings.catch_warnings(): 4697db96d56Sopenharmony_ci warnings.simplefilter('ignore', category=DeprecationWarning) 4707db96d56Sopenharmony_ci import audioop 4717db96d56Sopenharmony_ci if not hasattr(self, '_adpcmstate'): 4727db96d56Sopenharmony_ci # first time 4737db96d56Sopenharmony_ci self._adpcmstate = None 4747db96d56Sopenharmony_ci data, self._adpcmstate = audioop.adpcm2lin(data, 2, self._adpcmstate) 4757db96d56Sopenharmony_ci return data 4767db96d56Sopenharmony_ci 4777db96d56Sopenharmony_ci def _sowt2lin(self, data): 4787db96d56Sopenharmony_ci with warnings.catch_warnings(): 4797db96d56Sopenharmony_ci warnings.simplefilter('ignore', category=DeprecationWarning) 4807db96d56Sopenharmony_ci import audioop 4817db96d56Sopenharmony_ci return audioop.byteswap(data, 2) 4827db96d56Sopenharmony_ci 4837db96d56Sopenharmony_ci def _read_comm_chunk(self, chunk): 4847db96d56Sopenharmony_ci self._nchannels = _read_short(chunk) 4857db96d56Sopenharmony_ci self._nframes = _read_long(chunk) 4867db96d56Sopenharmony_ci self._sampwidth = (_read_short(chunk) + 7) // 8 4877db96d56Sopenharmony_ci self._framerate = int(_read_float(chunk)) 4887db96d56Sopenharmony_ci if self._sampwidth <= 0: 4897db96d56Sopenharmony_ci raise Error('bad sample width') 4907db96d56Sopenharmony_ci if self._nchannels <= 0: 4917db96d56Sopenharmony_ci raise Error('bad # of channels') 4927db96d56Sopenharmony_ci self._framesize = self._nchannels * self._sampwidth 4937db96d56Sopenharmony_ci if self._aifc: 4947db96d56Sopenharmony_ci #DEBUG: SGI's soundeditor produces a bad size :-( 4957db96d56Sopenharmony_ci kludge = 0 4967db96d56Sopenharmony_ci if chunk.chunksize == 18: 4977db96d56Sopenharmony_ci kludge = 1 4987db96d56Sopenharmony_ci warnings.warn('Warning: bad COMM chunk size') 4997db96d56Sopenharmony_ci chunk.chunksize = 23 5007db96d56Sopenharmony_ci #DEBUG end 5017db96d56Sopenharmony_ci self._comptype = chunk.read(4) 5027db96d56Sopenharmony_ci #DEBUG start 5037db96d56Sopenharmony_ci if kludge: 5047db96d56Sopenharmony_ci length = ord(chunk.file.read(1)) 5057db96d56Sopenharmony_ci if length & 1 == 0: 5067db96d56Sopenharmony_ci length = length + 1 5077db96d56Sopenharmony_ci chunk.chunksize = chunk.chunksize + length 5087db96d56Sopenharmony_ci chunk.file.seek(-1, 1) 5097db96d56Sopenharmony_ci #DEBUG end 5107db96d56Sopenharmony_ci self._compname = _read_string(chunk) 5117db96d56Sopenharmony_ci if self._comptype != b'NONE': 5127db96d56Sopenharmony_ci if self._comptype == b'G722': 5137db96d56Sopenharmony_ci self._convert = self._adpcm2lin 5147db96d56Sopenharmony_ci elif self._comptype in (b'ulaw', b'ULAW'): 5157db96d56Sopenharmony_ci self._convert = self._ulaw2lin 5167db96d56Sopenharmony_ci elif self._comptype in (b'alaw', b'ALAW'): 5177db96d56Sopenharmony_ci self._convert = self._alaw2lin 5187db96d56Sopenharmony_ci elif self._comptype in (b'sowt', b'SOWT'): 5197db96d56Sopenharmony_ci self._convert = self._sowt2lin 5207db96d56Sopenharmony_ci else: 5217db96d56Sopenharmony_ci raise Error('unsupported compression type') 5227db96d56Sopenharmony_ci self._sampwidth = 2 5237db96d56Sopenharmony_ci else: 5247db96d56Sopenharmony_ci self._comptype = b'NONE' 5257db96d56Sopenharmony_ci self._compname = b'not compressed' 5267db96d56Sopenharmony_ci 5277db96d56Sopenharmony_ci def _readmark(self, chunk): 5287db96d56Sopenharmony_ci nmarkers = _read_short(chunk) 5297db96d56Sopenharmony_ci # Some files appear to contain invalid counts. 5307db96d56Sopenharmony_ci # Cope with this by testing for EOF. 5317db96d56Sopenharmony_ci try: 5327db96d56Sopenharmony_ci for i in range(nmarkers): 5337db96d56Sopenharmony_ci id = _read_short(chunk) 5347db96d56Sopenharmony_ci pos = _read_long(chunk) 5357db96d56Sopenharmony_ci name = _read_string(chunk) 5367db96d56Sopenharmony_ci if pos or name: 5377db96d56Sopenharmony_ci # some files appear to have 5387db96d56Sopenharmony_ci # dummy markers consisting of 5397db96d56Sopenharmony_ci # a position 0 and name '' 5407db96d56Sopenharmony_ci self._markers.append((id, pos, name)) 5417db96d56Sopenharmony_ci except EOFError: 5427db96d56Sopenharmony_ci w = ('Warning: MARK chunk contains only %s marker%s instead of %s' % 5437db96d56Sopenharmony_ci (len(self._markers), '' if len(self._markers) == 1 else 's', 5447db96d56Sopenharmony_ci nmarkers)) 5457db96d56Sopenharmony_ci warnings.warn(w) 5467db96d56Sopenharmony_ci 5477db96d56Sopenharmony_ciclass Aifc_write: 5487db96d56Sopenharmony_ci # Variables used in this class: 5497db96d56Sopenharmony_ci # 5507db96d56Sopenharmony_ci # These variables are user settable through appropriate methods 5517db96d56Sopenharmony_ci # of this class: 5527db96d56Sopenharmony_ci # _file -- the open file with methods write(), close(), tell(), seek() 5537db96d56Sopenharmony_ci # set through the __init__() method 5547db96d56Sopenharmony_ci # _comptype -- the AIFF-C compression type ('NONE' in AIFF) 5557db96d56Sopenharmony_ci # set through the setcomptype() or setparams() method 5567db96d56Sopenharmony_ci # _compname -- the human-readable AIFF-C compression type 5577db96d56Sopenharmony_ci # set through the setcomptype() or setparams() method 5587db96d56Sopenharmony_ci # _nchannels -- the number of audio channels 5597db96d56Sopenharmony_ci # set through the setnchannels() or setparams() method 5607db96d56Sopenharmony_ci # _sampwidth -- the number of bytes per audio sample 5617db96d56Sopenharmony_ci # set through the setsampwidth() or setparams() method 5627db96d56Sopenharmony_ci # _framerate -- the sampling frequency 5637db96d56Sopenharmony_ci # set through the setframerate() or setparams() method 5647db96d56Sopenharmony_ci # _nframes -- the number of audio frames written to the header 5657db96d56Sopenharmony_ci # set through the setnframes() or setparams() method 5667db96d56Sopenharmony_ci # _aifc -- whether we're writing an AIFF-C file or an AIFF file 5677db96d56Sopenharmony_ci # set through the aifc() method, reset through the 5687db96d56Sopenharmony_ci # aiff() method 5697db96d56Sopenharmony_ci # 5707db96d56Sopenharmony_ci # These variables are used internally only: 5717db96d56Sopenharmony_ci # _version -- the AIFF-C version number 5727db96d56Sopenharmony_ci # _comp -- the compressor from builtin module cl 5737db96d56Sopenharmony_ci # _nframeswritten -- the number of audio frames actually written 5747db96d56Sopenharmony_ci # _datalength -- the size of the audio samples written to the header 5757db96d56Sopenharmony_ci # _datawritten -- the size of the audio samples actually written 5767db96d56Sopenharmony_ci 5777db96d56Sopenharmony_ci _file = None # Set here since __del__ checks it 5787db96d56Sopenharmony_ci 5797db96d56Sopenharmony_ci def __init__(self, f): 5807db96d56Sopenharmony_ci if isinstance(f, str): 5817db96d56Sopenharmony_ci file_object = builtins.open(f, 'wb') 5827db96d56Sopenharmony_ci try: 5837db96d56Sopenharmony_ci self.initfp(file_object) 5847db96d56Sopenharmony_ci except: 5857db96d56Sopenharmony_ci file_object.close() 5867db96d56Sopenharmony_ci raise 5877db96d56Sopenharmony_ci 5887db96d56Sopenharmony_ci # treat .aiff file extensions as non-compressed audio 5897db96d56Sopenharmony_ci if f.endswith('.aiff'): 5907db96d56Sopenharmony_ci self._aifc = 0 5917db96d56Sopenharmony_ci else: 5927db96d56Sopenharmony_ci # assume it is an open file object already 5937db96d56Sopenharmony_ci self.initfp(f) 5947db96d56Sopenharmony_ci 5957db96d56Sopenharmony_ci def initfp(self, file): 5967db96d56Sopenharmony_ci self._file = file 5977db96d56Sopenharmony_ci self._version = _AIFC_version 5987db96d56Sopenharmony_ci self._comptype = b'NONE' 5997db96d56Sopenharmony_ci self._compname = b'not compressed' 6007db96d56Sopenharmony_ci self._convert = None 6017db96d56Sopenharmony_ci self._nchannels = 0 6027db96d56Sopenharmony_ci self._sampwidth = 0 6037db96d56Sopenharmony_ci self._framerate = 0 6047db96d56Sopenharmony_ci self._nframes = 0 6057db96d56Sopenharmony_ci self._nframeswritten = 0 6067db96d56Sopenharmony_ci self._datawritten = 0 6077db96d56Sopenharmony_ci self._datalength = 0 6087db96d56Sopenharmony_ci self._markers = [] 6097db96d56Sopenharmony_ci self._marklength = 0 6107db96d56Sopenharmony_ci self._aifc = 1 # AIFF-C is default 6117db96d56Sopenharmony_ci 6127db96d56Sopenharmony_ci def __del__(self): 6137db96d56Sopenharmony_ci self.close() 6147db96d56Sopenharmony_ci 6157db96d56Sopenharmony_ci def __enter__(self): 6167db96d56Sopenharmony_ci return self 6177db96d56Sopenharmony_ci 6187db96d56Sopenharmony_ci def __exit__(self, *args): 6197db96d56Sopenharmony_ci self.close() 6207db96d56Sopenharmony_ci 6217db96d56Sopenharmony_ci # 6227db96d56Sopenharmony_ci # User visible methods. 6237db96d56Sopenharmony_ci # 6247db96d56Sopenharmony_ci def aiff(self): 6257db96d56Sopenharmony_ci if self._nframeswritten: 6267db96d56Sopenharmony_ci raise Error('cannot change parameters after starting to write') 6277db96d56Sopenharmony_ci self._aifc = 0 6287db96d56Sopenharmony_ci 6297db96d56Sopenharmony_ci def aifc(self): 6307db96d56Sopenharmony_ci if self._nframeswritten: 6317db96d56Sopenharmony_ci raise Error('cannot change parameters after starting to write') 6327db96d56Sopenharmony_ci self._aifc = 1 6337db96d56Sopenharmony_ci 6347db96d56Sopenharmony_ci def setnchannels(self, nchannels): 6357db96d56Sopenharmony_ci if self._nframeswritten: 6367db96d56Sopenharmony_ci raise Error('cannot change parameters after starting to write') 6377db96d56Sopenharmony_ci if nchannels < 1: 6387db96d56Sopenharmony_ci raise Error('bad # of channels') 6397db96d56Sopenharmony_ci self._nchannels = nchannels 6407db96d56Sopenharmony_ci 6417db96d56Sopenharmony_ci def getnchannels(self): 6427db96d56Sopenharmony_ci if not self._nchannels: 6437db96d56Sopenharmony_ci raise Error('number of channels not set') 6447db96d56Sopenharmony_ci return self._nchannels 6457db96d56Sopenharmony_ci 6467db96d56Sopenharmony_ci def setsampwidth(self, sampwidth): 6477db96d56Sopenharmony_ci if self._nframeswritten: 6487db96d56Sopenharmony_ci raise Error('cannot change parameters after starting to write') 6497db96d56Sopenharmony_ci if sampwidth < 1 or sampwidth > 4: 6507db96d56Sopenharmony_ci raise Error('bad sample width') 6517db96d56Sopenharmony_ci self._sampwidth = sampwidth 6527db96d56Sopenharmony_ci 6537db96d56Sopenharmony_ci def getsampwidth(self): 6547db96d56Sopenharmony_ci if not self._sampwidth: 6557db96d56Sopenharmony_ci raise Error('sample width not set') 6567db96d56Sopenharmony_ci return self._sampwidth 6577db96d56Sopenharmony_ci 6587db96d56Sopenharmony_ci def setframerate(self, framerate): 6597db96d56Sopenharmony_ci if self._nframeswritten: 6607db96d56Sopenharmony_ci raise Error('cannot change parameters after starting to write') 6617db96d56Sopenharmony_ci if framerate <= 0: 6627db96d56Sopenharmony_ci raise Error('bad frame rate') 6637db96d56Sopenharmony_ci self._framerate = framerate 6647db96d56Sopenharmony_ci 6657db96d56Sopenharmony_ci def getframerate(self): 6667db96d56Sopenharmony_ci if not self._framerate: 6677db96d56Sopenharmony_ci raise Error('frame rate not set') 6687db96d56Sopenharmony_ci return self._framerate 6697db96d56Sopenharmony_ci 6707db96d56Sopenharmony_ci def setnframes(self, nframes): 6717db96d56Sopenharmony_ci if self._nframeswritten: 6727db96d56Sopenharmony_ci raise Error('cannot change parameters after starting to write') 6737db96d56Sopenharmony_ci self._nframes = nframes 6747db96d56Sopenharmony_ci 6757db96d56Sopenharmony_ci def getnframes(self): 6767db96d56Sopenharmony_ci return self._nframeswritten 6777db96d56Sopenharmony_ci 6787db96d56Sopenharmony_ci def setcomptype(self, comptype, compname): 6797db96d56Sopenharmony_ci if self._nframeswritten: 6807db96d56Sopenharmony_ci raise Error('cannot change parameters after starting to write') 6817db96d56Sopenharmony_ci if comptype not in (b'NONE', b'ulaw', b'ULAW', 6827db96d56Sopenharmony_ci b'alaw', b'ALAW', b'G722', b'sowt', b'SOWT'): 6837db96d56Sopenharmony_ci raise Error('unsupported compression type') 6847db96d56Sopenharmony_ci self._comptype = comptype 6857db96d56Sopenharmony_ci self._compname = compname 6867db96d56Sopenharmony_ci 6877db96d56Sopenharmony_ci def getcomptype(self): 6887db96d56Sopenharmony_ci return self._comptype 6897db96d56Sopenharmony_ci 6907db96d56Sopenharmony_ci def getcompname(self): 6917db96d56Sopenharmony_ci return self._compname 6927db96d56Sopenharmony_ci 6937db96d56Sopenharmony_ci## def setversion(self, version): 6947db96d56Sopenharmony_ci## if self._nframeswritten: 6957db96d56Sopenharmony_ci## raise Error, 'cannot change parameters after starting to write' 6967db96d56Sopenharmony_ci## self._version = version 6977db96d56Sopenharmony_ci 6987db96d56Sopenharmony_ci def setparams(self, params): 6997db96d56Sopenharmony_ci nchannels, sampwidth, framerate, nframes, comptype, compname = params 7007db96d56Sopenharmony_ci if self._nframeswritten: 7017db96d56Sopenharmony_ci raise Error('cannot change parameters after starting to write') 7027db96d56Sopenharmony_ci if comptype not in (b'NONE', b'ulaw', b'ULAW', 7037db96d56Sopenharmony_ci b'alaw', b'ALAW', b'G722', b'sowt', b'SOWT'): 7047db96d56Sopenharmony_ci raise Error('unsupported compression type') 7057db96d56Sopenharmony_ci self.setnchannels(nchannels) 7067db96d56Sopenharmony_ci self.setsampwidth(sampwidth) 7077db96d56Sopenharmony_ci self.setframerate(framerate) 7087db96d56Sopenharmony_ci self.setnframes(nframes) 7097db96d56Sopenharmony_ci self.setcomptype(comptype, compname) 7107db96d56Sopenharmony_ci 7117db96d56Sopenharmony_ci def getparams(self): 7127db96d56Sopenharmony_ci if not self._nchannels or not self._sampwidth or not self._framerate: 7137db96d56Sopenharmony_ci raise Error('not all parameters set') 7147db96d56Sopenharmony_ci return _aifc_params(self._nchannels, self._sampwidth, self._framerate, 7157db96d56Sopenharmony_ci self._nframes, self._comptype, self._compname) 7167db96d56Sopenharmony_ci 7177db96d56Sopenharmony_ci def setmark(self, id, pos, name): 7187db96d56Sopenharmony_ci if id <= 0: 7197db96d56Sopenharmony_ci raise Error('marker ID must be > 0') 7207db96d56Sopenharmony_ci if pos < 0: 7217db96d56Sopenharmony_ci raise Error('marker position must be >= 0') 7227db96d56Sopenharmony_ci if not isinstance(name, bytes): 7237db96d56Sopenharmony_ci raise Error('marker name must be bytes') 7247db96d56Sopenharmony_ci for i in range(len(self._markers)): 7257db96d56Sopenharmony_ci if id == self._markers[i][0]: 7267db96d56Sopenharmony_ci self._markers[i] = id, pos, name 7277db96d56Sopenharmony_ci return 7287db96d56Sopenharmony_ci self._markers.append((id, pos, name)) 7297db96d56Sopenharmony_ci 7307db96d56Sopenharmony_ci def getmark(self, id): 7317db96d56Sopenharmony_ci for marker in self._markers: 7327db96d56Sopenharmony_ci if id == marker[0]: 7337db96d56Sopenharmony_ci return marker 7347db96d56Sopenharmony_ci raise Error('marker {0!r} does not exist'.format(id)) 7357db96d56Sopenharmony_ci 7367db96d56Sopenharmony_ci def getmarkers(self): 7377db96d56Sopenharmony_ci if len(self._markers) == 0: 7387db96d56Sopenharmony_ci return None 7397db96d56Sopenharmony_ci return self._markers 7407db96d56Sopenharmony_ci 7417db96d56Sopenharmony_ci def tell(self): 7427db96d56Sopenharmony_ci return self._nframeswritten 7437db96d56Sopenharmony_ci 7447db96d56Sopenharmony_ci def writeframesraw(self, data): 7457db96d56Sopenharmony_ci if not isinstance(data, (bytes, bytearray)): 7467db96d56Sopenharmony_ci data = memoryview(data).cast('B') 7477db96d56Sopenharmony_ci self._ensure_header_written(len(data)) 7487db96d56Sopenharmony_ci nframes = len(data) // (self._sampwidth * self._nchannels) 7497db96d56Sopenharmony_ci if self._convert: 7507db96d56Sopenharmony_ci data = self._convert(data) 7517db96d56Sopenharmony_ci self._file.write(data) 7527db96d56Sopenharmony_ci self._nframeswritten = self._nframeswritten + nframes 7537db96d56Sopenharmony_ci self._datawritten = self._datawritten + len(data) 7547db96d56Sopenharmony_ci 7557db96d56Sopenharmony_ci def writeframes(self, data): 7567db96d56Sopenharmony_ci self.writeframesraw(data) 7577db96d56Sopenharmony_ci if self._nframeswritten != self._nframes or \ 7587db96d56Sopenharmony_ci self._datalength != self._datawritten: 7597db96d56Sopenharmony_ci self._patchheader() 7607db96d56Sopenharmony_ci 7617db96d56Sopenharmony_ci def close(self): 7627db96d56Sopenharmony_ci if self._file is None: 7637db96d56Sopenharmony_ci return 7647db96d56Sopenharmony_ci try: 7657db96d56Sopenharmony_ci self._ensure_header_written(0) 7667db96d56Sopenharmony_ci if self._datawritten & 1: 7677db96d56Sopenharmony_ci # quick pad to even size 7687db96d56Sopenharmony_ci self._file.write(b'\x00') 7697db96d56Sopenharmony_ci self._datawritten = self._datawritten + 1 7707db96d56Sopenharmony_ci self._writemarkers() 7717db96d56Sopenharmony_ci if self._nframeswritten != self._nframes or \ 7727db96d56Sopenharmony_ci self._datalength != self._datawritten or \ 7737db96d56Sopenharmony_ci self._marklength: 7747db96d56Sopenharmony_ci self._patchheader() 7757db96d56Sopenharmony_ci finally: 7767db96d56Sopenharmony_ci # Prevent ref cycles 7777db96d56Sopenharmony_ci self._convert = None 7787db96d56Sopenharmony_ci f = self._file 7797db96d56Sopenharmony_ci self._file = None 7807db96d56Sopenharmony_ci f.close() 7817db96d56Sopenharmony_ci 7827db96d56Sopenharmony_ci # 7837db96d56Sopenharmony_ci # Internal methods. 7847db96d56Sopenharmony_ci # 7857db96d56Sopenharmony_ci 7867db96d56Sopenharmony_ci def _lin2alaw(self, data): 7877db96d56Sopenharmony_ci with warnings.catch_warnings(): 7887db96d56Sopenharmony_ci warnings.simplefilter('ignore', category=DeprecationWarning) 7897db96d56Sopenharmony_ci import audioop 7907db96d56Sopenharmony_ci return audioop.lin2alaw(data, 2) 7917db96d56Sopenharmony_ci 7927db96d56Sopenharmony_ci def _lin2ulaw(self, data): 7937db96d56Sopenharmony_ci with warnings.catch_warnings(): 7947db96d56Sopenharmony_ci warnings.simplefilter('ignore', category=DeprecationWarning) 7957db96d56Sopenharmony_ci import audioop 7967db96d56Sopenharmony_ci return audioop.lin2ulaw(data, 2) 7977db96d56Sopenharmony_ci 7987db96d56Sopenharmony_ci def _lin2adpcm(self, data): 7997db96d56Sopenharmony_ci with warnings.catch_warnings(): 8007db96d56Sopenharmony_ci warnings.simplefilter('ignore', category=DeprecationWarning) 8017db96d56Sopenharmony_ci import audioop 8027db96d56Sopenharmony_ci if not hasattr(self, '_adpcmstate'): 8037db96d56Sopenharmony_ci self._adpcmstate = None 8047db96d56Sopenharmony_ci data, self._adpcmstate = audioop.lin2adpcm(data, 2, self._adpcmstate) 8057db96d56Sopenharmony_ci return data 8067db96d56Sopenharmony_ci 8077db96d56Sopenharmony_ci def _lin2sowt(self, data): 8087db96d56Sopenharmony_ci with warnings.catch_warnings(): 8097db96d56Sopenharmony_ci warnings.simplefilter('ignore', category=DeprecationWarning) 8107db96d56Sopenharmony_ci import audioop 8117db96d56Sopenharmony_ci return audioop.byteswap(data, 2) 8127db96d56Sopenharmony_ci 8137db96d56Sopenharmony_ci def _ensure_header_written(self, datasize): 8147db96d56Sopenharmony_ci if not self._nframeswritten: 8157db96d56Sopenharmony_ci if self._comptype in (b'ULAW', b'ulaw', 8167db96d56Sopenharmony_ci b'ALAW', b'alaw', b'G722', 8177db96d56Sopenharmony_ci b'sowt', b'SOWT'): 8187db96d56Sopenharmony_ci if not self._sampwidth: 8197db96d56Sopenharmony_ci self._sampwidth = 2 8207db96d56Sopenharmony_ci if self._sampwidth != 2: 8217db96d56Sopenharmony_ci raise Error('sample width must be 2 when compressing ' 8227db96d56Sopenharmony_ci 'with ulaw/ULAW, alaw/ALAW, sowt/SOWT ' 8237db96d56Sopenharmony_ci 'or G7.22 (ADPCM)') 8247db96d56Sopenharmony_ci if not self._nchannels: 8257db96d56Sopenharmony_ci raise Error('# channels not specified') 8267db96d56Sopenharmony_ci if not self._sampwidth: 8277db96d56Sopenharmony_ci raise Error('sample width not specified') 8287db96d56Sopenharmony_ci if not self._framerate: 8297db96d56Sopenharmony_ci raise Error('sampling rate not specified') 8307db96d56Sopenharmony_ci self._write_header(datasize) 8317db96d56Sopenharmony_ci 8327db96d56Sopenharmony_ci def _init_compression(self): 8337db96d56Sopenharmony_ci if self._comptype == b'G722': 8347db96d56Sopenharmony_ci self._convert = self._lin2adpcm 8357db96d56Sopenharmony_ci elif self._comptype in (b'ulaw', b'ULAW'): 8367db96d56Sopenharmony_ci self._convert = self._lin2ulaw 8377db96d56Sopenharmony_ci elif self._comptype in (b'alaw', b'ALAW'): 8387db96d56Sopenharmony_ci self._convert = self._lin2alaw 8397db96d56Sopenharmony_ci elif self._comptype in (b'sowt', b'SOWT'): 8407db96d56Sopenharmony_ci self._convert = self._lin2sowt 8417db96d56Sopenharmony_ci 8427db96d56Sopenharmony_ci def _write_header(self, initlength): 8437db96d56Sopenharmony_ci if self._aifc and self._comptype != b'NONE': 8447db96d56Sopenharmony_ci self._init_compression() 8457db96d56Sopenharmony_ci self._file.write(b'FORM') 8467db96d56Sopenharmony_ci if not self._nframes: 8477db96d56Sopenharmony_ci self._nframes = initlength // (self._nchannels * self._sampwidth) 8487db96d56Sopenharmony_ci self._datalength = self._nframes * self._nchannels * self._sampwidth 8497db96d56Sopenharmony_ci if self._datalength & 1: 8507db96d56Sopenharmony_ci self._datalength = self._datalength + 1 8517db96d56Sopenharmony_ci if self._aifc: 8527db96d56Sopenharmony_ci if self._comptype in (b'ulaw', b'ULAW', b'alaw', b'ALAW'): 8537db96d56Sopenharmony_ci self._datalength = self._datalength // 2 8547db96d56Sopenharmony_ci if self._datalength & 1: 8557db96d56Sopenharmony_ci self._datalength = self._datalength + 1 8567db96d56Sopenharmony_ci elif self._comptype == b'G722': 8577db96d56Sopenharmony_ci self._datalength = (self._datalength + 3) // 4 8587db96d56Sopenharmony_ci if self._datalength & 1: 8597db96d56Sopenharmony_ci self._datalength = self._datalength + 1 8607db96d56Sopenharmony_ci try: 8617db96d56Sopenharmony_ci self._form_length_pos = self._file.tell() 8627db96d56Sopenharmony_ci except (AttributeError, OSError): 8637db96d56Sopenharmony_ci self._form_length_pos = None 8647db96d56Sopenharmony_ci commlength = self._write_form_length(self._datalength) 8657db96d56Sopenharmony_ci if self._aifc: 8667db96d56Sopenharmony_ci self._file.write(b'AIFC') 8677db96d56Sopenharmony_ci self._file.write(b'FVER') 8687db96d56Sopenharmony_ci _write_ulong(self._file, 4) 8697db96d56Sopenharmony_ci _write_ulong(self._file, self._version) 8707db96d56Sopenharmony_ci else: 8717db96d56Sopenharmony_ci self._file.write(b'AIFF') 8727db96d56Sopenharmony_ci self._file.write(b'COMM') 8737db96d56Sopenharmony_ci _write_ulong(self._file, commlength) 8747db96d56Sopenharmony_ci _write_short(self._file, self._nchannels) 8757db96d56Sopenharmony_ci if self._form_length_pos is not None: 8767db96d56Sopenharmony_ci self._nframes_pos = self._file.tell() 8777db96d56Sopenharmony_ci _write_ulong(self._file, self._nframes) 8787db96d56Sopenharmony_ci if self._comptype in (b'ULAW', b'ulaw', b'ALAW', b'alaw', b'G722'): 8797db96d56Sopenharmony_ci _write_short(self._file, 8) 8807db96d56Sopenharmony_ci else: 8817db96d56Sopenharmony_ci _write_short(self._file, self._sampwidth * 8) 8827db96d56Sopenharmony_ci _write_float(self._file, self._framerate) 8837db96d56Sopenharmony_ci if self._aifc: 8847db96d56Sopenharmony_ci self._file.write(self._comptype) 8857db96d56Sopenharmony_ci _write_string(self._file, self._compname) 8867db96d56Sopenharmony_ci self._file.write(b'SSND') 8877db96d56Sopenharmony_ci if self._form_length_pos is not None: 8887db96d56Sopenharmony_ci self._ssnd_length_pos = self._file.tell() 8897db96d56Sopenharmony_ci _write_ulong(self._file, self._datalength + 8) 8907db96d56Sopenharmony_ci _write_ulong(self._file, 0) 8917db96d56Sopenharmony_ci _write_ulong(self._file, 0) 8927db96d56Sopenharmony_ci 8937db96d56Sopenharmony_ci def _write_form_length(self, datalength): 8947db96d56Sopenharmony_ci if self._aifc: 8957db96d56Sopenharmony_ci commlength = 18 + 5 + len(self._compname) 8967db96d56Sopenharmony_ci if commlength & 1: 8977db96d56Sopenharmony_ci commlength = commlength + 1 8987db96d56Sopenharmony_ci verslength = 12 8997db96d56Sopenharmony_ci else: 9007db96d56Sopenharmony_ci commlength = 18 9017db96d56Sopenharmony_ci verslength = 0 9027db96d56Sopenharmony_ci _write_ulong(self._file, 4 + verslength + self._marklength + \ 9037db96d56Sopenharmony_ci 8 + commlength + 16 + datalength) 9047db96d56Sopenharmony_ci return commlength 9057db96d56Sopenharmony_ci 9067db96d56Sopenharmony_ci def _patchheader(self): 9077db96d56Sopenharmony_ci curpos = self._file.tell() 9087db96d56Sopenharmony_ci if self._datawritten & 1: 9097db96d56Sopenharmony_ci datalength = self._datawritten + 1 9107db96d56Sopenharmony_ci self._file.write(b'\x00') 9117db96d56Sopenharmony_ci else: 9127db96d56Sopenharmony_ci datalength = self._datawritten 9137db96d56Sopenharmony_ci if datalength == self._datalength and \ 9147db96d56Sopenharmony_ci self._nframes == self._nframeswritten and \ 9157db96d56Sopenharmony_ci self._marklength == 0: 9167db96d56Sopenharmony_ci self._file.seek(curpos, 0) 9177db96d56Sopenharmony_ci return 9187db96d56Sopenharmony_ci self._file.seek(self._form_length_pos, 0) 9197db96d56Sopenharmony_ci dummy = self._write_form_length(datalength) 9207db96d56Sopenharmony_ci self._file.seek(self._nframes_pos, 0) 9217db96d56Sopenharmony_ci _write_ulong(self._file, self._nframeswritten) 9227db96d56Sopenharmony_ci self._file.seek(self._ssnd_length_pos, 0) 9237db96d56Sopenharmony_ci _write_ulong(self._file, datalength + 8) 9247db96d56Sopenharmony_ci self._file.seek(curpos, 0) 9257db96d56Sopenharmony_ci self._nframes = self._nframeswritten 9267db96d56Sopenharmony_ci self._datalength = datalength 9277db96d56Sopenharmony_ci 9287db96d56Sopenharmony_ci def _writemarkers(self): 9297db96d56Sopenharmony_ci if len(self._markers) == 0: 9307db96d56Sopenharmony_ci return 9317db96d56Sopenharmony_ci self._file.write(b'MARK') 9327db96d56Sopenharmony_ci length = 2 9337db96d56Sopenharmony_ci for marker in self._markers: 9347db96d56Sopenharmony_ci id, pos, name = marker 9357db96d56Sopenharmony_ci length = length + len(name) + 1 + 6 9367db96d56Sopenharmony_ci if len(name) & 1 == 0: 9377db96d56Sopenharmony_ci length = length + 1 9387db96d56Sopenharmony_ci _write_ulong(self._file, length) 9397db96d56Sopenharmony_ci self._marklength = length + 8 9407db96d56Sopenharmony_ci _write_short(self._file, len(self._markers)) 9417db96d56Sopenharmony_ci for marker in self._markers: 9427db96d56Sopenharmony_ci id, pos, name = marker 9437db96d56Sopenharmony_ci _write_short(self._file, id) 9447db96d56Sopenharmony_ci _write_ulong(self._file, pos) 9457db96d56Sopenharmony_ci _write_string(self._file, name) 9467db96d56Sopenharmony_ci 9477db96d56Sopenharmony_cidef open(f, mode=None): 9487db96d56Sopenharmony_ci if mode is None: 9497db96d56Sopenharmony_ci if hasattr(f, 'mode'): 9507db96d56Sopenharmony_ci mode = f.mode 9517db96d56Sopenharmony_ci else: 9527db96d56Sopenharmony_ci mode = 'rb' 9537db96d56Sopenharmony_ci if mode in ('r', 'rb'): 9547db96d56Sopenharmony_ci return Aifc_read(f) 9557db96d56Sopenharmony_ci elif mode in ('w', 'wb'): 9567db96d56Sopenharmony_ci return Aifc_write(f) 9577db96d56Sopenharmony_ci else: 9587db96d56Sopenharmony_ci raise Error("mode must be 'r', 'rb', 'w', or 'wb'") 9597db96d56Sopenharmony_ci 9607db96d56Sopenharmony_ci 9617db96d56Sopenharmony_ciif __name__ == '__main__': 9627db96d56Sopenharmony_ci import sys 9637db96d56Sopenharmony_ci if not sys.argv[1:]: 9647db96d56Sopenharmony_ci sys.argv.append('/usr/demos/data/audio/bach.aiff') 9657db96d56Sopenharmony_ci fn = sys.argv[1] 9667db96d56Sopenharmony_ci with open(fn, 'r') as f: 9677db96d56Sopenharmony_ci print("Reading", fn) 9687db96d56Sopenharmony_ci print("nchannels =", f.getnchannels()) 9697db96d56Sopenharmony_ci print("nframes =", f.getnframes()) 9707db96d56Sopenharmony_ci print("sampwidth =", f.getsampwidth()) 9717db96d56Sopenharmony_ci print("framerate =", f.getframerate()) 9727db96d56Sopenharmony_ci print("comptype =", f.getcomptype()) 9737db96d56Sopenharmony_ci print("compname =", f.getcompname()) 9747db96d56Sopenharmony_ci if sys.argv[2:]: 9757db96d56Sopenharmony_ci gn = sys.argv[2] 9767db96d56Sopenharmony_ci print("Writing", gn) 9777db96d56Sopenharmony_ci with open(gn, 'w') as g: 9787db96d56Sopenharmony_ci g.setparams(f.getparams()) 9797db96d56Sopenharmony_ci while 1: 9807db96d56Sopenharmony_ci data = f.readframes(1024) 9817db96d56Sopenharmony_ci if not data: 9827db96d56Sopenharmony_ci break 9837db96d56Sopenharmony_ci g.writeframes(data) 9847db96d56Sopenharmony_ci print("Done.") 985