blob: c9a021ee9d0309c3680794fc00d6781028151b7f [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
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000291 def initfp(self, file):
292 self._version = 0
293 self._decomp = None
294 self._convert = None
295 self._markers = []
296 self._soundpos = 0
R. David Murray8fd522f2009-05-07 16:27:02 +0000297 self._file = file
298 chunk = Chunk(file)
299 if chunk.getname() != 'FORM':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000300 raise Error, 'file does not start with FORM id'
R. David Murray8fd522f2009-05-07 16:27:02 +0000301 formdata = chunk.read(4)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000302 if formdata == 'AIFF':
303 self._aifc = 0
304 elif formdata == 'AIFC':
305 self._aifc = 1
306 else:
307 raise Error, 'not an AIFF or AIFF-C file'
308 self._comm_chunk_read = 0
309 while 1:
310 self._ssnd_seek_needed = 1
311 try:
312 chunk = Chunk(self._file)
313 except EOFError:
314 break
315 chunkname = chunk.getname()
316 if chunkname == 'COMM':
317 self._read_comm_chunk(chunk)
318 self._comm_chunk_read = 1
319 elif chunkname == 'SSND':
320 self._ssnd_chunk = chunk
321 dummy = chunk.read(8)
322 self._ssnd_seek_needed = 0
323 elif chunkname == 'FVER':
Guido van Rossum820819c2002-08-12 22:11:28 +0000324 self._version = _read_ulong(chunk)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000325 elif chunkname == 'MARK':
326 self._readmark(chunk)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000327 chunk.skip()
328 if not self._comm_chunk_read or not self._ssnd_chunk:
329 raise Error, 'COMM chunk and/or SSND chunk missing'
330 if self._aifc and self._decomp:
331 import cl
332 params = [cl.ORIGINAL_FORMAT, 0,
333 cl.BITS_PER_COMPONENT, self._sampwidth * 8,
334 cl.FRAME_RATE, self._framerate]
335 if self._nchannels == 1:
336 params[1] = cl.MONO
337 elif self._nchannels == 2:
338 params[1] = cl.STEREO_INTERLEAVED
339 else:
340 raise Error, 'cannot compress more than 2 channels'
341 self._decomp.SetParams(params)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000342
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000343 def __init__(self, f):
344 if type(f) == type(''):
345 f = __builtin__.open(f, 'rb')
346 # else, assume it is an open file object already
347 self.initfp(f)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000348
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000349 #
350 # User visible methods.
351 #
352 def getfp(self):
353 return self._file
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000354
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000355 def rewind(self):
356 self._ssnd_seek_needed = 1
357 self._soundpos = 0
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000358
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000359 def close(self):
Serhiy Storchaka1aa2c0f2015-04-10 13:24:10 +0300360 decomp = self._decomp
361 try:
362 if decomp:
363 self._decomp = None
364 decomp.CloseDecompressor()
365 finally:
366 self._file.close()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000367
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000368 def tell(self):
369 return self._soundpos
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000370
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000371 def getnchannels(self):
372 return self._nchannels
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000373
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000374 def getnframes(self):
375 return self._nframes
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000376
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000377 def getsampwidth(self):
378 return self._sampwidth
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000379
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000380 def getframerate(self):
381 return self._framerate
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000382
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000383 def getcomptype(self):
384 return self._comptype
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000385
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000386 def getcompname(self):
387 return self._compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000388
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000389## def getversion(self):
390## return self._version
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000391
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000392 def getparams(self):
393 return self.getnchannels(), self.getsampwidth(), \
394 self.getframerate(), self.getnframes(), \
395 self.getcomptype(), self.getcompname()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000396
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000397 def getmarkers(self):
398 if len(self._markers) == 0:
399 return None
400 return self._markers
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000401
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000402 def getmark(self, id):
403 for marker in self._markers:
404 if id == marker[0]:
405 return marker
Walter Dörwald70a6b492004-02-12 17:35:32 +0000406 raise Error, 'marker %r does not exist' % (id,)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000407
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000408 def setpos(self, pos):
409 if pos < 0 or pos > self._nframes:
410 raise Error, 'position not in range'
411 self._soundpos = pos
412 self._ssnd_seek_needed = 1
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000413
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000414 def readframes(self, nframes):
415 if self._ssnd_seek_needed:
416 self._ssnd_chunk.seek(0)
417 dummy = self._ssnd_chunk.read(8)
418 pos = self._soundpos * self._framesize
419 if pos:
Guido van Rossum2663c132000-03-07 15:19:31 +0000420 self._ssnd_chunk.seek(pos + 8)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000421 self._ssnd_seek_needed = 0
422 if nframes == 0:
423 return ''
424 data = self._ssnd_chunk.read(nframes * self._framesize)
425 if self._convert and data:
426 data = self._convert(data)
Antoine Pitroubca7b482009-10-14 17:30:52 +0000427 self._soundpos = self._soundpos + len(data) // (self._nchannels * self._sampwidth)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000428 return data
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000429
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000430 #
431 # Internal methods.
432 #
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000433
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000434 def _decomp_data(self, data):
435 import cl
436 dummy = self._decomp.SetParam(cl.FRAME_BUFFER_SIZE,
437 len(data) * 2)
Antoine Pitroubca7b482009-10-14 17:30:52 +0000438 return self._decomp.Decompress(len(data) // self._nchannels,
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000439 data)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000440
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000441 def _ulaw2lin(self, data):
442 import audioop
443 return audioop.ulaw2lin(data, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000444
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000445 def _adpcm2lin(self, data):
446 import audioop
447 if not hasattr(self, '_adpcmstate'):
448 # first time
449 self._adpcmstate = None
450 data, self._adpcmstate = audioop.adpcm2lin(data, 2,
451 self._adpcmstate)
452 return data
Sjoerd Mullender1f057541994-09-06 16:17:51 +0000453
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000454 def _read_comm_chunk(self, chunk):
455 self._nchannels = _read_short(chunk)
456 self._nframes = _read_long(chunk)
Antoine Pitroubca7b482009-10-14 17:30:52 +0000457 self._sampwidth = (_read_short(chunk) + 7) // 8
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000458 self._framerate = int(_read_float(chunk))
459 self._framesize = self._nchannels * self._sampwidth
460 if self._aifc:
461 #DEBUG: SGI's soundeditor produces a bad size :-(
462 kludge = 0
463 if chunk.chunksize == 18:
464 kludge = 1
465 print 'Warning: bad COMM chunk size'
466 chunk.chunksize = 23
467 #DEBUG end
468 self._comptype = chunk.read(4)
469 #DEBUG start
470 if kludge:
471 length = ord(chunk.file.read(1))
472 if length & 1 == 0:
473 length = length + 1
474 chunk.chunksize = chunk.chunksize + length
475 chunk.file.seek(-1, 1)
476 #DEBUG end
477 self._compname = _read_string(chunk)
478 if self._comptype != 'NONE':
479 if self._comptype == 'G722':
480 try:
481 import audioop
482 except ImportError:
483 pass
484 else:
485 self._convert = self._adpcm2lin
Serhiy Storchaka986b5ee2013-10-12 18:21:12 +0300486 self._sampwidth = 2
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000487 return
488 # for ULAW and ALAW try Compression Library
489 try:
490 import cl
491 except ImportError:
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300492 if self._comptype in ('ULAW', 'ulaw'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000493 try:
494 import audioop
495 self._convert = self._ulaw2lin
Serhiy Storchaka986b5ee2013-10-12 18:21:12 +0300496 self._sampwidth = 2
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000497 return
498 except ImportError:
499 pass
500 raise Error, 'cannot read compressed AIFF-C files'
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300501 if self._comptype in ('ULAW', 'ulaw'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000502 scheme = cl.G711_ULAW
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300503 elif self._comptype in ('ALAW', 'alaw'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000504 scheme = cl.G711_ALAW
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000505 else:
506 raise Error, 'unsupported compression type'
507 self._decomp = cl.OpenDecompressor(scheme)
508 self._convert = self._decomp_data
Serhiy Storchaka986b5ee2013-10-12 18:21:12 +0300509 self._sampwidth = 2
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000510 else:
511 self._comptype = 'NONE'
512 self._compname = 'not compressed'
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000513
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000514 def _readmark(self, chunk):
515 nmarkers = _read_short(chunk)
516 # Some files appear to contain invalid counts.
517 # Cope with this by testing for EOF.
518 try:
519 for i in range(nmarkers):
520 id = _read_short(chunk)
521 pos = _read_long(chunk)
522 name = _read_string(chunk)
523 if pos or name:
524 # some files appear to have
525 # dummy markers consisting of
526 # a position 0 and name ''
527 self._markers.append((id, pos, name))
528 except EOFError:
529 print 'Warning: MARK chunk contains only',
530 print len(self._markers),
531 if len(self._markers) == 1: print 'marker',
532 else: print 'markers',
533 print 'instead of', nmarkers
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000534
Guido van Rossumd3166071993-05-24 14:16:22 +0000535class Aifc_write:
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000536 # Variables used in this class:
537 #
538 # These variables are user settable through appropriate methods
539 # of this class:
540 # _file -- the open file with methods write(), close(), tell(), seek()
541 # set through the __init__() method
542 # _comptype -- the AIFF-C compression type ('NONE' in AIFF)
543 # set through the setcomptype() or setparams() method
544 # _compname -- the human-readable AIFF-C compression type
545 # set through the setcomptype() or setparams() method
546 # _nchannels -- the number of audio channels
547 # set through the setnchannels() or setparams() method
548 # _sampwidth -- the number of bytes per audio sample
549 # set through the setsampwidth() or setparams() method
550 # _framerate -- the sampling frequency
551 # set through the setframerate() or setparams() method
552 # _nframes -- the number of audio frames written to the header
553 # set through the setnframes() or setparams() method
554 # _aifc -- whether we're writing an AIFF-C file or an AIFF file
555 # set through the aifc() method, reset through the
556 # aiff() method
557 #
558 # These variables are used internally only:
559 # _version -- the AIFF-C version number
560 # _comp -- the compressor from builtin module cl
561 # _nframeswritten -- the number of audio frames actually written
562 # _datalength -- the size of the audio samples written to the header
563 # _datawritten -- the size of the audio samples actually written
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000564
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000565 def __init__(self, f):
566 if type(f) == type(''):
567 filename = f
568 f = __builtin__.open(f, 'wb')
569 else:
570 # else, assume it is an open file object already
571 filename = '???'
572 self.initfp(f)
573 if filename[-5:] == '.aiff':
574 self._aifc = 0
575 else:
576 self._aifc = 1
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000577
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000578 def initfp(self, file):
579 self._file = file
580 self._version = _AIFC_version
581 self._comptype = 'NONE'
582 self._compname = 'not compressed'
583 self._comp = None
584 self._convert = None
585 self._nchannels = 0
586 self._sampwidth = 0
587 self._framerate = 0
588 self._nframes = 0
589 self._nframeswritten = 0
590 self._datawritten = 0
591 self._datalength = 0
592 self._markers = []
593 self._marklength = 0
594 self._aifc = 1 # AIFF-C is default
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000595
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000596 def __del__(self):
597 if self._file:
598 self.close()
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000599
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000600 #
601 # User visible methods.
602 #
603 def aiff(self):
604 if self._nframeswritten:
605 raise Error, 'cannot change parameters after starting to write'
606 self._aifc = 0
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000607
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000608 def aifc(self):
609 if self._nframeswritten:
610 raise Error, 'cannot change parameters after starting to write'
611 self._aifc = 1
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000612
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000613 def setnchannels(self, nchannels):
614 if self._nframeswritten:
615 raise Error, 'cannot change parameters after starting to write'
616 if nchannels < 1:
617 raise Error, 'bad # of channels'
618 self._nchannels = nchannels
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000619
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000620 def getnchannels(self):
621 if not self._nchannels:
622 raise Error, 'number of channels not set'
623 return self._nchannels
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000624
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000625 def setsampwidth(self, sampwidth):
626 if self._nframeswritten:
627 raise Error, 'cannot change parameters after starting to write'
628 if sampwidth < 1 or sampwidth > 4:
629 raise Error, 'bad sample width'
630 self._sampwidth = sampwidth
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000631
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000632 def getsampwidth(self):
633 if not self._sampwidth:
634 raise Error, 'sample width not set'
635 return self._sampwidth
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000636
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000637 def setframerate(self, framerate):
638 if self._nframeswritten:
639 raise Error, 'cannot change parameters after starting to write'
640 if framerate <= 0:
641 raise Error, 'bad frame rate'
642 self._framerate = framerate
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000643
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000644 def getframerate(self):
645 if not self._framerate:
646 raise Error, 'frame rate not set'
647 return self._framerate
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000648
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000649 def setnframes(self, nframes):
650 if self._nframeswritten:
651 raise Error, 'cannot change parameters after starting to write'
652 self._nframes = nframes
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000653
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000654 def getnframes(self):
655 return self._nframeswritten
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000656
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000657 def setcomptype(self, comptype, compname):
658 if self._nframeswritten:
659 raise Error, 'cannot change parameters after starting to write'
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300660 if comptype not in ('NONE', 'ULAW', 'ulaw', 'ALAW', 'alaw', 'G722'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000661 raise Error, 'unsupported compression type'
662 self._comptype = comptype
663 self._compname = compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000664
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000665 def getcomptype(self):
666 return self._comptype
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000667
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000668 def getcompname(self):
669 return self._compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000670
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000671## def setversion(self, version):
672## if self._nframeswritten:
673## raise Error, 'cannot change parameters after starting to write'
674## self._version = version
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000675
Brett Cannond13e4ba2008-08-04 21:33:00 +0000676 def setparams(self, info):
677 nchannels, sampwidth, framerate, nframes, comptype, compname = info
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000678 if self._nframeswritten:
679 raise Error, 'cannot change parameters after starting to write'
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300680 if comptype not in ('NONE', 'ULAW', 'ulaw', 'ALAW', 'alaw', 'G722'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000681 raise Error, 'unsupported compression type'
682 self.setnchannels(nchannels)
683 self.setsampwidth(sampwidth)
684 self.setframerate(framerate)
685 self.setnframes(nframes)
686 self.setcomptype(comptype, compname)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000687
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000688 def getparams(self):
689 if not self._nchannels or not self._sampwidth or not self._framerate:
690 raise Error, 'not all parameters set'
691 return self._nchannels, self._sampwidth, self._framerate, \
692 self._nframes, self._comptype, self._compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000693
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000694 def setmark(self, id, pos, name):
695 if id <= 0:
696 raise Error, 'marker ID must be > 0'
697 if pos < 0:
698 raise Error, 'marker position must be >= 0'
699 if type(name) != type(''):
700 raise Error, 'marker name must be a string'
701 for i in range(len(self._markers)):
702 if id == self._markers[i][0]:
703 self._markers[i] = id, pos, name
704 return
705 self._markers.append((id, pos, name))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000706
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000707 def getmark(self, id):
708 for marker in self._markers:
709 if id == marker[0]:
710 return marker
Walter Dörwald70a6b492004-02-12 17:35:32 +0000711 raise Error, 'marker %r does not exist' % (id,)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000712
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000713 def getmarkers(self):
714 if len(self._markers) == 0:
715 return None
716 return self._markers
Tim Peters146965a2001-01-14 18:09:23 +0000717
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000718 def tell(self):
719 return self._nframeswritten
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000720
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000721 def writeframesraw(self, data):
722 self._ensure_header_written(len(data))
Antoine Pitroubca7b482009-10-14 17:30:52 +0000723 nframes = len(data) // (self._sampwidth * self._nchannels)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000724 if self._convert:
725 data = self._convert(data)
726 self._file.write(data)
727 self._nframeswritten = self._nframeswritten + nframes
728 self._datawritten = self._datawritten + len(data)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000729
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000730 def writeframes(self, data):
731 self.writeframesraw(data)
732 if self._nframeswritten != self._nframes or \
733 self._datalength != self._datawritten:
734 self._patchheader()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000735
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000736 def close(self):
Serhiy Storchaka4ed797e2012-12-29 22:25:59 +0200737 if self._file is None:
738 return
739 try:
740 self._ensure_header_written(0)
741 if self._datawritten & 1:
742 # quick pad to even size
743 self._file.write(chr(0))
744 self._datawritten = self._datawritten + 1
745 self._writemarkers()
746 if self._nframeswritten != self._nframes or \
747 self._datalength != self._datawritten or \
748 self._marklength:
749 self._patchheader()
750 if self._comp:
751 self._comp.CloseCompressor()
752 self._comp = None
753 finally:
754 # Prevent ref cycles
755 self._convert = None
756 f = self._file
757 self._file = None
758 f.close()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000759
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000760 #
761 # Internal methods.
762 #
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000763
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000764 def _comp_data(self, data):
765 import cl
Neal Norwitz086ac002002-02-11 17:56:27 +0000766 dummy = self._comp.SetParam(cl.FRAME_BUFFER_SIZE, len(data))
767 dummy = self._comp.SetParam(cl.COMPRESSED_BUFFER_SIZE, len(data))
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000768 return self._comp.Compress(self._nframes, data)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000769
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000770 def _lin2ulaw(self, data):
771 import audioop
772 return audioop.lin2ulaw(data, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000773
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000774 def _lin2adpcm(self, data):
775 import audioop
776 if not hasattr(self, '_adpcmstate'):
777 self._adpcmstate = None
778 data, self._adpcmstate = audioop.lin2adpcm(data, 2,
779 self._adpcmstate)
780 return data
Sjoerd Mullender1f057541994-09-06 16:17:51 +0000781
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000782 def _ensure_header_written(self, datasize):
783 if not self._nframeswritten:
Serhiy Storchakad1a61dc2013-12-14 20:34:33 +0200784 if self._comptype in ('ULAW', 'ulaw', 'ALAW', 'alaw'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000785 if not self._sampwidth:
786 self._sampwidth = 2
787 if self._sampwidth != 2:
788 raise Error, 'sample width must be 2 when compressing with ULAW or ALAW'
789 if self._comptype == 'G722':
790 if not self._sampwidth:
791 self._sampwidth = 2
792 if self._sampwidth != 2:
793 raise Error, 'sample width must be 2 when compressing with G7.22 (ADPCM)'
794 if not self._nchannels:
795 raise Error, '# channels not specified'
796 if not self._sampwidth:
797 raise Error, 'sample width not specified'
798 if not self._framerate:
799 raise Error, 'sampling rate not specified'
800 self._write_header(datasize)
Guido van Rossum52fc1f61993-06-17 12:38:10 +0000801
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000802 def _init_compression(self):
803 if self._comptype == 'G722':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000804 self._convert = self._lin2adpcm
805 return
806 try:
807 import cl
808 except ImportError:
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300809 if self._comptype in ('ULAW', 'ulaw'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000810 try:
811 import audioop
812 self._convert = self._lin2ulaw
813 return
814 except ImportError:
815 pass
816 raise Error, 'cannot write compressed AIFF-C files'
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300817 if self._comptype in ('ULAW', 'ulaw'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000818 scheme = cl.G711_ULAW
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300819 elif self._comptype in ('ALAW', 'alaw'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000820 scheme = cl.G711_ALAW
821 else:
822 raise Error, 'unsupported compression type'
823 self._comp = cl.OpenCompressor(scheme)
824 params = [cl.ORIGINAL_FORMAT, 0,
825 cl.BITS_PER_COMPONENT, self._sampwidth * 8,
826 cl.FRAME_RATE, self._framerate,
827 cl.FRAME_BUFFER_SIZE, 100,
828 cl.COMPRESSED_BUFFER_SIZE, 100]
829 if self._nchannels == 1:
830 params[1] = cl.MONO
831 elif self._nchannels == 2:
832 params[1] = cl.STEREO_INTERLEAVED
833 else:
834 raise Error, 'cannot compress more than 2 channels'
835 self._comp.SetParams(params)
836 # the compressor produces a header which we ignore
837 dummy = self._comp.Compress(0, '')
838 self._convert = self._comp_data
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000839
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000840 def _write_header(self, initlength):
841 if self._aifc and self._comptype != 'NONE':
842 self._init_compression()
843 self._file.write('FORM')
844 if not self._nframes:
Antoine Pitroubca7b482009-10-14 17:30:52 +0000845 self._nframes = initlength // (self._nchannels * self._sampwidth)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000846 self._datalength = self._nframes * self._nchannels * self._sampwidth
847 if self._datalength & 1:
848 self._datalength = self._datalength + 1
849 if self._aifc:
Serhiy Storchakad1a61dc2013-12-14 20:34:33 +0200850 if self._comptype in ('ULAW', 'ulaw', 'ALAW', 'alaw'):
Antoine Pitroubca7b482009-10-14 17:30:52 +0000851 self._datalength = self._datalength // 2
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000852 if self._datalength & 1:
853 self._datalength = self._datalength + 1
854 elif self._comptype == 'G722':
Antoine Pitroubca7b482009-10-14 17:30:52 +0000855 self._datalength = (self._datalength + 3) // 4
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000856 if self._datalength & 1:
857 self._datalength = self._datalength + 1
Serhiy Storchakad1a61dc2013-12-14 20:34:33 +0200858 try:
859 self._form_length_pos = self._file.tell()
860 except (AttributeError, IOError):
861 self._form_length_pos = None
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000862 commlength = self._write_form_length(self._datalength)
863 if self._aifc:
864 self._file.write('AIFC')
865 self._file.write('FVER')
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100866 _write_ulong(self._file, 4)
867 _write_ulong(self._file, self._version)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000868 else:
869 self._file.write('AIFF')
870 self._file.write('COMM')
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100871 _write_ulong(self._file, commlength)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000872 _write_short(self._file, self._nchannels)
Serhiy Storchakad1a61dc2013-12-14 20:34:33 +0200873 if self._form_length_pos is not None:
874 self._nframes_pos = self._file.tell()
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100875 _write_ulong(self._file, self._nframes)
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300876 if self._comptype in ('ULAW', 'ulaw', 'ALAW', 'alaw', 'G722'):
Serhiy Storchaka986b5ee2013-10-12 18:21:12 +0300877 _write_short(self._file, 8)
878 else:
879 _write_short(self._file, self._sampwidth * 8)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000880 _write_float(self._file, self._framerate)
881 if self._aifc:
882 self._file.write(self._comptype)
883 _write_string(self._file, self._compname)
884 self._file.write('SSND')
Serhiy Storchakad1a61dc2013-12-14 20:34:33 +0200885 if self._form_length_pos is not None:
886 self._ssnd_length_pos = self._file.tell()
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100887 _write_ulong(self._file, self._datalength + 8)
888 _write_ulong(self._file, 0)
889 _write_ulong(self._file, 0)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000890
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000891 def _write_form_length(self, datalength):
892 if self._aifc:
893 commlength = 18 + 5 + len(self._compname)
894 if commlength & 1:
895 commlength = commlength + 1
896 verslength = 12
897 else:
898 commlength = 18
899 verslength = 0
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100900 _write_ulong(self._file, 4 + verslength + self._marklength + \
901 8 + commlength + 16 + datalength)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000902 return commlength
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000903
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000904 def _patchheader(self):
905 curpos = self._file.tell()
906 if self._datawritten & 1:
907 datalength = self._datawritten + 1
908 self._file.write(chr(0))
909 else:
910 datalength = self._datawritten
911 if datalength == self._datalength and \
912 self._nframes == self._nframeswritten and \
913 self._marklength == 0:
914 self._file.seek(curpos, 0)
915 return
916 self._file.seek(self._form_length_pos, 0)
917 dummy = self._write_form_length(datalength)
918 self._file.seek(self._nframes_pos, 0)
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100919 _write_ulong(self._file, self._nframeswritten)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000920 self._file.seek(self._ssnd_length_pos, 0)
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100921 _write_ulong(self._file, datalength + 8)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000922 self._file.seek(curpos, 0)
923 self._nframes = self._nframeswritten
924 self._datalength = datalength
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000925
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000926 def _writemarkers(self):
927 if len(self._markers) == 0:
928 return
929 self._file.write('MARK')
930 length = 2
931 for marker in self._markers:
932 id, pos, name = marker
933 length = length + len(name) + 1 + 6
934 if len(name) & 1 == 0:
935 length = length + 1
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100936 _write_ulong(self._file, length)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000937 self._marklength = length + 8
938 _write_short(self._file, len(self._markers))
939 for marker in self._markers:
940 id, pos, name = marker
941 _write_short(self._file, id)
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100942 _write_ulong(self._file, pos)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000943 _write_string(self._file, name)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000944
Fred Drake43161351999-06-22 21:23:23 +0000945def open(f, mode=None):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000946 if mode is None:
947 if hasattr(f, 'mode'):
948 mode = f.mode
949 else:
950 mode = 'rb'
951 if mode in ('r', 'rb'):
952 return Aifc_read(f)
953 elif mode in ('w', 'wb'):
954 return Aifc_write(f)
955 else:
956 raise Error, "mode must be 'r', 'rb', 'w', or 'wb'"
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000957
Guido van Rossum7bc817d1993-12-17 15:25:27 +0000958openfp = open # B/W compatibility
Guido van Rossum36bb1811996-12-31 05:57:34 +0000959
960if __name__ == '__main__':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000961 import sys
962 if not sys.argv[1:]:
963 sys.argv.append('/usr/demos/data/audio/bach.aiff')
964 fn = sys.argv[1]
965 f = open(fn, 'r')
Serhiy Storchaka879a2132013-08-25 19:12:33 +0300966 try:
967 print "Reading", fn
968 print "nchannels =", f.getnchannels()
969 print "nframes =", f.getnframes()
970 print "sampwidth =", f.getsampwidth()
971 print "framerate =", f.getframerate()
972 print "comptype =", f.getcomptype()
973 print "compname =", f.getcompname()
974 if sys.argv[2:]:
975 gn = sys.argv[2]
976 print "Writing", gn
977 g = open(gn, 'w')
978 try:
979 g.setparams(f.getparams())
980 while 1:
981 data = f.readframes(1024)
982 if not data:
983 break
984 g.writeframes(data)
985 finally:
986 g.close()
987 print "Done."
988 finally:
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000989 f.close()