blob: 981f8010690e00e75f1b4605f456edf41d32527a [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
Miss Islington (bot)b852d8c2018-02-20 22:37:18 -0800311 self._ssnd_chunk = None
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000312 while 1:
313 self._ssnd_seek_needed = 1
314 try:
315 chunk = Chunk(self._file)
316 except EOFError:
317 break
318 chunkname = chunk.getname()
319 if chunkname == 'COMM':
320 self._read_comm_chunk(chunk)
321 self._comm_chunk_read = 1
322 elif chunkname == 'SSND':
323 self._ssnd_chunk = chunk
324 dummy = chunk.read(8)
325 self._ssnd_seek_needed = 0
326 elif chunkname == 'FVER':
Guido van Rossum820819c2002-08-12 22:11:28 +0000327 self._version = _read_ulong(chunk)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000328 elif chunkname == 'MARK':
329 self._readmark(chunk)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000330 chunk.skip()
331 if not self._comm_chunk_read or not self._ssnd_chunk:
332 raise Error, 'COMM chunk and/or SSND chunk missing'
333 if self._aifc and self._decomp:
334 import cl
335 params = [cl.ORIGINAL_FORMAT, 0,
336 cl.BITS_PER_COMPONENT, self._sampwidth * 8,
337 cl.FRAME_RATE, self._framerate]
338 if self._nchannels == 1:
339 params[1] = cl.MONO
340 elif self._nchannels == 2:
341 params[1] = cl.STEREO_INTERLEAVED
342 else:
343 raise Error, 'cannot compress more than 2 channels'
344 self._decomp.SetParams(params)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000345
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000346 def __init__(self, f):
INADA Naoki02eb4b02017-02-28 20:39:30 +0900347 if isinstance(f, basestring):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000348 f = __builtin__.open(f, 'rb')
INADA Naoki02eb4b02017-02-28 20:39:30 +0900349 try:
350 self.initfp(f)
351 except:
352 f.close()
353 raise
354 else:
355 # assume it is an open file object already
356 self.initfp(f)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000357
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000358 #
359 # User visible methods.
360 #
361 def getfp(self):
362 return self._file
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000363
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000364 def rewind(self):
365 self._ssnd_seek_needed = 1
366 self._soundpos = 0
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000367
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000368 def close(self):
Serhiy Storchaka1aa2c0f2015-04-10 13:24:10 +0300369 decomp = self._decomp
370 try:
371 if decomp:
372 self._decomp = None
373 decomp.CloseDecompressor()
374 finally:
375 self._file.close()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000376
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000377 def tell(self):
378 return self._soundpos
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000379
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000380 def getnchannels(self):
381 return self._nchannels
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000382
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000383 def getnframes(self):
384 return self._nframes
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000385
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000386 def getsampwidth(self):
387 return self._sampwidth
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000388
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000389 def getframerate(self):
390 return self._framerate
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000391
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000392 def getcomptype(self):
393 return self._comptype
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000394
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000395 def getcompname(self):
396 return self._compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000397
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000398## def getversion(self):
399## return self._version
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000400
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000401 def getparams(self):
402 return self.getnchannels(), self.getsampwidth(), \
403 self.getframerate(), self.getnframes(), \
404 self.getcomptype(), self.getcompname()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000405
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000406 def getmarkers(self):
407 if len(self._markers) == 0:
408 return None
409 return self._markers
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000410
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000411 def getmark(self, id):
412 for marker in self._markers:
413 if id == marker[0]:
414 return marker
Walter Dörwald70a6b492004-02-12 17:35:32 +0000415 raise Error, 'marker %r does not exist' % (id,)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000416
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000417 def setpos(self, pos):
418 if pos < 0 or pos > self._nframes:
419 raise Error, 'position not in range'
420 self._soundpos = pos
421 self._ssnd_seek_needed = 1
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000422
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000423 def readframes(self, nframes):
424 if self._ssnd_seek_needed:
425 self._ssnd_chunk.seek(0)
426 dummy = self._ssnd_chunk.read(8)
427 pos = self._soundpos * self._framesize
428 if pos:
Guido van Rossum2663c132000-03-07 15:19:31 +0000429 self._ssnd_chunk.seek(pos + 8)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000430 self._ssnd_seek_needed = 0
431 if nframes == 0:
432 return ''
433 data = self._ssnd_chunk.read(nframes * self._framesize)
434 if self._convert and data:
435 data = self._convert(data)
Antoine Pitroubca7b482009-10-14 17:30:52 +0000436 self._soundpos = self._soundpos + len(data) // (self._nchannels * self._sampwidth)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000437 return data
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000438
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000439 #
440 # Internal methods.
441 #
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000442
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000443 def _decomp_data(self, data):
444 import cl
445 dummy = self._decomp.SetParam(cl.FRAME_BUFFER_SIZE,
446 len(data) * 2)
Antoine Pitroubca7b482009-10-14 17:30:52 +0000447 return self._decomp.Decompress(len(data) // self._nchannels,
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000448 data)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000449
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000450 def _ulaw2lin(self, data):
451 import audioop
452 return audioop.ulaw2lin(data, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000453
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000454 def _adpcm2lin(self, data):
455 import audioop
456 if not hasattr(self, '_adpcmstate'):
457 # first time
458 self._adpcmstate = None
459 data, self._adpcmstate = audioop.adpcm2lin(data, 2,
460 self._adpcmstate)
461 return data
Sjoerd Mullender1f057541994-09-06 16:17:51 +0000462
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000463 def _read_comm_chunk(self, chunk):
464 self._nchannels = _read_short(chunk)
465 self._nframes = _read_long(chunk)
Antoine Pitroubca7b482009-10-14 17:30:52 +0000466 self._sampwidth = (_read_short(chunk) + 7) // 8
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000467 self._framerate = int(_read_float(chunk))
468 self._framesize = self._nchannels * self._sampwidth
469 if self._aifc:
470 #DEBUG: SGI's soundeditor produces a bad size :-(
471 kludge = 0
472 if chunk.chunksize == 18:
473 kludge = 1
474 print 'Warning: bad COMM chunk size'
475 chunk.chunksize = 23
476 #DEBUG end
477 self._comptype = chunk.read(4)
478 #DEBUG start
479 if kludge:
480 length = ord(chunk.file.read(1))
481 if length & 1 == 0:
482 length = length + 1
483 chunk.chunksize = chunk.chunksize + length
484 chunk.file.seek(-1, 1)
485 #DEBUG end
486 self._compname = _read_string(chunk)
487 if self._comptype != 'NONE':
488 if self._comptype == 'G722':
489 try:
490 import audioop
491 except ImportError:
492 pass
493 else:
494 self._convert = self._adpcm2lin
Serhiy Storchaka986b5ee2013-10-12 18:21:12 +0300495 self._sampwidth = 2
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000496 return
497 # for ULAW and ALAW try Compression Library
498 try:
499 import cl
500 except ImportError:
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300501 if self._comptype in ('ULAW', 'ulaw'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000502 try:
503 import audioop
504 self._convert = self._ulaw2lin
Serhiy Storchaka986b5ee2013-10-12 18:21:12 +0300505 self._sampwidth = 2
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000506 return
507 except ImportError:
508 pass
509 raise Error, 'cannot read compressed AIFF-C files'
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300510 if self._comptype in ('ULAW', 'ulaw'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000511 scheme = cl.G711_ULAW
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300512 elif self._comptype in ('ALAW', 'alaw'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000513 scheme = cl.G711_ALAW
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000514 else:
515 raise Error, 'unsupported compression type'
516 self._decomp = cl.OpenDecompressor(scheme)
517 self._convert = self._decomp_data
Serhiy Storchaka986b5ee2013-10-12 18:21:12 +0300518 self._sampwidth = 2
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000519 else:
520 self._comptype = 'NONE'
521 self._compname = 'not compressed'
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000522
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000523 def _readmark(self, chunk):
524 nmarkers = _read_short(chunk)
525 # Some files appear to contain invalid counts.
526 # Cope with this by testing for EOF.
527 try:
528 for i in range(nmarkers):
529 id = _read_short(chunk)
530 pos = _read_long(chunk)
531 name = _read_string(chunk)
532 if pos or name:
533 # some files appear to have
534 # dummy markers consisting of
535 # a position 0 and name ''
536 self._markers.append((id, pos, name))
537 except EOFError:
538 print 'Warning: MARK chunk contains only',
539 print len(self._markers),
540 if len(self._markers) == 1: print 'marker',
541 else: print 'markers',
542 print 'instead of', nmarkers
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000543
Guido van Rossumd3166071993-05-24 14:16:22 +0000544class Aifc_write:
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000545 # Variables used in this class:
546 #
547 # These variables are user settable through appropriate methods
548 # of this class:
549 # _file -- the open file with methods write(), close(), tell(), seek()
550 # set through the __init__() method
551 # _comptype -- the AIFF-C compression type ('NONE' in AIFF)
552 # set through the setcomptype() or setparams() method
553 # _compname -- the human-readable AIFF-C compression type
554 # set through the setcomptype() or setparams() method
555 # _nchannels -- the number of audio channels
556 # set through the setnchannels() or setparams() method
557 # _sampwidth -- the number of bytes per audio sample
558 # set through the setsampwidth() or setparams() method
559 # _framerate -- the sampling frequency
560 # set through the setframerate() or setparams() method
561 # _nframes -- the number of audio frames written to the header
562 # set through the setnframes() or setparams() method
563 # _aifc -- whether we're writing an AIFF-C file or an AIFF file
564 # set through the aifc() method, reset through the
565 # aiff() method
566 #
567 # These variables are used internally only:
568 # _version -- the AIFF-C version number
569 # _comp -- the compressor from builtin module cl
570 # _nframeswritten -- the number of audio frames actually written
571 # _datalength -- the size of the audio samples written to the header
572 # _datawritten -- the size of the audio samples actually written
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000573
INADA Naoki02eb4b02017-02-28 20:39:30 +0900574 _file = None # Set here since __del__ checks it
575
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000576 def __init__(self, f):
INADA Naoki02eb4b02017-02-28 20:39:30 +0900577 if isinstance(f, basestring):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000578 filename = f
579 f = __builtin__.open(f, 'wb')
580 else:
581 # else, assume it is an open file object already
582 filename = '???'
583 self.initfp(f)
584 if filename[-5:] == '.aiff':
585 self._aifc = 0
586 else:
587 self._aifc = 1
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000588
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000589 def initfp(self, file):
590 self._file = file
591 self._version = _AIFC_version
592 self._comptype = 'NONE'
593 self._compname = 'not compressed'
594 self._comp = None
595 self._convert = None
596 self._nchannels = 0
597 self._sampwidth = 0
598 self._framerate = 0
599 self._nframes = 0
600 self._nframeswritten = 0
601 self._datawritten = 0
602 self._datalength = 0
603 self._markers = []
604 self._marklength = 0
605 self._aifc = 1 # AIFF-C is default
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000606
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000607 def __del__(self):
608 if self._file:
609 self.close()
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000610
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000611 #
612 # User visible methods.
613 #
614 def aiff(self):
615 if self._nframeswritten:
616 raise Error, 'cannot change parameters after starting to write'
617 self._aifc = 0
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000618
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000619 def aifc(self):
620 if self._nframeswritten:
621 raise Error, 'cannot change parameters after starting to write'
622 self._aifc = 1
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000623
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000624 def setnchannels(self, nchannels):
625 if self._nframeswritten:
626 raise Error, 'cannot change parameters after starting to write'
627 if nchannels < 1:
628 raise Error, 'bad # of channels'
629 self._nchannels = nchannels
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000630
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000631 def getnchannels(self):
632 if not self._nchannels:
633 raise Error, 'number of channels not set'
634 return self._nchannels
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000635
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000636 def setsampwidth(self, sampwidth):
637 if self._nframeswritten:
638 raise Error, 'cannot change parameters after starting to write'
639 if sampwidth < 1 or sampwidth > 4:
640 raise Error, 'bad sample width'
641 self._sampwidth = sampwidth
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000642
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000643 def getsampwidth(self):
644 if not self._sampwidth:
645 raise Error, 'sample width not set'
646 return self._sampwidth
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000647
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000648 def setframerate(self, framerate):
649 if self._nframeswritten:
650 raise Error, 'cannot change parameters after starting to write'
651 if framerate <= 0:
652 raise Error, 'bad frame rate'
653 self._framerate = framerate
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000654
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000655 def getframerate(self):
656 if not self._framerate:
657 raise Error, 'frame rate not set'
658 return self._framerate
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000659
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000660 def setnframes(self, nframes):
661 if self._nframeswritten:
662 raise Error, 'cannot change parameters after starting to write'
663 self._nframes = nframes
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000664
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000665 def getnframes(self):
666 return self._nframeswritten
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000667
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000668 def setcomptype(self, comptype, compname):
669 if self._nframeswritten:
670 raise Error, 'cannot change parameters after starting to write'
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300671 if comptype not in ('NONE', 'ULAW', 'ulaw', 'ALAW', 'alaw', 'G722'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000672 raise Error, 'unsupported compression type'
673 self._comptype = comptype
674 self._compname = compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000675
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000676 def getcomptype(self):
677 return self._comptype
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000678
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000679 def getcompname(self):
680 return self._compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000681
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000682## def setversion(self, version):
683## if self._nframeswritten:
684## raise Error, 'cannot change parameters after starting to write'
685## self._version = version
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000686
Brett Cannond13e4ba2008-08-04 21:33:00 +0000687 def setparams(self, info):
688 nchannels, sampwidth, framerate, nframes, comptype, compname = info
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000689 if self._nframeswritten:
690 raise Error, 'cannot change parameters after starting to write'
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300691 if comptype not in ('NONE', 'ULAW', 'ulaw', 'ALAW', 'alaw', 'G722'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000692 raise Error, 'unsupported compression type'
693 self.setnchannels(nchannels)
694 self.setsampwidth(sampwidth)
695 self.setframerate(framerate)
696 self.setnframes(nframes)
697 self.setcomptype(comptype, compname)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000698
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000699 def getparams(self):
700 if not self._nchannels or not self._sampwidth or not self._framerate:
701 raise Error, 'not all parameters set'
702 return self._nchannels, self._sampwidth, self._framerate, \
703 self._nframes, self._comptype, self._compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000704
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000705 def setmark(self, id, pos, name):
706 if id <= 0:
707 raise Error, 'marker ID must be > 0'
708 if pos < 0:
709 raise Error, 'marker position must be >= 0'
710 if type(name) != type(''):
711 raise Error, 'marker name must be a string'
712 for i in range(len(self._markers)):
713 if id == self._markers[i][0]:
714 self._markers[i] = id, pos, name
715 return
716 self._markers.append((id, pos, name))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000717
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000718 def getmark(self, id):
719 for marker in self._markers:
720 if id == marker[0]:
721 return marker
Walter Dörwald70a6b492004-02-12 17:35:32 +0000722 raise Error, 'marker %r does not exist' % (id,)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000723
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000724 def getmarkers(self):
725 if len(self._markers) == 0:
726 return None
727 return self._markers
Tim Peters146965a2001-01-14 18:09:23 +0000728
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000729 def tell(self):
730 return self._nframeswritten
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000731
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000732 def writeframesraw(self, data):
733 self._ensure_header_written(len(data))
Antoine Pitroubca7b482009-10-14 17:30:52 +0000734 nframes = len(data) // (self._sampwidth * self._nchannels)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000735 if self._convert:
736 data = self._convert(data)
737 self._file.write(data)
738 self._nframeswritten = self._nframeswritten + nframes
739 self._datawritten = self._datawritten + len(data)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000740
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000741 def writeframes(self, data):
742 self.writeframesraw(data)
743 if self._nframeswritten != self._nframes or \
744 self._datalength != self._datawritten:
745 self._patchheader()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000746
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000747 def close(self):
Serhiy Storchaka4ed797e2012-12-29 22:25:59 +0200748 if self._file is None:
749 return
750 try:
751 self._ensure_header_written(0)
752 if self._datawritten & 1:
753 # quick pad to even size
754 self._file.write(chr(0))
755 self._datawritten = self._datawritten + 1
756 self._writemarkers()
757 if self._nframeswritten != self._nframes or \
758 self._datalength != self._datawritten or \
759 self._marklength:
760 self._patchheader()
761 if self._comp:
762 self._comp.CloseCompressor()
763 self._comp = None
764 finally:
765 # Prevent ref cycles
766 self._convert = None
767 f = self._file
768 self._file = None
769 f.close()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000770
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000771 #
772 # Internal methods.
773 #
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000774
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000775 def _comp_data(self, data):
776 import cl
Neal Norwitz086ac002002-02-11 17:56:27 +0000777 dummy = self._comp.SetParam(cl.FRAME_BUFFER_SIZE, len(data))
778 dummy = self._comp.SetParam(cl.COMPRESSED_BUFFER_SIZE, len(data))
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000779 return self._comp.Compress(self._nframes, data)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000780
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000781 def _lin2ulaw(self, data):
782 import audioop
783 return audioop.lin2ulaw(data, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000784
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000785 def _lin2adpcm(self, data):
786 import audioop
787 if not hasattr(self, '_adpcmstate'):
788 self._adpcmstate = None
789 data, self._adpcmstate = audioop.lin2adpcm(data, 2,
790 self._adpcmstate)
791 return data
Sjoerd Mullender1f057541994-09-06 16:17:51 +0000792
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000793 def _ensure_header_written(self, datasize):
794 if not self._nframeswritten:
Serhiy Storchakad1a61dc2013-12-14 20:34:33 +0200795 if self._comptype in ('ULAW', 'ulaw', 'ALAW', 'alaw'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000796 if not self._sampwidth:
797 self._sampwidth = 2
798 if self._sampwidth != 2:
799 raise Error, 'sample width must be 2 when compressing with ULAW or ALAW'
800 if self._comptype == 'G722':
801 if not self._sampwidth:
802 self._sampwidth = 2
803 if self._sampwidth != 2:
804 raise Error, 'sample width must be 2 when compressing with G7.22 (ADPCM)'
805 if not self._nchannels:
806 raise Error, '# channels not specified'
807 if not self._sampwidth:
808 raise Error, 'sample width not specified'
809 if not self._framerate:
810 raise Error, 'sampling rate not specified'
811 self._write_header(datasize)
Guido van Rossum52fc1f61993-06-17 12:38:10 +0000812
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000813 def _init_compression(self):
814 if self._comptype == 'G722':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000815 self._convert = self._lin2adpcm
816 return
817 try:
818 import cl
819 except ImportError:
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300820 if self._comptype in ('ULAW', 'ulaw'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000821 try:
822 import audioop
823 self._convert = self._lin2ulaw
824 return
825 except ImportError:
826 pass
827 raise Error, 'cannot write compressed AIFF-C files'
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300828 if self._comptype in ('ULAW', 'ulaw'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000829 scheme = cl.G711_ULAW
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300830 elif self._comptype in ('ALAW', 'alaw'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000831 scheme = cl.G711_ALAW
832 else:
833 raise Error, 'unsupported compression type'
834 self._comp = cl.OpenCompressor(scheme)
835 params = [cl.ORIGINAL_FORMAT, 0,
836 cl.BITS_PER_COMPONENT, self._sampwidth * 8,
837 cl.FRAME_RATE, self._framerate,
838 cl.FRAME_BUFFER_SIZE, 100,
839 cl.COMPRESSED_BUFFER_SIZE, 100]
840 if self._nchannels == 1:
841 params[1] = cl.MONO
842 elif self._nchannels == 2:
843 params[1] = cl.STEREO_INTERLEAVED
844 else:
845 raise Error, 'cannot compress more than 2 channels'
846 self._comp.SetParams(params)
847 # the compressor produces a header which we ignore
848 dummy = self._comp.Compress(0, '')
849 self._convert = self._comp_data
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000850
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000851 def _write_header(self, initlength):
852 if self._aifc and self._comptype != 'NONE':
853 self._init_compression()
854 self._file.write('FORM')
855 if not self._nframes:
Antoine Pitroubca7b482009-10-14 17:30:52 +0000856 self._nframes = initlength // (self._nchannels * self._sampwidth)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000857 self._datalength = self._nframes * self._nchannels * self._sampwidth
858 if self._datalength & 1:
859 self._datalength = self._datalength + 1
860 if self._aifc:
Serhiy Storchakad1a61dc2013-12-14 20:34:33 +0200861 if self._comptype in ('ULAW', 'ulaw', 'ALAW', 'alaw'):
Antoine Pitroubca7b482009-10-14 17:30:52 +0000862 self._datalength = self._datalength // 2
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000863 if self._datalength & 1:
864 self._datalength = self._datalength + 1
865 elif self._comptype == 'G722':
Antoine Pitroubca7b482009-10-14 17:30:52 +0000866 self._datalength = (self._datalength + 3) // 4
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000867 if self._datalength & 1:
868 self._datalength = self._datalength + 1
Serhiy Storchakad1a61dc2013-12-14 20:34:33 +0200869 try:
870 self._form_length_pos = self._file.tell()
871 except (AttributeError, IOError):
872 self._form_length_pos = None
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000873 commlength = self._write_form_length(self._datalength)
874 if self._aifc:
875 self._file.write('AIFC')
876 self._file.write('FVER')
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100877 _write_ulong(self._file, 4)
878 _write_ulong(self._file, self._version)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000879 else:
880 self._file.write('AIFF')
881 self._file.write('COMM')
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100882 _write_ulong(self._file, commlength)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000883 _write_short(self._file, self._nchannels)
Serhiy Storchakad1a61dc2013-12-14 20:34:33 +0200884 if self._form_length_pos is not None:
885 self._nframes_pos = self._file.tell()
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100886 _write_ulong(self._file, self._nframes)
Serhiy Storchaka499d8212013-10-12 21:35:33 +0300887 if self._comptype in ('ULAW', 'ulaw', 'ALAW', 'alaw', 'G722'):
Serhiy Storchaka986b5ee2013-10-12 18:21:12 +0300888 _write_short(self._file, 8)
889 else:
890 _write_short(self._file, self._sampwidth * 8)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000891 _write_float(self._file, self._framerate)
892 if self._aifc:
893 self._file.write(self._comptype)
894 _write_string(self._file, self._compname)
895 self._file.write('SSND')
Serhiy Storchakad1a61dc2013-12-14 20:34:33 +0200896 if self._form_length_pos is not None:
897 self._ssnd_length_pos = self._file.tell()
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100898 _write_ulong(self._file, self._datalength + 8)
899 _write_ulong(self._file, 0)
900 _write_ulong(self._file, 0)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000901
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000902 def _write_form_length(self, datalength):
903 if self._aifc:
904 commlength = 18 + 5 + len(self._compname)
905 if commlength & 1:
906 commlength = commlength + 1
907 verslength = 12
908 else:
909 commlength = 18
910 verslength = 0
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100911 _write_ulong(self._file, 4 + verslength + self._marklength + \
912 8 + commlength + 16 + datalength)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000913 return commlength
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000914
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000915 def _patchheader(self):
916 curpos = self._file.tell()
917 if self._datawritten & 1:
918 datalength = self._datawritten + 1
919 self._file.write(chr(0))
920 else:
921 datalength = self._datawritten
922 if datalength == self._datalength and \
923 self._nframes == self._nframeswritten and \
924 self._marklength == 0:
925 self._file.seek(curpos, 0)
926 return
927 self._file.seek(self._form_length_pos, 0)
928 dummy = self._write_form_length(datalength)
929 self._file.seek(self._nframes_pos, 0)
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100930 _write_ulong(self._file, self._nframeswritten)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000931 self._file.seek(self._ssnd_length_pos, 0)
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100932 _write_ulong(self._file, datalength + 8)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000933 self._file.seek(curpos, 0)
934 self._nframes = self._nframeswritten
935 self._datalength = datalength
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000936
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000937 def _writemarkers(self):
938 if len(self._markers) == 0:
939 return
940 self._file.write('MARK')
941 length = 2
942 for marker in self._markers:
943 id, pos, name = marker
944 length = length + len(name) + 1 + 6
945 if len(name) & 1 == 0:
946 length = length + 1
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100947 _write_ulong(self._file, length)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000948 self._marklength = length + 8
949 _write_short(self._file, len(self._markers))
950 for marker in self._markers:
951 id, pos, name = marker
952 _write_short(self._file, id)
Antoine Pitrou3b6a3142012-01-17 17:13:04 +0100953 _write_ulong(self._file, pos)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000954 _write_string(self._file, name)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000955
Fred Drake43161351999-06-22 21:23:23 +0000956def open(f, mode=None):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000957 if mode is None:
958 if hasattr(f, 'mode'):
959 mode = f.mode
960 else:
961 mode = 'rb'
962 if mode in ('r', 'rb'):
963 return Aifc_read(f)
964 elif mode in ('w', 'wb'):
965 return Aifc_write(f)
966 else:
967 raise Error, "mode must be 'r', 'rb', 'w', or 'wb'"
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000968
Guido van Rossum7bc817d1993-12-17 15:25:27 +0000969openfp = open # B/W compatibility
Guido van Rossum36bb1811996-12-31 05:57:34 +0000970
971if __name__ == '__main__':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000972 import sys
973 if not sys.argv[1:]:
974 sys.argv.append('/usr/demos/data/audio/bach.aiff')
975 fn = sys.argv[1]
976 f = open(fn, 'r')
Serhiy Storchaka879a2132013-08-25 19:12:33 +0300977 try:
978 print "Reading", fn
979 print "nchannels =", f.getnchannels()
980 print "nframes =", f.getnframes()
981 print "sampwidth =", f.getsampwidth()
982 print "framerate =", f.getframerate()
983 print "comptype =", f.getcomptype()
984 print "compname =", f.getcompname()
985 if sys.argv[2:]:
986 gn = sys.argv[2]
987 print "Writing", gn
988 g = open(gn, 'w')
989 try:
990 g.setparams(f.getparams())
991 while 1:
992 data = f.readframes(1024)
993 if not data:
994 break
995 g.writeframes(data)
996 finally:
997 g.close()
998 print "Done."
999 finally:
Guido van Rossum4acc25b2000-02-02 15:10:15 +00001000 f.close()