blob: e6783277a471c1adcb50a3c5515743076c7fc842 [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.
Ezio Melottif5469cf2013-08-17 15:43:51 +0300126Marks can be added anytime. If there are any marks, you must call
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000127close() 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
Guido van Rossum3db6ebc1994-01-28 09:59:35 +0000138import __builtin__
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000139
Skip Montanaroe99d5ea2001-01-20 19:54:20 +0000140__all__ = ["Error","open","openfp"]
141
Fred Drake227b1202000-08-17 05:06:49 +0000142class Error(Exception):
143 pass
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000144
Guido van Rossum820819c2002-08-12 22:11:28 +0000145_AIFC_version = 0xA2805140L # Version 1 of AIFF-C
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000146
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000147def _read_long(file):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000148 try:
149 return struct.unpack('>l', file.read(4))[0]
150 except struct.error:
151 raise EOFError
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000152
153def _read_ulong(file):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000154 try:
155 return struct.unpack('>L', file.read(4))[0]
156 except struct.error:
157 raise EOFError
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000158
159def _read_short(file):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000160 try:
161 return struct.unpack('>h', file.read(2))[0]
162 except struct.error:
163 raise EOFError
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000164
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100165def _read_ushort(file):
166 try:
167 return struct.unpack('>H', file.read(2))[0]
168 except struct.error:
169 raise EOFError
170
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000171def _read_string(file):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000172 length = ord(file.read(1))
173 if length == 0:
174 data = ''
175 else:
176 data = file.read(length)
177 if length & 1 == 0:
178 dummy = file.read(1)
179 return data
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000180
181_HUGE_VAL = 1.79769313486231e+308 # See <limits.h>
182
183def _read_float(f): # 10 bytes
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000184 expon = _read_short(f) # 2 bytes
185 sign = 1
186 if expon < 0:
187 sign = -1
188 expon = expon + 0x8000
189 himant = _read_ulong(f) # 4 bytes
190 lomant = _read_ulong(f) # 4 bytes
191 if expon == himant == lomant == 0:
192 f = 0.0
193 elif expon == 0x7FFF:
194 f = _HUGE_VAL
195 else:
196 expon = expon - 16383
197 f = (himant * 0x100000000L + lomant) * pow(2.0, expon - 63)
198 return sign * f
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000199
200def _write_short(f, x):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000201 f.write(struct.pack('>h', x))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000202
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100203def _write_ushort(f, x):
204 f.write(struct.pack('>H', x))
205
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000206def _write_long(f, x):
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100207 f.write(struct.pack('>l', x))
208
209def _write_ulong(f, x):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000210 f.write(struct.pack('>L', x))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000211
212def _write_string(f, s):
Bob Ippolito6067f202006-05-30 00:26:01 +0000213 if len(s) > 255:
214 raise ValueError("string exceeds maximum pstring length")
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100215 f.write(struct.pack('B', len(s)))
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000216 f.write(s)
217 if len(s) & 1 == 0:
218 f.write(chr(0))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000219
220def _write_float(f, x):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000221 import math
222 if x < 0:
223 sign = 0x8000
224 x = x * -1
225 else:
226 sign = 0
227 if x == 0:
228 expon = 0
229 himant = 0
230 lomant = 0
231 else:
232 fmant, expon = math.frexp(x)
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100233 if expon > 16384 or fmant >= 1 or fmant != fmant: # Infinity or NaN
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000234 expon = sign|0x7FFF
235 himant = 0
236 lomant = 0
237 else: # Finite
238 expon = expon + 16382
239 if expon < 0: # denormalized
240 fmant = math.ldexp(fmant, expon)
241 expon = 0
242 expon = expon | sign
243 fmant = math.ldexp(fmant, 32)
244 fsmant = math.floor(fmant)
245 himant = long(fsmant)
246 fmant = math.ldexp(fmant - fsmant, 32)
247 fsmant = math.floor(fmant)
248 lomant = long(fsmant)
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100249 _write_ushort(f, expon)
250 _write_ulong(f, himant)
251 _write_ulong(f, lomant)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000252
Guido van Rossum8ea7bb81999-06-09 13:32:28 +0000253from chunk import Chunk
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000254
Guido van Rossumd3166071993-05-24 14:16:22 +0000255class Aifc_read:
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000256 # Variables used in this class:
257 #
258 # These variables are available to the user though appropriate
259 # methods of this class:
260 # _file -- the open file with methods read(), close(), and seek()
261 # set through the __init__() method
262 # _nchannels -- the number of audio channels
263 # available through the getnchannels() method
264 # _nframes -- the number of audio frames
265 # available through the getnframes() method
266 # _sampwidth -- the number of bytes per audio sample
267 # available through the getsampwidth() method
268 # _framerate -- the sampling frequency
269 # available through the getframerate() method
270 # _comptype -- the AIFF-C compression type ('NONE' if AIFF)
271 # available through the getcomptype() method
272 # _compname -- the human-readable AIFF-C compression type
273 # available through the getcomptype() method
274 # _markers -- the marks in the audio file
275 # available through the getmarkers() and getmark()
276 # methods
277 # _soundpos -- the position in the audio stream
278 # available through the tell() method, set through the
279 # setpos() method
280 #
281 # These variables are used internally only:
282 # _version -- the AIFF-C version number
283 # _decomp -- the decompressor from builtin module cl
284 # _comm_chunk_read -- 1 iff the COMM chunk has been read
285 # _aifc -- 1 iff reading an AIFF-C file
286 # _ssnd_seek_needed -- 1 iff positioned correctly in audio
287 # file for readframes()
288 # _ssnd_chunk -- instantiation of a chunk class for the SSND chunk
289 # _framesize -- size of one frame in the file
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000290
INADA Naoki02eb4b02017-02-28 20:39:30 +0900291 _file = None # Set here since __del__ checks it
292
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000293 def initfp(self, file):
294 self._version = 0
295 self._decomp = None
296 self._convert = None
297 self._markers = []
298 self._soundpos = 0
R. David Murray8fd522f2009-05-07 16:27:02 +0000299 self._file = file
300 chunk = Chunk(file)
301 if chunk.getname() != 'FORM':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000302 raise Error, 'file does not start with FORM id'
R. David Murray8fd522f2009-05-07 16:27:02 +0000303 formdata = chunk.read(4)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000304 if formdata == 'AIFF':
305 self._aifc = 0
306 elif formdata == 'AIFC':
307 self._aifc = 1
308 else:
309 raise Error, 'not an AIFF or AIFF-C file'
310 self._comm_chunk_read = 0
311 while 1:
312 self._ssnd_seek_needed = 1
313 try:
314 chunk = Chunk(self._file)
315 except EOFError:
316 break
317 chunkname = chunk.getname()
318 if chunkname == 'COMM':
319 self._read_comm_chunk(chunk)
320 self._comm_chunk_read = 1
321 elif chunkname == 'SSND':
322 self._ssnd_chunk = chunk
323 dummy = chunk.read(8)
324 self._ssnd_seek_needed = 0
325 elif chunkname == 'FVER':
Guido van Rossum820819c2002-08-12 22:11:28 +0000326 self._version = _read_ulong(chunk)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000327 elif chunkname == 'MARK':
328 self._readmark(chunk)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000329 chunk.skip()
330 if not self._comm_chunk_read or not self._ssnd_chunk:
331 raise Error, 'COMM chunk and/or SSND chunk missing'
332 if self._aifc and self._decomp:
333 import cl
334 params = [cl.ORIGINAL_FORMAT, 0,
335 cl.BITS_PER_COMPONENT, self._sampwidth * 8,
336 cl.FRAME_RATE, self._framerate]
337 if self._nchannels == 1:
338 params[1] = cl.MONO
339 elif self._nchannels == 2:
340 params[1] = cl.STEREO_INTERLEAVED
341 else:
342 raise Error, 'cannot compress more than 2 channels'
343 self._decomp.SetParams(params)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000344
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000345 def __init__(self, f):
INADA Naoki02eb4b02017-02-28 20:39:30 +0900346 if isinstance(f, basestring):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000347 f = __builtin__.open(f, 'rb')
INADA Naoki02eb4b02017-02-28 20:39:30 +0900348 try:
349 self.initfp(f)
350 except:
351 f.close()
352 raise
353 else:
354 # assume it is an open file object already
355 self.initfp(f)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000356
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000357 #
358 # User visible methods.
359 #
360 def getfp(self):
361 return self._file
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000362
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000363 def rewind(self):
364 self._ssnd_seek_needed = 1
365 self._soundpos = 0
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000366
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000367 def close(self):
Serhiy Storchaka1aa2c0f2015-04-10 13:24:10 +0300368 decomp = self._decomp
369 try:
370 if decomp:
371 self._decomp = None
372 decomp.CloseDecompressor()
373 finally:
374 self._file.close()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000375
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000376 def tell(self):
377 return self._soundpos
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000378
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000379 def getnchannels(self):
380 return self._nchannels
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000381
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000382 def getnframes(self):
383 return self._nframes
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000384
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000385 def getsampwidth(self):
386 return self._sampwidth
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000387
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000388 def getframerate(self):
389 return self._framerate
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000390
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000391 def getcomptype(self):
392 return self._comptype
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000393
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000394 def getcompname(self):
395 return self._compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000396
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000397## def getversion(self):
398## return self._version
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000399
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000400 def getparams(self):
401 return self.getnchannels(), self.getsampwidth(), \
402 self.getframerate(), self.getnframes(), \
403 self.getcomptype(), self.getcompname()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000404
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000405 def getmarkers(self):
406 if len(self._markers) == 0:
407 return None
408 return self._markers
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000409
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000410 def getmark(self, id):
411 for marker in self._markers:
412 if id == marker[0]:
413 return marker
Walter Dörwald70a6b492004-02-12 17:35:32 +0000414 raise Error, 'marker %r does not exist' % (id,)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000415
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000416 def setpos(self, pos):
417 if pos < 0 or pos > self._nframes:
418 raise Error, 'position not in range'
419 self._soundpos = pos
420 self._ssnd_seek_needed = 1
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000421
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000422 def readframes(self, nframes):
423 if self._ssnd_seek_needed:
424 self._ssnd_chunk.seek(0)
425 dummy = self._ssnd_chunk.read(8)
426 pos = self._soundpos * self._framesize
427 if pos:
Guido van Rossum2663c132000-03-07 15:19:31 +0000428 self._ssnd_chunk.seek(pos + 8)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000429 self._ssnd_seek_needed = 0
430 if nframes == 0:
431 return ''
432 data = self._ssnd_chunk.read(nframes * self._framesize)
433 if self._convert and data:
434 data = self._convert(data)
Antoine Pitroubca7b482009-10-14 17:30:52 +0000435 self._soundpos = self._soundpos + len(data) // (self._nchannels * self._sampwidth)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000436 return data
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000437
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000438 #
439 # Internal methods.
440 #
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000441
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000442 def _decomp_data(self, data):
443 import cl
444 dummy = self._decomp.SetParam(cl.FRAME_BUFFER_SIZE,
445 len(data) * 2)
Antoine Pitroubca7b482009-10-14 17:30:52 +0000446 return self._decomp.Decompress(len(data) // self._nchannels,
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000447 data)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000448
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000449 def _ulaw2lin(self, data):
450 import audioop
451 return audioop.ulaw2lin(data, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000452
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000453 def _adpcm2lin(self, data):
454 import audioop
455 if not hasattr(self, '_adpcmstate'):
456 # first time
457 self._adpcmstate = None
458 data, self._adpcmstate = audioop.adpcm2lin(data, 2,
459 self._adpcmstate)
460 return data
Sjoerd Mullender1f057541994-09-06 16:17:51 +0000461
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000462 def _read_comm_chunk(self, chunk):
463 self._nchannels = _read_short(chunk)
464 self._nframes = _read_long(chunk)
Antoine Pitroubca7b482009-10-14 17:30:52 +0000465 self._sampwidth = (_read_short(chunk) + 7) // 8
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000466 self._framerate = int(_read_float(chunk))
467 self._framesize = self._nchannels * self._sampwidth
468 if self._aifc:
469 #DEBUG: SGI's soundeditor produces a bad size :-(
470 kludge = 0
471 if chunk.chunksize == 18:
472 kludge = 1
473 print 'Warning: bad COMM chunk size'
474 chunk.chunksize = 23
475 #DEBUG end
476 self._comptype = chunk.read(4)
477 #DEBUG start
478 if kludge:
479 length = ord(chunk.file.read(1))
480 if length & 1 == 0:
481 length = length + 1
482 chunk.chunksize = chunk.chunksize + length
483 chunk.file.seek(-1, 1)
484 #DEBUG end
485 self._compname = _read_string(chunk)
486 if self._comptype != 'NONE':
487 if self._comptype == 'G722':
488 try:
489 import audioop
490 except ImportError:
491 pass
492 else:
493 self._convert = self._adpcm2lin
Serhiy Storchaka986b5ee2013-10-12 18:21:12 +0300494 self._sampwidth = 2
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000495 return
496 # for ULAW and ALAW try Compression Library
497 try:
498 import cl
499 except ImportError:
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300500 if self._comptype in ('ULAW', 'ulaw'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000501 try:
502 import audioop
503 self._convert = self._ulaw2lin
Serhiy Storchaka986b5ee2013-10-12 18:21:12 +0300504 self._sampwidth = 2
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000505 return
506 except ImportError:
507 pass
508 raise Error, 'cannot read compressed AIFF-C files'
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300509 if self._comptype in ('ULAW', 'ulaw'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000510 scheme = cl.G711_ULAW
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300511 elif self._comptype in ('ALAW', 'alaw'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000512 scheme = cl.G711_ALAW
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000513 else:
514 raise Error, 'unsupported compression type'
515 self._decomp = cl.OpenDecompressor(scheme)
516 self._convert = self._decomp_data
Serhiy Storchaka986b5ee2013-10-12 18:21:12 +0300517 self._sampwidth = 2
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000518 else:
519 self._comptype = 'NONE'
520 self._compname = 'not compressed'
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000521
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000522 def _readmark(self, chunk):
523 nmarkers = _read_short(chunk)
524 # Some files appear to contain invalid counts.
525 # Cope with this by testing for EOF.
526 try:
527 for i in range(nmarkers):
528 id = _read_short(chunk)
529 pos = _read_long(chunk)
530 name = _read_string(chunk)
531 if pos or name:
532 # some files appear to have
533 # dummy markers consisting of
534 # a position 0 and name ''
535 self._markers.append((id, pos, name))
536 except EOFError:
537 print 'Warning: MARK chunk contains only',
538 print len(self._markers),
539 if len(self._markers) == 1: print 'marker',
540 else: print 'markers',
541 print 'instead of', nmarkers
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000542
Guido van Rossumd3166071993-05-24 14:16:22 +0000543class Aifc_write:
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000544 # Variables used in this class:
545 #
546 # These variables are user settable through appropriate methods
547 # of this class:
548 # _file -- the open file with methods write(), close(), tell(), seek()
549 # set through the __init__() method
550 # _comptype -- the AIFF-C compression type ('NONE' in AIFF)
551 # set through the setcomptype() or setparams() method
552 # _compname -- the human-readable AIFF-C compression type
553 # set through the setcomptype() or setparams() method
554 # _nchannels -- the number of audio channels
555 # set through the setnchannels() or setparams() method
556 # _sampwidth -- the number of bytes per audio sample
557 # set through the setsampwidth() or setparams() method
558 # _framerate -- the sampling frequency
559 # set through the setframerate() or setparams() method
560 # _nframes -- the number of audio frames written to the header
561 # set through the setnframes() or setparams() method
562 # _aifc -- whether we're writing an AIFF-C file or an AIFF file
563 # set through the aifc() method, reset through the
564 # aiff() method
565 #
566 # These variables are used internally only:
567 # _version -- the AIFF-C version number
568 # _comp -- the compressor from builtin module cl
569 # _nframeswritten -- the number of audio frames actually written
570 # _datalength -- the size of the audio samples written to the header
571 # _datawritten -- the size of the audio samples actually written
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000572
INADA Naoki02eb4b02017-02-28 20:39:30 +0900573 _file = None # Set here since __del__ checks it
574
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000575 def __init__(self, f):
INADA Naoki02eb4b02017-02-28 20:39:30 +0900576 if isinstance(f, basestring):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000577 filename = f
578 f = __builtin__.open(f, 'wb')
579 else:
580 # else, assume it is an open file object already
581 filename = '???'
582 self.initfp(f)
583 if filename[-5:] == '.aiff':
584 self._aifc = 0
585 else:
586 self._aifc = 1
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000587
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000588 def initfp(self, file):
589 self._file = file
590 self._version = _AIFC_version
591 self._comptype = 'NONE'
592 self._compname = 'not compressed'
593 self._comp = None
594 self._convert = None
595 self._nchannels = 0
596 self._sampwidth = 0
597 self._framerate = 0
598 self._nframes = 0
599 self._nframeswritten = 0
600 self._datawritten = 0
601 self._datalength = 0
602 self._markers = []
603 self._marklength = 0
604 self._aifc = 1 # AIFF-C is default
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000605
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000606 def __del__(self):
607 if self._file:
608 self.close()
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000609
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000610 #
611 # User visible methods.
612 #
613 def aiff(self):
614 if self._nframeswritten:
615 raise Error, 'cannot change parameters after starting to write'
616 self._aifc = 0
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000617
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000618 def aifc(self):
619 if self._nframeswritten:
620 raise Error, 'cannot change parameters after starting to write'
621 self._aifc = 1
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000622
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000623 def setnchannels(self, nchannels):
624 if self._nframeswritten:
625 raise Error, 'cannot change parameters after starting to write'
626 if nchannels < 1:
627 raise Error, 'bad # of channels'
628 self._nchannels = nchannels
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000629
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000630 def getnchannels(self):
631 if not self._nchannels:
632 raise Error, 'number of channels not set'
633 return self._nchannels
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000634
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000635 def setsampwidth(self, sampwidth):
636 if self._nframeswritten:
637 raise Error, 'cannot change parameters after starting to write'
638 if sampwidth < 1 or sampwidth > 4:
639 raise Error, 'bad sample width'
640 self._sampwidth = sampwidth
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000641
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000642 def getsampwidth(self):
643 if not self._sampwidth:
644 raise Error, 'sample width not set'
645 return self._sampwidth
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000646
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000647 def setframerate(self, framerate):
648 if self._nframeswritten:
649 raise Error, 'cannot change parameters after starting to write'
650 if framerate <= 0:
651 raise Error, 'bad frame rate'
652 self._framerate = framerate
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000653
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000654 def getframerate(self):
655 if not self._framerate:
656 raise Error, 'frame rate not set'
657 return self._framerate
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000658
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000659 def setnframes(self, nframes):
660 if self._nframeswritten:
661 raise Error, 'cannot change parameters after starting to write'
662 self._nframes = nframes
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000663
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000664 def getnframes(self):
665 return self._nframeswritten
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000666
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000667 def setcomptype(self, comptype, compname):
668 if self._nframeswritten:
669 raise Error, 'cannot change parameters after starting to write'
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300670 if comptype not in ('NONE', 'ULAW', 'ulaw', 'ALAW', 'alaw', 'G722'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000671 raise Error, 'unsupported compression type'
672 self._comptype = comptype
673 self._compname = compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000674
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000675 def getcomptype(self):
676 return self._comptype
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000677
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000678 def getcompname(self):
679 return self._compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000680
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000681## def setversion(self, version):
682## if self._nframeswritten:
683## raise Error, 'cannot change parameters after starting to write'
684## self._version = version
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000685
Brett Cannond13e4ba2008-08-04 21:33:00 +0000686 def setparams(self, info):
687 nchannels, sampwidth, framerate, nframes, comptype, compname = info
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000688 if self._nframeswritten:
689 raise Error, 'cannot change parameters after starting to write'
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300690 if comptype not in ('NONE', 'ULAW', 'ulaw', 'ALAW', 'alaw', 'G722'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000691 raise Error, 'unsupported compression type'
692 self.setnchannels(nchannels)
693 self.setsampwidth(sampwidth)
694 self.setframerate(framerate)
695 self.setnframes(nframes)
696 self.setcomptype(comptype, compname)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000697
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000698 def getparams(self):
699 if not self._nchannels or not self._sampwidth or not self._framerate:
700 raise Error, 'not all parameters set'
701 return self._nchannels, self._sampwidth, self._framerate, \
702 self._nframes, self._comptype, self._compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000703
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000704 def setmark(self, id, pos, name):
705 if id <= 0:
706 raise Error, 'marker ID must be > 0'
707 if pos < 0:
708 raise Error, 'marker position must be >= 0'
709 if type(name) != type(''):
710 raise Error, 'marker name must be a string'
711 for i in range(len(self._markers)):
712 if id == self._markers[i][0]:
713 self._markers[i] = id, pos, name
714 return
715 self._markers.append((id, pos, name))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000716
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000717 def getmark(self, id):
718 for marker in self._markers:
719 if id == marker[0]:
720 return marker
Walter Dörwald70a6b492004-02-12 17:35:32 +0000721 raise Error, 'marker %r does not exist' % (id,)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000722
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000723 def getmarkers(self):
724 if len(self._markers) == 0:
725 return None
726 return self._markers
Tim Peters146965a2001-01-14 18:09:23 +0000727
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000728 def tell(self):
729 return self._nframeswritten
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000730
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000731 def writeframesraw(self, data):
732 self._ensure_header_written(len(data))
Antoine Pitroubca7b482009-10-14 17:30:52 +0000733 nframes = len(data) // (self._sampwidth * self._nchannels)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000734 if self._convert:
735 data = self._convert(data)
736 self._file.write(data)
737 self._nframeswritten = self._nframeswritten + nframes
738 self._datawritten = self._datawritten + len(data)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000739
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000740 def writeframes(self, data):
741 self.writeframesraw(data)
742 if self._nframeswritten != self._nframes or \
743 self._datalength != self._datawritten:
744 self._patchheader()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000745
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000746 def close(self):
Serhiy Storchaka4ed797e2012-12-29 22:25:59 +0200747 if self._file is None:
748 return
749 try:
750 self._ensure_header_written(0)
751 if self._datawritten & 1:
752 # quick pad to even size
753 self._file.write(chr(0))
754 self._datawritten = self._datawritten + 1
755 self._writemarkers()
756 if self._nframeswritten != self._nframes or \
757 self._datalength != self._datawritten or \
758 self._marklength:
759 self._patchheader()
760 if self._comp:
761 self._comp.CloseCompressor()
762 self._comp = None
763 finally:
764 # Prevent ref cycles
765 self._convert = None
766 f = self._file
767 self._file = None
768 f.close()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000769
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000770 #
771 # Internal methods.
772 #
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000773
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000774 def _comp_data(self, data):
775 import cl
Neal Norwitz086ac002002-02-11 17:56:27 +0000776 dummy = self._comp.SetParam(cl.FRAME_BUFFER_SIZE, len(data))
777 dummy = self._comp.SetParam(cl.COMPRESSED_BUFFER_SIZE, len(data))
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000778 return self._comp.Compress(self._nframes, data)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000779
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000780 def _lin2ulaw(self, data):
781 import audioop
782 return audioop.lin2ulaw(data, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000783
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000784 def _lin2adpcm(self, data):
785 import audioop
786 if not hasattr(self, '_adpcmstate'):
787 self._adpcmstate = None
788 data, self._adpcmstate = audioop.lin2adpcm(data, 2,
789 self._adpcmstate)
790 return data
Sjoerd Mullender1f057541994-09-06 16:17:51 +0000791
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000792 def _ensure_header_written(self, datasize):
793 if not self._nframeswritten:
Serhiy Storchakad1a61dc2013-12-14 20:34:33 +0200794 if self._comptype in ('ULAW', 'ulaw', 'ALAW', 'alaw'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000795 if not self._sampwidth:
796 self._sampwidth = 2
797 if self._sampwidth != 2:
798 raise Error, 'sample width must be 2 when compressing with ULAW or ALAW'
799 if self._comptype == 'G722':
800 if not self._sampwidth:
801 self._sampwidth = 2
802 if self._sampwidth != 2:
803 raise Error, 'sample width must be 2 when compressing with G7.22 (ADPCM)'
804 if not self._nchannels:
805 raise Error, '# channels not specified'
806 if not self._sampwidth:
807 raise Error, 'sample width not specified'
808 if not self._framerate:
809 raise Error, 'sampling rate not specified'
810 self._write_header(datasize)
Guido van Rossum52fc1f61993-06-17 12:38:10 +0000811
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000812 def _init_compression(self):
813 if self._comptype == 'G722':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000814 self._convert = self._lin2adpcm
815 return
816 try:
817 import cl
818 except ImportError:
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300819 if self._comptype in ('ULAW', 'ulaw'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000820 try:
821 import audioop
822 self._convert = self._lin2ulaw
823 return
824 except ImportError:
825 pass
826 raise Error, 'cannot write compressed AIFF-C files'
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300827 if self._comptype in ('ULAW', 'ulaw'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000828 scheme = cl.G711_ULAW
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300829 elif self._comptype in ('ALAW', 'alaw'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000830 scheme = cl.G711_ALAW
831 else:
832 raise Error, 'unsupported compression type'
833 self._comp = cl.OpenCompressor(scheme)
834 params = [cl.ORIGINAL_FORMAT, 0,
835 cl.BITS_PER_COMPONENT, self._sampwidth * 8,
836 cl.FRAME_RATE, self._framerate,
837 cl.FRAME_BUFFER_SIZE, 100,
838 cl.COMPRESSED_BUFFER_SIZE, 100]
839 if self._nchannels == 1:
840 params[1] = cl.MONO
841 elif self._nchannels == 2:
842 params[1] = cl.STEREO_INTERLEAVED
843 else:
844 raise Error, 'cannot compress more than 2 channels'
845 self._comp.SetParams(params)
846 # the compressor produces a header which we ignore
847 dummy = self._comp.Compress(0, '')
848 self._convert = self._comp_data
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000849
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000850 def _write_header(self, initlength):
851 if self._aifc and self._comptype != 'NONE':
852 self._init_compression()
853 self._file.write('FORM')
854 if not self._nframes:
Antoine Pitroubca7b482009-10-14 17:30:52 +0000855 self._nframes = initlength // (self._nchannels * self._sampwidth)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000856 self._datalength = self._nframes * self._nchannels * self._sampwidth
857 if self._datalength & 1:
858 self._datalength = self._datalength + 1
859 if self._aifc:
Serhiy Storchakad1a61dc2013-12-14 20:34:33 +0200860 if self._comptype in ('ULAW', 'ulaw', 'ALAW', 'alaw'):
Antoine Pitroubca7b482009-10-14 17:30:52 +0000861 self._datalength = self._datalength // 2
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000862 if self._datalength & 1:
863 self._datalength = self._datalength + 1
864 elif self._comptype == 'G722':
Antoine Pitroubca7b482009-10-14 17:30:52 +0000865 self._datalength = (self._datalength + 3) // 4
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000866 if self._datalength & 1:
867 self._datalength = self._datalength + 1
Serhiy Storchakad1a61dc2013-12-14 20:34:33 +0200868 try:
869 self._form_length_pos = self._file.tell()
870 except (AttributeError, IOError):
871 self._form_length_pos = None
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000872 commlength = self._write_form_length(self._datalength)
873 if self._aifc:
874 self._file.write('AIFC')
875 self._file.write('FVER')
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100876 _write_ulong(self._file, 4)
877 _write_ulong(self._file, self._version)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000878 else:
879 self._file.write('AIFF')
880 self._file.write('COMM')
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100881 _write_ulong(self._file, commlength)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000882 _write_short(self._file, self._nchannels)
Serhiy Storchakad1a61dc2013-12-14 20:34:33 +0200883 if self._form_length_pos is not None:
884 self._nframes_pos = self._file.tell()
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100885 _write_ulong(self._file, self._nframes)
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300886 if self._comptype in ('ULAW', 'ulaw', 'ALAW', 'alaw', 'G722'):
Serhiy Storchaka986b5ee2013-10-12 18:21:12 +0300887 _write_short(self._file, 8)
888 else:
889 _write_short(self._file, self._sampwidth * 8)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000890 _write_float(self._file, self._framerate)
891 if self._aifc:
892 self._file.write(self._comptype)
893 _write_string(self._file, self._compname)
894 self._file.write('SSND')
Serhiy Storchakad1a61dc2013-12-14 20:34:33 +0200895 if self._form_length_pos is not None:
896 self._ssnd_length_pos = self._file.tell()
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100897 _write_ulong(self._file, self._datalength + 8)
898 _write_ulong(self._file, 0)
899 _write_ulong(self._file, 0)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000900
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000901 def _write_form_length(self, datalength):
902 if self._aifc:
903 commlength = 18 + 5 + len(self._compname)
904 if commlength & 1:
905 commlength = commlength + 1
906 verslength = 12
907 else:
908 commlength = 18
909 verslength = 0
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100910 _write_ulong(self._file, 4 + verslength + self._marklength + \
911 8 + commlength + 16 + datalength)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000912 return commlength
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000913
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000914 def _patchheader(self):
915 curpos = self._file.tell()
916 if self._datawritten & 1:
917 datalength = self._datawritten + 1
918 self._file.write(chr(0))
919 else:
920 datalength = self._datawritten
921 if datalength == self._datalength and \
922 self._nframes == self._nframeswritten and \
923 self._marklength == 0:
924 self._file.seek(curpos, 0)
925 return
926 self._file.seek(self._form_length_pos, 0)
927 dummy = self._write_form_length(datalength)
928 self._file.seek(self._nframes_pos, 0)
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100929 _write_ulong(self._file, self._nframeswritten)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000930 self._file.seek(self._ssnd_length_pos, 0)
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100931 _write_ulong(self._file, datalength + 8)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000932 self._file.seek(curpos, 0)
933 self._nframes = self._nframeswritten
934 self._datalength = datalength
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000935
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000936 def _writemarkers(self):
937 if len(self._markers) == 0:
938 return
939 self._file.write('MARK')
940 length = 2
941 for marker in self._markers:
942 id, pos, name = marker
943 length = length + len(name) + 1 + 6
944 if len(name) & 1 == 0:
945 length = length + 1
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100946 _write_ulong(self._file, length)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000947 self._marklength = length + 8
948 _write_short(self._file, len(self._markers))
949 for marker in self._markers:
950 id, pos, name = marker
951 _write_short(self._file, id)
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100952 _write_ulong(self._file, pos)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000953 _write_string(self._file, name)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000954
Fred Drake43161351999-06-22 21:23:23 +0000955def open(f, mode=None):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000956 if mode is None:
957 if hasattr(f, 'mode'):
958 mode = f.mode
959 else:
960 mode = 'rb'
961 if mode in ('r', 'rb'):
962 return Aifc_read(f)
963 elif mode in ('w', 'wb'):
964 return Aifc_write(f)
965 else:
966 raise Error, "mode must be 'r', 'rb', 'w', or 'wb'"
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000967
Guido van Rossum7bc817d1993-12-17 15:25:27 +0000968openfp = open # B/W compatibility
Guido van Rossum36bb1811996-12-31 05:57:34 +0000969
970if __name__ == '__main__':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000971 import sys
972 if not sys.argv[1:]:
973 sys.argv.append('/usr/demos/data/audio/bach.aiff')
974 fn = sys.argv[1]
975 f = open(fn, 'r')
Serhiy Storchaka879a2132013-08-25 19:12:33 +0300976 try:
977 print "Reading", fn
978 print "nchannels =", f.getnchannels()
979 print "nframes =", f.getnframes()
980 print "sampwidth =", f.getsampwidth()
981 print "framerate =", f.getframerate()
982 print "comptype =", f.getcomptype()
983 print "compname =", f.getcompname()
984 if sys.argv[2:]:
985 gn = sys.argv[2]
986 print "Writing", gn
987 g = open(gn, 'w')
988 try:
989 g.setparams(f.getparams())
990 while 1:
991 data = f.readframes(1024)
992 if not data:
993 break
994 g.writeframes(data)
995 finally:
996 g.close()
997 print "Done."
998 finally:
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000999 f.close()