blob: 777432528a703dd23a76d2851de3919edf912277 [file] [log] [blame]
Guido van Rossum4acc25b2000-02-02 15:10:15 +00001"""Stuff to parse AIFF-C and AIFF files.
2
3Unless explicitly stated otherwise, the description below is true
4both for AIFF-C files and AIFF files.
5
6An AIFF-C file has the following structure.
7
8 +-----------------+
9 | FORM |
10 +-----------------+
11 | <size> |
12 +----+------------+
13 | | AIFC |
14 | +------------+
15 | | <chunks> |
16 | | . |
17 | | . |
18 | | . |
19 +----+------------+
20
21An AIFF file has the string "AIFF" instead of "AIFC".
22
23A chunk consists of an identifier (4 bytes) followed by a size (4 bytes,
24big endian order), followed by the data. The size field does not include
25the size of the 8 byte header.
26
27The following chunk types are recognized.
28
29 FVER
30 <version number of AIFF-C defining document> (AIFF-C only).
31 MARK
32 <# of markers> (2 bytes)
33 list of markers:
34 <marker ID> (2 bytes, must be > 0)
35 <position> (4 bytes)
36 <marker name> ("pstring")
37 COMM
38 <# of channels> (2 bytes)
39 <# of sound frames> (4 bytes)
40 <size of the samples> (2 bytes)
41 <sampling frequency> (10 bytes, IEEE 80-bit extended
42 floating point)
43 in AIFF-C files only:
44 <compression type> (4 bytes)
45 <human-readable version of compression type> ("pstring")
46 SSND
47 <offset> (4 bytes, not used by this program)
48 <blocksize> (4 bytes, not used by this program)
49 <sound data>
50
51A pstring consists of 1 byte length, a string of characters, and 0 or 1
52byte pad to make the total length even.
53
54Usage.
55
56Reading AIFF files:
57 f = aifc.open(file, 'r')
58where file is either the name of a file or an open file pointer.
59The open file pointer must have methods read(), seek(), and close().
60In some types of audio files, if the setpos() method is not used,
61the seek() method is not necessary.
62
63This returns an instance of a class with the following public methods:
64 getnchannels() -- returns number of audio channels (1 for
65 mono, 2 for stereo)
66 getsampwidth() -- returns sample width in bytes
67 getframerate() -- returns sampling frequency
68 getnframes() -- returns number of audio frames
69 getcomptype() -- returns compression type ('NONE' for AIFF files)
70 getcompname() -- returns human-readable version of
71 compression type ('not compressed' for AIFF files)
72 getparams() -- returns a tuple consisting of all of the
73 above in the above order
74 getmarkers() -- get the list of marks in the audio file or None
75 if there are no marks
76 getmark(id) -- get mark with the specified id (raises an error
77 if the mark does not exist)
78 readframes(n) -- returns at most n frames of audio
79 rewind() -- rewind to the beginning of the audio stream
80 setpos(pos) -- seek to the specified position
81 tell() -- return the current position
82 close() -- close the instance (make it unusable)
83The position returned by tell(), the position given to setpos() and
84the position of marks are all compatible and have nothing to do with
Thomas Wouters7e474022000-07-16 12:04:32 +000085the actual position in the file.
Guido van Rossum4acc25b2000-02-02 15:10:15 +000086The close() method is called automatically when the class instance
87is destroyed.
88
89Writing AIFF files:
90 f = aifc.open(file, 'w')
91where file is either the name of a file or an open file pointer.
92The open file pointer must have methods write(), tell(), seek(), and
93close().
94
95This returns an instance of a class with the following public methods:
96 aiff() -- create an AIFF file (AIFF-C default)
97 aifc() -- create an AIFF-C file
98 setnchannels(n) -- set the number of channels
99 setsampwidth(n) -- set the sample width
100 setframerate(n) -- set the frame rate
101 setnframes(n) -- set the number of frames
102 setcomptype(type, name)
103 -- set the compression type and the
104 human-readable compression type
105 setparams(tuple)
106 -- set all parameters at once
107 setmark(id, pos, name)
108 -- add specified mark to the list of marks
109 tell() -- return current position in output file (useful
110 in combination with setmark())
111 writeframesraw(data)
112 -- write audio frames without pathing up the
113 file header
114 writeframes(data)
115 -- write audio frames and patch up the file header
116 close() -- patch up the file header and close the
117 output file
118You should set the parameters before the first writeframesraw or
119writeframes. The total number of frames does not need to be set,
120but when it is set to the correct value, the header does not have to
121be patched up.
122It is best to first set all parameters, perhaps possibly the
123compression type, and then write audio frames using writeframesraw.
124When all frames have been written, either call writeframes('') or
125close() to patch up the sizes in the header.
126Marks can be added anytime. If there are any marks, ypu must call
127close() after all frames have been written.
128The close() method is called automatically when the class instance
129is destroyed.
130
131When a file is opened with the extension '.aiff', an AIFF file is
132written, otherwise an AIFF-C file is written. This default can be
133changed by calling aiff() or aifc() before the first writeframes or
134writeframesraw.
135"""
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000136
Guido van Rossum36bb1811996-12-31 05:57:34 +0000137import struct
Georg Brandl1a3284e2007-12-02 09:40:06 +0000138import builtins
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000139
Georg Brandl2095cfe2008-06-07 19:01:03 +0000140__all__ = ["Error", "open", "openfp"]
Skip Montanaroe99d5ea2001-01-20 19:54:20 +0000141
Fred Drake227b1202000-08-17 05:06:49 +0000142class Error(Exception):
143 pass
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000144
Guido van Rossume2a383d2007-01-15 16:59:06 +0000145_AIFC_version = 0xA2805140 # Version 1 of AIFF-C
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000146
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
165def _read_string(file):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000166 length = ord(file.read(1))
167 if length == 0:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000168 data = b''
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000169 else:
170 data = file.read(length)
171 if length & 1 == 0:
172 dummy = file.read(1)
173 return data
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000174
175_HUGE_VAL = 1.79769313486231e+308 # See <limits.h>
176
177def _read_float(f): # 10 bytes
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000178 expon = _read_short(f) # 2 bytes
179 sign = 1
180 if expon < 0:
181 sign = -1
182 expon = expon + 0x8000
183 himant = _read_ulong(f) # 4 bytes
184 lomant = _read_ulong(f) # 4 bytes
185 if expon == himant == lomant == 0:
186 f = 0.0
187 elif expon == 0x7FFF:
188 f = _HUGE_VAL
189 else:
190 expon = expon - 16383
Guido van Rossume2a383d2007-01-15 16:59:06 +0000191 f = (himant * 0x100000000 + lomant) * pow(2.0, expon - 63)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000192 return sign * f
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000193
194def _write_short(f, x):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000195 f.write(struct.pack('>h', x))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000196
197def _write_long(f, x):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000198 f.write(struct.pack('>L', x))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000199
200def _write_string(f, s):
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000201 if len(s) > 255:
202 raise ValueError("string exceeds maximum pstring length")
Georg Brandl2095cfe2008-06-07 19:01:03 +0000203 f.write(struct.pack('b', len(s)))
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000204 f.write(s)
205 if len(s) & 1 == 0:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000206 f.write(b'\x00')
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000207
208def _write_float(f, x):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000209 import math
210 if x < 0:
211 sign = 0x8000
212 x = x * -1
213 else:
214 sign = 0
215 if x == 0:
216 expon = 0
217 himant = 0
218 lomant = 0
219 else:
220 fmant, expon = math.frexp(x)
221 if expon > 16384 or fmant >= 1: # Infinity or NaN
222 expon = sign|0x7FFF
223 himant = 0
224 lomant = 0
225 else: # Finite
226 expon = expon + 16382
227 if expon < 0: # denormalized
228 fmant = math.ldexp(fmant, expon)
229 expon = 0
230 expon = expon | sign
231 fmant = math.ldexp(fmant, 32)
232 fsmant = math.floor(fmant)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000233 himant = int(fsmant)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000234 fmant = math.ldexp(fmant - fsmant, 32)
235 fsmant = math.floor(fmant)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000236 lomant = int(fsmant)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000237 _write_short(f, expon)
238 _write_long(f, himant)
239 _write_long(f, lomant)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000240
Guido van Rossum8ea7bb81999-06-09 13:32:28 +0000241from chunk import Chunk
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000242
Guido van Rossumd3166071993-05-24 14:16:22 +0000243class Aifc_read:
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000244 # Variables used in this class:
245 #
246 # These variables are available to the user though appropriate
247 # methods of this class:
248 # _file -- the open file with methods read(), close(), and seek()
249 # set through the __init__() method
250 # _nchannels -- the number of audio channels
251 # available through the getnchannels() method
252 # _nframes -- the number of audio frames
253 # available through the getnframes() method
254 # _sampwidth -- the number of bytes per audio sample
255 # available through the getsampwidth() method
256 # _framerate -- the sampling frequency
257 # available through the getframerate() method
258 # _comptype -- the AIFF-C compression type ('NONE' if AIFF)
259 # available through the getcomptype() method
260 # _compname -- the human-readable AIFF-C compression type
261 # available through the getcomptype() method
262 # _markers -- the marks in the audio file
263 # available through the getmarkers() and getmark()
264 # methods
265 # _soundpos -- the position in the audio stream
266 # available through the tell() method, set through the
267 # setpos() method
268 #
269 # These variables are used internally only:
270 # _version -- the AIFF-C version number
271 # _decomp -- the decompressor from builtin module cl
272 # _comm_chunk_read -- 1 iff the COMM chunk has been read
273 # _aifc -- 1 iff reading an AIFF-C file
274 # _ssnd_seek_needed -- 1 iff positioned correctly in audio
275 # file for readframes()
276 # _ssnd_chunk -- instantiation of a chunk class for the SSND chunk
277 # _framesize -- size of one frame in the file
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000278
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000279 def initfp(self, file):
280 self._version = 0
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000281 self._convert = None
282 self._markers = []
283 self._soundpos = 0
R. David Murray99352742009-05-07 18:24:38 +0000284 self._file = file
285 chunk = Chunk(file)
286 if chunk.getname() != b'FORM':
Collin Winterce36ad82007-08-30 01:19:48 +0000287 raise Error('file does not start with FORM id')
R. David Murray99352742009-05-07 18:24:38 +0000288 formdata = chunk.read(4)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000289 if formdata == b'AIFF':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000290 self._aifc = 0
Georg Brandl2095cfe2008-06-07 19:01:03 +0000291 elif formdata == b'AIFC':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000292 self._aifc = 1
293 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000294 raise Error('not an AIFF or AIFF-C file')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000295 self._comm_chunk_read = 0
296 while 1:
297 self._ssnd_seek_needed = 1
298 try:
299 chunk = Chunk(self._file)
300 except EOFError:
301 break
302 chunkname = chunk.getname()
Georg Brandl2095cfe2008-06-07 19:01:03 +0000303 if chunkname == b'COMM':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000304 self._read_comm_chunk(chunk)
305 self._comm_chunk_read = 1
Georg Brandl2095cfe2008-06-07 19:01:03 +0000306 elif chunkname == b'SSND':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000307 self._ssnd_chunk = chunk
308 dummy = chunk.read(8)
309 self._ssnd_seek_needed = 0
Georg Brandl2095cfe2008-06-07 19:01:03 +0000310 elif chunkname == b'FVER':
Guido van Rossum820819c2002-08-12 22:11:28 +0000311 self._version = _read_ulong(chunk)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000312 elif chunkname == b'MARK':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000313 self._readmark(chunk)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000314 chunk.skip()
315 if not self._comm_chunk_read or not self._ssnd_chunk:
Collin Winterce36ad82007-08-30 01:19:48 +0000316 raise Error('COMM chunk and/or SSND chunk missing')
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000317
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000318 def __init__(self, f):
Georg Brandl2095cfe2008-06-07 19:01:03 +0000319 if isinstance(f, str):
Georg Brandl1a3284e2007-12-02 09:40:06 +0000320 f = builtins.open(f, 'rb')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000321 # else, assume it is an open file object already
322 self.initfp(f)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000323
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000324 #
325 # User visible methods.
326 #
327 def getfp(self):
328 return self._file
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000329
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000330 def rewind(self):
331 self._ssnd_seek_needed = 1
332 self._soundpos = 0
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000333
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000334 def close(self):
Benjamin Peterson1d1285d2009-05-07 11:53:38 +0000335 self._file.close()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000336
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000337 def tell(self):
338 return self._soundpos
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000339
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000340 def getnchannels(self):
341 return self._nchannels
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000342
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000343 def getnframes(self):
344 return self._nframes
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000345
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000346 def getsampwidth(self):
347 return self._sampwidth
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000348
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000349 def getframerate(self):
350 return self._framerate
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000351
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000352 def getcomptype(self):
353 return self._comptype
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000354
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000355 def getcompname(self):
356 return self._compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000357
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000358## def getversion(self):
359## return self._version
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000360
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000361 def getparams(self):
362 return self.getnchannels(), self.getsampwidth(), \
363 self.getframerate(), self.getnframes(), \
364 self.getcomptype(), self.getcompname()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000365
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000366 def getmarkers(self):
367 if len(self._markers) == 0:
368 return None
369 return self._markers
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000370
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000371 def getmark(self, id):
372 for marker in self._markers:
373 if id == marker[0]:
374 return marker
Georg Brandl2095cfe2008-06-07 19:01:03 +0000375 raise Error('marker {0!r} does not exist'.format(id))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000376
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000377 def setpos(self, pos):
378 if pos < 0 or pos > self._nframes:
Collin Winterce36ad82007-08-30 01:19:48 +0000379 raise Error('position not in range')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000380 self._soundpos = pos
381 self._ssnd_seek_needed = 1
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000382
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000383 def readframes(self, nframes):
384 if self._ssnd_seek_needed:
385 self._ssnd_chunk.seek(0)
386 dummy = self._ssnd_chunk.read(8)
387 pos = self._soundpos * self._framesize
388 if pos:
Guido van Rossum2663c132000-03-07 15:19:31 +0000389 self._ssnd_chunk.seek(pos + 8)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000390 self._ssnd_seek_needed = 0
391 if nframes == 0:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000392 return b''
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000393 data = self._ssnd_chunk.read(nframes * self._framesize)
394 if self._convert and data:
395 data = self._convert(data)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000396 self._soundpos = self._soundpos + len(data) // (self._nchannels
397 * self._sampwidth)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000398 return data
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000399
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000400 #
401 # Internal methods.
402 #
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000403
Georg Brandl2095cfe2008-06-07 19:01:03 +0000404 def _alaw2lin(self, data):
405 import audioop
406 return audioop.alaw2lin(data, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000407
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000408 def _ulaw2lin(self, data):
409 import audioop
410 return audioop.ulaw2lin(data, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000411
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000412 def _adpcm2lin(self, data):
413 import audioop
414 if not hasattr(self, '_adpcmstate'):
415 # first time
416 self._adpcmstate = None
Georg Brandl2095cfe2008-06-07 19:01:03 +0000417 data, self._adpcmstate = audioop.adpcm2lin(data, 2, self._adpcmstate)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000418 return data
Sjoerd Mullender1f057541994-09-06 16:17:51 +0000419
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000420 def _read_comm_chunk(self, chunk):
421 self._nchannels = _read_short(chunk)
422 self._nframes = _read_long(chunk)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000423 self._sampwidth = (_read_short(chunk) + 7) // 8
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000424 self._framerate = int(_read_float(chunk))
425 self._framesize = self._nchannels * self._sampwidth
426 if self._aifc:
427 #DEBUG: SGI's soundeditor produces a bad size :-(
428 kludge = 0
429 if chunk.chunksize == 18:
430 kludge = 1
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000431 print('Warning: bad COMM chunk size')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000432 chunk.chunksize = 23
433 #DEBUG end
434 self._comptype = chunk.read(4)
435 #DEBUG start
436 if kludge:
437 length = ord(chunk.file.read(1))
438 if length & 1 == 0:
439 length = length + 1
440 chunk.chunksize = chunk.chunksize + length
441 chunk.file.seek(-1, 1)
442 #DEBUG end
443 self._compname = _read_string(chunk)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000444 if self._comptype != b'NONE':
445 if self._comptype == b'G722':
446 self._convert = self._adpcm2lin
447 self._framesize = self._framesize // 4
448 elif self._comptype in (b'ulaw', b'ULAW'):
449 self._convert = self._ulaw2lin
450 self._framesize = self._framesize // 2
451 elif self._comptype in (b'alaw', b'ALAW'):
452 self._convert = self._alaw2lin
453 self._framesize = self._framesize // 2
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000454 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000455 raise Error('unsupported compression type')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000456 else:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000457 self._comptype = b'NONE'
458 self._compname = b'not compressed'
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000459
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000460 def _readmark(self, chunk):
461 nmarkers = _read_short(chunk)
462 # Some files appear to contain invalid counts.
463 # Cope with this by testing for EOF.
464 try:
465 for i in range(nmarkers):
466 id = _read_short(chunk)
467 pos = _read_long(chunk)
468 name = _read_string(chunk)
469 if pos or name:
470 # some files appear to have
471 # dummy markers consisting of
472 # a position 0 and name ''
473 self._markers.append((id, pos, name))
474 except EOFError:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000475 print('Warning: MARK chunk contains only', end=' ')
476 print(len(self._markers), end=' ')
477 if len(self._markers) == 1: print('marker', end=' ')
478 else: print('markers', end=' ')
479 print('instead of', nmarkers)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000480
Guido van Rossumd3166071993-05-24 14:16:22 +0000481class Aifc_write:
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000482 # Variables used in this class:
483 #
484 # These variables are user settable through appropriate methods
485 # of this class:
486 # _file -- the open file with methods write(), close(), tell(), seek()
487 # set through the __init__() method
488 # _comptype -- the AIFF-C compression type ('NONE' in AIFF)
489 # set through the setcomptype() or setparams() method
490 # _compname -- the human-readable AIFF-C compression type
491 # set through the setcomptype() or setparams() method
492 # _nchannels -- the number of audio channels
493 # set through the setnchannels() or setparams() method
494 # _sampwidth -- the number of bytes per audio sample
495 # set through the setsampwidth() or setparams() method
496 # _framerate -- the sampling frequency
497 # set through the setframerate() or setparams() method
498 # _nframes -- the number of audio frames written to the header
499 # set through the setnframes() or setparams() method
500 # _aifc -- whether we're writing an AIFF-C file or an AIFF file
501 # set through the aifc() method, reset through the
502 # aiff() method
503 #
504 # These variables are used internally only:
505 # _version -- the AIFF-C version number
506 # _comp -- the compressor from builtin module cl
507 # _nframeswritten -- the number of audio frames actually written
508 # _datalength -- the size of the audio samples written to the header
509 # _datawritten -- the size of the audio samples actually written
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000510
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000511 def __init__(self, f):
Georg Brandl2095cfe2008-06-07 19:01:03 +0000512 if isinstance(f, str):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000513 filename = f
Georg Brandl1a3284e2007-12-02 09:40:06 +0000514 f = builtins.open(f, 'wb')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000515 else:
516 # else, assume it is an open file object already
517 filename = '???'
518 self.initfp(f)
519 if filename[-5:] == '.aiff':
520 self._aifc = 0
521 else:
522 self._aifc = 1
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000523
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000524 def initfp(self, file):
525 self._file = file
526 self._version = _AIFC_version
Georg Brandl2095cfe2008-06-07 19:01:03 +0000527 self._comptype = b'NONE'
528 self._compname = b'not compressed'
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000529 self._convert = None
530 self._nchannels = 0
531 self._sampwidth = 0
532 self._framerate = 0
533 self._nframes = 0
534 self._nframeswritten = 0
535 self._datawritten = 0
536 self._datalength = 0
537 self._markers = []
538 self._marklength = 0
539 self._aifc = 1 # AIFF-C is default
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000540
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000541 def __del__(self):
Sandro Tosi70efbef2012-01-01 22:53:08 +0100542 self.close()
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000543
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000544 #
545 # User visible methods.
546 #
547 def aiff(self):
548 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000549 raise Error('cannot change parameters after starting to write')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000550 self._aifc = 0
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000551
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000552 def aifc(self):
553 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000554 raise Error('cannot change parameters after starting to write')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000555 self._aifc = 1
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000556
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000557 def setnchannels(self, nchannels):
558 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000559 raise Error('cannot change parameters after starting to write')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000560 if nchannels < 1:
Collin Winterce36ad82007-08-30 01:19:48 +0000561 raise Error('bad # of channels')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000562 self._nchannels = nchannels
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000563
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000564 def getnchannels(self):
565 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000566 raise Error('number of channels not set')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000567 return self._nchannels
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000568
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000569 def setsampwidth(self, sampwidth):
570 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000571 raise Error('cannot change parameters after starting to write')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000572 if sampwidth < 1 or sampwidth > 4:
Collin Winterce36ad82007-08-30 01:19:48 +0000573 raise Error('bad sample width')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000574 self._sampwidth = sampwidth
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000575
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000576 def getsampwidth(self):
577 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000578 raise Error('sample width not set')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000579 return self._sampwidth
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000580
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000581 def setframerate(self, framerate):
582 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000583 raise Error('cannot change parameters after starting to write')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000584 if framerate <= 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000585 raise Error('bad frame rate')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000586 self._framerate = framerate
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000587
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000588 def getframerate(self):
589 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000590 raise Error('frame rate not set')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000591 return self._framerate
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000592
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000593 def setnframes(self, nframes):
594 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000595 raise Error('cannot change parameters after starting to write')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000596 self._nframes = nframes
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000597
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000598 def getnframes(self):
599 return self._nframeswritten
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000600
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000601 def setcomptype(self, comptype, compname):
602 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000603 raise Error('cannot change parameters after starting to write')
Georg Brandl2095cfe2008-06-07 19:01:03 +0000604 if comptype not in (b'NONE', b'ulaw', b'ULAW',
605 b'alaw', b'ALAW', b'G722'):
Collin Winterce36ad82007-08-30 01:19:48 +0000606 raise Error('unsupported compression type')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000607 self._comptype = comptype
608 self._compname = compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000609
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000610 def getcomptype(self):
611 return self._comptype
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000612
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000613 def getcompname(self):
614 return self._compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000615
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000616## def setversion(self, version):
617## if self._nframeswritten:
618## raise Error, 'cannot change parameters after starting to write'
619## self._version = version
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000620
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000621 def setparams(self, params):
622 nchannels, sampwidth, framerate, nframes, comptype, compname = params
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000623 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000624 raise Error('cannot change parameters after starting to write')
Georg Brandl2095cfe2008-06-07 19:01:03 +0000625 if comptype not in (b'NONE', b'ulaw', b'ULAW',
626 b'alaw', b'ALAW', b'G722'):
Collin Winterce36ad82007-08-30 01:19:48 +0000627 raise Error('unsupported compression type')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000628 self.setnchannels(nchannels)
629 self.setsampwidth(sampwidth)
630 self.setframerate(framerate)
631 self.setnframes(nframes)
632 self.setcomptype(comptype, compname)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000633
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000634 def getparams(self):
635 if not self._nchannels or not self._sampwidth or not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000636 raise Error('not all parameters set')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000637 return self._nchannels, self._sampwidth, self._framerate, \
638 self._nframes, self._comptype, self._compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000639
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000640 def setmark(self, id, pos, name):
641 if id <= 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000642 raise Error('marker ID must be > 0')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000643 if pos < 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000644 raise Error('marker position must be >= 0')
Sandro Tosi70efbef2012-01-01 22:53:08 +0100645 if not isinstance(name, bytes):
646 raise Error('marker name must be bytes')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000647 for i in range(len(self._markers)):
648 if id == self._markers[i][0]:
649 self._markers[i] = id, pos, name
650 return
651 self._markers.append((id, pos, name))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000652
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000653 def getmark(self, id):
654 for marker in self._markers:
655 if id == marker[0]:
656 return marker
Georg Brandl2095cfe2008-06-07 19:01:03 +0000657 raise Error('marker {0!r} does not exist'.format(id))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000658
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000659 def getmarkers(self):
660 if len(self._markers) == 0:
661 return None
662 return self._markers
Tim Peters146965a2001-01-14 18:09:23 +0000663
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000664 def tell(self):
665 return self._nframeswritten
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000666
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000667 def writeframesraw(self, data):
668 self._ensure_header_written(len(data))
Georg Brandl2095cfe2008-06-07 19:01:03 +0000669 nframes = len(data) // (self._sampwidth * self._nchannels)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000670 if self._convert:
671 data = self._convert(data)
672 self._file.write(data)
673 self._nframeswritten = self._nframeswritten + nframes
674 self._datawritten = self._datawritten + len(data)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000675
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000676 def writeframes(self, data):
677 self.writeframesraw(data)
678 if self._nframeswritten != self._nframes or \
679 self._datalength != self._datawritten:
680 self._patchheader()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000681
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000682 def close(self):
Sandro Tosi70efbef2012-01-01 22:53:08 +0100683 if self._file:
684 self._ensure_header_written(0)
685 if self._datawritten & 1:
686 # quick pad to even size
687 self._file.write(b'\x00')
688 self._datawritten = self._datawritten + 1
689 self._writemarkers()
690 if self._nframeswritten != self._nframes or \
691 self._datalength != self._datawritten or \
692 self._marklength:
693 self._patchheader()
694 # Prevent ref cycles
695 self._convert = None
696 self._file.close()
697 self._file = None
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000698
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000699 #
700 # Internal methods.
701 #
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000702
Georg Brandl2095cfe2008-06-07 19:01:03 +0000703 def _lin2alaw(self, data):
704 import audioop
705 return audioop.lin2alaw(data, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000706
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000707 def _lin2ulaw(self, data):
708 import audioop
709 return audioop.lin2ulaw(data, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000710
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000711 def _lin2adpcm(self, data):
712 import audioop
713 if not hasattr(self, '_adpcmstate'):
714 self._adpcmstate = None
Georg Brandl2095cfe2008-06-07 19:01:03 +0000715 data, self._adpcmstate = audioop.lin2adpcm(data, 2, self._adpcmstate)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000716 return data
Sjoerd Mullender1f057541994-09-06 16:17:51 +0000717
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000718 def _ensure_header_written(self, datasize):
719 if not self._nframeswritten:
Sandro Tosibdd53542012-01-01 18:04:37 +0100720 if self._comptype in (b'ULAW', b'ulaw', b'ALAW', b'alaw', b'G722'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000721 if not self._sampwidth:
722 self._sampwidth = 2
723 if self._sampwidth != 2:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000724 raise Error('sample width must be 2 when compressing '
Sandro Tosibdd53542012-01-01 18:04:37 +0100725 'with ulaw/ULAW, alaw/ALAW or G7.22 (ADPCM)')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000726 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000727 raise Error('# channels not specified')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000728 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000729 raise Error('sample width not specified')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000730 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000731 raise Error('sampling rate not specified')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000732 self._write_header(datasize)
Guido van Rossum52fc1f61993-06-17 12:38:10 +0000733
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000734 def _init_compression(self):
Georg Brandl2095cfe2008-06-07 19:01:03 +0000735 if self._comptype == b'G722':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000736 self._convert = self._lin2adpcm
Georg Brandl2095cfe2008-06-07 19:01:03 +0000737 elif self._comptype in (b'ulaw', b'ULAW'):
738 self._convert = self._lin2ulaw
739 elif self._comptype in (b'alaw', b'ALAW'):
740 self._convert = self._lin2alaw
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000741
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000742 def _write_header(self, initlength):
Georg Brandl2095cfe2008-06-07 19:01:03 +0000743 if self._aifc and self._comptype != b'NONE':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000744 self._init_compression()
Georg Brandl2095cfe2008-06-07 19:01:03 +0000745 self._file.write(b'FORM')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000746 if not self._nframes:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000747 self._nframes = initlength // (self._nchannels * self._sampwidth)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000748 self._datalength = self._nframes * self._nchannels * self._sampwidth
749 if self._datalength & 1:
750 self._datalength = self._datalength + 1
751 if self._aifc:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000752 if self._comptype in (b'ulaw', b'ULAW', b'alaw', b'ALAW'):
753 self._datalength = self._datalength // 2
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000754 if self._datalength & 1:
755 self._datalength = self._datalength + 1
Georg Brandl2095cfe2008-06-07 19:01:03 +0000756 elif self._comptype == b'G722':
757 self._datalength = (self._datalength + 3) // 4
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000758 if self._datalength & 1:
759 self._datalength = self._datalength + 1
760 self._form_length_pos = self._file.tell()
761 commlength = self._write_form_length(self._datalength)
762 if self._aifc:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000763 self._file.write(b'AIFC')
764 self._file.write(b'FVER')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000765 _write_long(self._file, 4)
766 _write_long(self._file, self._version)
767 else:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000768 self._file.write(b'AIFF')
769 self._file.write(b'COMM')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000770 _write_long(self._file, commlength)
771 _write_short(self._file, self._nchannels)
772 self._nframes_pos = self._file.tell()
773 _write_long(self._file, self._nframes)
774 _write_short(self._file, self._sampwidth * 8)
775 _write_float(self._file, self._framerate)
776 if self._aifc:
777 self._file.write(self._comptype)
778 _write_string(self._file, self._compname)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000779 self._file.write(b'SSND')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000780 self._ssnd_length_pos = self._file.tell()
781 _write_long(self._file, self._datalength + 8)
782 _write_long(self._file, 0)
783 _write_long(self._file, 0)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000784
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000785 def _write_form_length(self, datalength):
786 if self._aifc:
787 commlength = 18 + 5 + len(self._compname)
788 if commlength & 1:
789 commlength = commlength + 1
790 verslength = 12
791 else:
792 commlength = 18
793 verslength = 0
794 _write_long(self._file, 4 + verslength + self._marklength + \
795 8 + commlength + 16 + datalength)
796 return commlength
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000797
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000798 def _patchheader(self):
799 curpos = self._file.tell()
800 if self._datawritten & 1:
801 datalength = self._datawritten + 1
Georg Brandl2095cfe2008-06-07 19:01:03 +0000802 self._file.write(b'\x00')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000803 else:
804 datalength = self._datawritten
805 if datalength == self._datalength and \
806 self._nframes == self._nframeswritten and \
807 self._marklength == 0:
808 self._file.seek(curpos, 0)
809 return
810 self._file.seek(self._form_length_pos, 0)
811 dummy = self._write_form_length(datalength)
812 self._file.seek(self._nframes_pos, 0)
813 _write_long(self._file, self._nframeswritten)
814 self._file.seek(self._ssnd_length_pos, 0)
815 _write_long(self._file, datalength + 8)
816 self._file.seek(curpos, 0)
817 self._nframes = self._nframeswritten
818 self._datalength = datalength
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000819
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000820 def _writemarkers(self):
821 if len(self._markers) == 0:
822 return
Georg Brandl2095cfe2008-06-07 19:01:03 +0000823 self._file.write(b'MARK')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000824 length = 2
825 for marker in self._markers:
826 id, pos, name = marker
827 length = length + len(name) + 1 + 6
828 if len(name) & 1 == 0:
829 length = length + 1
830 _write_long(self._file, length)
831 self._marklength = length + 8
832 _write_short(self._file, len(self._markers))
833 for marker in self._markers:
834 id, pos, name = marker
835 _write_short(self._file, id)
836 _write_long(self._file, pos)
837 _write_string(self._file, name)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000838
Fred Drake43161351999-06-22 21:23:23 +0000839def open(f, mode=None):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000840 if mode is None:
841 if hasattr(f, 'mode'):
842 mode = f.mode
843 else:
844 mode = 'rb'
845 if mode in ('r', 'rb'):
846 return Aifc_read(f)
847 elif mode in ('w', 'wb'):
848 return Aifc_write(f)
849 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000850 raise Error("mode must be 'r', 'rb', 'w', or 'wb'")
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000851
Guido van Rossum7bc817d1993-12-17 15:25:27 +0000852openfp = open # B/W compatibility
Guido van Rossum36bb1811996-12-31 05:57:34 +0000853
854if __name__ == '__main__':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000855 import sys
856 if not sys.argv[1:]:
857 sys.argv.append('/usr/demos/data/audio/bach.aiff')
858 fn = sys.argv[1]
859 f = open(fn, 'r')
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000860 print("Reading", fn)
861 print("nchannels =", f.getnchannels())
862 print("nframes =", f.getnframes())
863 print("sampwidth =", f.getsampwidth())
864 print("framerate =", f.getframerate())
865 print("comptype =", f.getcomptype())
866 print("compname =", f.getcompname())
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000867 if sys.argv[2:]:
868 gn = sys.argv[2]
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000869 print("Writing", gn)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000870 g = open(gn, 'w')
871 g.setparams(f.getparams())
872 while 1:
873 data = f.readframes(1024)
874 if not data:
875 break
876 g.writeframes(data)
877 g.close()
878 f.close()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000879 print("Done.")