blob: 015d3985d6bfbca2afb45cf590ad06a4c23df430 [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):
542 if self._file:
543 self.close()
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000544
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000545 #
546 # User visible methods.
547 #
548 def aiff(self):
549 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000550 raise Error('cannot change parameters after starting to write')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000551 self._aifc = 0
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000552
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000553 def aifc(self):
554 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000555 raise Error('cannot change parameters after starting to write')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000556 self._aifc = 1
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000557
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000558 def setnchannels(self, nchannels):
559 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000560 raise Error('cannot change parameters after starting to write')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000561 if nchannels < 1:
Collin Winterce36ad82007-08-30 01:19:48 +0000562 raise Error('bad # of channels')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000563 self._nchannels = nchannels
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000564
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000565 def getnchannels(self):
566 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000567 raise Error('number of channels not set')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000568 return self._nchannels
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000569
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000570 def setsampwidth(self, sampwidth):
571 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000572 raise Error('cannot change parameters after starting to write')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000573 if sampwidth < 1 or sampwidth > 4:
Collin Winterce36ad82007-08-30 01:19:48 +0000574 raise Error('bad sample width')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000575 self._sampwidth = sampwidth
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000576
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000577 def getsampwidth(self):
578 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000579 raise Error('sample width not set')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000580 return self._sampwidth
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000581
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000582 def setframerate(self, framerate):
583 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000584 raise Error('cannot change parameters after starting to write')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000585 if framerate <= 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000586 raise Error('bad frame rate')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000587 self._framerate = framerate
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000588
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000589 def getframerate(self):
590 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000591 raise Error('frame rate not set')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000592 return self._framerate
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000593
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000594 def setnframes(self, nframes):
595 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000596 raise Error('cannot change parameters after starting to write')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000597 self._nframes = nframes
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000598
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000599 def getnframes(self):
600 return self._nframeswritten
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000601
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000602 def setcomptype(self, comptype, compname):
603 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000604 raise Error('cannot change parameters after starting to write')
Georg Brandl2095cfe2008-06-07 19:01:03 +0000605 if comptype not in (b'NONE', b'ulaw', b'ULAW',
606 b'alaw', b'ALAW', b'G722'):
Collin Winterce36ad82007-08-30 01:19:48 +0000607 raise Error('unsupported compression type')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000608 self._comptype = comptype
609 self._compname = compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000610
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000611 def getcomptype(self):
612 return self._comptype
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000613
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000614 def getcompname(self):
615 return self._compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000616
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000617## def setversion(self, version):
618## if self._nframeswritten:
619## raise Error, 'cannot change parameters after starting to write'
620## self._version = version
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000621
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000622 def setparams(self, params):
623 nchannels, sampwidth, framerate, nframes, comptype, compname = params
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000624 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000625 raise Error('cannot change parameters after starting to write')
Georg Brandl2095cfe2008-06-07 19:01:03 +0000626 if comptype not in (b'NONE', b'ulaw', b'ULAW',
627 b'alaw', b'ALAW', b'G722'):
Collin Winterce36ad82007-08-30 01:19:48 +0000628 raise Error('unsupported compression type')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000629 self.setnchannels(nchannels)
630 self.setsampwidth(sampwidth)
631 self.setframerate(framerate)
632 self.setnframes(nframes)
633 self.setcomptype(comptype, compname)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000634
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000635 def getparams(self):
636 if not self._nchannels or not self._sampwidth or not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000637 raise Error('not all parameters set')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000638 return self._nchannels, self._sampwidth, self._framerate, \
639 self._nframes, self._comptype, self._compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000640
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000641 def setmark(self, id, pos, name):
642 if id <= 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000643 raise Error('marker ID must be > 0')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000644 if pos < 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000645 raise Error('marker position must be >= 0')
Georg Brandl2095cfe2008-06-07 19:01:03 +0000646 if not isinstance(name, str):
Collin Winterce36ad82007-08-30 01:19:48 +0000647 raise Error('marker name must be a string')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000648 for i in range(len(self._markers)):
649 if id == self._markers[i][0]:
650 self._markers[i] = id, pos, name
651 return
652 self._markers.append((id, pos, name))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000653
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000654 def getmark(self, id):
655 for marker in self._markers:
656 if id == marker[0]:
657 return marker
Georg Brandl2095cfe2008-06-07 19:01:03 +0000658 raise Error('marker {0!r} does not exist'.format(id))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000659
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000660 def getmarkers(self):
661 if len(self._markers) == 0:
662 return None
663 return self._markers
Tim Peters146965a2001-01-14 18:09:23 +0000664
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000665 def tell(self):
666 return self._nframeswritten
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000667
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000668 def writeframesraw(self, data):
669 self._ensure_header_written(len(data))
Georg Brandl2095cfe2008-06-07 19:01:03 +0000670 nframes = len(data) // (self._sampwidth * self._nchannels)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000671 if self._convert:
672 data = self._convert(data)
673 self._file.write(data)
674 self._nframeswritten = self._nframeswritten + nframes
675 self._datawritten = self._datawritten + len(data)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000676
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000677 def writeframes(self, data):
678 self.writeframesraw(data)
679 if self._nframeswritten != self._nframes or \
680 self._datalength != self._datawritten:
681 self._patchheader()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000682
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000683 def close(self):
684 self._ensure_header_written(0)
685 if self._datawritten & 1:
686 # quick pad to even size
Georg Brandl2095cfe2008-06-07 19:01:03 +0000687 self._file.write(b'\x00')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000688 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()
Benjamin Petersoncc65c1d2009-04-30 00:30:08 +0000694 # Prevent ref cycles
695 self._convert = None
Benjamin Peterson1d1285d2009-05-07 11:53:38 +0000696 self._file.close()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000697
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000698 #
699 # Internal methods.
700 #
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000701
Georg Brandl2095cfe2008-06-07 19:01:03 +0000702 def _lin2alaw(self, data):
703 import audioop
704 return audioop.lin2alaw(data, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000705
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000706 def _lin2ulaw(self, data):
707 import audioop
708 return audioop.lin2ulaw(data, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000709
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000710 def _lin2adpcm(self, data):
711 import audioop
712 if not hasattr(self, '_adpcmstate'):
713 self._adpcmstate = None
Georg Brandl2095cfe2008-06-07 19:01:03 +0000714 data, self._adpcmstate = audioop.lin2adpcm(data, 2, self._adpcmstate)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000715 return data
Sjoerd Mullender1f057541994-09-06 16:17:51 +0000716
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000717 def _ensure_header_written(self, datasize):
718 if not self._nframeswritten:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000719 if self._comptype in (b'ULAW', b'ALAW'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000720 if not self._sampwidth:
721 self._sampwidth = 2
722 if self._sampwidth != 2:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000723 raise Error('sample width must be 2 when compressing '
724 'with ulaw/ULAW or alaw/ALAW')
725 if self._comptype == b'G722':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000726 if not self._sampwidth:
727 self._sampwidth = 2
728 if self._sampwidth != 2:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000729 raise Error('sample width must be 2 when compressing '
730 'with G7.22 (ADPCM)')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000731 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000732 raise Error('# channels not specified')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000733 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000734 raise Error('sample width not specified')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000735 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000736 raise Error('sampling rate not specified')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000737 self._write_header(datasize)
Guido van Rossum52fc1f61993-06-17 12:38:10 +0000738
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000739 def _init_compression(self):
Georg Brandl2095cfe2008-06-07 19:01:03 +0000740 if self._comptype == b'G722':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000741 self._convert = self._lin2adpcm
Georg Brandl2095cfe2008-06-07 19:01:03 +0000742 elif self._comptype in (b'ulaw', b'ULAW'):
743 self._convert = self._lin2ulaw
744 elif self._comptype in (b'alaw', b'ALAW'):
745 self._convert = self._lin2alaw
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000746 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000747 raise Error('unsupported compression type')
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000748
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000749 def _write_header(self, initlength):
Georg Brandl2095cfe2008-06-07 19:01:03 +0000750 if self._aifc and self._comptype != b'NONE':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000751 self._init_compression()
Georg Brandl2095cfe2008-06-07 19:01:03 +0000752 self._file.write(b'FORM')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000753 if not self._nframes:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000754 self._nframes = initlength // (self._nchannels * self._sampwidth)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000755 self._datalength = self._nframes * self._nchannels * self._sampwidth
756 if self._datalength & 1:
757 self._datalength = self._datalength + 1
758 if self._aifc:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000759 if self._comptype in (b'ulaw', b'ULAW', b'alaw', b'ALAW'):
760 self._datalength = self._datalength // 2
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000761 if self._datalength & 1:
762 self._datalength = self._datalength + 1
Georg Brandl2095cfe2008-06-07 19:01:03 +0000763 elif self._comptype == b'G722':
764 self._datalength = (self._datalength + 3) // 4
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000765 if self._datalength & 1:
766 self._datalength = self._datalength + 1
767 self._form_length_pos = self._file.tell()
768 commlength = self._write_form_length(self._datalength)
769 if self._aifc:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000770 self._file.write(b'AIFC')
771 self._file.write(b'FVER')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000772 _write_long(self._file, 4)
773 _write_long(self._file, self._version)
774 else:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000775 self._file.write(b'AIFF')
776 self._file.write(b'COMM')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000777 _write_long(self._file, commlength)
778 _write_short(self._file, self._nchannels)
779 self._nframes_pos = self._file.tell()
780 _write_long(self._file, self._nframes)
781 _write_short(self._file, self._sampwidth * 8)
782 _write_float(self._file, self._framerate)
783 if self._aifc:
784 self._file.write(self._comptype)
785 _write_string(self._file, self._compname)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000786 self._file.write(b'SSND')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000787 self._ssnd_length_pos = self._file.tell()
788 _write_long(self._file, self._datalength + 8)
789 _write_long(self._file, 0)
790 _write_long(self._file, 0)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000791
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000792 def _write_form_length(self, datalength):
793 if self._aifc:
794 commlength = 18 + 5 + len(self._compname)
795 if commlength & 1:
796 commlength = commlength + 1
797 verslength = 12
798 else:
799 commlength = 18
800 verslength = 0
801 _write_long(self._file, 4 + verslength + self._marklength + \
802 8 + commlength + 16 + datalength)
803 return commlength
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000804
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000805 def _patchheader(self):
806 curpos = self._file.tell()
807 if self._datawritten & 1:
808 datalength = self._datawritten + 1
Georg Brandl2095cfe2008-06-07 19:01:03 +0000809 self._file.write(b'\x00')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000810 else:
811 datalength = self._datawritten
812 if datalength == self._datalength and \
813 self._nframes == self._nframeswritten and \
814 self._marklength == 0:
815 self._file.seek(curpos, 0)
816 return
817 self._file.seek(self._form_length_pos, 0)
818 dummy = self._write_form_length(datalength)
819 self._file.seek(self._nframes_pos, 0)
820 _write_long(self._file, self._nframeswritten)
821 self._file.seek(self._ssnd_length_pos, 0)
822 _write_long(self._file, datalength + 8)
823 self._file.seek(curpos, 0)
824 self._nframes = self._nframeswritten
825 self._datalength = datalength
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000826
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000827 def _writemarkers(self):
828 if len(self._markers) == 0:
829 return
Georg Brandl2095cfe2008-06-07 19:01:03 +0000830 self._file.write(b'MARK')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000831 length = 2
832 for marker in self._markers:
833 id, pos, name = marker
834 length = length + len(name) + 1 + 6
835 if len(name) & 1 == 0:
836 length = length + 1
837 _write_long(self._file, length)
838 self._marklength = length + 8
839 _write_short(self._file, len(self._markers))
840 for marker in self._markers:
841 id, pos, name = marker
842 _write_short(self._file, id)
843 _write_long(self._file, pos)
844 _write_string(self._file, name)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000845
Fred Drake43161351999-06-22 21:23:23 +0000846def open(f, mode=None):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000847 if mode is None:
848 if hasattr(f, 'mode'):
849 mode = f.mode
850 else:
851 mode = 'rb'
852 if mode in ('r', 'rb'):
853 return Aifc_read(f)
854 elif mode in ('w', 'wb'):
855 return Aifc_write(f)
856 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000857 raise Error("mode must be 'r', 'rb', 'w', or 'wb'")
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000858
Guido van Rossum7bc817d1993-12-17 15:25:27 +0000859openfp = open # B/W compatibility
Guido van Rossum36bb1811996-12-31 05:57:34 +0000860
861if __name__ == '__main__':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000862 import sys
863 if not sys.argv[1:]:
864 sys.argv.append('/usr/demos/data/audio/bach.aiff')
865 fn = sys.argv[1]
866 f = open(fn, 'r')
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000867 print("Reading", fn)
868 print("nchannels =", f.getnchannels())
869 print("nframes =", f.getnframes())
870 print("sampwidth =", f.getsampwidth())
871 print("framerate =", f.getframerate())
872 print("comptype =", f.getcomptype())
873 print("compname =", f.getcompname())
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000874 if sys.argv[2:]:
875 gn = sys.argv[2]
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000876 print("Writing", gn)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000877 g = open(gn, 'w')
878 g.setparams(f.getparams())
879 while 1:
880 data = f.readframes(1024)
881 if not data:
882 break
883 g.writeframes(data)
884 g.close()
885 f.close()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000886 print("Done.")