183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh"""Stuff to parse AIFF-C and AIFF files.
283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehUnless explicitly stated otherwise, the description below is true
483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehboth for AIFF-C files and AIFF files.
583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehAn AIFF-C file has the following structure.
783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  +-----------------+
983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  | FORM            |
1083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  +-----------------+
1183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  | <size>          |
1283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  +----+------------+
1383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  |    | AIFC       |
1483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  |    +------------+
1583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  |    | <chunks>   |
1683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  |    |    .       |
1783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  |    |    .       |
1883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  |    |    .       |
1983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  +----+------------+
2083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
2183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehAn AIFF file has the string "AIFF" instead of "AIFC".
2283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
2383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehA chunk consists of an identifier (4 bytes) followed by a size (4 bytes,
2483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehbig endian order), followed by the data.  The size field does not include
2583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehthe size of the 8 byte header.
2683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
2783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehThe following chunk types are recognized.
2883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
2983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  FVER
3083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh      <version number of AIFF-C defining document> (AIFF-C only).
3183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  MARK
3283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh      <# of markers> (2 bytes)
3383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh      list of markers:
3483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh          <marker ID> (2 bytes, must be > 0)
3583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh          <position> (4 bytes)
3683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh          <marker name> ("pstring")
3783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  COMM
3883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh      <# of channels> (2 bytes)
3983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh      <# of sound frames> (4 bytes)
4083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh      <size of the samples> (2 bytes)
4183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh      <sampling frequency> (10 bytes, IEEE 80-bit extended
4283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh          floating point)
4383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh      in AIFF-C files only:
4483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh      <compression type> (4 bytes)
4583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh      <human-readable version of compression type> ("pstring")
4683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  SSND
4783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh      <offset> (4 bytes, not used by this program)
4883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh      <blocksize> (4 bytes, not used by this program)
4983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh      <sound data>
5083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
5183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehA pstring consists of 1 byte length, a string of characters, and 0 or 1
5283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehbyte pad to make the total length even.
5383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
5483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehUsage.
5583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
5683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehReading AIFF files:
5783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  f = aifc.open(file, 'r')
5883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehwhere file is either the name of a file or an open file pointer.
5983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehThe open file pointer must have methods read(), seek(), and close().
6083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehIn some types of audio files, if the setpos() method is not used,
6183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehthe seek() method is not necessary.
6283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
6383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehThis returns an instance of a class with the following public methods:
6483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  getnchannels()  -- returns number of audio channels (1 for
6583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh             mono, 2 for stereo)
6683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  getsampwidth()  -- returns sample width in bytes
6783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  getframerate()  -- returns sampling frequency
6883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  getnframes()    -- returns number of audio frames
6983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  getcomptype()   -- returns compression type ('NONE' for AIFF files)
7083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  getcompname()   -- returns human-readable version of
7183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh             compression type ('not compressed' for AIFF files)
7283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  getparams() -- returns a tuple consisting of all of the
7383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh             above in the above order
7483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  getmarkers()    -- get the list of marks in the audio file or None
7583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh             if there are no marks
7683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  getmark(id) -- get mark with the specified id (raises an error
7783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh             if the mark does not exist)
7883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  readframes(n)   -- returns at most n frames of audio
7983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  rewind()    -- rewind to the beginning of the audio stream
8083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  setpos(pos) -- seek to the specified position
8183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  tell()      -- return the current position
8283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  close()     -- close the instance (make it unusable)
8383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehThe position returned by tell(), the position given to setpos() and
8483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehthe position of marks are all compatible and have nothing to do with
8583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehthe actual position in the file.
8683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehThe close() method is called automatically when the class instance
8783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehis destroyed.
8883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
8983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehWriting AIFF files:
9083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  f = aifc.open(file, 'w')
9183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehwhere file is either the name of a file or an open file pointer.
9283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehThe open file pointer must have methods write(), tell(), seek(), and
9383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclose().
9483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
9583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehThis returns an instance of a class with the following public methods:
9683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  aiff()      -- create an AIFF file (AIFF-C default)
9783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  aifc()      -- create an AIFF-C file
9883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  setnchannels(n) -- set the number of channels
9983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  setsampwidth(n) -- set the sample width
10083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  setframerate(n) -- set the frame rate
10183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  setnframes(n)   -- set the number of frames
10283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  setcomptype(type, name)
10383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh          -- set the compression type and the
10483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh             human-readable compression type
10583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  setparams(tuple)
10683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh          -- set all parameters at once
10783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  setmark(id, pos, name)
10883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh          -- add specified mark to the list of marks
10983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  tell()      -- return current position in output file (useful
11083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh             in combination with setmark())
11183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  writeframesraw(data)
11283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh          -- write audio frames without pathing up the
11383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh             file header
11483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  writeframes(data)
11583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh          -- write audio frames and patch up the file header
11683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  close()     -- patch up the file header and close the
11783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh             output file
11883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehYou should set the parameters before the first writeframesraw or
11983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehwriteframes.  The total number of frames does not need to be set,
12083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehbut when it is set to the correct value, the header does not have to
12183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehbe patched up.
12283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehIt is best to first set all parameters, perhaps possibly the
12383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehcompression type, and then write audio frames using writeframesraw.
12483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehWhen all frames have been written, either call writeframes('') or
12583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclose() to patch up the sizes in the header.
12683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehMarks can be added anytime.  If there are any marks, ypu must call
12783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclose() after all frames have been written.
12883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehThe close() method is called automatically when the class instance
12983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehis destroyed.
13083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
13183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehWhen a file is opened with the extension '.aiff', an AIFF file is
13283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehwritten, otherwise an AIFF-C file is written.  This default can be
13383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehchanged by calling aiff() or aifc() before the first writeframes or
13483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehwriteframesraw.
13583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh"""
13683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
13783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehimport struct
13883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehimport __builtin__
13983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
14083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh__all__ = ["Error","open","openfp"]
14183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
14283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass Error(Exception):
14383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    pass
14483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
14583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh_AIFC_version = 0xA2805140L     # Version 1 of AIFF-C
14683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
14783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef _read_long(file):
14883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    try:
14983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return struct.unpack('>l', file.read(4))[0]
15083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    except struct.error:
15183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        raise EOFError
15283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
15383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef _read_ulong(file):
15483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    try:
15583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return struct.unpack('>L', file.read(4))[0]
15683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    except struct.error:
15783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        raise EOFError
15883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
15983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef _read_short(file):
16083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    try:
16183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return struct.unpack('>h', file.read(2))[0]
16283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    except struct.error:
16383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        raise EOFError
16483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
16583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef _read_ushort(file):
16683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    try:
16783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return struct.unpack('>H', file.read(2))[0]
16883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    except struct.error:
16983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        raise EOFError
17083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
17183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef _read_string(file):
17283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    length = ord(file.read(1))
17383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if length == 0:
17483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        data = ''
17583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    else:
17683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        data = file.read(length)
17783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if length & 1 == 0:
17883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        dummy = file.read(1)
17983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return data
18083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
18183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh_HUGE_VAL = 1.79769313486231e+308 # See <limits.h>
18283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
18383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef _read_float(f): # 10 bytes
18483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    expon = _read_short(f) # 2 bytes
18583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    sign = 1
18683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if expon < 0:
18783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        sign = -1
18883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        expon = expon + 0x8000
18983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    himant = _read_ulong(f) # 4 bytes
19083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    lomant = _read_ulong(f) # 4 bytes
19183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if expon == himant == lomant == 0:
19283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        f = 0.0
19383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    elif expon == 0x7FFF:
19483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        f = _HUGE_VAL
19583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    else:
19683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        expon = expon - 16383
19783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        f = (himant * 0x100000000L + lomant) * pow(2.0, expon - 63)
19883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return sign * f
19983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
20083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef _write_short(f, x):
20183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    f.write(struct.pack('>h', x))
20283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
20383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef _write_ushort(f, x):
20483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    f.write(struct.pack('>H', x))
20583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
20683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef _write_long(f, x):
20783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    f.write(struct.pack('>l', x))
20883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
20983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef _write_ulong(f, x):
21083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    f.write(struct.pack('>L', x))
21183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
21283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef _write_string(f, s):
21383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if len(s) > 255:
21483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        raise ValueError("string exceeds maximum pstring length")
21583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    f.write(struct.pack('B', len(s)))
21683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    f.write(s)
21783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if len(s) & 1 == 0:
21883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        f.write(chr(0))
21983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
22083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef _write_float(f, x):
22183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    import math
22283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if x < 0:
22383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        sign = 0x8000
22483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        x = x * -1
22583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    else:
22683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        sign = 0
22783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if x == 0:
22883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        expon = 0
22983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        himant = 0
23083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        lomant = 0
23183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    else:
23283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        fmant, expon = math.frexp(x)
23383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if expon > 16384 or fmant >= 1 or fmant != fmant: # Infinity or NaN
23483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            expon = sign|0x7FFF
23583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            himant = 0
23683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            lomant = 0
23783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:                   # Finite
23883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            expon = expon + 16382
23983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if expon < 0:           # denormalized
24083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                fmant = math.ldexp(fmant, expon)
24183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                expon = 0
24283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            expon = expon | sign
24383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            fmant = math.ldexp(fmant, 32)
24483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            fsmant = math.floor(fmant)
24583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            himant = long(fsmant)
24683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            fmant = math.ldexp(fmant - fsmant, 32)
24783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            fsmant = math.floor(fmant)
24883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            lomant = long(fsmant)
24983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    _write_ushort(f, expon)
25083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    _write_ulong(f, himant)
25183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    _write_ulong(f, lomant)
25283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
25383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehfrom chunk import Chunk
25483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
25583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass Aifc_read:
25683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Variables used in this class:
25783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
25883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # These variables are available to the user though appropriate
25983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # methods of this class:
26083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _file -- the open file with methods read(), close(), and seek()
26183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #       set through the __init__() method
26283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _nchannels -- the number of audio channels
26383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #       available through the getnchannels() method
26483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _nframes -- the number of audio frames
26583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #       available through the getnframes() method
26683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _sampwidth -- the number of bytes per audio sample
26783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #       available through the getsampwidth() method
26883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _framerate -- the sampling frequency
26983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #       available through the getframerate() method
27083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _comptype -- the AIFF-C compression type ('NONE' if AIFF)
27183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #       available through the getcomptype() method
27283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _compname -- the human-readable AIFF-C compression type
27383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #       available through the getcomptype() method
27483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _markers -- the marks in the audio file
27583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #       available through the getmarkers() and getmark()
27683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #       methods
27783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _soundpos -- the position in the audio stream
27883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #       available through the tell() method, set through the
27983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #       setpos() method
28083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
28183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # These variables are used internally only:
28283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _version -- the AIFF-C version number
28383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _decomp -- the decompressor from builtin module cl
28483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _comm_chunk_read -- 1 iff the COMM chunk has been read
28583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _aifc -- 1 iff reading an AIFF-C file
28683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _ssnd_seek_needed -- 1 iff positioned correctly in audio
28783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #       file for readframes()
28883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _ssnd_chunk -- instantiation of a chunk class for the SSND chunk
28983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _framesize -- size of one frame in the file
29083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
29183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def initfp(self, file):
29283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._version = 0
29383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._decomp = None
29483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._convert = None
29583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._markers = []
29683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._soundpos = 0
29783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._file = file
29883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        chunk = Chunk(file)
29983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if chunk.getname() != 'FORM':
30083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise Error, 'file does not start with FORM id'
30183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        formdata = chunk.read(4)
30283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if formdata == 'AIFF':
30383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._aifc = 0
30483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        elif formdata == 'AIFC':
30583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._aifc = 1
30683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
30783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise Error, 'not an AIFF or AIFF-C file'
30883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._comm_chunk_read = 0
30983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        while 1:
31083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._ssnd_seek_needed = 1
31183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            try:
31283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                chunk = Chunk(self._file)
31383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            except EOFError:
31483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                break
31583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            chunkname = chunk.getname()
31683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if chunkname == 'COMM':
31783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                self._read_comm_chunk(chunk)
31883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                self._comm_chunk_read = 1
31983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            elif chunkname == 'SSND':
32083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                self._ssnd_chunk = chunk
32183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                dummy = chunk.read(8)
32283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                self._ssnd_seek_needed = 0
32383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            elif chunkname == 'FVER':
32483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                self._version = _read_ulong(chunk)
32583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            elif chunkname == 'MARK':
32683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                self._readmark(chunk)
32783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            chunk.skip()
32883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if not self._comm_chunk_read or not self._ssnd_chunk:
32983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise Error, 'COMM chunk and/or SSND chunk missing'
33083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._aifc and self._decomp:
33183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            import cl
33283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            params = [cl.ORIGINAL_FORMAT, 0,
33383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                  cl.BITS_PER_COMPONENT, self._sampwidth * 8,
33483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                  cl.FRAME_RATE, self._framerate]
33583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if self._nchannels == 1:
33683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                params[1] = cl.MONO
33783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            elif self._nchannels == 2:
33883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                params[1] = cl.STEREO_INTERLEAVED
33983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            else:
34083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                raise Error, 'cannot compress more than 2 channels'
34183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._decomp.SetParams(params)
34283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
34383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __init__(self, f):
34483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if type(f) == type(''):
34583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            f = __builtin__.open(f, 'rb')
34683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # else, assume it is an open file object already
34783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.initfp(f)
34883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
34983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
35083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # User visible methods.
35183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
35283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def getfp(self):
35383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return self._file
35483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
35583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def rewind(self):
35683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._ssnd_seek_needed = 1
35783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._soundpos = 0
35883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
35983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def close(self):
36083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._decomp:
36183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._decomp.CloseDecompressor()
36283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._decomp = None
36383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._file.close()
36483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
36583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def tell(self):
36683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return self._soundpos
36783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
36883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def getnchannels(self):
36983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return self._nchannels
37083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
37183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def getnframes(self):
37283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return self._nframes
37383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
37483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def getsampwidth(self):
37583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return self._sampwidth
37683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
37783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def getframerate(self):
37883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return self._framerate
37983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
38083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def getcomptype(self):
38183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return self._comptype
38283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
38383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def getcompname(self):
38483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return self._compname
38583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
38683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh##  def getversion(self):
38783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh##      return self._version
38883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
38983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def getparams(self):
39083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return self.getnchannels(), self.getsampwidth(), \
39183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh              self.getframerate(), self.getnframes(), \
39283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh              self.getcomptype(), self.getcompname()
39383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
39483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def getmarkers(self):
39583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if len(self._markers) == 0:
39683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return None
39783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return self._markers
39883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
39983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def getmark(self, id):
40083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        for marker in self._markers:
40183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if id == marker[0]:
40283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                return marker
40383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        raise Error, 'marker %r does not exist' % (id,)
40483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
40583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def setpos(self, pos):
40683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if pos < 0 or pos > self._nframes:
40783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise Error, 'position not in range'
40883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._soundpos = pos
40983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._ssnd_seek_needed = 1
41083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
41183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def readframes(self, nframes):
41283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._ssnd_seek_needed:
41383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._ssnd_chunk.seek(0)
41483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            dummy = self._ssnd_chunk.read(8)
41583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            pos = self._soundpos * self._framesize
41683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if pos:
41783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                self._ssnd_chunk.seek(pos + 8)
41883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._ssnd_seek_needed = 0
41983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if nframes == 0:
42083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return ''
42183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        data = self._ssnd_chunk.read(nframes * self._framesize)
42283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._convert and data:
42383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            data = self._convert(data)
42483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._soundpos = self._soundpos + len(data) // (self._nchannels * self._sampwidth)
42583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return data
42683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
42783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
42883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Internal methods.
42983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
43083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
43183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def _decomp_data(self, data):
43283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        import cl
43383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        dummy = self._decomp.SetParam(cl.FRAME_BUFFER_SIZE,
43483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                          len(data) * 2)
43583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return self._decomp.Decompress(len(data) // self._nchannels,
43683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                           data)
43783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
43883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def _ulaw2lin(self, data):
43983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        import audioop
44083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return audioop.ulaw2lin(data, 2)
44183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
44283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def _adpcm2lin(self, data):
44383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        import audioop
44483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if not hasattr(self, '_adpcmstate'):
44583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # first time
44683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._adpcmstate = None
44783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        data, self._adpcmstate = audioop.adpcm2lin(data, 2,
44883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                               self._adpcmstate)
44983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return data
45083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
45183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def _read_comm_chunk(self, chunk):
45283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._nchannels = _read_short(chunk)
45383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._nframes = _read_long(chunk)
45483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._sampwidth = (_read_short(chunk) + 7) // 8
45583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._framerate = int(_read_float(chunk))
45683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._framesize = self._nchannels * self._sampwidth
45783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._aifc:
45883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            #DEBUG: SGI's soundeditor produces a bad size :-(
45983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            kludge = 0
46083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if chunk.chunksize == 18:
46183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                kludge = 1
46283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                print 'Warning: bad COMM chunk size'
46383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                chunk.chunksize = 23
46483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            #DEBUG end
46583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._comptype = chunk.read(4)
46683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            #DEBUG start
46783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if kludge:
46883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                length = ord(chunk.file.read(1))
46983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                if length & 1 == 0:
47083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    length = length + 1
47183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                chunk.chunksize = chunk.chunksize + length
47283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                chunk.file.seek(-1, 1)
47383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            #DEBUG end
47483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._compname = _read_string(chunk)
47583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if self._comptype != 'NONE':
47683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                if self._comptype == 'G722':
47783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    try:
47883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                        import audioop
47983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    except ImportError:
48083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                        pass
48183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    else:
48283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                        self._convert = self._adpcm2lin
48383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                        self._framesize = self._framesize // 4
48483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                        return
48583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                # for ULAW and ALAW try Compression Library
48683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                try:
48783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    import cl
48883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                except ImportError:
48983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    if self._comptype == 'ULAW':
49083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                        try:
49183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                            import audioop
49283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                            self._convert = self._ulaw2lin
49383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                            self._framesize = self._framesize // 2
49483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                            return
49583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                        except ImportError:
49683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                            pass
49783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    raise Error, 'cannot read compressed AIFF-C files'
49883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                if self._comptype == 'ULAW':
49983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    scheme = cl.G711_ULAW
50083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    self._framesize = self._framesize // 2
50183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                elif self._comptype == 'ALAW':
50283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    scheme = cl.G711_ALAW
50383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    self._framesize = self._framesize // 2
50483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                else:
50583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    raise Error, 'unsupported compression type'
50683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                self._decomp = cl.OpenDecompressor(scheme)
50783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                self._convert = self._decomp_data
50883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
50983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._comptype = 'NONE'
51083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._compname = 'not compressed'
51183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
51283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def _readmark(self, chunk):
51383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        nmarkers = _read_short(chunk)
51483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # Some files appear to contain invalid counts.
51583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # Cope with this by testing for EOF.
51683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        try:
51783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            for i in range(nmarkers):
51883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                id = _read_short(chunk)
51983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                pos = _read_long(chunk)
52083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                name = _read_string(chunk)
52183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                if pos or name:
52283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    # some files appear to have
52383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    # dummy markers consisting of
52483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    # a position 0 and name ''
52583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    self._markers.append((id, pos, name))
52683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        except EOFError:
52783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            print 'Warning: MARK chunk contains only',
52883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            print len(self._markers),
52983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if len(self._markers) == 1: print 'marker',
53083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            else: print 'markers',
53183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            print 'instead of', nmarkers
53283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
53383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass Aifc_write:
53483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Variables used in this class:
53583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
53683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # These variables are user settable through appropriate methods
53783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # of this class:
53883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _file -- the open file with methods write(), close(), tell(), seek()
53983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #       set through the __init__() method
54083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _comptype -- the AIFF-C compression type ('NONE' in AIFF)
54183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #       set through the setcomptype() or setparams() method
54283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _compname -- the human-readable AIFF-C compression type
54383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #       set through the setcomptype() or setparams() method
54483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _nchannels -- the number of audio channels
54583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #       set through the setnchannels() or setparams() method
54683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _sampwidth -- the number of bytes per audio sample
54783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #       set through the setsampwidth() or setparams() method
54883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _framerate -- the sampling frequency
54983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #       set through the setframerate() or setparams() method
55083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _nframes -- the number of audio frames written to the header
55183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #       set through the setnframes() or setparams() method
55283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _aifc -- whether we're writing an AIFF-C file or an AIFF file
55383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #       set through the aifc() method, reset through the
55483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #       aiff() method
55583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
55683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # These variables are used internally only:
55783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _version -- the AIFF-C version number
55883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _comp -- the compressor from builtin module cl
55983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _nframeswritten -- the number of audio frames actually written
56083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _datalength -- the size of the audio samples written to the header
56183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # _datawritten -- the size of the audio samples actually written
56283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
56383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __init__(self, f):
56483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if type(f) == type(''):
56583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            filename = f
56683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            f = __builtin__.open(f, 'wb')
56783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
56883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # else, assume it is an open file object already
56983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            filename = '???'
57083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.initfp(f)
57183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if filename[-5:] == '.aiff':
57283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._aifc = 0
57383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
57483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._aifc = 1
57583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
57683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def initfp(self, file):
57783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._file = file
57883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._version = _AIFC_version
57983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._comptype = 'NONE'
58083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._compname = 'not compressed'
58183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._comp = None
58283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._convert = None
58383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._nchannels = 0
58483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._sampwidth = 0
58583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._framerate = 0
58683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._nframes = 0
58783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._nframeswritten = 0
58883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._datawritten = 0
58983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._datalength = 0
59083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._markers = []
59183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._marklength = 0
59283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._aifc = 1      # AIFF-C is default
59383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
59483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __del__(self):
59583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._file:
59683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self.close()
59783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
59883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
59983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # User visible methods.
60083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
60183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def aiff(self):
60283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._nframeswritten:
60383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise Error, 'cannot change parameters after starting to write'
60483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._aifc = 0
60583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
60683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def aifc(self):
60783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._nframeswritten:
60883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise Error, 'cannot change parameters after starting to write'
60983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._aifc = 1
61083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
61183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def setnchannels(self, nchannels):
61283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._nframeswritten:
61383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise Error, 'cannot change parameters after starting to write'
61483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if nchannels < 1:
61583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise Error, 'bad # of channels'
61683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._nchannels = nchannels
61783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
61883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def getnchannels(self):
61983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if not self._nchannels:
62083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise Error, 'number of channels not set'
62183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return self._nchannels
62283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
62383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def setsampwidth(self, sampwidth):
62483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._nframeswritten:
62583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise Error, 'cannot change parameters after starting to write'
62683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if sampwidth < 1 or sampwidth > 4:
62783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise Error, 'bad sample width'
62883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._sampwidth = sampwidth
62983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
63083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def getsampwidth(self):
63183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if not self._sampwidth:
63283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise Error, 'sample width not set'
63383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return self._sampwidth
63483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
63583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def setframerate(self, framerate):
63683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._nframeswritten:
63783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise Error, 'cannot change parameters after starting to write'
63883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if framerate <= 0:
63983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise Error, 'bad frame rate'
64083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._framerate = framerate
64183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
64283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def getframerate(self):
64383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if not self._framerate:
64483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise Error, 'frame rate not set'
64583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return self._framerate
64683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
64783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def setnframes(self, nframes):
64883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._nframeswritten:
64983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise Error, 'cannot change parameters after starting to write'
65083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._nframes = nframes
65183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
65283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def getnframes(self):
65383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return self._nframeswritten
65483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
65583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def setcomptype(self, comptype, compname):
65683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._nframeswritten:
65783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise Error, 'cannot change parameters after starting to write'
65883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if comptype not in ('NONE', 'ULAW', 'ALAW', 'G722'):
65983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise Error, 'unsupported compression type'
66083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._comptype = comptype
66183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._compname = compname
66283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
66383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def getcomptype(self):
66483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return self._comptype
66583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
66683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def getcompname(self):
66783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return self._compname
66883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
66983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh##  def setversion(self, version):
67083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh##      if self._nframeswritten:
67183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh##          raise Error, 'cannot change parameters after starting to write'
67283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh##      self._version = version
67383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
67483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def setparams(self, info):
67583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        nchannels, sampwidth, framerate, nframes, comptype, compname = info
67683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._nframeswritten:
67783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise Error, 'cannot change parameters after starting to write'
67883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if comptype not in ('NONE', 'ULAW', 'ALAW', 'G722'):
67983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise Error, 'unsupported compression type'
68083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.setnchannels(nchannels)
68183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.setsampwidth(sampwidth)
68283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.setframerate(framerate)
68383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.setnframes(nframes)
68483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.setcomptype(comptype, compname)
68583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
68683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def getparams(self):
68783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if not self._nchannels or not self._sampwidth or not self._framerate:
68883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise Error, 'not all parameters set'
68983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return self._nchannels, self._sampwidth, self._framerate, \
69083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh              self._nframes, self._comptype, self._compname
69183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
69283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def setmark(self, id, pos, name):
69383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if id <= 0:
69483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise Error, 'marker ID must be > 0'
69583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if pos < 0:
69683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise Error, 'marker position must be >= 0'
69783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if type(name) != type(''):
69883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise Error, 'marker name must be a string'
69983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        for i in range(len(self._markers)):
70083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if id == self._markers[i][0]:
70183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                self._markers[i] = id, pos, name
70283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                return
70383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._markers.append((id, pos, name))
70483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
70583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def getmark(self, id):
70683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        for marker in self._markers:
70783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if id == marker[0]:
70883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                return marker
70983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        raise Error, 'marker %r does not exist' % (id,)
71083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
71183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def getmarkers(self):
71283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if len(self._markers) == 0:
71383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return None
71483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return self._markers
71583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
71683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def tell(self):
71783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return self._nframeswritten
71883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
71983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def writeframesraw(self, data):
72083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._ensure_header_written(len(data))
72183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        nframes = len(data) // (self._sampwidth * self._nchannels)
72283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._convert:
72383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            data = self._convert(data)
72483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._file.write(data)
72583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._nframeswritten = self._nframeswritten + nframes
72683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._datawritten = self._datawritten + len(data)
72783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
72883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def writeframes(self, data):
72983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.writeframesraw(data)
73083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._nframeswritten != self._nframes or \
73183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh              self._datalength != self._datawritten:
73283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._patchheader()
73383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
73483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def close(self):
73583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._file is None:
73683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return
73783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        try:
73883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._ensure_header_written(0)
73983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if self._datawritten & 1:
74083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                # quick pad to even size
74183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                self._file.write(chr(0))
74283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                self._datawritten = self._datawritten + 1
74383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._writemarkers()
74483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if self._nframeswritten != self._nframes or \
74583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                  self._datalength != self._datawritten or \
74683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                  self._marklength:
74783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                self._patchheader()
74883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if self._comp:
74983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                self._comp.CloseCompressor()
75083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                self._comp = None
75183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        finally:
75283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # Prevent ref cycles
75383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._convert = None
75483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            f = self._file
75583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._file = None
75683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            f.close()
75783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
75883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
75983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Internal methods.
76083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
76183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
76283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def _comp_data(self, data):
76383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        import cl
76483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        dummy = self._comp.SetParam(cl.FRAME_BUFFER_SIZE, len(data))
76583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        dummy = self._comp.SetParam(cl.COMPRESSED_BUFFER_SIZE, len(data))
76683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return self._comp.Compress(self._nframes, data)
76783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
76883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def _lin2ulaw(self, data):
76983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        import audioop
77083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return audioop.lin2ulaw(data, 2)
77183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
77283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def _lin2adpcm(self, data):
77383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        import audioop
77483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if not hasattr(self, '_adpcmstate'):
77583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._adpcmstate = None
77683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        data, self._adpcmstate = audioop.lin2adpcm(data, 2,
77783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                               self._adpcmstate)
77883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return data
77983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
78083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def _ensure_header_written(self, datasize):
78183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if not self._nframeswritten:
78283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if self._comptype in ('ULAW', 'ALAW'):
78383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                if not self._sampwidth:
78483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    self._sampwidth = 2
78583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                if self._sampwidth != 2:
78683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    raise Error, 'sample width must be 2 when compressing with ULAW or ALAW'
78783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if self._comptype == 'G722':
78883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                if not self._sampwidth:
78983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    self._sampwidth = 2
79083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                if self._sampwidth != 2:
79183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    raise Error, 'sample width must be 2 when compressing with G7.22 (ADPCM)'
79283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if not self._nchannels:
79383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                raise Error, '# channels not specified'
79483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if not self._sampwidth:
79583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                raise Error, 'sample width not specified'
79683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if not self._framerate:
79783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                raise Error, 'sampling rate not specified'
79883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._write_header(datasize)
79983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
80083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def _init_compression(self):
80183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._comptype == 'G722':
80283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._convert = self._lin2adpcm
80383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return
80483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        try:
80583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            import cl
80683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        except ImportError:
80783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if self._comptype == 'ULAW':
80883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                try:
80983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    import audioop
81083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    self._convert = self._lin2ulaw
81183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    return
81283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                except ImportError:
81383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    pass
81483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise Error, 'cannot write compressed AIFF-C files'
81583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._comptype == 'ULAW':
81683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            scheme = cl.G711_ULAW
81783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        elif self._comptype == 'ALAW':
81883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            scheme = cl.G711_ALAW
81983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
82083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise Error, 'unsupported compression type'
82183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._comp = cl.OpenCompressor(scheme)
82283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        params = [cl.ORIGINAL_FORMAT, 0,
82383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh              cl.BITS_PER_COMPONENT, self._sampwidth * 8,
82483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh              cl.FRAME_RATE, self._framerate,
82583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh              cl.FRAME_BUFFER_SIZE, 100,
82683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh              cl.COMPRESSED_BUFFER_SIZE, 100]
82783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._nchannels == 1:
82883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            params[1] = cl.MONO
82983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        elif self._nchannels == 2:
83083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            params[1] = cl.STEREO_INTERLEAVED
83183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
83283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise Error, 'cannot compress more than 2 channels'
83383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._comp.SetParams(params)
83483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # the compressor produces a header which we ignore
83583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        dummy = self._comp.Compress(0, '')
83683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._convert = self._comp_data
83783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
83883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def _write_header(self, initlength):
83983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._aifc and self._comptype != 'NONE':
84083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._init_compression()
84183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._file.write('FORM')
84283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if not self._nframes:
84383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._nframes = initlength // (self._nchannels * self._sampwidth)
84483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._datalength = self._nframes * self._nchannels * self._sampwidth
84583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._datalength & 1:
84683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._datalength = self._datalength + 1
84783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._aifc:
84883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if self._comptype in ('ULAW', 'ALAW'):
84983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                self._datalength = self._datalength // 2
85083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                if self._datalength & 1:
85183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    self._datalength = self._datalength + 1
85283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            elif self._comptype == 'G722':
85383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                self._datalength = (self._datalength + 3) // 4
85483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                if self._datalength & 1:
85583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    self._datalength = self._datalength + 1
85683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._form_length_pos = self._file.tell()
85783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        commlength = self._write_form_length(self._datalength)
85883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._aifc:
85983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._file.write('AIFC')
86083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._file.write('FVER')
86183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            _write_ulong(self._file, 4)
86283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            _write_ulong(self._file, self._version)
86383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
86483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._file.write('AIFF')
86583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._file.write('COMM')
86683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        _write_ulong(self._file, commlength)
86783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        _write_short(self._file, self._nchannels)
86883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._nframes_pos = self._file.tell()
86983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        _write_ulong(self._file, self._nframes)
87083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        _write_short(self._file, self._sampwidth * 8)
87183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        _write_float(self._file, self._framerate)
87283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._aifc:
87383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._file.write(self._comptype)
87483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            _write_string(self._file, self._compname)
87583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._file.write('SSND')
87683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._ssnd_length_pos = self._file.tell()
87783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        _write_ulong(self._file, self._datalength + 8)
87883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        _write_ulong(self._file, 0)
87983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        _write_ulong(self._file, 0)
88083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
88183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def _write_form_length(self, datalength):
88283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._aifc:
88383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            commlength = 18 + 5 + len(self._compname)
88483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if commlength & 1:
88583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                commlength = commlength + 1
88683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            verslength = 12
88783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
88883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            commlength = 18
88983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            verslength = 0
89083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        _write_ulong(self._file, 4 + verslength + self._marklength + \
89183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                     8 + commlength + 16 + datalength)
89283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return commlength
89383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
89483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def _patchheader(self):
89583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        curpos = self._file.tell()
89683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._datawritten & 1:
89783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            datalength = self._datawritten + 1
89883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._file.write(chr(0))
89983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
90083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            datalength = self._datawritten
90183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if datalength == self._datalength and \
90283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh              self._nframes == self._nframeswritten and \
90383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh              self._marklength == 0:
90483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._file.seek(curpos, 0)
90583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return
90683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._file.seek(self._form_length_pos, 0)
90783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        dummy = self._write_form_length(datalength)
90883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._file.seek(self._nframes_pos, 0)
90983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        _write_ulong(self._file, self._nframeswritten)
91083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._file.seek(self._ssnd_length_pos, 0)
91183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        _write_ulong(self._file, datalength + 8)
91283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._file.seek(curpos, 0)
91383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._nframes = self._nframeswritten
91483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._datalength = datalength
91583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
91683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def _writemarkers(self):
91783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if len(self._markers) == 0:
91883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return
91983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._file.write('MARK')
92083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        length = 2
92183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        for marker in self._markers:
92283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            id, pos, name = marker
92383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            length = length + len(name) + 1 + 6
92483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if len(name) & 1 == 0:
92583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                length = length + 1
92683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        _write_ulong(self._file, length)
92783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._marklength = length + 8
92883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        _write_short(self._file, len(self._markers))
92983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        for marker in self._markers:
93083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            id, pos, name = marker
93183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            _write_short(self._file, id)
93283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            _write_ulong(self._file, pos)
93383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            _write_string(self._file, name)
93483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
93583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef open(f, mode=None):
93683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if mode is None:
93783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if hasattr(f, 'mode'):
93883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            mode = f.mode
93983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
94083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            mode = 'rb'
94183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if mode in ('r', 'rb'):
94283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return Aifc_read(f)
94383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    elif mode in ('w', 'wb'):
94483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return Aifc_write(f)
94583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    else:
94683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        raise Error, "mode must be 'r', 'rb', 'w', or 'wb'"
94783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
94883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehopenfp = open # B/W compatibility
94983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
95083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehif __name__ == '__main__':
95183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    import sys
95283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if not sys.argv[1:]:
95383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        sys.argv.append('/usr/demos/data/audio/bach.aiff')
95483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    fn = sys.argv[1]
95583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    f = open(fn, 'r')
95683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    print "Reading", fn
95783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    print "nchannels =", f.getnchannels()
95883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    print "nframes   =", f.getnframes()
95983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    print "sampwidth =", f.getsampwidth()
96083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    print "framerate =", f.getframerate()
96183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    print "comptype  =", f.getcomptype()
96283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    print "compname  =", f.getcompname()
96383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if sys.argv[2:]:
96483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        gn = sys.argv[2]
96583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        print "Writing", gn
96683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        g = open(gn, 'w')
96783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        g.setparams(f.getparams())
96883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        while 1:
96983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            data = f.readframes(1024)
97083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if not data:
97183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                break
97283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            g.writeframes(data)
97383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        g.close()
97483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        f.close()
97583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        print "Done."
976