blob: 26b0ff5b7a30b7261581a7607fde18543cefde83 [file] [log] [blame]
Guido van Rossum4acc25b2000-02-02 15:10:15 +00001"""Stuff to parse AIFF-C and AIFF files.
2
3Unless explicitly stated otherwise, the description below is true
4both for AIFF-C files and AIFF files.
5
6An AIFF-C file has the following structure.
7
8 +-----------------+
9 | FORM |
10 +-----------------+
11 | <size> |
12 +----+------------+
13 | | AIFC |
14 | +------------+
15 | | <chunks> |
16 | | . |
17 | | . |
18 | | . |
19 +----+------------+
20
21An AIFF file has the string "AIFF" instead of "AIFC".
22
23A chunk consists of an identifier (4 bytes) followed by a size (4 bytes,
24big endian order), followed by the data. The size field does not include
25the size of the 8 byte header.
26
27The following chunk types are recognized.
28
29 FVER
30 <version number of AIFF-C defining document> (AIFF-C only).
31 MARK
32 <# of markers> (2 bytes)
33 list of markers:
34 <marker ID> (2 bytes, must be > 0)
35 <position> (4 bytes)
36 <marker name> ("pstring")
37 COMM
38 <# of channels> (2 bytes)
39 <# of sound frames> (4 bytes)
40 <size of the samples> (2 bytes)
41 <sampling frequency> (10 bytes, IEEE 80-bit extended
42 floating point)
43 in AIFF-C files only:
44 <compression type> (4 bytes)
45 <human-readable version of compression type> ("pstring")
46 SSND
47 <offset> (4 bytes, not used by this program)
48 <blocksize> (4 bytes, not used by this program)
49 <sound data>
50
51A pstring consists of 1 byte length, a string of characters, and 0 or 1
52byte pad to make the total length even.
53
54Usage.
55
56Reading AIFF files:
57 f = aifc.open(file, 'r')
58where file is either the name of a file or an open file pointer.
59The open file pointer must have methods read(), seek(), and close().
60In some types of audio files, if the setpos() method is not used,
61the seek() method is not necessary.
62
63This returns an instance of a class with the following public methods:
64 getnchannels() -- returns number of audio channels (1 for
65 mono, 2 for stereo)
66 getsampwidth() -- returns sample width in bytes
67 getframerate() -- returns sampling frequency
68 getnframes() -- returns number of audio frames
69 getcomptype() -- returns compression type ('NONE' for AIFF files)
70 getcompname() -- returns human-readable version of
71 compression type ('not compressed' for AIFF files)
72 getparams() -- returns a tuple consisting of all of the
73 above in the above order
74 getmarkers() -- get the list of marks in the audio file or None
75 if there are no marks
76 getmark(id) -- get mark with the specified id (raises an error
77 if the mark does not exist)
78 readframes(n) -- returns at most n frames of audio
79 rewind() -- rewind to the beginning of the audio stream
80 setpos(pos) -- seek to the specified position
81 tell() -- return the current position
82 close() -- close the instance (make it unusable)
83The position returned by tell(), the position given to setpos() and
84the position of marks are all compatible and have nothing to do with
Thomas Wouters7e474022000-07-16 12:04:32 +000085the actual position in the file.
Guido van Rossum4acc25b2000-02-02 15:10:15 +000086The close() method is called automatically when the class instance
87is destroyed.
88
89Writing AIFF files:
90 f = aifc.open(file, 'w')
91where file is either the name of a file or an open file pointer.
92The open file pointer must have methods write(), tell(), seek(), and
93close().
94
95This returns an instance of a class with the following public methods:
96 aiff() -- create an AIFF file (AIFF-C default)
97 aifc() -- create an AIFF-C file
98 setnchannels(n) -- set the number of channels
99 setsampwidth(n) -- set the sample width
100 setframerate(n) -- set the frame rate
101 setnframes(n) -- set the number of frames
102 setcomptype(type, name)
103 -- set the compression type and the
104 human-readable compression type
105 setparams(tuple)
106 -- set all parameters at once
107 setmark(id, pos, name)
108 -- add specified mark to the list of marks
109 tell() -- return current position in output file (useful
110 in combination with setmark())
111 writeframesraw(data)
112 -- write audio frames without pathing up the
113 file header
114 writeframes(data)
115 -- write audio frames and patch up the file header
116 close() -- patch up the file header and close the
117 output file
118You should set the parameters before the first writeframesraw or
119writeframes. The total number of frames does not need to be set,
120but when it is set to the correct value, the header does not have to
121be patched up.
122It is best to first set all parameters, perhaps possibly the
123compression type, and then write audio frames using writeframesraw.
124When all frames have been written, either call writeframes('') or
125close() to patch up the sizes in the header.
126Marks can be added anytime. If there are any marks, ypu must call
127close() after all frames have been written.
128The close() method is called automatically when the class instance
129is destroyed.
130
131When a file is opened with the extension '.aiff', an AIFF file is
132written, otherwise an AIFF-C file is written. This default can be
133changed by calling aiff() or aifc() before the first writeframes or
134writeframesraw.
135"""
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000136
Guido van Rossum36bb1811996-12-31 05:57:34 +0000137import struct
Georg Brandl1a3284e2007-12-02 09:40:06 +0000138import builtins
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000139
Georg Brandl2095cfe2008-06-07 19:01:03 +0000140__all__ = ["Error", "open", "openfp"]
Skip Montanaroe99d5ea2001-01-20 19:54:20 +0000141
Fred Drake227b1202000-08-17 05:06:49 +0000142class Error(Exception):
143 pass
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000144
Guido van Rossume2a383d2007-01-15 16:59:06 +0000145_AIFC_version = 0xA2805140 # Version 1 of AIFF-C
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000146
Georg Brandl2095cfe2008-06-07 19:01:03 +0000147_skiplist = b'COMT', b'INST', b'MIDI', b'AESD', \
148 b'APPL', b'NAME', b'AUTH', b'(c) ', b'ANNO'
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000149
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000150def _read_long(file):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000151 try:
152 return struct.unpack('>l', file.read(4))[0]
153 except struct.error:
154 raise EOFError
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000155
156def _read_ulong(file):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000157 try:
158 return struct.unpack('>L', file.read(4))[0]
159 except struct.error:
160 raise EOFError
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000161
162def _read_short(file):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000163 try:
164 return struct.unpack('>h', file.read(2))[0]
165 except struct.error:
166 raise EOFError
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000167
168def _read_string(file):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000169 length = ord(file.read(1))
170 if length == 0:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000171 data = b''
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000172 else:
173 data = file.read(length)
174 if length & 1 == 0:
175 dummy = file.read(1)
176 return data
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000177
178_HUGE_VAL = 1.79769313486231e+308 # See <limits.h>
179
180def _read_float(f): # 10 bytes
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000181 expon = _read_short(f) # 2 bytes
182 sign = 1
183 if expon < 0:
184 sign = -1
185 expon = expon + 0x8000
186 himant = _read_ulong(f) # 4 bytes
187 lomant = _read_ulong(f) # 4 bytes
188 if expon == himant == lomant == 0:
189 f = 0.0
190 elif expon == 0x7FFF:
191 f = _HUGE_VAL
192 else:
193 expon = expon - 16383
Guido van Rossume2a383d2007-01-15 16:59:06 +0000194 f = (himant * 0x100000000 + lomant) * pow(2.0, expon - 63)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000195 return sign * f
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000196
197def _write_short(f, x):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000198 f.write(struct.pack('>h', x))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000199
200def _write_long(f, x):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000201 f.write(struct.pack('>L', x))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000202
203def _write_string(f, s):
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000204 if len(s) > 255:
205 raise ValueError("string exceeds maximum pstring length")
Georg Brandl2095cfe2008-06-07 19:01:03 +0000206 f.write(struct.pack('b', len(s)))
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000207 f.write(s)
208 if len(s) & 1 == 0:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000209 f.write(b'\x00')
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000210
211def _write_float(f, x):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000212 import math
213 if x < 0:
214 sign = 0x8000
215 x = x * -1
216 else:
217 sign = 0
218 if x == 0:
219 expon = 0
220 himant = 0
221 lomant = 0
222 else:
223 fmant, expon = math.frexp(x)
224 if expon > 16384 or fmant >= 1: # Infinity or NaN
225 expon = sign|0x7FFF
226 himant = 0
227 lomant = 0
228 else: # Finite
229 expon = expon + 16382
230 if expon < 0: # denormalized
231 fmant = math.ldexp(fmant, expon)
232 expon = 0
233 expon = expon | sign
234 fmant = math.ldexp(fmant, 32)
235 fsmant = math.floor(fmant)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000236 himant = int(fsmant)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000237 fmant = math.ldexp(fmant - fsmant, 32)
238 fsmant = math.floor(fmant)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000239 lomant = int(fsmant)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000240 _write_short(f, expon)
241 _write_long(f, himant)
242 _write_long(f, lomant)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000243
Guido van Rossum8ea7bb81999-06-09 13:32:28 +0000244from chunk import Chunk
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000245
Guido van Rossumd3166071993-05-24 14:16:22 +0000246class Aifc_read:
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000247 # Variables used in this class:
248 #
249 # These variables are available to the user though appropriate
250 # methods of this class:
251 # _file -- the open file with methods read(), close(), and seek()
252 # set through the __init__() method
253 # _nchannels -- the number of audio channels
254 # available through the getnchannels() method
255 # _nframes -- the number of audio frames
256 # available through the getnframes() method
257 # _sampwidth -- the number of bytes per audio sample
258 # available through the getsampwidth() method
259 # _framerate -- the sampling frequency
260 # available through the getframerate() method
261 # _comptype -- the AIFF-C compression type ('NONE' if AIFF)
262 # available through the getcomptype() method
263 # _compname -- the human-readable AIFF-C compression type
264 # available through the getcomptype() method
265 # _markers -- the marks in the audio file
266 # available through the getmarkers() and getmark()
267 # methods
268 # _soundpos -- the position in the audio stream
269 # available through the tell() method, set through the
270 # setpos() method
271 #
272 # These variables are used internally only:
273 # _version -- the AIFF-C version number
274 # _decomp -- the decompressor from builtin module cl
275 # _comm_chunk_read -- 1 iff the COMM chunk has been read
276 # _aifc -- 1 iff reading an AIFF-C file
277 # _ssnd_seek_needed -- 1 iff positioned correctly in audio
278 # file for readframes()
279 # _ssnd_chunk -- instantiation of a chunk class for the SSND chunk
280 # _framesize -- size of one frame in the file
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000281
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000282 def initfp(self, file):
283 self._version = 0
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000284 self._convert = None
285 self._markers = []
286 self._soundpos = 0
287 self._file = Chunk(file)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000288 if self._file.getname() != b'FORM':
Collin Winterce36ad82007-08-30 01:19:48 +0000289 raise Error('file does not start with FORM id')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000290 formdata = self._file.read(4)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000291 if formdata == b'AIFF':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000292 self._aifc = 0
Georg Brandl2095cfe2008-06-07 19:01:03 +0000293 elif formdata == b'AIFC':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000294 self._aifc = 1
295 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000296 raise Error('not an AIFF or AIFF-C file')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000297 self._comm_chunk_read = 0
298 while 1:
299 self._ssnd_seek_needed = 1
300 try:
301 chunk = Chunk(self._file)
302 except EOFError:
303 break
304 chunkname = chunk.getname()
Georg Brandl2095cfe2008-06-07 19:01:03 +0000305 if chunkname == b'COMM':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000306 self._read_comm_chunk(chunk)
307 self._comm_chunk_read = 1
Georg Brandl2095cfe2008-06-07 19:01:03 +0000308 elif chunkname == b'SSND':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000309 self._ssnd_chunk = chunk
310 dummy = chunk.read(8)
311 self._ssnd_seek_needed = 0
Georg Brandl2095cfe2008-06-07 19:01:03 +0000312 elif chunkname == b'FVER':
Guido van Rossum820819c2002-08-12 22:11:28 +0000313 self._version = _read_ulong(chunk)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000314 elif chunkname == b'MARK':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000315 self._readmark(chunk)
316 elif chunkname in _skiplist:
317 pass
318 else:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000319 raise Error('unrecognized chunk type ' +
320 chunkname.decode('latin1'))
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000321 chunk.skip()
322 if not self._comm_chunk_read or not self._ssnd_chunk:
Collin Winterce36ad82007-08-30 01:19:48 +0000323 raise Error('COMM chunk and/or SSND chunk missing')
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000324
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000325 def __init__(self, f):
Georg Brandl2095cfe2008-06-07 19:01:03 +0000326 if isinstance(f, str):
Georg Brandl1a3284e2007-12-02 09:40:06 +0000327 f = builtins.open(f, 'rb')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000328 # else, assume it is an open file object already
329 self.initfp(f)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000330
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000331 #
332 # User visible methods.
333 #
334 def getfp(self):
335 return self._file
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000336
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000337 def rewind(self):
338 self._ssnd_seek_needed = 1
339 self._soundpos = 0
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000340
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000341 def close(self):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000342 self._file = None
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000343
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000344 def tell(self):
345 return self._soundpos
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000346
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000347 def getnchannels(self):
348 return self._nchannels
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000349
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000350 def getnframes(self):
351 return self._nframes
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000352
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000353 def getsampwidth(self):
354 return self._sampwidth
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000355
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000356 def getframerate(self):
357 return self._framerate
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000358
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000359 def getcomptype(self):
360 return self._comptype
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000361
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000362 def getcompname(self):
363 return self._compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000364
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000365## def getversion(self):
366## return self._version
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000367
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000368 def getparams(self):
369 return self.getnchannels(), self.getsampwidth(), \
370 self.getframerate(), self.getnframes(), \
371 self.getcomptype(), self.getcompname()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000372
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000373 def getmarkers(self):
374 if len(self._markers) == 0:
375 return None
376 return self._markers
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000377
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000378 def getmark(self, id):
379 for marker in self._markers:
380 if id == marker[0]:
381 return marker
Georg Brandl2095cfe2008-06-07 19:01:03 +0000382 raise Error('marker {0!r} does not exist'.format(id))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000383
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000384 def setpos(self, pos):
385 if pos < 0 or pos > self._nframes:
Collin Winterce36ad82007-08-30 01:19:48 +0000386 raise Error('position not in range')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000387 self._soundpos = pos
388 self._ssnd_seek_needed = 1
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000389
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000390 def readframes(self, nframes):
391 if self._ssnd_seek_needed:
392 self._ssnd_chunk.seek(0)
393 dummy = self._ssnd_chunk.read(8)
394 pos = self._soundpos * self._framesize
395 if pos:
Guido van Rossum2663c132000-03-07 15:19:31 +0000396 self._ssnd_chunk.seek(pos + 8)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000397 self._ssnd_seek_needed = 0
398 if nframes == 0:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000399 return b''
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000400 data = self._ssnd_chunk.read(nframes * self._framesize)
401 if self._convert and data:
402 data = self._convert(data)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000403 self._soundpos = self._soundpos + len(data) // (self._nchannels
404 * self._sampwidth)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000405 return data
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000406
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000407 #
408 # Internal methods.
409 #
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000410
Georg Brandl2095cfe2008-06-07 19:01:03 +0000411 def _alaw2lin(self, data):
412 import audioop
413 return audioop.alaw2lin(data, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000414
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000415 def _ulaw2lin(self, data):
416 import audioop
417 return audioop.ulaw2lin(data, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000418
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000419 def _adpcm2lin(self, data):
420 import audioop
421 if not hasattr(self, '_adpcmstate'):
422 # first time
423 self._adpcmstate = None
Georg Brandl2095cfe2008-06-07 19:01:03 +0000424 data, self._adpcmstate = audioop.adpcm2lin(data, 2, self._adpcmstate)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000425 return data
Sjoerd Mullender1f057541994-09-06 16:17:51 +0000426
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000427 def _read_comm_chunk(self, chunk):
428 self._nchannels = _read_short(chunk)
429 self._nframes = _read_long(chunk)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000430 self._sampwidth = (_read_short(chunk) + 7) // 8
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000431 self._framerate = int(_read_float(chunk))
432 self._framesize = self._nchannels * self._sampwidth
433 if self._aifc:
434 #DEBUG: SGI's soundeditor produces a bad size :-(
435 kludge = 0
436 if chunk.chunksize == 18:
437 kludge = 1
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000438 print('Warning: bad COMM chunk size')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000439 chunk.chunksize = 23
440 #DEBUG end
441 self._comptype = chunk.read(4)
442 #DEBUG start
443 if kludge:
444 length = ord(chunk.file.read(1))
445 if length & 1 == 0:
446 length = length + 1
447 chunk.chunksize = chunk.chunksize + length
448 chunk.file.seek(-1, 1)
449 #DEBUG end
450 self._compname = _read_string(chunk)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000451 if self._comptype != b'NONE':
452 if self._comptype == b'G722':
453 self._convert = self._adpcm2lin
454 self._framesize = self._framesize // 4
455 elif self._comptype in (b'ulaw', b'ULAW'):
456 self._convert = self._ulaw2lin
457 self._framesize = self._framesize // 2
458 elif self._comptype in (b'alaw', b'ALAW'):
459 self._convert = self._alaw2lin
460 self._framesize = self._framesize // 2
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000461 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000462 raise Error('unsupported compression type')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000463 else:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000464 self._comptype = b'NONE'
465 self._compname = b'not compressed'
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000466
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000467 def _readmark(self, chunk):
468 nmarkers = _read_short(chunk)
469 # Some files appear to contain invalid counts.
470 # Cope with this by testing for EOF.
471 try:
472 for i in range(nmarkers):
473 id = _read_short(chunk)
474 pos = _read_long(chunk)
475 name = _read_string(chunk)
476 if pos or name:
477 # some files appear to have
478 # dummy markers consisting of
479 # a position 0 and name ''
480 self._markers.append((id, pos, name))
481 except EOFError:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000482 print('Warning: MARK chunk contains only', end=' ')
483 print(len(self._markers), end=' ')
484 if len(self._markers) == 1: print('marker', end=' ')
485 else: print('markers', end=' ')
486 print('instead of', nmarkers)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000487
Guido van Rossumd3166071993-05-24 14:16:22 +0000488class Aifc_write:
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000489 # Variables used in this class:
490 #
491 # These variables are user settable through appropriate methods
492 # of this class:
493 # _file -- the open file with methods write(), close(), tell(), seek()
494 # set through the __init__() method
495 # _comptype -- the AIFF-C compression type ('NONE' in AIFF)
496 # set through the setcomptype() or setparams() method
497 # _compname -- the human-readable AIFF-C compression type
498 # set through the setcomptype() or setparams() method
499 # _nchannels -- the number of audio channels
500 # set through the setnchannels() or setparams() method
501 # _sampwidth -- the number of bytes per audio sample
502 # set through the setsampwidth() or setparams() method
503 # _framerate -- the sampling frequency
504 # set through the setframerate() or setparams() method
505 # _nframes -- the number of audio frames written to the header
506 # set through the setnframes() or setparams() method
507 # _aifc -- whether we're writing an AIFF-C file or an AIFF file
508 # set through the aifc() method, reset through the
509 # aiff() method
510 #
511 # These variables are used internally only:
512 # _version -- the AIFF-C version number
513 # _comp -- the compressor from builtin module cl
514 # _nframeswritten -- the number of audio frames actually written
515 # _datalength -- the size of the audio samples written to the header
516 # _datawritten -- the size of the audio samples actually written
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000517
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000518 def __init__(self, f):
Georg Brandl2095cfe2008-06-07 19:01:03 +0000519 if isinstance(f, str):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000520 filename = f
Georg Brandl1a3284e2007-12-02 09:40:06 +0000521 f = builtins.open(f, 'wb')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000522 else:
523 # else, assume it is an open file object already
524 filename = '???'
525 self.initfp(f)
526 if filename[-5:] == '.aiff':
527 self._aifc = 0
528 else:
529 self._aifc = 1
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000530
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000531 def initfp(self, file):
532 self._file = file
533 self._version = _AIFC_version
Georg Brandl2095cfe2008-06-07 19:01:03 +0000534 self._comptype = b'NONE'
535 self._compname = b'not compressed'
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000536 self._convert = None
537 self._nchannels = 0
538 self._sampwidth = 0
539 self._framerate = 0
540 self._nframes = 0
541 self._nframeswritten = 0
542 self._datawritten = 0
543 self._datalength = 0
544 self._markers = []
545 self._marklength = 0
546 self._aifc = 1 # AIFF-C is default
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000547
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000548 def __del__(self):
549 if self._file:
550 self.close()
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000551
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000552 #
553 # User visible methods.
554 #
555 def aiff(self):
556 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000557 raise Error('cannot change parameters after starting to write')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000558 self._aifc = 0
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000559
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000560 def aifc(self):
561 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000562 raise Error('cannot change parameters after starting to write')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000563 self._aifc = 1
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000564
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000565 def setnchannels(self, nchannels):
566 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000567 raise Error('cannot change parameters after starting to write')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000568 if nchannels < 1:
Collin Winterce36ad82007-08-30 01:19:48 +0000569 raise Error('bad # of channels')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000570 self._nchannels = nchannels
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000571
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000572 def getnchannels(self):
573 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000574 raise Error('number of channels not set')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000575 return self._nchannels
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000576
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000577 def setsampwidth(self, sampwidth):
578 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000579 raise Error('cannot change parameters after starting to write')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000580 if sampwidth < 1 or sampwidth > 4:
Collin Winterce36ad82007-08-30 01:19:48 +0000581 raise Error('bad sample width')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000582 self._sampwidth = sampwidth
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000583
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000584 def getsampwidth(self):
585 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000586 raise Error('sample width not set')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000587 return self._sampwidth
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000588
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000589 def setframerate(self, framerate):
590 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000591 raise Error('cannot change parameters after starting to write')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000592 if framerate <= 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000593 raise Error('bad frame rate')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000594 self._framerate = framerate
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000595
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000596 def getframerate(self):
597 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000598 raise Error('frame rate not set')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000599 return self._framerate
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000600
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000601 def setnframes(self, nframes):
602 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000603 raise Error('cannot change parameters after starting to write')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000604 self._nframes = nframes
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000605
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000606 def getnframes(self):
607 return self._nframeswritten
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000608
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000609 def setcomptype(self, comptype, compname):
610 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000611 raise Error('cannot change parameters after starting to write')
Georg Brandl2095cfe2008-06-07 19:01:03 +0000612 if comptype not in (b'NONE', b'ulaw', b'ULAW',
613 b'alaw', b'ALAW', b'G722'):
Collin Winterce36ad82007-08-30 01:19:48 +0000614 raise Error('unsupported compression type')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000615 self._comptype = comptype
616 self._compname = compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000617
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000618 def getcomptype(self):
619 return self._comptype
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000620
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000621 def getcompname(self):
622 return self._compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000623
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000624## def setversion(self, version):
625## if self._nframeswritten:
626## raise Error, 'cannot change parameters after starting to write'
627## self._version = version
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000628
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000629 def setparams(self, params):
630 nchannels, sampwidth, framerate, nframes, comptype, compname = params
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000631 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000632 raise Error('cannot change parameters after starting to write')
Georg Brandl2095cfe2008-06-07 19:01:03 +0000633 if comptype not in (b'NONE', b'ulaw', b'ULAW',
634 b'alaw', b'ALAW', b'G722'):
Collin Winterce36ad82007-08-30 01:19:48 +0000635 raise Error('unsupported compression type')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000636 self.setnchannels(nchannels)
637 self.setsampwidth(sampwidth)
638 self.setframerate(framerate)
639 self.setnframes(nframes)
640 self.setcomptype(comptype, compname)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000641
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000642 def getparams(self):
643 if not self._nchannels or not self._sampwidth or not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000644 raise Error('not all parameters set')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000645 return self._nchannels, self._sampwidth, self._framerate, \
646 self._nframes, self._comptype, self._compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000647
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000648 def setmark(self, id, pos, name):
649 if id <= 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000650 raise Error('marker ID must be > 0')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000651 if pos < 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000652 raise Error('marker position must be >= 0')
Georg Brandl2095cfe2008-06-07 19:01:03 +0000653 if not isinstance(name, str):
Collin Winterce36ad82007-08-30 01:19:48 +0000654 raise Error('marker name must be a string')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000655 for i in range(len(self._markers)):
656 if id == self._markers[i][0]:
657 self._markers[i] = id, pos, name
658 return
659 self._markers.append((id, pos, name))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000660
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000661 def getmark(self, id):
662 for marker in self._markers:
663 if id == marker[0]:
664 return marker
Georg Brandl2095cfe2008-06-07 19:01:03 +0000665 raise Error('marker {0!r} does not exist'.format(id))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000666
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000667 def getmarkers(self):
668 if len(self._markers) == 0:
669 return None
670 return self._markers
Tim Peters146965a2001-01-14 18:09:23 +0000671
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000672 def tell(self):
673 return self._nframeswritten
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000674
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000675 def writeframesraw(self, data):
676 self._ensure_header_written(len(data))
Georg Brandl2095cfe2008-06-07 19:01:03 +0000677 nframes = len(data) // (self._sampwidth * self._nchannels)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000678 if self._convert:
679 data = self._convert(data)
680 self._file.write(data)
681 self._nframeswritten = self._nframeswritten + nframes
682 self._datawritten = self._datawritten + len(data)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000683
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000684 def writeframes(self, data):
685 self.writeframesraw(data)
686 if self._nframeswritten != self._nframes or \
687 self._datalength != self._datawritten:
688 self._patchheader()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000689
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000690 def close(self):
691 self._ensure_header_written(0)
692 if self._datawritten & 1:
693 # quick pad to even size
Georg Brandl2095cfe2008-06-07 19:01:03 +0000694 self._file.write(b'\x00')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000695 self._datawritten = self._datawritten + 1
696 self._writemarkers()
697 if self._nframeswritten != self._nframes or \
698 self._datalength != self._datawritten or \
699 self._marklength:
700 self._patchheader()
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000701 self._file.flush()
702 self._file = None
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000703
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000704 #
705 # Internal methods.
706 #
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000707
Georg Brandl2095cfe2008-06-07 19:01:03 +0000708 def _lin2alaw(self, data):
709 import audioop
710 return audioop.lin2alaw(data, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000711
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000712 def _lin2ulaw(self, data):
713 import audioop
714 return audioop.lin2ulaw(data, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000715
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000716 def _lin2adpcm(self, data):
717 import audioop
718 if not hasattr(self, '_adpcmstate'):
719 self._adpcmstate = None
Georg Brandl2095cfe2008-06-07 19:01:03 +0000720 data, self._adpcmstate = audioop.lin2adpcm(data, 2, self._adpcmstate)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000721 return data
Sjoerd Mullender1f057541994-09-06 16:17:51 +0000722
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000723 def _ensure_header_written(self, datasize):
724 if not self._nframeswritten:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000725 if self._comptype in (b'ULAW', b'ALAW'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000726 if not self._sampwidth:
727 self._sampwidth = 2
728 if self._sampwidth != 2:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000729 raise Error('sample width must be 2 when compressing '
730 'with ulaw/ULAW or alaw/ALAW')
731 if self._comptype == b'G722':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000732 if not self._sampwidth:
733 self._sampwidth = 2
734 if self._sampwidth != 2:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000735 raise Error('sample width must be 2 when compressing '
736 'with G7.22 (ADPCM)')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000737 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000738 raise Error('# channels not specified')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000739 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000740 raise Error('sample width not specified')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000741 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000742 raise Error('sampling rate not specified')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000743 self._write_header(datasize)
Guido van Rossum52fc1f61993-06-17 12:38:10 +0000744
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000745 def _init_compression(self):
Georg Brandl2095cfe2008-06-07 19:01:03 +0000746 if self._comptype == b'G722':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000747 self._convert = self._lin2adpcm
Georg Brandl2095cfe2008-06-07 19:01:03 +0000748 elif self._comptype in (b'ulaw', b'ULAW'):
749 self._convert = self._lin2ulaw
750 elif self._comptype in (b'alaw', b'ALAW'):
751 self._convert = self._lin2alaw
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000752 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000753 raise Error('unsupported compression type')
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000754
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000755 def _write_header(self, initlength):
Georg Brandl2095cfe2008-06-07 19:01:03 +0000756 if self._aifc and self._comptype != b'NONE':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000757 self._init_compression()
Georg Brandl2095cfe2008-06-07 19:01:03 +0000758 self._file.write(b'FORM')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000759 if not self._nframes:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000760 self._nframes = initlength // (self._nchannels * self._sampwidth)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000761 self._datalength = self._nframes * self._nchannels * self._sampwidth
762 if self._datalength & 1:
763 self._datalength = self._datalength + 1
764 if self._aifc:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000765 if self._comptype in (b'ulaw', b'ULAW', b'alaw', b'ALAW'):
766 self._datalength = self._datalength // 2
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000767 if self._datalength & 1:
768 self._datalength = self._datalength + 1
Georg Brandl2095cfe2008-06-07 19:01:03 +0000769 elif self._comptype == b'G722':
770 self._datalength = (self._datalength + 3) // 4
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000771 if self._datalength & 1:
772 self._datalength = self._datalength + 1
773 self._form_length_pos = self._file.tell()
774 commlength = self._write_form_length(self._datalength)
775 if self._aifc:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000776 self._file.write(b'AIFC')
777 self._file.write(b'FVER')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000778 _write_long(self._file, 4)
779 _write_long(self._file, self._version)
780 else:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000781 self._file.write(b'AIFF')
782 self._file.write(b'COMM')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000783 _write_long(self._file, commlength)
784 _write_short(self._file, self._nchannels)
785 self._nframes_pos = self._file.tell()
786 _write_long(self._file, self._nframes)
787 _write_short(self._file, self._sampwidth * 8)
788 _write_float(self._file, self._framerate)
789 if self._aifc:
790 self._file.write(self._comptype)
791 _write_string(self._file, self._compname)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000792 self._file.write(b'SSND')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000793 self._ssnd_length_pos = self._file.tell()
794 _write_long(self._file, self._datalength + 8)
795 _write_long(self._file, 0)
796 _write_long(self._file, 0)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000797
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000798 def _write_form_length(self, datalength):
799 if self._aifc:
800 commlength = 18 + 5 + len(self._compname)
801 if commlength & 1:
802 commlength = commlength + 1
803 verslength = 12
804 else:
805 commlength = 18
806 verslength = 0
807 _write_long(self._file, 4 + verslength + self._marklength + \
808 8 + commlength + 16 + datalength)
809 return commlength
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000810
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000811 def _patchheader(self):
812 curpos = self._file.tell()
813 if self._datawritten & 1:
814 datalength = self._datawritten + 1
Georg Brandl2095cfe2008-06-07 19:01:03 +0000815 self._file.write(b'\x00')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000816 else:
817 datalength = self._datawritten
818 if datalength == self._datalength and \
819 self._nframes == self._nframeswritten and \
820 self._marklength == 0:
821 self._file.seek(curpos, 0)
822 return
823 self._file.seek(self._form_length_pos, 0)
824 dummy = self._write_form_length(datalength)
825 self._file.seek(self._nframes_pos, 0)
826 _write_long(self._file, self._nframeswritten)
827 self._file.seek(self._ssnd_length_pos, 0)
828 _write_long(self._file, datalength + 8)
829 self._file.seek(curpos, 0)
830 self._nframes = self._nframeswritten
831 self._datalength = datalength
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000832
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000833 def _writemarkers(self):
834 if len(self._markers) == 0:
835 return
Georg Brandl2095cfe2008-06-07 19:01:03 +0000836 self._file.write(b'MARK')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000837 length = 2
838 for marker in self._markers:
839 id, pos, name = marker
840 length = length + len(name) + 1 + 6
841 if len(name) & 1 == 0:
842 length = length + 1
843 _write_long(self._file, length)
844 self._marklength = length + 8
845 _write_short(self._file, len(self._markers))
846 for marker in self._markers:
847 id, pos, name = marker
848 _write_short(self._file, id)
849 _write_long(self._file, pos)
850 _write_string(self._file, name)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000851
Fred Drake43161351999-06-22 21:23:23 +0000852def open(f, mode=None):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000853 if mode is None:
854 if hasattr(f, 'mode'):
855 mode = f.mode
856 else:
857 mode = 'rb'
858 if mode in ('r', 'rb'):
859 return Aifc_read(f)
860 elif mode in ('w', 'wb'):
861 return Aifc_write(f)
862 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000863 raise Error("mode must be 'r', 'rb', 'w', or 'wb'")
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000864
Guido van Rossum7bc817d1993-12-17 15:25:27 +0000865openfp = open # B/W compatibility
Guido van Rossum36bb1811996-12-31 05:57:34 +0000866
867if __name__ == '__main__':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000868 import sys
869 if not sys.argv[1:]:
870 sys.argv.append('/usr/demos/data/audio/bach.aiff')
871 fn = sys.argv[1]
872 f = open(fn, 'r')
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000873 print("Reading", fn)
874 print("nchannels =", f.getnchannels())
875 print("nframes =", f.getnframes())
876 print("sampwidth =", f.getsampwidth())
877 print("framerate =", f.getframerate())
878 print("comptype =", f.getcomptype())
879 print("compname =", f.getcompname())
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000880 if sys.argv[2:]:
881 gn = sys.argv[2]
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000882 print("Writing", gn)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000883 g = open(gn, 'w')
884 g.setparams(f.getparams())
885 while 1:
886 data = f.readframes(1024)
887 if not data:
888 break
889 g.writeframes(data)
890 g.close()
891 f.close()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000892 print("Done.")