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