blob: 775f39c74fb5f554226712607df7fd7c80ba7bdd [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
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100165def _read_ushort(file):
166 try:
167 return struct.unpack('>H', file.read(2))[0]
168 except struct.error:
169 raise EOFError
170
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000171def _read_string(file):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000172 length = ord(file.read(1))
173 if length == 0:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000174 data = b''
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000175 else:
176 data = file.read(length)
177 if length & 1 == 0:
178 dummy = file.read(1)
179 return data
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000180
181_HUGE_VAL = 1.79769313486231e+308 # See <limits.h>
182
183def _read_float(f): # 10 bytes
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000184 expon = _read_short(f) # 2 bytes
185 sign = 1
186 if expon < 0:
187 sign = -1
188 expon = expon + 0x8000
189 himant = _read_ulong(f) # 4 bytes
190 lomant = _read_ulong(f) # 4 bytes
191 if expon == himant == lomant == 0:
192 f = 0.0
193 elif expon == 0x7FFF:
194 f = _HUGE_VAL
195 else:
196 expon = expon - 16383
Guido van Rossume2a383d2007-01-15 16:59:06 +0000197 f = (himant * 0x100000000 + lomant) * pow(2.0, expon - 63)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000198 return sign * f
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000199
200def _write_short(f, x):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000201 f.write(struct.pack('>h', x))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000202
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100203def _write_ushort(f, x):
204 f.write(struct.pack('>H', x))
205
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000206def _write_long(f, x):
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100207 f.write(struct.pack('>l', x))
208
209def _write_ulong(f, x):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000210 f.write(struct.pack('>L', x))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000211
212def _write_string(f, s):
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000213 if len(s) > 255:
214 raise ValueError("string exceeds maximum pstring length")
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100215 f.write(struct.pack('B', len(s)))
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000216 f.write(s)
217 if len(s) & 1 == 0:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000218 f.write(b'\x00')
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000219
220def _write_float(f, x):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000221 import math
222 if x < 0:
223 sign = 0x8000
224 x = x * -1
225 else:
226 sign = 0
227 if x == 0:
228 expon = 0
229 himant = 0
230 lomant = 0
231 else:
232 fmant, expon = math.frexp(x)
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100233 if expon > 16384 or fmant >= 1 or fmant != fmant: # Infinity or NaN
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000234 expon = sign|0x7FFF
235 himant = 0
236 lomant = 0
237 else: # Finite
238 expon = expon + 16382
239 if expon < 0: # denormalized
240 fmant = math.ldexp(fmant, expon)
241 expon = 0
242 expon = expon | sign
243 fmant = math.ldexp(fmant, 32)
244 fsmant = math.floor(fmant)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000245 himant = int(fsmant)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000246 fmant = math.ldexp(fmant - fsmant, 32)
247 fsmant = math.floor(fmant)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000248 lomant = int(fsmant)
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100249 _write_ushort(f, expon)
250 _write_ulong(f, himant)
251 _write_ulong(f, lomant)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000252
Guido van Rossum8ea7bb81999-06-09 13:32:28 +0000253from chunk import Chunk
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000254
Guido van Rossumd3166071993-05-24 14:16:22 +0000255class Aifc_read:
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000256 # Variables used in this class:
257 #
258 # These variables are available to the user though appropriate
259 # methods of this class:
260 # _file -- the open file with methods read(), close(), and seek()
261 # set through the __init__() method
262 # _nchannels -- the number of audio channels
263 # available through the getnchannels() method
264 # _nframes -- the number of audio frames
265 # available through the getnframes() method
266 # _sampwidth -- the number of bytes per audio sample
267 # available through the getsampwidth() method
268 # _framerate -- the sampling frequency
269 # available through the getframerate() method
270 # _comptype -- the AIFF-C compression type ('NONE' if AIFF)
271 # available through the getcomptype() method
272 # _compname -- the human-readable AIFF-C compression type
273 # available through the getcomptype() method
274 # _markers -- the marks in the audio file
275 # available through the getmarkers() and getmark()
276 # methods
277 # _soundpos -- the position in the audio stream
278 # available through the tell() method, set through the
279 # setpos() method
280 #
281 # These variables are used internally only:
282 # _version -- the AIFF-C version number
283 # _decomp -- the decompressor from builtin module cl
284 # _comm_chunk_read -- 1 iff the COMM chunk has been read
285 # _aifc -- 1 iff reading an AIFF-C file
286 # _ssnd_seek_needed -- 1 iff positioned correctly in audio
287 # file for readframes()
288 # _ssnd_chunk -- instantiation of a chunk class for the SSND chunk
289 # _framesize -- size of one frame in the file
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000290
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000291 def initfp(self, file):
292 self._version = 0
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000293 self._convert = None
294 self._markers = []
295 self._soundpos = 0
R. David Murray99352742009-05-07 18:24:38 +0000296 self._file = file
297 chunk = Chunk(file)
298 if chunk.getname() != b'FORM':
Collin Winterce36ad82007-08-30 01:19:48 +0000299 raise Error('file does not start with FORM id')
R. David Murray99352742009-05-07 18:24:38 +0000300 formdata = chunk.read(4)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000301 if formdata == b'AIFF':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000302 self._aifc = 0
Georg Brandl2095cfe2008-06-07 19:01:03 +0000303 elif formdata == b'AIFC':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000304 self._aifc = 1
305 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000306 raise Error('not an AIFF or AIFF-C file')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000307 self._comm_chunk_read = 0
308 while 1:
309 self._ssnd_seek_needed = 1
310 try:
311 chunk = Chunk(self._file)
312 except EOFError:
313 break
314 chunkname = chunk.getname()
Georg Brandl2095cfe2008-06-07 19:01:03 +0000315 if chunkname == b'COMM':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000316 self._read_comm_chunk(chunk)
317 self._comm_chunk_read = 1
Georg Brandl2095cfe2008-06-07 19:01:03 +0000318 elif chunkname == b'SSND':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000319 self._ssnd_chunk = chunk
320 dummy = chunk.read(8)
321 self._ssnd_seek_needed = 0
Georg Brandl2095cfe2008-06-07 19:01:03 +0000322 elif chunkname == b'FVER':
Guido van Rossum820819c2002-08-12 22:11:28 +0000323 self._version = _read_ulong(chunk)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000324 elif chunkname == b'MARK':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000325 self._readmark(chunk)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000326 chunk.skip()
327 if not self._comm_chunk_read or not self._ssnd_chunk:
Collin Winterce36ad82007-08-30 01:19:48 +0000328 raise Error('COMM chunk and/or SSND chunk missing')
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000329
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000330 def __init__(self, f):
Georg Brandl2095cfe2008-06-07 19:01:03 +0000331 if isinstance(f, str):
Georg Brandl1a3284e2007-12-02 09:40:06 +0000332 f = builtins.open(f, 'rb')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000333 # else, assume it is an open file object already
334 self.initfp(f)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000335
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000336 #
337 # User visible methods.
338 #
339 def getfp(self):
340 return self._file
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000341
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000342 def rewind(self):
343 self._ssnd_seek_needed = 1
344 self._soundpos = 0
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000345
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000346 def close(self):
Benjamin Peterson1d1285d2009-05-07 11:53:38 +0000347 self._file.close()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000348
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000349 def tell(self):
350 return self._soundpos
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000351
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000352 def getnchannels(self):
353 return self._nchannels
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000354
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000355 def getnframes(self):
356 return self._nframes
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000357
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000358 def getsampwidth(self):
359 return self._sampwidth
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000360
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000361 def getframerate(self):
362 return self._framerate
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000363
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000364 def getcomptype(self):
365 return self._comptype
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000366
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000367 def getcompname(self):
368 return self._compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000369
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000370## def getversion(self):
371## return self._version
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000372
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000373 def getparams(self):
374 return self.getnchannels(), self.getsampwidth(), \
375 self.getframerate(), self.getnframes(), \
376 self.getcomptype(), self.getcompname()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000377
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000378 def getmarkers(self):
379 if len(self._markers) == 0:
380 return None
381 return self._markers
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000382
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000383 def getmark(self, id):
384 for marker in self._markers:
385 if id == marker[0]:
386 return marker
Georg Brandl2095cfe2008-06-07 19:01:03 +0000387 raise Error('marker {0!r} does not exist'.format(id))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000388
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000389 def setpos(self, pos):
390 if pos < 0 or pos > self._nframes:
Collin Winterce36ad82007-08-30 01:19:48 +0000391 raise Error('position not in range')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000392 self._soundpos = pos
393 self._ssnd_seek_needed = 1
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000394
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000395 def readframes(self, nframes):
396 if self._ssnd_seek_needed:
397 self._ssnd_chunk.seek(0)
398 dummy = self._ssnd_chunk.read(8)
399 pos = self._soundpos * self._framesize
400 if pos:
Guido van Rossum2663c132000-03-07 15:19:31 +0000401 self._ssnd_chunk.seek(pos + 8)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000402 self._ssnd_seek_needed = 0
403 if nframes == 0:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000404 return b''
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000405 data = self._ssnd_chunk.read(nframes * self._framesize)
406 if self._convert and data:
407 data = self._convert(data)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000408 self._soundpos = self._soundpos + len(data) // (self._nchannels
409 * self._sampwidth)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000410 return data
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000411
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000412 #
413 # Internal methods.
414 #
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000415
Georg Brandl2095cfe2008-06-07 19:01:03 +0000416 def _alaw2lin(self, data):
417 import audioop
418 return audioop.alaw2lin(data, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000419
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000420 def _ulaw2lin(self, data):
421 import audioop
422 return audioop.ulaw2lin(data, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000423
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000424 def _adpcm2lin(self, data):
425 import audioop
426 if not hasattr(self, '_adpcmstate'):
427 # first time
428 self._adpcmstate = None
Georg Brandl2095cfe2008-06-07 19:01:03 +0000429 data, self._adpcmstate = audioop.adpcm2lin(data, 2, self._adpcmstate)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000430 return data
Sjoerd Mullender1f057541994-09-06 16:17:51 +0000431
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000432 def _read_comm_chunk(self, chunk):
433 self._nchannels = _read_short(chunk)
434 self._nframes = _read_long(chunk)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000435 self._sampwidth = (_read_short(chunk) + 7) // 8
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000436 self._framerate = int(_read_float(chunk))
437 self._framesize = self._nchannels * self._sampwidth
438 if self._aifc:
439 #DEBUG: SGI's soundeditor produces a bad size :-(
440 kludge = 0
441 if chunk.chunksize == 18:
442 kludge = 1
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000443 print('Warning: bad COMM chunk size')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000444 chunk.chunksize = 23
445 #DEBUG end
446 self._comptype = chunk.read(4)
447 #DEBUG start
448 if kludge:
449 length = ord(chunk.file.read(1))
450 if length & 1 == 0:
451 length = length + 1
452 chunk.chunksize = chunk.chunksize + length
453 chunk.file.seek(-1, 1)
454 #DEBUG end
455 self._compname = _read_string(chunk)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000456 if self._comptype != b'NONE':
457 if self._comptype == b'G722':
458 self._convert = self._adpcm2lin
459 self._framesize = self._framesize // 4
460 elif self._comptype in (b'ulaw', b'ULAW'):
461 self._convert = self._ulaw2lin
462 self._framesize = self._framesize // 2
463 elif self._comptype in (b'alaw', b'ALAW'):
464 self._convert = self._alaw2lin
465 self._framesize = self._framesize // 2
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000466 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000467 raise Error('unsupported compression type')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000468 else:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000469 self._comptype = b'NONE'
470 self._compname = b'not compressed'
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000471
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000472 def _readmark(self, chunk):
473 nmarkers = _read_short(chunk)
474 # Some files appear to contain invalid counts.
475 # Cope with this by testing for EOF.
476 try:
477 for i in range(nmarkers):
478 id = _read_short(chunk)
479 pos = _read_long(chunk)
480 name = _read_string(chunk)
481 if pos or name:
482 # some files appear to have
483 # dummy markers consisting of
484 # a position 0 and name ''
485 self._markers.append((id, pos, name))
486 except EOFError:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000487 print('Warning: MARK chunk contains only', end=' ')
488 print(len(self._markers), end=' ')
489 if len(self._markers) == 1: print('marker', end=' ')
490 else: print('markers', end=' ')
491 print('instead of', nmarkers)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000492
Guido van Rossumd3166071993-05-24 14:16:22 +0000493class Aifc_write:
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000494 # Variables used in this class:
495 #
496 # These variables are user settable through appropriate methods
497 # of this class:
498 # _file -- the open file with methods write(), close(), tell(), seek()
499 # set through the __init__() method
500 # _comptype -- the AIFF-C compression type ('NONE' in AIFF)
501 # set through the setcomptype() or setparams() method
502 # _compname -- the human-readable AIFF-C compression type
503 # set through the setcomptype() or setparams() method
504 # _nchannels -- the number of audio channels
505 # set through the setnchannels() or setparams() method
506 # _sampwidth -- the number of bytes per audio sample
507 # set through the setsampwidth() or setparams() method
508 # _framerate -- the sampling frequency
509 # set through the setframerate() or setparams() method
510 # _nframes -- the number of audio frames written to the header
511 # set through the setnframes() or setparams() method
512 # _aifc -- whether we're writing an AIFF-C file or an AIFF file
513 # set through the aifc() method, reset through the
514 # aiff() method
515 #
516 # These variables are used internally only:
517 # _version -- the AIFF-C version number
518 # _comp -- the compressor from builtin module cl
519 # _nframeswritten -- the number of audio frames actually written
520 # _datalength -- the size of the audio samples written to the header
521 # _datawritten -- the size of the audio samples actually written
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000522
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000523 def __init__(self, f):
Georg Brandl2095cfe2008-06-07 19:01:03 +0000524 if isinstance(f, str):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000525 filename = f
Georg Brandl1a3284e2007-12-02 09:40:06 +0000526 f = builtins.open(f, 'wb')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000527 else:
528 # else, assume it is an open file object already
529 filename = '???'
530 self.initfp(f)
531 if filename[-5:] == '.aiff':
532 self._aifc = 0
533 else:
534 self._aifc = 1
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000535
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000536 def initfp(self, file):
537 self._file = file
538 self._version = _AIFC_version
Georg Brandl2095cfe2008-06-07 19:01:03 +0000539 self._comptype = b'NONE'
540 self._compname = b'not compressed'
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000541 self._convert = None
542 self._nchannels = 0
543 self._sampwidth = 0
544 self._framerate = 0
545 self._nframes = 0
546 self._nframeswritten = 0
547 self._datawritten = 0
548 self._datalength = 0
549 self._markers = []
550 self._marklength = 0
551 self._aifc = 1 # AIFF-C is default
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000552
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000553 def __del__(self):
Sandro Tosi70efbef2012-01-01 22:53:08 +0100554 self.close()
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000555
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000556 #
557 # User visible methods.
558 #
559 def aiff(self):
560 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000561 raise Error('cannot change parameters after starting to write')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000562 self._aifc = 0
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000563
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000564 def aifc(self):
565 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000566 raise Error('cannot change parameters after starting to write')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000567 self._aifc = 1
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000568
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000569 def setnchannels(self, nchannels):
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 nchannels < 1:
Collin Winterce36ad82007-08-30 01:19:48 +0000573 raise Error('bad # of channels')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000574 self._nchannels = nchannels
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000575
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000576 def getnchannels(self):
577 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000578 raise Error('number of channels not set')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000579 return self._nchannels
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000580
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000581 def setsampwidth(self, sampwidth):
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 sampwidth < 1 or sampwidth > 4:
Collin Winterce36ad82007-08-30 01:19:48 +0000585 raise Error('bad sample width')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000586 self._sampwidth = sampwidth
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000587
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000588 def getsampwidth(self):
589 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000590 raise Error('sample width not set')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000591 return self._sampwidth
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000592
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000593 def setframerate(self, framerate):
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 framerate <= 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000597 raise Error('bad frame rate')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000598 self._framerate = framerate
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000599
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000600 def getframerate(self):
601 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000602 raise Error('frame rate not set')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000603 return self._framerate
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000604
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000605 def setnframes(self, nframes):
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 self._nframes = nframes
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000609
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000610 def getnframes(self):
611 return self._nframeswritten
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000612
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000613 def setcomptype(self, comptype, compname):
614 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000615 raise Error('cannot change parameters after starting to write')
Georg Brandl2095cfe2008-06-07 19:01:03 +0000616 if comptype not in (b'NONE', b'ulaw', b'ULAW',
617 b'alaw', b'ALAW', b'G722'):
Collin Winterce36ad82007-08-30 01:19:48 +0000618 raise Error('unsupported compression type')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000619 self._comptype = comptype
620 self._compname = compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000621
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000622 def getcomptype(self):
623 return self._comptype
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000624
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000625 def getcompname(self):
626 return self._compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000627
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000628## def setversion(self, version):
629## if self._nframeswritten:
630## raise Error, 'cannot change parameters after starting to write'
631## self._version = version
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000632
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000633 def setparams(self, params):
634 nchannels, sampwidth, framerate, nframes, comptype, compname = params
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000635 if self._nframeswritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000636 raise Error('cannot change parameters after starting to write')
Georg Brandl2095cfe2008-06-07 19:01:03 +0000637 if comptype not in (b'NONE', b'ulaw', b'ULAW',
638 b'alaw', b'ALAW', b'G722'):
Collin Winterce36ad82007-08-30 01:19:48 +0000639 raise Error('unsupported compression type')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000640 self.setnchannels(nchannels)
641 self.setsampwidth(sampwidth)
642 self.setframerate(framerate)
643 self.setnframes(nframes)
644 self.setcomptype(comptype, compname)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000645
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000646 def getparams(self):
647 if not self._nchannels or not self._sampwidth or not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000648 raise Error('not all parameters set')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000649 return self._nchannels, self._sampwidth, self._framerate, \
650 self._nframes, self._comptype, self._compname
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000651
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000652 def setmark(self, id, pos, name):
653 if id <= 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000654 raise Error('marker ID must be > 0')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000655 if pos < 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000656 raise Error('marker position must be >= 0')
Sandro Tosi70efbef2012-01-01 22:53:08 +0100657 if not isinstance(name, bytes):
658 raise Error('marker name must be bytes')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000659 for i in range(len(self._markers)):
660 if id == self._markers[i][0]:
661 self._markers[i] = id, pos, name
662 return
663 self._markers.append((id, pos, name))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000664
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000665 def getmark(self, id):
666 for marker in self._markers:
667 if id == marker[0]:
668 return marker
Georg Brandl2095cfe2008-06-07 19:01:03 +0000669 raise Error('marker {0!r} does not exist'.format(id))
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000670
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000671 def getmarkers(self):
672 if len(self._markers) == 0:
673 return None
674 return self._markers
Tim Peters146965a2001-01-14 18:09:23 +0000675
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000676 def tell(self):
677 return self._nframeswritten
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000678
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000679 def writeframesraw(self, data):
680 self._ensure_header_written(len(data))
Georg Brandl2095cfe2008-06-07 19:01:03 +0000681 nframes = len(data) // (self._sampwidth * self._nchannels)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000682 if self._convert:
683 data = self._convert(data)
684 self._file.write(data)
685 self._nframeswritten = self._nframeswritten + nframes
686 self._datawritten = self._datawritten + len(data)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000687
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000688 def writeframes(self, data):
689 self.writeframesraw(data)
690 if self._nframeswritten != self._nframes or \
691 self._datalength != self._datawritten:
692 self._patchheader()
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000693
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000694 def close(self):
Sandro Tosi70efbef2012-01-01 22:53:08 +0100695 if self._file:
696 self._ensure_header_written(0)
697 if self._datawritten & 1:
698 # quick pad to even size
699 self._file.write(b'\x00')
700 self._datawritten = self._datawritten + 1
701 self._writemarkers()
702 if self._nframeswritten != self._nframes or \
703 self._datalength != self._datawritten or \
704 self._marklength:
705 self._patchheader()
706 # Prevent ref cycles
707 self._convert = None
708 self._file.close()
709 self._file = None
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000710
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000711 #
712 # Internal methods.
713 #
Sjoerd Mullender2a451411993-12-20 09:36:01 +0000714
Georg Brandl2095cfe2008-06-07 19:01:03 +0000715 def _lin2alaw(self, data):
716 import audioop
717 return audioop.lin2alaw(data, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000718
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000719 def _lin2ulaw(self, data):
720 import audioop
721 return audioop.lin2ulaw(data, 2)
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000722
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000723 def _lin2adpcm(self, data):
724 import audioop
725 if not hasattr(self, '_adpcmstate'):
726 self._adpcmstate = None
Georg Brandl2095cfe2008-06-07 19:01:03 +0000727 data, self._adpcmstate = audioop.lin2adpcm(data, 2, self._adpcmstate)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000728 return data
Sjoerd Mullender1f057541994-09-06 16:17:51 +0000729
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000730 def _ensure_header_written(self, datasize):
731 if not self._nframeswritten:
Sandro Tosibdd53542012-01-01 18:04:37 +0100732 if self._comptype in (b'ULAW', b'ulaw', b'ALAW', b'alaw', b'G722'):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000733 if not self._sampwidth:
734 self._sampwidth = 2
735 if self._sampwidth != 2:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000736 raise Error('sample width must be 2 when compressing '
Sandro Tosibdd53542012-01-01 18:04:37 +0100737 'with ulaw/ULAW, alaw/ALAW or G7.22 (ADPCM)')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000738 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000739 raise Error('# channels not specified')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000740 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000741 raise Error('sample width not specified')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000742 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000743 raise Error('sampling rate not specified')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000744 self._write_header(datasize)
Guido van Rossum52fc1f61993-06-17 12:38:10 +0000745
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000746 def _init_compression(self):
Georg Brandl2095cfe2008-06-07 19:01:03 +0000747 if self._comptype == b'G722':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000748 self._convert = self._lin2adpcm
Georg Brandl2095cfe2008-06-07 19:01:03 +0000749 elif self._comptype in (b'ulaw', b'ULAW'):
750 self._convert = self._lin2ulaw
751 elif self._comptype in (b'alaw', b'ALAW'):
752 self._convert = self._lin2alaw
Sjoerd Mullender43bf0bc1993-12-13 11:42:39 +0000753
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000754 def _write_header(self, initlength):
Georg Brandl2095cfe2008-06-07 19:01:03 +0000755 if self._aifc and self._comptype != b'NONE':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000756 self._init_compression()
Georg Brandl2095cfe2008-06-07 19:01:03 +0000757 self._file.write(b'FORM')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000758 if not self._nframes:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000759 self._nframes = initlength // (self._nchannels * self._sampwidth)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000760 self._datalength = self._nframes * self._nchannels * self._sampwidth
761 if self._datalength & 1:
762 self._datalength = self._datalength + 1
763 if self._aifc:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000764 if self._comptype in (b'ulaw', b'ULAW', b'alaw', b'ALAW'):
765 self._datalength = self._datalength // 2
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000766 if self._datalength & 1:
767 self._datalength = self._datalength + 1
Georg Brandl2095cfe2008-06-07 19:01:03 +0000768 elif self._comptype == b'G722':
769 self._datalength = (self._datalength + 3) // 4
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000770 if self._datalength & 1:
771 self._datalength = self._datalength + 1
772 self._form_length_pos = self._file.tell()
773 commlength = self._write_form_length(self._datalength)
774 if self._aifc:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000775 self._file.write(b'AIFC')
776 self._file.write(b'FVER')
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100777 _write_ulong(self._file, 4)
778 _write_ulong(self._file, self._version)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000779 else:
Georg Brandl2095cfe2008-06-07 19:01:03 +0000780 self._file.write(b'AIFF')
781 self._file.write(b'COMM')
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100782 _write_ulong(self._file, commlength)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000783 _write_short(self._file, self._nchannels)
784 self._nframes_pos = self._file.tell()
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100785 _write_ulong(self._file, self._nframes)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000786 _write_short(self._file, self._sampwidth * 8)
787 _write_float(self._file, self._framerate)
788 if self._aifc:
789 self._file.write(self._comptype)
790 _write_string(self._file, self._compname)
Georg Brandl2095cfe2008-06-07 19:01:03 +0000791 self._file.write(b'SSND')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000792 self._ssnd_length_pos = self._file.tell()
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100793 _write_ulong(self._file, self._datalength + 8)
794 _write_ulong(self._file, 0)
795 _write_ulong(self._file, 0)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000796
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000797 def _write_form_length(self, datalength):
798 if self._aifc:
799 commlength = 18 + 5 + len(self._compname)
800 if commlength & 1:
801 commlength = commlength + 1
802 verslength = 12
803 else:
804 commlength = 18
805 verslength = 0
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100806 _write_ulong(self._file, 4 + verslength + self._marklength + \
807 8 + commlength + 16 + datalength)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000808 return commlength
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000809
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000810 def _patchheader(self):
811 curpos = self._file.tell()
812 if self._datawritten & 1:
813 datalength = self._datawritten + 1
Georg Brandl2095cfe2008-06-07 19:01:03 +0000814 self._file.write(b'\x00')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000815 else:
816 datalength = self._datawritten
817 if datalength == self._datalength and \
818 self._nframes == self._nframeswritten and \
819 self._marklength == 0:
820 self._file.seek(curpos, 0)
821 return
822 self._file.seek(self._form_length_pos, 0)
823 dummy = self._write_form_length(datalength)
824 self._file.seek(self._nframes_pos, 0)
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100825 _write_ulong(self._file, self._nframeswritten)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000826 self._file.seek(self._ssnd_length_pos, 0)
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100827 _write_ulong(self._file, datalength + 8)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000828 self._file.seek(curpos, 0)
829 self._nframes = self._nframeswritten
830 self._datalength = datalength
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000831
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000832 def _writemarkers(self):
833 if len(self._markers) == 0:
834 return
Georg Brandl2095cfe2008-06-07 19:01:03 +0000835 self._file.write(b'MARK')
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000836 length = 2
837 for marker in self._markers:
838 id, pos, name = marker
839 length = length + len(name) + 1 + 6
840 if len(name) & 1 == 0:
841 length = length + 1
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100842 _write_ulong(self._file, length)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000843 self._marklength = length + 8
844 _write_short(self._file, len(self._markers))
845 for marker in self._markers:
846 id, pos, name = marker
847 _write_short(self._file, id)
Antoine Pitrou03757ec2012-01-17 17:13:04 +0100848 _write_ulong(self._file, pos)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000849 _write_string(self._file, name)
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000850
Fred Drake43161351999-06-22 21:23:23 +0000851def open(f, mode=None):
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000852 if mode is None:
853 if hasattr(f, 'mode'):
854 mode = f.mode
855 else:
856 mode = 'rb'
857 if mode in ('r', 'rb'):
858 return Aifc_read(f)
859 elif mode in ('w', 'wb'):
860 return Aifc_write(f)
861 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000862 raise Error("mode must be 'r', 'rb', 'w', or 'wb'")
Sjoerd Mullendereeabe7e1993-01-22 12:53:11 +0000863
Guido van Rossum7bc817d1993-12-17 15:25:27 +0000864openfp = open # B/W compatibility
Guido van Rossum36bb1811996-12-31 05:57:34 +0000865
866if __name__ == '__main__':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000867 import sys
868 if not sys.argv[1:]:
869 sys.argv.append('/usr/demos/data/audio/bach.aiff')
870 fn = sys.argv[1]
871 f = open(fn, 'r')
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000872 print("Reading", fn)
873 print("nchannels =", f.getnchannels())
874 print("nframes =", f.getnframes())
875 print("sampwidth =", f.getsampwidth())
876 print("framerate =", f.getframerate())
877 print("comptype =", f.getcomptype())
878 print("compname =", f.getcompname())
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000879 if sys.argv[2:]:
880 gn = sys.argv[2]
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000881 print("Writing", gn)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000882 g = open(gn, 'w')
883 g.setparams(f.getparams())
884 while 1:
885 data = f.readframes(1024)
886 if not data:
887 break
888 g.writeframes(data)
889 g.close()
890 f.close()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000891 print("Done.")