blob: 67ea5dadc0bf1819d1b68ca508e148332ad4d2b6 [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
Ezio Melotti48d578c2012-03-12 23:57:18 +0200139import warnings
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000140
Georg Brandl2095cfe2008-06-07 19:01:03 +0000141__all__ = ["Error", "open", "openfp"]
Skip Montanaroe99d5ea2001-01-20 19:54:20 +0000142
Fred Drake227b1202000-08-17 05:06:49 +0000143class Error(Exception):
144 pass
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000145
Guido van Rossume2a383d2007-01-15 16:59:06 +0000146_AIFC_version = 0xA2805140 # Version 1 of AIFF-C
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000147
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000148def _read_long(file):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000149 try:
150 return struct.unpack('>l', file.read(4))[0]
151 except struct.error:
152 raise EOFError
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000153
154def _read_ulong(file):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000155 try:
156 return struct.unpack('>L', file.read(4))[0]
157 except struct.error:
158 raise EOFError
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000159
160def _read_short(file):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000161 try:
162 return struct.unpack('>h', file.read(2))[0]
163 except struct.error:
164 raise EOFError
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000165
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100166def _read_ushort(file):
167 try:
168 return struct.unpack('>H', file.read(2))[0]
169 except struct.error:
170 raise EOFError
171
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000172def _read_string(file):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000173 length = ord(file.read(1))
174 if length == 0:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000175 data = b''
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000176 else:
177 data = file.read(length)
178 if length & 1 == 0:
179 dummy = file.read(1)
180 return data
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000181
182_HUGE_VAL = 1.79769313486231e+308 # See <limits.h>
183
184def _read_float(f): # 10 bytes
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000185 expon = _read_short(f) # 2 bytes
186 sign = 1
187 if expon < 0:
188 sign = -1
189 expon = expon + 0x8000
190 himant = _read_ulong(f) # 4 bytes
191 lomant = _read_ulong(f) # 4 bytes
192 if expon == himant == lomant == 0:
193 f = 0.0
194 elif expon == 0x7FFF:
195 f = _HUGE_VAL
196 else:
197 expon = expon - 16383
Guido van Rossume2a383d2007-01-15 16:59:06 +0000198 f = (himant * 0x100000000 + lomant) * pow(2.0, expon - 63)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000199 return sign * f
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000200
201def _write_short(f, x):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000202 f.write(struct.pack('>h', x))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000203
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100204def _write_ushort(f, x):
205 f.write(struct.pack('>H', x))
206
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000207def _write_long(f, x):
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100208 f.write(struct.pack('>l', x))
209
210def _write_ulong(f, x):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000211 f.write(struct.pack('>L', x))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000212
213def _write_string(f, s):
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000214 if len(s) > 255:
215 raise ValueError("string exceeds maximum pstring length")
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100216 f.write(struct.pack('B', len(s)))
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000217 f.write(s)
218 if len(s) & 1 == 0:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000219 f.write(b'\x00')
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000220
221def _write_float(f, x):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000222 import math
223 if x < 0:
224 sign = 0x8000
225 x = x * -1
226 else:
227 sign = 0
228 if x == 0:
229 expon = 0
230 himant = 0
231 lomant = 0
232 else:
233 fmant, expon = math.frexp(x)
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100234 if expon > 16384 or fmant >= 1 or fmant != fmant: # Infinity or NaN
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000235 expon = sign|0x7FFF
236 himant = 0
237 lomant = 0
238 else: # Finite
239 expon = expon + 16382
240 if expon < 0: # denormalized
241 fmant = math.ldexp(fmant, expon)
242 expon = 0
243 expon = expon | sign
244 fmant = math.ldexp(fmant, 32)
245 fsmant = math.floor(fmant)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000246 himant = int(fsmant)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000247 fmant = math.ldexp(fmant - fsmant, 32)
248 fsmant = math.floor(fmant)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000249 lomant = int(fsmant)
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100250 _write_ushort(f, expon)
251 _write_ulong(f, himant)
252 _write_ulong(f, lomant)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000253
Guido van Rossum8ea7bb81999-06-09 13:32:28 +0000254from chunk import Chunk
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000255
Guido van Rossumd3166071993-05-24 14:16:22 +0000256class Aifc_read:
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000257 # Variables used in this class:
258 #
259 # These variables are available to the user though appropriate
260 # methods of this class:
261 # _file -- the open file with methods read(), close(), and seek()
262 # set through the __init__() method
263 # _nchannels -- the number of audio channels
264 # available through the getnchannels() method
265 # _nframes -- the number of audio frames
266 # available through the getnframes() method
267 # _sampwidth -- the number of bytes per audio sample
268 # available through the getsampwidth() method
269 # _framerate -- the sampling frequency
270 # available through the getframerate() method
271 # _comptype -- the AIFF-C compression type ('NONE' if AIFF)
272 # available through the getcomptype() method
273 # _compname -- the human-readable AIFF-C compression type
274 # available through the getcomptype() method
275 # _markers -- the marks in the audio file
276 # available through the getmarkers() and getmark()
277 # methods
278 # _soundpos -- the position in the audio stream
279 # available through the tell() method, set through the
280 # setpos() method
281 #
282 # These variables are used internally only:
283 # _version -- the AIFF-C version number
284 # _decomp -- the decompressor from builtin module cl
285 # _comm_chunk_read -- 1 iff the COMM chunk has been read
286 # _aifc -- 1 iff reading an AIFF-C file
287 # _ssnd_seek_needed -- 1 iff positioned correctly in audio
288 # file for readframes()
289 # _ssnd_chunk -- instantiation of a chunk class for the SSND chunk
290 # _framesize -- size of one frame in the file
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000291
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000292 def initfp(self, file):
293 self._version = 0
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000294 self._convert = None
295 self._markers = []
296 self._soundpos = 0
R. David Murray99352742009-05-07 18:24:38 +0000297 self._file = file
298 chunk = Chunk(file)
299 if chunk.getname() != b'FORM':
Collin Winterce36ad82007-08-30 01:19:48 +0000300 raise Error('file does not start with FORM id')
R. David Murray99352742009-05-07 18:24:38 +0000301 formdata = chunk.read(4)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000302 if formdata == b'AIFF':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000303 self._aifc = 0
Georg Brandl2095cfe2008-06-07 19:01:03 +0000304 elif formdata == b'AIFC':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000305 self._aifc = 1
306 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000307 raise Error('not an AIFF or AIFF-C file')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000308 self._comm_chunk_read = 0
309 while 1:
310 self._ssnd_seek_needed = 1
311 try:
312 chunk = Chunk(self._file)
313 except EOFError:
314 break
315 chunkname = chunk.getname()
Georg Brandl2095cfe2008-06-07 19:01:03 +0000316 if chunkname == b'COMM':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000317 self._read_comm_chunk(chunk)
318 self._comm_chunk_read = 1
Georg Brandl2095cfe2008-06-07 19:01:03 +0000319 elif chunkname == b'SSND':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000320 self._ssnd_chunk = chunk
321 dummy = chunk.read(8)
322 self._ssnd_seek_needed = 0
Georg Brandl2095cfe2008-06-07 19:01:03 +0000323 elif chunkname == b'FVER':
Guido van Rossum820819c2002-08-12 22:11:28 +0000324 self._version = _read_ulong(chunk)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000325 elif chunkname == b'MARK':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000326 self._readmark(chunk)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000327 chunk.skip()
328 if not self._comm_chunk_read or not self._ssnd_chunk:
Collin Winterce36ad82007-08-30 01:19:48 +0000329 raise Error('COMM chunk and/or SSND chunk missing')
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000330
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000331 def __init__(self, f):
Georg Brandl2095cfe2008-06-07 19:01:03 +0000332 if isinstance(f, str):
Georg Brandl1a3284e2007-12-02 09:40:06 +0000333 f = builtins.open(f, 'rb')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000334 # else, assume it is an open file object already
335 self.initfp(f)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000336
Serhiy Storchaka44c66c72012-12-29 22:54:49 +0200337 def __enter__(self):
338 return self
339
340 def __exit__(self, *args):
341 self.close()
342
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000343 #
344 # User visible methods.
345 #
346 def getfp(self):
347 return self._file
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000348
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000349 def rewind(self):
350 self._ssnd_seek_needed = 1
351 self._soundpos = 0
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000352
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000353 def close(self):
Benjamin Peterson1d1285d2009-05-07 11:53:38 +0000354 self._file.close()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000355
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000356 def tell(self):
357 return self._soundpos
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000358
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000359 def getnchannels(self):
360 return self._nchannels
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000361
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000362 def getnframes(self):
363 return self._nframes
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000364
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000365 def getsampwidth(self):
366 return self._sampwidth
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000367
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000368 def getframerate(self):
369 return self._framerate
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000370
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000371 def getcomptype(self):
372 return self._comptype
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000373
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000374 def getcompname(self):
375 return self._compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000376
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000377## def getversion(self):
378## return self._version
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000379
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000380 def getparams(self):
381 return self.getnchannels(), self.getsampwidth(), \
382 self.getframerate(), self.getnframes(), \
383 self.getcomptype(), self.getcompname()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000384
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000385 def getmarkers(self):
386 if len(self._markers) == 0:
387 return None
388 return self._markers
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000389
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000390 def getmark(self, id):
391 for marker in self._markers:
392 if id == marker[0]:
393 return marker
Georg Brandl2095cfe2008-06-07 19:01:03 +0000394 raise Error('marker {0!r} does not exist'.format(id))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000395
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000396 def setpos(self, pos):
397 if pos < 0 or pos > self._nframes:
Collin Winterce36ad82007-08-30 01:19:48 +0000398 raise Error('position not in range')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000399 self._soundpos = pos
400 self._ssnd_seek_needed = 1
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000401
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000402 def readframes(self, nframes):
403 if self._ssnd_seek_needed:
404 self._ssnd_chunk.seek(0)
405 dummy = self._ssnd_chunk.read(8)
406 pos = self._soundpos * self._framesize
407 if pos:
Guido van Rossum2663c132000-03-07 15:19:31 +0000408 self._ssnd_chunk.seek(pos + 8)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000409 self._ssnd_seek_needed = 0
410 if nframes == 0:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000411 return b''
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000412 data = self._ssnd_chunk.read(nframes * self._framesize)
413 if self._convert and data:
414 data = self._convert(data)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000415 self._soundpos = self._soundpos + len(data) // (self._nchannels
416 * self._sampwidth)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000417 return data
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000418
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000419 #
420 # Internal methods.
421 #
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000422
Georg Brandl2095cfe2008-06-07 19:01:03 +0000423 def _alaw2lin(self, data):
424 import audioop
425 return audioop.alaw2lin(data, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000426
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000427 def _ulaw2lin(self, data):
428 import audioop
429 return audioop.ulaw2lin(data, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000430
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000431 def _adpcm2lin(self, data):
432 import audioop
433 if not hasattr(self, '_adpcmstate'):
434 # first time
435 self._adpcmstate = None
Georg Brandl2095cfe2008-06-07 19:01:03 +0000436 data, self._adpcmstate = audioop.adpcm2lin(data, 2, self._adpcmstate)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000437 return data
Sjoerd Mullender1f057541994-09-06 16:17:51 +0000438
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000439 def _read_comm_chunk(self, chunk):
440 self._nchannels = _read_short(chunk)
441 self._nframes = _read_long(chunk)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000442 self._sampwidth = (_read_short(chunk) + 7) // 8
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000443 self._framerate = int(_read_float(chunk))
444 self._framesize = self._nchannels * self._sampwidth
445 if self._aifc:
446 #DEBUG: SGI's soundeditor produces a bad size :-(
447 kludge = 0
448 if chunk.chunksize == 18:
449 kludge = 1
Ezio Melotti48d578c2012-03-12 23:57:18 +0200450 warnings.warn('Warning: bad COMM chunk size')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000451 chunk.chunksize = 23
452 #DEBUG end
453 self._comptype = chunk.read(4)
454 #DEBUG start
455 if kludge:
456 length = ord(chunk.file.read(1))
457 if length & 1 == 0:
458 length = length + 1
459 chunk.chunksize = chunk.chunksize + length
460 chunk.file.seek(-1, 1)
461 #DEBUG end
462 self._compname = _read_string(chunk)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000463 if self._comptype != b'NONE':
464 if self._comptype == b'G722':
465 self._convert = self._adpcm2lin
466 self._framesize = self._framesize // 4
467 elif self._comptype in (b'ulaw', b'ULAW'):
468 self._convert = self._ulaw2lin
469 self._framesize = self._framesize // 2
470 elif self._comptype in (b'alaw', b'ALAW'):
471 self._convert = self._alaw2lin
472 self._framesize = self._framesize // 2
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000473 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000474 raise Error('unsupported compression type')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000475 else:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000476 self._comptype = b'NONE'
477 self._compname = b'not compressed'
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000478
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000479 def _readmark(self, chunk):
480 nmarkers = _read_short(chunk)
481 # Some files appear to contain invalid counts.
482 # Cope with this by testing for EOF.
483 try:
484 for i in range(nmarkers):
485 id = _read_short(chunk)
486 pos = _read_long(chunk)
487 name = _read_string(chunk)
488 if pos or name:
489 # some files appear to have
490 # dummy markers consisting of
491 # a position 0 and name ''
492 self._markers.append((id, pos, name))
493 except EOFError:
Ezio Melotti48d578c2012-03-12 23:57:18 +0200494 w = ('Warning: MARK chunk contains only %s marker%s instead of %s' %
495 (len(self._markers), '' if len(self._markers) == 1 else 's',
496 nmarkers))
497 warnings.warn(w)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000498
Guido van Rossumd3166071993-05-24 14:16:22 +0000499class Aifc_write:
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000500 # Variables used in this class:
501 #
502 # These variables are user settable through appropriate methods
503 # of this class:
504 # _file -- the open file with methods write(), close(), tell(), seek()
505 # set through the __init__() method
506 # _comptype -- the AIFF-C compression type ('NONE' in AIFF)
507 # set through the setcomptype() or setparams() method
508 # _compname -- the human-readable AIFF-C compression type
509 # set through the setcomptype() or setparams() method
510 # _nchannels -- the number of audio channels
511 # set through the setnchannels() or setparams() method
512 # _sampwidth -- the number of bytes per audio sample
513 # set through the setsampwidth() or setparams() method
514 # _framerate -- the sampling frequency
515 # set through the setframerate() or setparams() method
516 # _nframes -- the number of audio frames written to the header
517 # set through the setnframes() or setparams() method
518 # _aifc -- whether we're writing an AIFF-C file or an AIFF file
519 # set through the aifc() method, reset through the
520 # aiff() method
521 #
522 # These variables are used internally only:
523 # _version -- the AIFF-C version number
524 # _comp -- the compressor from builtin module cl
525 # _nframeswritten -- the number of audio frames actually written
526 # _datalength -- the size of the audio samples written to the header
527 # _datawritten -- the size of the audio samples actually written
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000528
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000529 def __init__(self, f):
Georg Brandl2095cfe2008-06-07 19:01:03 +0000530 if isinstance(f, str):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000531 filename = f
Georg Brandl1a3284e2007-12-02 09:40:06 +0000532 f = builtins.open(f, 'wb')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000533 else:
534 # else, assume it is an open file object already
535 filename = '???'
536 self.initfp(f)
537 if filename[-5:] == '.aiff':
538 self._aifc = 0
539 else:
540 self._aifc = 1
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000541
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000542 def initfp(self, file):
543 self._file = file
544 self._version = _AIFC_version
Georg Brandl2095cfe2008-06-07 19:01:03 +0000545 self._comptype = b'NONE'
546 self._compname = b'not compressed'
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000547 self._convert = None
548 self._nchannels = 0
549 self._sampwidth = 0
550 self._framerate = 0
551 self._nframes = 0
552 self._nframeswritten = 0
553 self._datawritten = 0
554 self._datalength = 0
555 self._markers = []
556 self._marklength = 0
557 self._aifc = 1 # AIFF-C is default
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000558
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000559 def __del__(self):
Sandro Tosi70efbef2012-01-01 22:53:08 +0100560 self.close()
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000561
Serhiy Storchaka44c66c72012-12-29 22:54:49 +0200562 def __enter__(self):
563 return self
564
565 def __exit__(self, *args):
566 self.close()
567
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000568 #
569 # User visible methods.
570 #
571 def aiff(self):
572 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000573 raise Error('cannot change parameters after starting to write')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000574 self._aifc = 0
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000575
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000576 def aifc(self):
577 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000578 raise Error('cannot change parameters after starting to write')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000579 self._aifc = 1
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000580
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000581 def setnchannels(self, nchannels):
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 nchannels < 1:
Collin Winterce36ad82007-08-30 01:19:48 +0000585 raise Error('bad # of channels')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000586 self._nchannels = nchannels
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000587
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000588 def getnchannels(self):
589 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000590 raise Error('number of channels not set')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000591 return self._nchannels
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000592
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000593 def setsampwidth(self, sampwidth):
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 if sampwidth < 1 or sampwidth > 4:
Collin Winterce36ad82007-08-30 01:19:48 +0000597 raise Error('bad sample width')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000598 self._sampwidth = sampwidth
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000599
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000600 def getsampwidth(self):
601 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000602 raise Error('sample width not set')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000603 return self._sampwidth
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000604
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000605 def setframerate(self, framerate):
606 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000607 raise Error('cannot change parameters after starting to write')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000608 if framerate <= 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000609 raise Error('bad frame rate')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000610 self._framerate = framerate
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000611
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000612 def getframerate(self):
613 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000614 raise Error('frame rate not set')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000615 return self._framerate
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000616
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000617 def setnframes(self, nframes):
618 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000619 raise Error('cannot change parameters after starting to write')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000620 self._nframes = nframes
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000621
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000622 def getnframes(self):
623 return self._nframeswritten
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000624
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000625 def setcomptype(self, comptype, compname):
626 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000627 raise Error('cannot change parameters after starting to write')
Georg Brandl2095cfe2008-06-07 19:01:03 +0000628 if comptype not in (b'NONE', b'ulaw', b'ULAW',
629 b'alaw', b'ALAW', b'G722'):
Collin Winterce36ad82007-08-30 01:19:48 +0000630 raise Error('unsupported compression type')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000631 self._comptype = comptype
632 self._compname = compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000633
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000634 def getcomptype(self):
635 return self._comptype
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000636
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000637 def getcompname(self):
638 return self._compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000639
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000640## def setversion(self, version):
641## if self._nframeswritten:
642## raise Error, 'cannot change parameters after starting to write'
643## self._version = version
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000644
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000645 def setparams(self, params):
646 nchannels, sampwidth, framerate, nframes, comptype, compname = params
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000647 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000648 raise Error('cannot change parameters after starting to write')
Georg Brandl2095cfe2008-06-07 19:01:03 +0000649 if comptype not in (b'NONE', b'ulaw', b'ULAW',
650 b'alaw', b'ALAW', b'G722'):
Collin Winterce36ad82007-08-30 01:19:48 +0000651 raise Error('unsupported compression type')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000652 self.setnchannels(nchannels)
653 self.setsampwidth(sampwidth)
654 self.setframerate(framerate)
655 self.setnframes(nframes)
656 self.setcomptype(comptype, compname)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000657
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000658 def getparams(self):
659 if not self._nchannels or not self._sampwidth or not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000660 raise Error('not all parameters set')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000661 return self._nchannels, self._sampwidth, self._framerate, \
662 self._nframes, self._comptype, self._compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000663
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000664 def setmark(self, id, pos, name):
665 if id <= 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000666 raise Error('marker ID must be > 0')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000667 if pos < 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000668 raise Error('marker position must be >= 0')
Sandro Tosi70efbef2012-01-01 22:53:08 +0100669 if not isinstance(name, bytes):
670 raise Error('marker name must be bytes')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000671 for i in range(len(self._markers)):
672 if id == self._markers[i][0]:
673 self._markers[i] = id, pos, name
674 return
675 self._markers.append((id, pos, name))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000676
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000677 def getmark(self, id):
678 for marker in self._markers:
679 if id == marker[0]:
680 return marker
Georg Brandl2095cfe2008-06-07 19:01:03 +0000681 raise Error('marker {0!r} does not exist'.format(id))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000682
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000683 def getmarkers(self):
684 if len(self._markers) == 0:
685 return None
686 return self._markers
Tim Peters146965a2001-01-14 18:09:23 +0000687
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000688 def tell(self):
689 return self._nframeswritten
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000690
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000691 def writeframesraw(self, data):
692 self._ensure_header_written(len(data))
Georg Brandl2095cfe2008-06-07 19:01:03 +0000693 nframes = len(data) // (self._sampwidth * self._nchannels)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000694 if self._convert:
695 data = self._convert(data)
696 self._file.write(data)
697 self._nframeswritten = self._nframeswritten + nframes
698 self._datawritten = self._datawritten + len(data)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000699
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000700 def writeframes(self, data):
701 self.writeframesraw(data)
702 if self._nframeswritten != self._nframes or \
703 self._datalength != self._datawritten:
704 self._patchheader()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000705
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000706 def close(self):
Serhiy Storchaka051722d2012-12-29 22:30:56 +0200707 if self._file is None:
708 return
709 try:
Sandro Tosi70efbef2012-01-01 22:53:08 +0100710 self._ensure_header_written(0)
711 if self._datawritten & 1:
712 # quick pad to even size
713 self._file.write(b'\x00')
714 self._datawritten = self._datawritten + 1
715 self._writemarkers()
716 if self._nframeswritten != self._nframes or \
717 self._datalength != self._datawritten or \
718 self._marklength:
719 self._patchheader()
Serhiy Storchaka051722d2012-12-29 22:30:56 +0200720 finally:
Sandro Tosi70efbef2012-01-01 22:53:08 +0100721 # Prevent ref cycles
722 self._convert = None
Serhiy Storchaka051722d2012-12-29 22:30:56 +0200723 f = self._file
Sandro Tosi70efbef2012-01-01 22:53:08 +0100724 self._file = None
Serhiy Storchaka051722d2012-12-29 22:30:56 +0200725 f.close()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000726
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000727 #
728 # Internal methods.
729 #
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000730
Georg Brandl2095cfe2008-06-07 19:01:03 +0000731 def _lin2alaw(self, data):
732 import audioop
733 return audioop.lin2alaw(data, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000734
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000735 def _lin2ulaw(self, data):
736 import audioop
737 return audioop.lin2ulaw(data, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000738
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000739 def _lin2adpcm(self, data):
740 import audioop
741 if not hasattr(self, '_adpcmstate'):
742 self._adpcmstate = None
Georg Brandl2095cfe2008-06-07 19:01:03 +0000743 data, self._adpcmstate = audioop.lin2adpcm(data, 2, self._adpcmstate)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000744 return data
Sjoerd Mullender1f057541994-09-06 16:17:51 +0000745
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000746 def _ensure_header_written(self, datasize):
747 if not self._nframeswritten:
Sandro Tosibdd53542012-01-01 18:04:37 +0100748 if self._comptype in (b'ULAW', b'ulaw', b'ALAW', b'alaw', b'G722'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000749 if not self._sampwidth:
750 self._sampwidth = 2
751 if self._sampwidth != 2:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000752 raise Error('sample width must be 2 when compressing '
Sandro Tosibdd53542012-01-01 18:04:37 +0100753 'with ulaw/ULAW, alaw/ALAW or G7.22 (ADPCM)')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000754 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000755 raise Error('# channels not specified')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000756 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000757 raise Error('sample width not specified')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000758 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000759 raise Error('sampling rate not specified')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000760 self._write_header(datasize)
Guido van Rossum52fc1f61993-06-17 12:38:10 +0000761
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000762 def _init_compression(self):
Georg Brandl2095cfe2008-06-07 19:01:03 +0000763 if self._comptype == b'G722':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000764 self._convert = self._lin2adpcm
Georg Brandl2095cfe2008-06-07 19:01:03 +0000765 elif self._comptype in (b'ulaw', b'ULAW'):
766 self._convert = self._lin2ulaw
767 elif self._comptype in (b'alaw', b'ALAW'):
768 self._convert = self._lin2alaw
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000769
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000770 def _write_header(self, initlength):
Georg Brandl2095cfe2008-06-07 19:01:03 +0000771 if self._aifc and self._comptype != b'NONE':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000772 self._init_compression()
Georg Brandl2095cfe2008-06-07 19:01:03 +0000773 self._file.write(b'FORM')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000774 if not self._nframes:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000775 self._nframes = initlength // (self._nchannels * self._sampwidth)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000776 self._datalength = self._nframes * self._nchannels * self._sampwidth
777 if self._datalength & 1:
778 self._datalength = self._datalength + 1
779 if self._aifc:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000780 if self._comptype in (b'ulaw', b'ULAW', b'alaw', b'ALAW'):
781 self._datalength = self._datalength // 2
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000782 if self._datalength & 1:
783 self._datalength = self._datalength + 1
Georg Brandl2095cfe2008-06-07 19:01:03 +0000784 elif self._comptype == b'G722':
785 self._datalength = (self._datalength + 3) // 4
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000786 if self._datalength & 1:
787 self._datalength = self._datalength + 1
788 self._form_length_pos = self._file.tell()
789 commlength = self._write_form_length(self._datalength)
790 if self._aifc:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000791 self._file.write(b'AIFC')
792 self._file.write(b'FVER')
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100793 _write_ulong(self._file, 4)
794 _write_ulong(self._file, self._version)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000795 else:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000796 self._file.write(b'AIFF')
797 self._file.write(b'COMM')
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100798 _write_ulong(self._file, commlength)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000799 _write_short(self._file, self._nchannels)
800 self._nframes_pos = self._file.tell()
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100801 _write_ulong(self._file, self._nframes)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000802 _write_short(self._file, self._sampwidth * 8)
803 _write_float(self._file, self._framerate)
804 if self._aifc:
805 self._file.write(self._comptype)
806 _write_string(self._file, self._compname)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000807 self._file.write(b'SSND')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000808 self._ssnd_length_pos = self._file.tell()
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100809 _write_ulong(self._file, self._datalength + 8)
810 _write_ulong(self._file, 0)
811 _write_ulong(self._file, 0)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000812
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000813 def _write_form_length(self, datalength):
814 if self._aifc:
815 commlength = 18 + 5 + len(self._compname)
816 if commlength & 1:
817 commlength = commlength + 1
818 verslength = 12
819 else:
820 commlength = 18
821 verslength = 0
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100822 _write_ulong(self._file, 4 + verslength + self._marklength + \
823 8 + commlength + 16 + datalength)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000824 return commlength
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000825
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000826 def _patchheader(self):
827 curpos = self._file.tell()
828 if self._datawritten & 1:
829 datalength = self._datawritten + 1
Georg Brandl2095cfe2008-06-07 19:01:03 +0000830 self._file.write(b'\x00')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000831 else:
832 datalength = self._datawritten
833 if datalength == self._datalength and \
834 self._nframes == self._nframeswritten and \
835 self._marklength == 0:
836 self._file.seek(curpos, 0)
837 return
838 self._file.seek(self._form_length_pos, 0)
839 dummy = self._write_form_length(datalength)
840 self._file.seek(self._nframes_pos, 0)
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100841 _write_ulong(self._file, self._nframeswritten)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000842 self._file.seek(self._ssnd_length_pos, 0)
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100843 _write_ulong(self._file, datalength + 8)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000844 self._file.seek(curpos, 0)
845 self._nframes = self._nframeswritten
846 self._datalength = datalength
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000847
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000848 def _writemarkers(self):
849 if len(self._markers) == 0:
850 return
Georg Brandl2095cfe2008-06-07 19:01:03 +0000851 self._file.write(b'MARK')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000852 length = 2
853 for marker in self._markers:
854 id, pos, name = marker
855 length = length + len(name) + 1 + 6
856 if len(name) & 1 == 0:
857 length = length + 1
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100858 _write_ulong(self._file, length)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000859 self._marklength = length + 8
860 _write_short(self._file, len(self._markers))
861 for marker in self._markers:
862 id, pos, name = marker
863 _write_short(self._file, id)
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100864 _write_ulong(self._file, pos)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000865 _write_string(self._file, name)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000866
Fred Drake43161351999-06-22 21:23:23 +0000867def open(f, mode=None):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000868 if mode is None:
869 if hasattr(f, 'mode'):
870 mode = f.mode
871 else:
872 mode = 'rb'
873 if mode in ('r', 'rb'):
874 return Aifc_read(f)
875 elif mode in ('w', 'wb'):
876 return Aifc_write(f)
877 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000878 raise Error("mode must be 'r', 'rb', 'w', or 'wb'")
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000879
Guido van Rossum7bc817d1993-12-17 15:25:27 +0000880openfp = open # B/W compatibility
Guido van Rossum36bb1811996-12-31 05:57:34 +0000881
882if __name__ == '__main__':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000883 import sys
884 if not sys.argv[1:]:
885 sys.argv.append('/usr/demos/data/audio/bach.aiff')
886 fn = sys.argv[1]
887 f = open(fn, 'r')
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000888 print("Reading", fn)
889 print("nchannels =", f.getnchannels())
890 print("nframes =", f.getnframes())
891 print("sampwidth =", f.getsampwidth())
892 print("framerate =", f.getframerate())
893 print("comptype =", f.getcomptype())
894 print("compname =", f.getcompname())
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000895 if sys.argv[2:]:
896 gn = sys.argv[2]
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000897 print("Writing", gn)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000898 g = open(gn, 'w')
899 g.setparams(f.getparams())
900 while 1:
901 data = f.readframes(1024)
902 if not data:
903 break
904 g.writeframes(data)
905 g.close()
906 f.close()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000907 print("Done.")