blob: 702a451b128c4f50ade36212b5c0c42df710ccdb [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
284 self._file = Chunk(file)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000285 if self._file.getname() != b'FORM':
Collin Winterce36ad82007-08-30 01:19:48 +0000286 raise Error('file does not start with FORM id')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000287 formdata = self._file.read(4)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000288 if formdata == b'AIFF':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000289 self._aifc = 0
Georg Brandl2095cfe2008-06-07 19:01:03 +0000290 elif formdata == b'AIFC':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000291 self._aifc = 1
292 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000293 raise Error('not an AIFF or AIFF-C file')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000294 self._comm_chunk_read = 0
295 while 1:
296 self._ssnd_seek_needed = 1
297 try:
298 chunk = Chunk(self._file)
299 except EOFError:
300 break
301 chunkname = chunk.getname()
Georg Brandl2095cfe2008-06-07 19:01:03 +0000302 if chunkname == b'COMM':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000303 self._read_comm_chunk(chunk)
304 self._comm_chunk_read = 1
Georg Brandl2095cfe2008-06-07 19:01:03 +0000305 elif chunkname == b'SSND':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000306 self._ssnd_chunk = chunk
307 dummy = chunk.read(8)
308 self._ssnd_seek_needed = 0
Georg Brandl2095cfe2008-06-07 19:01:03 +0000309 elif chunkname == b'FVER':
Guido van Rossum820819c2002-08-12 22:11:28 +0000310 self._version = _read_ulong(chunk)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000311 elif chunkname == b'MARK':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000312 self._readmark(chunk)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000313 chunk.skip()
314 if not self._comm_chunk_read or not self._ssnd_chunk:
Collin Winterce36ad82007-08-30 01:19:48 +0000315 raise Error('COMM chunk and/or SSND chunk missing')
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000316
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000317 def __init__(self, f):
Georg Brandl2095cfe2008-06-07 19:01:03 +0000318 if isinstance(f, str):
Georg Brandl1a3284e2007-12-02 09:40:06 +0000319 f = builtins.open(f, 'rb')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000320 # else, assume it is an open file object already
321 self.initfp(f)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000322
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000323 #
324 # User visible methods.
325 #
326 def getfp(self):
327 return self._file
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000328
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000329 def rewind(self):
330 self._ssnd_seek_needed = 1
331 self._soundpos = 0
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000332
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000333 def close(self):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000334 self._file = None
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000335
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000336 def tell(self):
337 return self._soundpos
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000338
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000339 def getnchannels(self):
340 return self._nchannels
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000341
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000342 def getnframes(self):
343 return self._nframes
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000344
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000345 def getsampwidth(self):
346 return self._sampwidth
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000347
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000348 def getframerate(self):
349 return self._framerate
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000350
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000351 def getcomptype(self):
352 return self._comptype
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000353
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000354 def getcompname(self):
355 return self._compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000356
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000357## def getversion(self):
358## return self._version
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000359
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000360 def getparams(self):
361 return self.getnchannels(), self.getsampwidth(), \
362 self.getframerate(), self.getnframes(), \
363 self.getcomptype(), self.getcompname()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000364
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000365 def getmarkers(self):
366 if len(self._markers) == 0:
367 return None
368 return self._markers
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000369
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000370 def getmark(self, id):
371 for marker in self._markers:
372 if id == marker[0]:
373 return marker
Georg Brandl2095cfe2008-06-07 19:01:03 +0000374 raise Error('marker {0!r} does not exist'.format(id))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000375
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000376 def setpos(self, pos):
377 if pos < 0 or pos > self._nframes:
Collin Winterce36ad82007-08-30 01:19:48 +0000378 raise Error('position not in range')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000379 self._soundpos = pos
380 self._ssnd_seek_needed = 1
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000381
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000382 def readframes(self, nframes):
383 if self._ssnd_seek_needed:
384 self._ssnd_chunk.seek(0)
385 dummy = self._ssnd_chunk.read(8)
386 pos = self._soundpos * self._framesize
387 if pos:
Guido van Rossum2663c132000-03-07 15:19:31 +0000388 self._ssnd_chunk.seek(pos + 8)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000389 self._ssnd_seek_needed = 0
390 if nframes == 0:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000391 return b''
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000392 data = self._ssnd_chunk.read(nframes * self._framesize)
393 if self._convert and data:
394 data = self._convert(data)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000395 self._soundpos = self._soundpos + len(data) // (self._nchannels
396 * self._sampwidth)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000397 return data
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000398
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000399 #
400 # Internal methods.
401 #
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000402
Georg Brandl2095cfe2008-06-07 19:01:03 +0000403 def _alaw2lin(self, data):
404 import audioop
405 return audioop.alaw2lin(data, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000406
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000407 def _ulaw2lin(self, data):
408 import audioop
409 return audioop.ulaw2lin(data, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000410
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000411 def _adpcm2lin(self, data):
412 import audioop
413 if not hasattr(self, '_adpcmstate'):
414 # first time
415 self._adpcmstate = None
Georg Brandl2095cfe2008-06-07 19:01:03 +0000416 data, self._adpcmstate = audioop.adpcm2lin(data, 2, self._adpcmstate)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000417 return data
Sjoerd Mullender1f057541994-09-06 16:17:51 +0000418
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000419 def _read_comm_chunk(self, chunk):
420 self._nchannels = _read_short(chunk)
421 self._nframes = _read_long(chunk)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000422 self._sampwidth = (_read_short(chunk) + 7) // 8
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000423 self._framerate = int(_read_float(chunk))
424 self._framesize = self._nchannels * self._sampwidth
425 if self._aifc:
426 #DEBUG: SGI's soundeditor produces a bad size :-(
427 kludge = 0
428 if chunk.chunksize == 18:
429 kludge = 1
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000430 print('Warning: bad COMM chunk size')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000431 chunk.chunksize = 23
432 #DEBUG end
433 self._comptype = chunk.read(4)
434 #DEBUG start
435 if kludge:
436 length = ord(chunk.file.read(1))
437 if length & 1 == 0:
438 length = length + 1
439 chunk.chunksize = chunk.chunksize + length
440 chunk.file.seek(-1, 1)
441 #DEBUG end
442 self._compname = _read_string(chunk)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000443 if self._comptype != b'NONE':
444 if self._comptype == b'G722':
445 self._convert = self._adpcm2lin
446 self._framesize = self._framesize // 4
447 elif self._comptype in (b'ulaw', b'ULAW'):
448 self._convert = self._ulaw2lin
449 self._framesize = self._framesize // 2
450 elif self._comptype in (b'alaw', b'ALAW'):
451 self._convert = self._alaw2lin
452 self._framesize = self._framesize // 2
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000453 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000454 raise Error('unsupported compression type')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000455 else:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000456 self._comptype = b'NONE'
457 self._compname = b'not compressed'
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000458
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000459 def _readmark(self, chunk):
460 nmarkers = _read_short(chunk)
461 # Some files appear to contain invalid counts.
462 # Cope with this by testing for EOF.
463 try:
464 for i in range(nmarkers):
465 id = _read_short(chunk)
466 pos = _read_long(chunk)
467 name = _read_string(chunk)
468 if pos or name:
469 # some files appear to have
470 # dummy markers consisting of
471 # a position 0 and name ''
472 self._markers.append((id, pos, name))
473 except EOFError:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000474 print('Warning: MARK chunk contains only', end=' ')
475 print(len(self._markers), end=' ')
476 if len(self._markers) == 1: print('marker', end=' ')
477 else: print('markers', end=' ')
478 print('instead of', nmarkers)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000479
Guido van Rossumd3166071993-05-24 14:16:22 +0000480class Aifc_write:
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000481 # Variables used in this class:
482 #
483 # These variables are user settable through appropriate methods
484 # of this class:
485 # _file -- the open file with methods write(), close(), tell(), seek()
486 # set through the __init__() method
487 # _comptype -- the AIFF-C compression type ('NONE' in AIFF)
488 # set through the setcomptype() or setparams() method
489 # _compname -- the human-readable AIFF-C compression type
490 # set through the setcomptype() or setparams() method
491 # _nchannels -- the number of audio channels
492 # set through the setnchannels() or setparams() method
493 # _sampwidth -- the number of bytes per audio sample
494 # set through the setsampwidth() or setparams() method
495 # _framerate -- the sampling frequency
496 # set through the setframerate() or setparams() method
497 # _nframes -- the number of audio frames written to the header
498 # set through the setnframes() or setparams() method
499 # _aifc -- whether we're writing an AIFF-C file or an AIFF file
500 # set through the aifc() method, reset through the
501 # aiff() method
502 #
503 # These variables are used internally only:
504 # _version -- the AIFF-C version number
505 # _comp -- the compressor from builtin module cl
506 # _nframeswritten -- the number of audio frames actually written
507 # _datalength -- the size of the audio samples written to the header
508 # _datawritten -- the size of the audio samples actually written
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000509
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000510 def __init__(self, f):
Georg Brandl2095cfe2008-06-07 19:01:03 +0000511 if isinstance(f, str):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000512 filename = f
Georg Brandl1a3284e2007-12-02 09:40:06 +0000513 f = builtins.open(f, 'wb')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000514 else:
515 # else, assume it is an open file object already
516 filename = '???'
517 self.initfp(f)
518 if filename[-5:] == '.aiff':
519 self._aifc = 0
520 else:
521 self._aifc = 1
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000522
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000523 def initfp(self, file):
524 self._file = file
525 self._version = _AIFC_version
Georg Brandl2095cfe2008-06-07 19:01:03 +0000526 self._comptype = b'NONE'
527 self._compname = b'not compressed'
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000528 self._convert = None
529 self._nchannels = 0
530 self._sampwidth = 0
531 self._framerate = 0
532 self._nframes = 0
533 self._nframeswritten = 0
534 self._datawritten = 0
535 self._datalength = 0
536 self._markers = []
537 self._marklength = 0
538 self._aifc = 1 # AIFF-C is default
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000539
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000540 def __del__(self):
541 if self._file:
542 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')
Georg Brandl2095cfe2008-06-07 19:01:03 +0000645 if not isinstance(name, str):
Collin Winterce36ad82007-08-30 01:19:48 +0000646 raise Error('marker name must be a string')
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):
683 self._ensure_header_written(0)
684 if self._datawritten & 1:
685 # quick pad to even size
Georg Brandl2095cfe2008-06-07 19:01:03 +0000686 self._file.write(b'\x00')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000687 self._datawritten = self._datawritten + 1
688 self._writemarkers()
689 if self._nframeswritten != self._nframes or \
690 self._datalength != self._datawritten or \
691 self._marklength:
692 self._patchheader()
Benjamin Petersoncc65c1d2009-04-30 00:30:08 +0000693 # Prevent ref cycles
694 self._convert = None
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000695 self._file.flush()
696 self._file = None
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.")