blob: 9ac710fbde5f98e1b6a0bb49670ab925001d83b6 [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):
360 if self._decomp:
361 self._decomp.CloseDecompressor()
362 self._decomp = None
Benjamin Peterson02588112009-05-07 11:45:38 +0000363 self._file.close()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000364
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000365 def tell(self):
366 return self._soundpos
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000367
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000368 def getnchannels(self):
369 return self._nchannels
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000370
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000371 def getnframes(self):
372 return self._nframes
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000373
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000374 def getsampwidth(self):
375 return self._sampwidth
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000376
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000377 def getframerate(self):
378 return self._framerate
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000379
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000380 def getcomptype(self):
381 return self._comptype
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000382
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000383 def getcompname(self):
384 return self._compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000385
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000386## def getversion(self):
387## return self._version
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000388
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000389 def getparams(self):
390 return self.getnchannels(), self.getsampwidth(), \
391 self.getframerate(), self.getnframes(), \
392 self.getcomptype(), self.getcompname()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000393
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000394 def getmarkers(self):
395 if len(self._markers) == 0:
396 return None
397 return self._markers
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000398
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000399 def getmark(self, id):
400 for marker in self._markers:
401 if id == marker[0]:
402 return marker
Walter Dörwald70a6b492004-02-12 17:35:32 +0000403 raise Error, 'marker %r does not exist' % (id,)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000404
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000405 def setpos(self, pos):
406 if pos < 0 or pos > self._nframes:
407 raise Error, 'position not in range'
408 self._soundpos = pos
409 self._ssnd_seek_needed = 1
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000410
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000411 def readframes(self, nframes):
412 if self._ssnd_seek_needed:
413 self._ssnd_chunk.seek(0)
414 dummy = self._ssnd_chunk.read(8)
415 pos = self._soundpos * self._framesize
416 if pos:
Guido van Rossum2663c132000-03-07 15:19:31 +0000417 self._ssnd_chunk.seek(pos + 8)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000418 self._ssnd_seek_needed = 0
419 if nframes == 0:
420 return ''
421 data = self._ssnd_chunk.read(nframes * self._framesize)
422 if self._convert and data:
423 data = self._convert(data)
Antoine Pitroubca7b482009-10-14 17:30:52 +0000424 self._soundpos = self._soundpos + len(data) // (self._nchannels * self._sampwidth)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000425 return data
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000426
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000427 #
428 # Internal methods.
429 #
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000430
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000431 def _decomp_data(self, data):
432 import cl
433 dummy = self._decomp.SetParam(cl.FRAME_BUFFER_SIZE,
434 len(data) * 2)
Antoine Pitroubca7b482009-10-14 17:30:52 +0000435 return self._decomp.Decompress(len(data) // self._nchannels,
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000436 data)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000437
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000438 def _ulaw2lin(self, data):
439 import audioop
440 return audioop.ulaw2lin(data, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000441
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000442 def _adpcm2lin(self, data):
443 import audioop
444 if not hasattr(self, '_adpcmstate'):
445 # first time
446 self._adpcmstate = None
447 data, self._adpcmstate = audioop.adpcm2lin(data, 2,
448 self._adpcmstate)
449 return data
Sjoerd Mullender1f057541994-09-06 16:17:51 +0000450
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000451 def _read_comm_chunk(self, chunk):
452 self._nchannels = _read_short(chunk)
453 self._nframes = _read_long(chunk)
Antoine Pitroubca7b482009-10-14 17:30:52 +0000454 self._sampwidth = (_read_short(chunk) + 7) // 8
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000455 self._framerate = int(_read_float(chunk))
456 self._framesize = self._nchannels * self._sampwidth
457 if self._aifc:
458 #DEBUG: SGI's soundeditor produces a bad size :-(
459 kludge = 0
460 if chunk.chunksize == 18:
461 kludge = 1
462 print 'Warning: bad COMM chunk size'
463 chunk.chunksize = 23
464 #DEBUG end
465 self._comptype = chunk.read(4)
466 #DEBUG start
467 if kludge:
468 length = ord(chunk.file.read(1))
469 if length & 1 == 0:
470 length = length + 1
471 chunk.chunksize = chunk.chunksize + length
472 chunk.file.seek(-1, 1)
473 #DEBUG end
474 self._compname = _read_string(chunk)
475 if self._comptype != 'NONE':
476 if self._comptype == 'G722':
477 try:
478 import audioop
479 except ImportError:
480 pass
481 else:
482 self._convert = self._adpcm2lin
Serhiy Storchaka986b5ee2013-10-12 18:21:12 +0300483 self._sampwidth = 2
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000484 return
485 # for ULAW and ALAW try Compression Library
486 try:
487 import cl
488 except ImportError:
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300489 if self._comptype in ('ULAW', 'ulaw'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000490 try:
491 import audioop
492 self._convert = self._ulaw2lin
Serhiy Storchaka986b5ee2013-10-12 18:21:12 +0300493 self._sampwidth = 2
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000494 return
495 except ImportError:
496 pass
497 raise Error, 'cannot read compressed AIFF-C files'
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300498 if self._comptype in ('ULAW', 'ulaw'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000499 scheme = cl.G711_ULAW
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300500 elif self._comptype in ('ALAW', 'alaw'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000501 scheme = cl.G711_ALAW
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000502 else:
503 raise Error, 'unsupported compression type'
504 self._decomp = cl.OpenDecompressor(scheme)
505 self._convert = self._decomp_data
Serhiy Storchaka986b5ee2013-10-12 18:21:12 +0300506 self._sampwidth = 2
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000507 else:
508 self._comptype = 'NONE'
509 self._compname = 'not compressed'
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000510
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000511 def _readmark(self, chunk):
512 nmarkers = _read_short(chunk)
513 # Some files appear to contain invalid counts.
514 # Cope with this by testing for EOF.
515 try:
516 for i in range(nmarkers):
517 id = _read_short(chunk)
518 pos = _read_long(chunk)
519 name = _read_string(chunk)
520 if pos or name:
521 # some files appear to have
522 # dummy markers consisting of
523 # a position 0 and name ''
524 self._markers.append((id, pos, name))
525 except EOFError:
526 print 'Warning: MARK chunk contains only',
527 print len(self._markers),
528 if len(self._markers) == 1: print 'marker',
529 else: print 'markers',
530 print 'instead of', nmarkers
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000531
Guido van Rossumd3166071993-05-24 14:16:22 +0000532class Aifc_write:
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000533 # Variables used in this class:
534 #
535 # These variables are user settable through appropriate methods
536 # of this class:
537 # _file -- the open file with methods write(), close(), tell(), seek()
538 # set through the __init__() method
539 # _comptype -- the AIFF-C compression type ('NONE' in AIFF)
540 # set through the setcomptype() or setparams() method
541 # _compname -- the human-readable AIFF-C compression type
542 # set through the setcomptype() or setparams() method
543 # _nchannels -- the number of audio channels
544 # set through the setnchannels() or setparams() method
545 # _sampwidth -- the number of bytes per audio sample
546 # set through the setsampwidth() or setparams() method
547 # _framerate -- the sampling frequency
548 # set through the setframerate() or setparams() method
549 # _nframes -- the number of audio frames written to the header
550 # set through the setnframes() or setparams() method
551 # _aifc -- whether we're writing an AIFF-C file or an AIFF file
552 # set through the aifc() method, reset through the
553 # aiff() method
554 #
555 # These variables are used internally only:
556 # _version -- the AIFF-C version number
557 # _comp -- the compressor from builtin module cl
558 # _nframeswritten -- the number of audio frames actually written
559 # _datalength -- the size of the audio samples written to the header
560 # _datawritten -- the size of the audio samples actually written
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000561
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000562 def __init__(self, f):
563 if type(f) == type(''):
564 filename = f
565 f = __builtin__.open(f, 'wb')
566 else:
567 # else, assume it is an open file object already
568 filename = '???'
569 self.initfp(f)
570 if filename[-5:] == '.aiff':
571 self._aifc = 0
572 else:
573 self._aifc = 1
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000574
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000575 def initfp(self, file):
576 self._file = file
577 self._version = _AIFC_version
578 self._comptype = 'NONE'
579 self._compname = 'not compressed'
580 self._comp = None
581 self._convert = None
582 self._nchannels = 0
583 self._sampwidth = 0
584 self._framerate = 0
585 self._nframes = 0
586 self._nframeswritten = 0
587 self._datawritten = 0
588 self._datalength = 0
589 self._markers = []
590 self._marklength = 0
591 self._aifc = 1 # AIFF-C is default
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000592
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000593 def __del__(self):
594 if self._file:
595 self.close()
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000596
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000597 #
598 # User visible methods.
599 #
600 def aiff(self):
601 if self._nframeswritten:
602 raise Error, 'cannot change parameters after starting to write'
603 self._aifc = 0
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000604
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000605 def aifc(self):
606 if self._nframeswritten:
607 raise Error, 'cannot change parameters after starting to write'
608 self._aifc = 1
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000609
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000610 def setnchannels(self, nchannels):
611 if self._nframeswritten:
612 raise Error, 'cannot change parameters after starting to write'
613 if nchannels < 1:
614 raise Error, 'bad # of channels'
615 self._nchannels = nchannels
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000616
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000617 def getnchannels(self):
618 if not self._nchannels:
619 raise Error, 'number of channels not set'
620 return self._nchannels
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000621
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000622 def setsampwidth(self, sampwidth):
623 if self._nframeswritten:
624 raise Error, 'cannot change parameters after starting to write'
625 if sampwidth < 1 or sampwidth > 4:
626 raise Error, 'bad sample width'
627 self._sampwidth = sampwidth
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000628
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000629 def getsampwidth(self):
630 if not self._sampwidth:
631 raise Error, 'sample width not set'
632 return self._sampwidth
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000633
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000634 def setframerate(self, framerate):
635 if self._nframeswritten:
636 raise Error, 'cannot change parameters after starting to write'
637 if framerate <= 0:
638 raise Error, 'bad frame rate'
639 self._framerate = framerate
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000640
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000641 def getframerate(self):
642 if not self._framerate:
643 raise Error, 'frame rate not set'
644 return self._framerate
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000645
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000646 def setnframes(self, nframes):
647 if self._nframeswritten:
648 raise Error, 'cannot change parameters after starting to write'
649 self._nframes = nframes
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000650
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000651 def getnframes(self):
652 return self._nframeswritten
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000653
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000654 def setcomptype(self, comptype, compname):
655 if self._nframeswritten:
656 raise Error, 'cannot change parameters after starting to write'
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300657 if comptype not in ('NONE', 'ULAW', 'ulaw', 'ALAW', 'alaw', 'G722'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000658 raise Error, 'unsupported compression type'
659 self._comptype = comptype
660 self._compname = compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000661
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000662 def getcomptype(self):
663 return self._comptype
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000664
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000665 def getcompname(self):
666 return self._compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000667
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000668## def setversion(self, version):
669## if self._nframeswritten:
670## raise Error, 'cannot change parameters after starting to write'
671## self._version = version
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000672
Brett Cannond13e4ba2008-08-04 21:33:00 +0000673 def setparams(self, info):
674 nchannels, sampwidth, framerate, nframes, comptype, compname = info
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000675 if self._nframeswritten:
676 raise Error, 'cannot change parameters after starting to write'
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300677 if comptype not in ('NONE', 'ULAW', 'ulaw', 'ALAW', 'alaw', 'G722'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000678 raise Error, 'unsupported compression type'
679 self.setnchannels(nchannels)
680 self.setsampwidth(sampwidth)
681 self.setframerate(framerate)
682 self.setnframes(nframes)
683 self.setcomptype(comptype, compname)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000684
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000685 def getparams(self):
686 if not self._nchannels or not self._sampwidth or not self._framerate:
687 raise Error, 'not all parameters set'
688 return self._nchannels, self._sampwidth, self._framerate, \
689 self._nframes, self._comptype, self._compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000690
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000691 def setmark(self, id, pos, name):
692 if id <= 0:
693 raise Error, 'marker ID must be > 0'
694 if pos < 0:
695 raise Error, 'marker position must be >= 0'
696 if type(name) != type(''):
697 raise Error, 'marker name must be a string'
698 for i in range(len(self._markers)):
699 if id == self._markers[i][0]:
700 self._markers[i] = id, pos, name
701 return
702 self._markers.append((id, pos, name))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000703
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000704 def getmark(self, id):
705 for marker in self._markers:
706 if id == marker[0]:
707 return marker
Walter Dörwald70a6b492004-02-12 17:35:32 +0000708 raise Error, 'marker %r does not exist' % (id,)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000709
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000710 def getmarkers(self):
711 if len(self._markers) == 0:
712 return None
713 return self._markers
Tim Peters146965a2001-01-14 18:09:23 +0000714
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000715 def tell(self):
716 return self._nframeswritten
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000717
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000718 def writeframesraw(self, data):
719 self._ensure_header_written(len(data))
Antoine Pitroubca7b482009-10-14 17:30:52 +0000720 nframes = len(data) // (self._sampwidth * self._nchannels)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000721 if self._convert:
722 data = self._convert(data)
723 self._file.write(data)
724 self._nframeswritten = self._nframeswritten + nframes
725 self._datawritten = self._datawritten + len(data)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000726
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000727 def writeframes(self, data):
728 self.writeframesraw(data)
729 if self._nframeswritten != self._nframes or \
730 self._datalength != self._datawritten:
731 self._patchheader()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000732
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000733 def close(self):
Serhiy Storchaka4ed797e2012-12-29 22:25:59 +0200734 if self._file is None:
735 return
736 try:
737 self._ensure_header_written(0)
738 if self._datawritten & 1:
739 # quick pad to even size
740 self._file.write(chr(0))
741 self._datawritten = self._datawritten + 1
742 self._writemarkers()
743 if self._nframeswritten != self._nframes or \
744 self._datalength != self._datawritten or \
745 self._marklength:
746 self._patchheader()
747 if self._comp:
748 self._comp.CloseCompressor()
749 self._comp = None
750 finally:
751 # Prevent ref cycles
752 self._convert = None
753 f = self._file
754 self._file = None
755 f.close()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000756
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000757 #
758 # Internal methods.
759 #
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000760
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000761 def _comp_data(self, data):
762 import cl
Neal Norwitz086ac002002-02-11 17:56:27 +0000763 dummy = self._comp.SetParam(cl.FRAME_BUFFER_SIZE, len(data))
764 dummy = self._comp.SetParam(cl.COMPRESSED_BUFFER_SIZE, len(data))
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000765 return self._comp.Compress(self._nframes, data)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000766
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000767 def _lin2ulaw(self, data):
768 import audioop
769 return audioop.lin2ulaw(data, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000770
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000771 def _lin2adpcm(self, data):
772 import audioop
773 if not hasattr(self, '_adpcmstate'):
774 self._adpcmstate = None
775 data, self._adpcmstate = audioop.lin2adpcm(data, 2,
776 self._adpcmstate)
777 return data
Sjoerd Mullender1f057541994-09-06 16:17:51 +0000778
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000779 def _ensure_header_written(self, datasize):
780 if not self._nframeswritten:
Serhiy Storchakad1a61dc2013-12-14 20:34:33 +0200781 if self._comptype in ('ULAW', 'ulaw', 'ALAW', 'alaw'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000782 if not self._sampwidth:
783 self._sampwidth = 2
784 if self._sampwidth != 2:
785 raise Error, 'sample width must be 2 when compressing with ULAW or ALAW'
786 if self._comptype == 'G722':
787 if not self._sampwidth:
788 self._sampwidth = 2
789 if self._sampwidth != 2:
790 raise Error, 'sample width must be 2 when compressing with G7.22 (ADPCM)'
791 if not self._nchannels:
792 raise Error, '# channels not specified'
793 if not self._sampwidth:
794 raise Error, 'sample width not specified'
795 if not self._framerate:
796 raise Error, 'sampling rate not specified'
797 self._write_header(datasize)
Guido van Rossum52fc1f61993-06-17 12:38:10 +0000798
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000799 def _init_compression(self):
800 if self._comptype == 'G722':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000801 self._convert = self._lin2adpcm
802 return
803 try:
804 import cl
805 except ImportError:
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300806 if self._comptype in ('ULAW', 'ulaw'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000807 try:
808 import audioop
809 self._convert = self._lin2ulaw
810 return
811 except ImportError:
812 pass
813 raise Error, 'cannot write compressed AIFF-C files'
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300814 if self._comptype in ('ULAW', 'ulaw'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000815 scheme = cl.G711_ULAW
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300816 elif self._comptype in ('ALAW', 'alaw'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000817 scheme = cl.G711_ALAW
818 else:
819 raise Error, 'unsupported compression type'
820 self._comp = cl.OpenCompressor(scheme)
821 params = [cl.ORIGINAL_FORMAT, 0,
822 cl.BITS_PER_COMPONENT, self._sampwidth * 8,
823 cl.FRAME_RATE, self._framerate,
824 cl.FRAME_BUFFER_SIZE, 100,
825 cl.COMPRESSED_BUFFER_SIZE, 100]
826 if self._nchannels == 1:
827 params[1] = cl.MONO
828 elif self._nchannels == 2:
829 params[1] = cl.STEREO_INTERLEAVED
830 else:
831 raise Error, 'cannot compress more than 2 channels'
832 self._comp.SetParams(params)
833 # the compressor produces a header which we ignore
834 dummy = self._comp.Compress(0, '')
835 self._convert = self._comp_data
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000836
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000837 def _write_header(self, initlength):
838 if self._aifc and self._comptype != 'NONE':
839 self._init_compression()
840 self._file.write('FORM')
841 if not self._nframes:
Antoine Pitroubca7b482009-10-14 17:30:52 +0000842 self._nframes = initlength // (self._nchannels * self._sampwidth)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000843 self._datalength = self._nframes * self._nchannels * self._sampwidth
844 if self._datalength & 1:
845 self._datalength = self._datalength + 1
846 if self._aifc:
Serhiy Storchakad1a61dc2013-12-14 20:34:33 +0200847 if self._comptype in ('ULAW', 'ulaw', 'ALAW', 'alaw'):
Antoine Pitroubca7b482009-10-14 17:30:52 +0000848 self._datalength = self._datalength // 2
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000849 if self._datalength & 1:
850 self._datalength = self._datalength + 1
851 elif self._comptype == 'G722':
Antoine Pitroubca7b482009-10-14 17:30:52 +0000852 self._datalength = (self._datalength + 3) // 4
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000853 if self._datalength & 1:
854 self._datalength = self._datalength + 1
Serhiy Storchakad1a61dc2013-12-14 20:34:33 +0200855 try:
856 self._form_length_pos = self._file.tell()
857 except (AttributeError, IOError):
858 self._form_length_pos = None
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000859 commlength = self._write_form_length(self._datalength)
860 if self._aifc:
861 self._file.write('AIFC')
862 self._file.write('FVER')
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100863 _write_ulong(self._file, 4)
864 _write_ulong(self._file, self._version)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000865 else:
866 self._file.write('AIFF')
867 self._file.write('COMM')
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100868 _write_ulong(self._file, commlength)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000869 _write_short(self._file, self._nchannels)
Serhiy Storchakad1a61dc2013-12-14 20:34:33 +0200870 if self._form_length_pos is not None:
871 self._nframes_pos = self._file.tell()
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100872 _write_ulong(self._file, self._nframes)
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300873 if self._comptype in ('ULAW', 'ulaw', 'ALAW', 'alaw', 'G722'):
Serhiy Storchaka986b5ee2013-10-12 18:21:12 +0300874 _write_short(self._file, 8)
875 else:
876 _write_short(self._file, self._sampwidth * 8)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000877 _write_float(self._file, self._framerate)
878 if self._aifc:
879 self._file.write(self._comptype)
880 _write_string(self._file, self._compname)
881 self._file.write('SSND')
Serhiy Storchakad1a61dc2013-12-14 20:34:33 +0200882 if self._form_length_pos is not None:
883 self._ssnd_length_pos = self._file.tell()
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100884 _write_ulong(self._file, self._datalength + 8)
885 _write_ulong(self._file, 0)
886 _write_ulong(self._file, 0)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000887
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000888 def _write_form_length(self, datalength):
889 if self._aifc:
890 commlength = 18 + 5 + len(self._compname)
891 if commlength & 1:
892 commlength = commlength + 1
893 verslength = 12
894 else:
895 commlength = 18
896 verslength = 0
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100897 _write_ulong(self._file, 4 + verslength + self._marklength + \
898 8 + commlength + 16 + datalength)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000899 return commlength
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000900
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000901 def _patchheader(self):
902 curpos = self._file.tell()
903 if self._datawritten & 1:
904 datalength = self._datawritten + 1
905 self._file.write(chr(0))
906 else:
907 datalength = self._datawritten
908 if datalength == self._datalength and \
909 self._nframes == self._nframeswritten and \
910 self._marklength == 0:
911 self._file.seek(curpos, 0)
912 return
913 self._file.seek(self._form_length_pos, 0)
914 dummy = self._write_form_length(datalength)
915 self._file.seek(self._nframes_pos, 0)
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100916 _write_ulong(self._file, self._nframeswritten)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000917 self._file.seek(self._ssnd_length_pos, 0)
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100918 _write_ulong(self._file, datalength + 8)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000919 self._file.seek(curpos, 0)
920 self._nframes = self._nframeswritten
921 self._datalength = datalength
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000922
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000923 def _writemarkers(self):
924 if len(self._markers) == 0:
925 return
926 self._file.write('MARK')
927 length = 2
928 for marker in self._markers:
929 id, pos, name = marker
930 length = length + len(name) + 1 + 6
931 if len(name) & 1 == 0:
932 length = length + 1
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100933 _write_ulong(self._file, length)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000934 self._marklength = length + 8
935 _write_short(self._file, len(self._markers))
936 for marker in self._markers:
937 id, pos, name = marker
938 _write_short(self._file, id)
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100939 _write_ulong(self._file, pos)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000940 _write_string(self._file, name)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000941
Fred Drake43161351999-06-22 21:23:23 +0000942def open(f, mode=None):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000943 if mode is None:
944 if hasattr(f, 'mode'):
945 mode = f.mode
946 else:
947 mode = 'rb'
948 if mode in ('r', 'rb'):
949 return Aifc_read(f)
950 elif mode in ('w', 'wb'):
951 return Aifc_write(f)
952 else:
953 raise Error, "mode must be 'r', 'rb', 'w', or 'wb'"
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000954
Guido van Rossum7bc817d1993-12-17 15:25:27 +0000955openfp = open # B/W compatibility
Guido van Rossum36bb1811996-12-31 05:57:34 +0000956
957if __name__ == '__main__':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000958 import sys
959 if not sys.argv[1:]:
960 sys.argv.append('/usr/demos/data/audio/bach.aiff')
961 fn = sys.argv[1]
962 f = open(fn, 'r')
Serhiy Storchaka879a2132013-08-25 19:12:33 +0300963 try:
964 print "Reading", fn
965 print "nchannels =", f.getnchannels()
966 print "nframes =", f.getnframes()
967 print "sampwidth =", f.getsampwidth()
968 print "framerate =", f.getframerate()
969 print "comptype =", f.getcomptype()
970 print "compname =", f.getcompname()
971 if sys.argv[2:]:
972 gn = sys.argv[2]
973 print "Writing", gn
974 g = open(gn, 'w')
975 try:
976 g.setparams(f.getparams())
977 while 1:
978 data = f.readframes(1024)
979 if not data:
980 break
981 g.writeframes(data)
982 finally:
983 g.close()
984 print "Done."
985 finally:
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000986 f.close()