| 
									
										
										
										
											2000-02-04 15:28:42 +00:00
										 |  |  | """Stuff to parse Sun and NeXT audio files.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-10-06 20:28:46 +00:00
										 |  |  | An audio file consists of a header followed by the data.  The structure | 
					
						
							| 
									
										
										
										
											2000-02-04 15:28:42 +00:00
										 |  |  | of the header is as follows. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         +---------------+ | 
					
						
							|  |  |  |         | magic word    | | 
					
						
							|  |  |  |         +---------------+ | 
					
						
							|  |  |  |         | header size   | | 
					
						
							|  |  |  |         +---------------+ | 
					
						
							|  |  |  |         | data size     | | 
					
						
							|  |  |  |         +---------------+ | 
					
						
							|  |  |  |         | encoding      | | 
					
						
							|  |  |  |         +---------------+ | 
					
						
							|  |  |  |         | sample rate   | | 
					
						
							|  |  |  |         +---------------+ | 
					
						
							|  |  |  |         | # of channels | | 
					
						
							|  |  |  |         +---------------+ | 
					
						
							|  |  |  |         | info          | | 
					
						
							|  |  |  |         |               | | 
					
						
							|  |  |  |         +---------------+ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The magic word consists of the 4 characters '.snd'.  Apart from the | 
					
						
							|  |  |  | info field, all header fields are 4 bytes in size.  They are all | 
					
						
							|  |  |  | 32-bit unsigned integers encoded in big-endian byte order. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The header size really gives the start of the data. | 
					
						
							|  |  |  | The data size is the physical size of the data.  From the other | 
					
						
							| 
									
										
										
										
											2000-10-06 20:28:46 +00:00
										 |  |  | parameters the number of frames can be calculated. | 
					
						
							| 
									
										
										
										
											2000-02-04 15:28:42 +00:00
										 |  |  | The encoding gives the way in which audio samples are encoded. | 
					
						
							|  |  |  | Possible values are listed below. | 
					
						
							|  |  |  | The info field currently consists of an ASCII string giving a | 
					
						
							|  |  |  | human-readable description of the audio file.  The info field is | 
					
						
							|  |  |  | padded with NUL bytes to the header size. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Usage. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Reading audio files: | 
					
						
							|  |  |  |         f = sunau.open(file, 'r') | 
					
						
							|  |  |  | where file is either the name of a file or an open file pointer. | 
					
						
							|  |  |  | The open file pointer must have methods read(), seek(), and close(). | 
					
						
							|  |  |  | When the setpos() and rewind() methods are not used, the seek() | 
					
						
							|  |  |  | method is not  necessary. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | This returns an instance of a class with the following public methods: | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         getnchannels()  -- returns number of audio channels (1 for | 
					
						
							|  |  |  |                            mono, 2 for stereo) | 
					
						
							|  |  |  |         getsampwidth()  -- returns sample width in bytes | 
					
						
							|  |  |  |         getframerate()  -- returns sampling frequency | 
					
						
							|  |  |  |         getnframes()    -- returns number of audio frames | 
					
						
							|  |  |  |         getcomptype()   -- returns compression type ('NONE' or 'ULAW') | 
					
						
							|  |  |  |         getcompname()   -- returns human-readable version of | 
					
						
							|  |  |  |                            compression type ('not compressed' matches 'NONE') | 
					
						
							| 
									
										
										
										
											2013-09-04 00:43:03 +03:00
										 |  |  |         getparams()     -- returns a namedtuple consisting of all of the | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |                            above in the above order | 
					
						
							|  |  |  |         getmarkers()    -- returns None (for compatibility with the | 
					
						
							|  |  |  |                            aifc module) | 
					
						
							|  |  |  |         getmark(id)     -- raises an error since the mark does not | 
					
						
							|  |  |  |                            exist (for compatibility with the aifc module) | 
					
						
							|  |  |  |         readframes(n)   -- returns at most n frames of audio | 
					
						
							|  |  |  |         rewind()        -- rewind to the beginning of the audio stream | 
					
						
							|  |  |  |         setpos(pos)     -- seek to the specified position | 
					
						
							|  |  |  |         tell()          -- return the current position | 
					
						
							|  |  |  |         close()         -- close the instance (make it unusable) | 
					
						
							| 
									
										
										
										
											2000-02-04 15:28:42 +00:00
										 |  |  | The position returned by tell() and the position given to setpos() | 
					
						
							| 
									
										
										
										
											2000-07-16 12:04:32 +00:00
										 |  |  | are compatible and have nothing to do with the actual position in the | 
					
						
							| 
									
										
										
										
											2000-02-04 15:28:42 +00:00
										 |  |  | file. | 
					
						
							|  |  |  | The close() method is called automatically when the class instance | 
					
						
							|  |  |  | is destroyed. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Writing audio files: | 
					
						
							|  |  |  |         f = sunau.open(file, 'w') | 
					
						
							|  |  |  | where file is either the name of a file or an open file pointer. | 
					
						
							|  |  |  | The open file pointer must have methods write(), tell(), seek(), and | 
					
						
							|  |  |  | close(). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | This returns an instance of a class with the following public methods: | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         setnchannels(n) -- set the number of channels | 
					
						
							|  |  |  |         setsampwidth(n) -- set the sample width | 
					
						
							|  |  |  |         setframerate(n) -- set the frame rate | 
					
						
							|  |  |  |         setnframes(n)   -- set the number of frames | 
					
						
							| 
									
										
										
										
											2000-02-04 15:28:42 +00:00
										 |  |  |         setcomptype(type, name) | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |                         -- set the compression type and the | 
					
						
							|  |  |  |                            human-readable compression type | 
					
						
							| 
									
										
										
										
											2000-02-04 15:28:42 +00:00
										 |  |  |         setparams(tuple)-- set all parameters at once | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         tell()          -- return current position in output file | 
					
						
							| 
									
										
										
										
											2000-02-04 15:28:42 +00:00
										 |  |  |         writeframesraw(data) | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |                         -- write audio frames without pathing up the | 
					
						
							|  |  |  |                            file header | 
					
						
							| 
									
										
										
										
											2000-02-04 15:28:42 +00:00
										 |  |  |         writeframes(data) | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |                         -- write audio frames and patch up the file header | 
					
						
							|  |  |  |         close()         -- patch up the file header and close the | 
					
						
							|  |  |  |                            output file | 
					
						
							| 
									
										
										
										
											2000-02-04 15:28:42 +00:00
										 |  |  | You should set the parameters before the first writeframesraw or | 
					
						
							|  |  |  | writeframes.  The total number of frames does not need to be set, | 
					
						
							|  |  |  | but when it is set to the correct value, the header does not have to | 
					
						
							|  |  |  | be patched up. | 
					
						
							|  |  |  | It is best to first set all parameters, perhaps possibly the | 
					
						
							|  |  |  | compression type, and then write audio frames using writeframesraw. | 
					
						
							|  |  |  | When all frames have been written, either call writeframes('') or | 
					
						
							|  |  |  | close() to patch up the sizes in the header. | 
					
						
							|  |  |  | The close() method is called automatically when the class instance | 
					
						
							|  |  |  | is destroyed. | 
					
						
							|  |  |  | """
 | 
					
						
							| 
									
										
										
										
											1993-12-13 11:42:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-04 00:43:03 +03:00
										 |  |  | from collections import namedtuple | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | _sunau_params = namedtuple('_sunau_params', | 
					
						
							|  |  |  |                            'nchannels sampwidth framerate nframes comptype compname') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1993-12-13 11:42:39 +00:00
										 |  |  | # from <multimedia/audio_filehdr.h> | 
					
						
							|  |  |  | AUDIO_FILE_MAGIC = 0x2e736e64 | 
					
						
							|  |  |  | AUDIO_FILE_ENCODING_MULAW_8 = 1 | 
					
						
							|  |  |  | AUDIO_FILE_ENCODING_LINEAR_8 = 2 | 
					
						
							|  |  |  | AUDIO_FILE_ENCODING_LINEAR_16 = 3 | 
					
						
							|  |  |  | AUDIO_FILE_ENCODING_LINEAR_24 = 4 | 
					
						
							|  |  |  | AUDIO_FILE_ENCODING_LINEAR_32 = 5 | 
					
						
							|  |  |  | AUDIO_FILE_ENCODING_FLOAT = 6 | 
					
						
							|  |  |  | AUDIO_FILE_ENCODING_DOUBLE = 7 | 
					
						
							|  |  |  | AUDIO_FILE_ENCODING_ADPCM_G721 = 23 | 
					
						
							|  |  |  | AUDIO_FILE_ENCODING_ADPCM_G722 = 24 | 
					
						
							|  |  |  | AUDIO_FILE_ENCODING_ADPCM_G723_3 = 25 | 
					
						
							|  |  |  | AUDIO_FILE_ENCODING_ADPCM_G723_5 = 26 | 
					
						
							|  |  |  | AUDIO_FILE_ENCODING_ALAW_8 = 27 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # from <multimedia/audio_hdr.h> | 
					
						
							| 
									
										
										
										
											2007-01-15 16:59:06 +00:00
										 |  |  | AUDIO_UNKNOWN_SIZE = 0xFFFFFFFF        # ((unsigned)(~0)) | 
					
						
							| 
									
										
										
										
											1993-12-13 11:42:39 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | _simple_encodings = [AUDIO_FILE_ENCODING_MULAW_8, | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |                      AUDIO_FILE_ENCODING_LINEAR_8, | 
					
						
							|  |  |  |                      AUDIO_FILE_ENCODING_LINEAR_16, | 
					
						
							|  |  |  |                      AUDIO_FILE_ENCODING_LINEAR_24, | 
					
						
							|  |  |  |                      AUDIO_FILE_ENCODING_LINEAR_32, | 
					
						
							|  |  |  |                      AUDIO_FILE_ENCODING_ALAW_8] | 
					
						
							| 
									
										
										
										
											1993-12-13 11:42:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-08-17 04:45:13 +00:00
										 |  |  | class Error(Exception): | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |     pass | 
					
						
							| 
									
										
										
										
											1995-08-14 07:49:51 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1993-12-13 11:42:39 +00:00
										 |  |  | def _read_u32(file): | 
					
						
							| 
									
										
										
										
											2007-01-15 16:59:06 +00:00
										 |  |  |     x = 0 | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |     for i in range(4): | 
					
						
							|  |  |  |         byte = file.read(1) | 
					
						
							| 
									
										
										
										
											2008-08-17 00:38:32 +00:00
										 |  |  |         if not byte: | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |             raise EOFError | 
					
						
							|  |  |  |         x = x*256 + ord(byte) | 
					
						
							|  |  |  |     return x | 
					
						
							| 
									
										
										
										
											1993-12-13 11:42:39 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def _write_u32(file, x): | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |     data = [] | 
					
						
							|  |  |  |     for i in range(4): | 
					
						
							|  |  |  |         d, m = divmod(x, 256) | 
					
						
							| 
									
										
										
										
											2008-08-17 00:38:32 +00:00
										 |  |  |         data.insert(0, int(m)) | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         x = d | 
					
						
							| 
									
										
										
										
											2008-08-17 00:38:32 +00:00
										 |  |  |     file.write(bytes(data)) | 
					
						
							| 
									
										
										
										
											1993-12-13 11:42:39 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class Au_read: | 
					
						
							| 
									
										
										
										
											1993-12-20 09:36:01 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |     def __init__(self, f): | 
					
						
							|  |  |  |         if type(f) == type(''): | 
					
						
							| 
									
										
										
										
											2007-12-02 09:40:06 +00:00
										 |  |  |             import builtins | 
					
						
							|  |  |  |             f = builtins.open(f, 'rb') | 
					
						
							| 
									
										
										
										
											2010-10-31 21:27:04 +00:00
										 |  |  |             self._opened = True | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self._opened = False | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         self.initfp(f) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __del__(self): | 
					
						
							|  |  |  |         if self._file: | 
					
						
							|  |  |  |             self.close() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-05 17:01:53 +03:00
										 |  |  |     def __enter__(self): | 
					
						
							|  |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __exit__(self, *args): | 
					
						
							|  |  |  |         self.close() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |     def initfp(self, file): | 
					
						
							|  |  |  |         self._file = file | 
					
						
							|  |  |  |         self._soundpos = 0 | 
					
						
							|  |  |  |         magic = int(_read_u32(file)) | 
					
						
							|  |  |  |         if magic != AUDIO_FILE_MAGIC: | 
					
						
							| 
									
										
										
										
											2007-08-30 01:19:48 +00:00
										 |  |  |             raise Error('bad magic number') | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         self._hdr_size = int(_read_u32(file)) | 
					
						
							|  |  |  |         if self._hdr_size < 24: | 
					
						
							| 
									
										
										
										
											2007-08-30 01:19:48 +00:00
										 |  |  |             raise Error('header size too small') | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         if self._hdr_size > 100: | 
					
						
							| 
									
										
										
										
											2007-08-30 01:19:48 +00:00
										 |  |  |             raise Error('header size ridiculously large') | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         self._data_size = _read_u32(file) | 
					
						
							|  |  |  |         if self._data_size != AUDIO_UNKNOWN_SIZE: | 
					
						
							|  |  |  |             self._data_size = int(self._data_size) | 
					
						
							|  |  |  |         self._encoding = int(_read_u32(file)) | 
					
						
							|  |  |  |         if self._encoding not in _simple_encodings: | 
					
						
							| 
									
										
										
										
											2007-08-30 01:19:48 +00:00
										 |  |  |             raise Error('encoding not (yet) supported') | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         if self._encoding in (AUDIO_FILE_ENCODING_MULAW_8, | 
					
						
							|  |  |  |                   AUDIO_FILE_ENCODING_ALAW_8): | 
					
						
							|  |  |  |             self._sampwidth = 2 | 
					
						
							|  |  |  |             self._framesize = 1 | 
					
						
							|  |  |  |         elif self._encoding == AUDIO_FILE_ENCODING_LINEAR_8: | 
					
						
							|  |  |  |             self._framesize = self._sampwidth = 1 | 
					
						
							|  |  |  |         elif self._encoding == AUDIO_FILE_ENCODING_LINEAR_16: | 
					
						
							|  |  |  |             self._framesize = self._sampwidth = 2 | 
					
						
							|  |  |  |         elif self._encoding == AUDIO_FILE_ENCODING_LINEAR_24: | 
					
						
							|  |  |  |             self._framesize = self._sampwidth = 3 | 
					
						
							|  |  |  |         elif self._encoding == AUDIO_FILE_ENCODING_LINEAR_32: | 
					
						
							|  |  |  |             self._framesize = self._sampwidth = 4 | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2007-08-30 01:19:48 +00:00
										 |  |  |             raise Error('unknown encoding') | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         self._framerate = int(_read_u32(file)) | 
					
						
							|  |  |  |         self._nchannels = int(_read_u32(file)) | 
					
						
							|  |  |  |         self._framesize = self._framesize * self._nchannels | 
					
						
							|  |  |  |         if self._hdr_size > 24: | 
					
						
							|  |  |  |             self._info = file.read(self._hdr_size - 24) | 
					
						
							|  |  |  |             for i in range(len(self._info)): | 
					
						
							| 
									
										
										
										
											2008-08-17 00:38:32 +00:00
										 |  |  |                 if self._info[i] == b'\0': | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |                     self._info = self._info[:i] | 
					
						
							|  |  |  |                     break | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self._info = '' | 
					
						
							| 
									
										
										
										
											2013-09-28 21:21:39 +03:00
										 |  |  |         try: | 
					
						
							|  |  |  |             self._data_pos = file.tell() | 
					
						
							|  |  |  |         except (AttributeError, OSError): | 
					
						
							|  |  |  |             self._data_pos = None | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def getfp(self): | 
					
						
							|  |  |  |         return self._file | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getnchannels(self): | 
					
						
							|  |  |  |         return self._nchannels | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getsampwidth(self): | 
					
						
							|  |  |  |         return self._sampwidth | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getframerate(self): | 
					
						
							|  |  |  |         return self._framerate | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getnframes(self): | 
					
						
							|  |  |  |         if self._data_size == AUDIO_UNKNOWN_SIZE: | 
					
						
							|  |  |  |             return AUDIO_UNKNOWN_SIZE | 
					
						
							|  |  |  |         if self._encoding in _simple_encodings: | 
					
						
							| 
									
										
										
										
											2013-09-28 21:21:39 +03:00
										 |  |  |             return self._data_size // self._framesize | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         return 0                # XXX--must do some arithmetic here | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getcomptype(self): | 
					
						
							|  |  |  |         if self._encoding == AUDIO_FILE_ENCODING_MULAW_8: | 
					
						
							|  |  |  |             return 'ULAW' | 
					
						
							|  |  |  |         elif self._encoding == AUDIO_FILE_ENCODING_ALAW_8: | 
					
						
							|  |  |  |             return 'ALAW' | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return 'NONE' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getcompname(self): | 
					
						
							|  |  |  |         if self._encoding == AUDIO_FILE_ENCODING_MULAW_8: | 
					
						
							|  |  |  |             return 'CCITT G.711 u-law' | 
					
						
							|  |  |  |         elif self._encoding == AUDIO_FILE_ENCODING_ALAW_8: | 
					
						
							|  |  |  |             return 'CCITT G.711 A-law' | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return 'not compressed' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getparams(self): | 
					
						
							| 
									
										
										
										
											2013-09-04 00:43:03 +03:00
										 |  |  |         return _sunau_params(self.getnchannels(), self.getsampwidth(), | 
					
						
							|  |  |  |                   self.getframerate(), self.getnframes(), | 
					
						
							|  |  |  |                   self.getcomptype(), self.getcompname()) | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def getmarkers(self): | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getmark(self, id): | 
					
						
							| 
									
										
										
										
											2007-08-30 01:19:48 +00:00
										 |  |  |         raise Error('no marks') | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def readframes(self, nframes): | 
					
						
							|  |  |  |         if self._encoding in _simple_encodings: | 
					
						
							|  |  |  |             if nframes == AUDIO_UNKNOWN_SIZE: | 
					
						
							|  |  |  |                 data = self._file.read() | 
					
						
							|  |  |  |             else: | 
					
						
							| 
									
										
										
										
											2013-09-28 21:21:39 +03:00
										 |  |  |                 data = self._file.read(nframes * self._framesize) | 
					
						
							|  |  |  |             self._soundpos += len(data) // self._framesize | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |             if self._encoding == AUDIO_FILE_ENCODING_MULAW_8: | 
					
						
							|  |  |  |                 import audioop | 
					
						
							|  |  |  |                 data = audioop.ulaw2lin(data, self._sampwidth) | 
					
						
							|  |  |  |             return data | 
					
						
							|  |  |  |         return None             # XXX--not implemented yet | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def rewind(self): | 
					
						
							| 
									
										
										
										
											2013-09-28 21:21:39 +03:00
										 |  |  |         if self._data_pos is None: | 
					
						
							|  |  |  |             raise OSError('cannot seek') | 
					
						
							|  |  |  |         self._file.seek(self._data_pos) | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         self._soundpos = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def tell(self): | 
					
						
							|  |  |  |         return self._soundpos | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setpos(self, pos): | 
					
						
							|  |  |  |         if pos < 0 or pos > self.getnframes(): | 
					
						
							| 
									
										
										
										
											2007-08-30 01:19:48 +00:00
										 |  |  |             raise Error('position not in range') | 
					
						
							| 
									
										
										
										
											2013-09-28 21:21:39 +03:00
										 |  |  |         if self._data_pos is None: | 
					
						
							|  |  |  |             raise OSError('cannot seek') | 
					
						
							|  |  |  |         self._file.seek(self._data_pos + pos * self._framesize) | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         self._soundpos = pos | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def close(self): | 
					
						
							| 
									
										
										
										
											2010-10-31 21:27:04 +00:00
										 |  |  |         if self._opened and self._file: | 
					
						
							|  |  |  |             self._file.close() | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         self._file = None | 
					
						
							| 
									
										
										
										
											1993-12-13 11:42:39 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class Au_write: | 
					
						
							| 
									
										
										
										
											1993-12-20 09:36:01 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |     def __init__(self, f): | 
					
						
							|  |  |  |         if type(f) == type(''): | 
					
						
							| 
									
										
										
										
											2007-12-02 09:40:06 +00:00
										 |  |  |             import builtins | 
					
						
							|  |  |  |             f = builtins.open(f, 'wb') | 
					
						
							| 
									
										
										
										
											2010-10-31 21:27:04 +00:00
										 |  |  |             self._opened = True | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self._opened = False | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         self.initfp(f) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __del__(self): | 
					
						
							|  |  |  |         if self._file: | 
					
						
							|  |  |  |             self.close() | 
					
						
							| 
									
										
										
										
											2010-10-31 21:27:04 +00:00
										 |  |  |         self._file = None | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-05 17:01:53 +03:00
										 |  |  |     def __enter__(self): | 
					
						
							|  |  |  |         return self | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __exit__(self, *args): | 
					
						
							|  |  |  |         self.close() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |     def initfp(self, file): | 
					
						
							|  |  |  |         self._file = file | 
					
						
							|  |  |  |         self._framerate = 0 | 
					
						
							|  |  |  |         self._nchannels = 0 | 
					
						
							|  |  |  |         self._sampwidth = 0 | 
					
						
							|  |  |  |         self._framesize = 0 | 
					
						
							|  |  |  |         self._nframes = AUDIO_UNKNOWN_SIZE | 
					
						
							|  |  |  |         self._nframeswritten = 0 | 
					
						
							|  |  |  |         self._datawritten = 0 | 
					
						
							|  |  |  |         self._datalength = 0 | 
					
						
							| 
									
										
										
										
											2010-06-07 20:14:04 +00:00
										 |  |  |         self._info = b'' | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         self._comptype = 'ULAW' # default is U-law | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setnchannels(self, nchannels): | 
					
						
							|  |  |  |         if self._nframeswritten: | 
					
						
							| 
									
										
										
										
											2007-08-30 01:19:48 +00:00
										 |  |  |             raise Error('cannot change parameters after starting to write') | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         if nchannels not in (1, 2, 4): | 
					
						
							| 
									
										
										
										
											2007-08-30 01:19:48 +00:00
										 |  |  |             raise Error('only 1, 2, or 4 channels supported') | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         self._nchannels = nchannels | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getnchannels(self): | 
					
						
							|  |  |  |         if not self._nchannels: | 
					
						
							| 
									
										
										
										
											2007-08-30 01:19:48 +00:00
										 |  |  |             raise Error('number of channels not set') | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         return self._nchannels | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setsampwidth(self, sampwidth): | 
					
						
							|  |  |  |         if self._nframeswritten: | 
					
						
							| 
									
										
										
										
											2007-08-30 01:19:48 +00:00
										 |  |  |             raise Error('cannot change parameters after starting to write') | 
					
						
							| 
									
										
										
										
											2013-11-10 21:02:53 +02:00
										 |  |  |         if sampwidth not in (1, 2, 3, 4): | 
					
						
							| 
									
										
										
										
											2007-08-30 01:19:48 +00:00
										 |  |  |             raise Error('bad sample width') | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         self._sampwidth = sampwidth | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getsampwidth(self): | 
					
						
							|  |  |  |         if not self._framerate: | 
					
						
							| 
									
										
										
										
											2007-08-30 01:19:48 +00:00
										 |  |  |             raise Error('sample width not specified') | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         return self._sampwidth | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setframerate(self, framerate): | 
					
						
							|  |  |  |         if self._nframeswritten: | 
					
						
							| 
									
										
										
										
											2007-08-30 01:19:48 +00:00
										 |  |  |             raise Error('cannot change parameters after starting to write') | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         self._framerate = framerate | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getframerate(self): | 
					
						
							|  |  |  |         if not self._framerate: | 
					
						
							| 
									
										
										
										
											2007-08-30 01:19:48 +00:00
										 |  |  |             raise Error('frame rate not set') | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         return self._framerate | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setnframes(self, nframes): | 
					
						
							|  |  |  |         if self._nframeswritten: | 
					
						
							| 
									
										
										
										
											2007-08-30 01:19:48 +00:00
										 |  |  |             raise Error('cannot change parameters after starting to write') | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         if nframes < 0: | 
					
						
							| 
									
										
										
										
											2007-08-30 01:19:48 +00:00
										 |  |  |             raise Error('# of frames cannot be negative') | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         self._nframes = nframes | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getnframes(self): | 
					
						
							|  |  |  |         return self._nframeswritten | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setcomptype(self, type, name): | 
					
						
							|  |  |  |         if type in ('NONE', 'ULAW'): | 
					
						
							|  |  |  |             self._comptype = type | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2007-08-30 01:19:48 +00:00
										 |  |  |             raise Error('unknown compression type') | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def getcomptype(self): | 
					
						
							|  |  |  |         return self._comptype | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getcompname(self): | 
					
						
							|  |  |  |         if self._comptype == 'ULAW': | 
					
						
							|  |  |  |             return 'CCITT G.711 u-law' | 
					
						
							|  |  |  |         elif self._comptype == 'ALAW': | 
					
						
							|  |  |  |             return 'CCITT G.711 A-law' | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return 'not compressed' | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-05-15 18:46:22 +00:00
										 |  |  |     def setparams(self, params): | 
					
						
							|  |  |  |         nchannels, sampwidth, framerate, nframes, comptype, compname = params | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         self.setnchannels(nchannels) | 
					
						
							|  |  |  |         self.setsampwidth(sampwidth) | 
					
						
							|  |  |  |         self.setframerate(framerate) | 
					
						
							|  |  |  |         self.setnframes(nframes) | 
					
						
							|  |  |  |         self.setcomptype(comptype, compname) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getparams(self): | 
					
						
							| 
									
										
										
										
											2013-09-04 14:30:16 +03:00
										 |  |  |         return _sunau_params(self.getnchannels(), self.getsampwidth(), | 
					
						
							| 
									
										
										
										
											2013-09-04 00:43:03 +03:00
										 |  |  |                   self.getframerate(), self.getnframes(), | 
					
						
							|  |  |  |                   self.getcomptype(), self.getcompname()) | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def tell(self): | 
					
						
							|  |  |  |         return self._nframeswritten | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def writeframesraw(self, data): | 
					
						
							| 
									
										
										
										
											2013-11-16 14:01:31 +02:00
										 |  |  |         if not isinstance(data, (bytes, bytearray)): | 
					
						
							|  |  |  |             data = memoryview(data).cast('B') | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         self._ensure_header_written() | 
					
						
							|  |  |  |         if self._comptype == 'ULAW': | 
					
						
							|  |  |  |             import audioop | 
					
						
							|  |  |  |             data = audioop.lin2ulaw(data, self._sampwidth) | 
					
						
							| 
									
										
										
										
											2013-09-28 21:21:39 +03:00
										 |  |  |         nframes = len(data) // self._framesize | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         self._file.write(data) | 
					
						
							|  |  |  |         self._nframeswritten = self._nframeswritten + nframes | 
					
						
							|  |  |  |         self._datawritten = self._datawritten + len(data) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def writeframes(self, data): | 
					
						
							|  |  |  |         self.writeframesraw(data) | 
					
						
							|  |  |  |         if self._nframeswritten != self._nframes or \ | 
					
						
							|  |  |  |                   self._datalength != self._datawritten: | 
					
						
							|  |  |  |             self._patchheader() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def close(self): | 
					
						
							| 
									
										
										
										
											2013-09-05 17:01:53 +03:00
										 |  |  |         if self._file: | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 self._ensure_header_written() | 
					
						
							|  |  |  |                 if self._nframeswritten != self._nframes or \ | 
					
						
							|  |  |  |                         self._datalength != self._datawritten: | 
					
						
							|  |  |  |                     self._patchheader() | 
					
						
							|  |  |  |                 self._file.flush() | 
					
						
							|  |  |  |             finally: | 
					
						
							|  |  |  |                 if self._opened and self._file: | 
					
						
							|  |  |  |                     self._file.close() | 
					
						
							|  |  |  |                 self._file = None | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # | 
					
						
							|  |  |  |     # private methods | 
					
						
							|  |  |  |     # | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _ensure_header_written(self): | 
					
						
							|  |  |  |         if not self._nframeswritten: | 
					
						
							|  |  |  |             if not self._nchannels: | 
					
						
							| 
									
										
										
										
											2007-08-30 01:19:48 +00:00
										 |  |  |                 raise Error('# of channels not specified') | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |             if not self._sampwidth: | 
					
						
							| 
									
										
										
										
											2007-08-30 01:19:48 +00:00
										 |  |  |                 raise Error('sample width not specified') | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |             if not self._framerate: | 
					
						
							| 
									
										
										
										
											2007-08-30 01:19:48 +00:00
										 |  |  |                 raise Error('frame rate not specified') | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |             self._write_header() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _write_header(self): | 
					
						
							|  |  |  |         if self._comptype == 'NONE': | 
					
						
							|  |  |  |             if self._sampwidth == 1: | 
					
						
							|  |  |  |                 encoding = AUDIO_FILE_ENCODING_LINEAR_8 | 
					
						
							|  |  |  |                 self._framesize = 1 | 
					
						
							|  |  |  |             elif self._sampwidth == 2: | 
					
						
							|  |  |  |                 encoding = AUDIO_FILE_ENCODING_LINEAR_16 | 
					
						
							|  |  |  |                 self._framesize = 2 | 
					
						
							| 
									
										
										
										
											2013-11-10 21:02:53 +02:00
										 |  |  |             elif self._sampwidth == 3: | 
					
						
							|  |  |  |                 encoding = AUDIO_FILE_ENCODING_LINEAR_24 | 
					
						
							|  |  |  |                 self._framesize = 3 | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |             elif self._sampwidth == 4: | 
					
						
							|  |  |  |                 encoding = AUDIO_FILE_ENCODING_LINEAR_32 | 
					
						
							|  |  |  |                 self._framesize = 4 | 
					
						
							|  |  |  |             else: | 
					
						
							| 
									
										
										
										
											2007-08-30 01:19:48 +00:00
										 |  |  |                 raise Error('internal error') | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         elif self._comptype == 'ULAW': | 
					
						
							|  |  |  |             encoding = AUDIO_FILE_ENCODING_MULAW_8 | 
					
						
							|  |  |  |             self._framesize = 1 | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2007-08-30 01:19:48 +00:00
										 |  |  |             raise Error('internal error') | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         self._framesize = self._framesize * self._nchannels | 
					
						
							|  |  |  |         _write_u32(self._file, AUDIO_FILE_MAGIC) | 
					
						
							|  |  |  |         header_size = 25 + len(self._info) | 
					
						
							|  |  |  |         header_size = (header_size + 7) & ~7 | 
					
						
							|  |  |  |         _write_u32(self._file, header_size) | 
					
						
							|  |  |  |         if self._nframes == AUDIO_UNKNOWN_SIZE: | 
					
						
							|  |  |  |             length = AUDIO_UNKNOWN_SIZE | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             length = self._nframes * self._framesize | 
					
						
							| 
									
										
										
										
											2013-09-28 21:21:39 +03:00
										 |  |  |         try: | 
					
						
							|  |  |  |             self._form_length_pos = self._file.tell() | 
					
						
							|  |  |  |         except (AttributeError, OSError): | 
					
						
							|  |  |  |             self._form_length_pos = None | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         _write_u32(self._file, length) | 
					
						
							|  |  |  |         self._datalength = length | 
					
						
							|  |  |  |         _write_u32(self._file, encoding) | 
					
						
							|  |  |  |         _write_u32(self._file, self._framerate) | 
					
						
							|  |  |  |         _write_u32(self._file, self._nchannels) | 
					
						
							|  |  |  |         self._file.write(self._info) | 
					
						
							| 
									
										
										
										
											2008-08-17 00:38:32 +00:00
										 |  |  |         self._file.write(b'\0'*(header_size - len(self._info) - 24)) | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def _patchheader(self): | 
					
						
							| 
									
										
										
										
											2013-09-28 21:21:39 +03:00
										 |  |  |         if self._form_length_pos is None: | 
					
						
							|  |  |  |             raise OSError('cannot seek') | 
					
						
							|  |  |  |         self._file.seek(self._form_length_pos) | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |         _write_u32(self._file, self._datawritten) | 
					
						
							|  |  |  |         self._datalength = self._datawritten | 
					
						
							|  |  |  |         self._file.seek(0, 2) | 
					
						
							| 
									
										
										
										
											1993-12-13 11:42:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1999-06-22 21:23:23 +00:00
										 |  |  | def open(f, mode=None): | 
					
						
							| 
									
										
										
										
											2001-01-15 01:36:40 +00:00
										 |  |  |     if mode is None: | 
					
						
							|  |  |  |         if hasattr(f, 'mode'): | 
					
						
							|  |  |  |             mode = f.mode | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             mode = 'rb' | 
					
						
							|  |  |  |     if mode in ('r', 'rb'): | 
					
						
							|  |  |  |         return Au_read(f) | 
					
						
							|  |  |  |     elif mode in ('w', 'wb'): | 
					
						
							|  |  |  |         return Au_write(f) | 
					
						
							|  |  |  |     else: | 
					
						
							| 
									
										
										
										
											2007-08-30 01:19:48 +00:00
										 |  |  |         raise Error("mode must be 'r', 'rb', 'w', or 'wb'") | 
					
						
							| 
									
										
										
										
											1993-12-13 11:42:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1993-12-17 15:25:27 +00:00
										 |  |  | openfp = open |