1 """Stuff to parse AIFF-C and AIFF files.
3 Unless explicitly stated otherwise, the description below is true
4 both for AIFF-C files and AIFF files.
6 An AIFF-C file has the following structure.
21 An AIFF file has the string "AIFF" instead of "AIFC".
23 A chunk consists of an identifier (4 bytes) followed by a size (4 bytes,
24 big endian order), followed by the data. The size field does not include
25 the size of the 8 byte header.
27 The following chunk types are recognized.
30 <version number of AIFF-C defining document> (AIFF-C only).
32 <# of markers> (2 bytes)
34 <marker ID> (2 bytes, must be > 0)
36 <marker name> ("pstring")
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
44 <compression type> (4 bytes)
45 <human-readable version of compression type> ("pstring")
47 <offset> (4 bytes, not used by this program)
48 <blocksize> (4 bytes, not used by this program)
51 A pstring consists of 1 byte length, a string of characters, and 0 or 1
52 byte pad to make the total length even.
57 f = aifc.open(file, 'r')
58 where file is either the name of a file or an open file pointer.
59 The open file pointer must have methods read(), seek(), and close().
60 In some types of audio files, if the setpos() method is not used,
61 the seek() method is not necessary.
63 This returns an instance of a class with the following public methods:
64 getnchannels() -- returns number of audio channels (1 for
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
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)
83 The position returned by tell(), the position given to setpos() and
84 the position of marks are all compatible and have nothing to do with
85 the actual position in the file.
86 The close() method is called automatically when the class instance
90 f = aifc.open(file, 'w')
91 where file is either the name of a file or an open file pointer.
92 The open file pointer must have methods write(), tell(), seek(), and
95 This 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
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())
112 -- write audio frames without pathing up the
115 -- write audio frames and patch up the file header
116 close() -- patch up the file header and close the
118 You should set the parameters before the first writeframesraw or
119 writeframes. The total number of frames does not need to be set,
120 but when it is set to the correct value, the header does not have to
122 It is best to first set all parameters, perhaps possibly the
123 compression type, and then write audio frames using writeframesraw.
124 When all frames have been written, either call writeframes('') or
125 close() to patch up the sizes in the header.
126 Marks can be added anytime. If there are any marks, ypu must call
127 close() after all frames have been written.
128 The close() method is called automatically when the class instance
131 When a file is opened with the extension '.aiff', an AIFF file is
132 written, otherwise an AIFF-C file is written. This default can be
133 changed by calling aiff() or aifc() before the first writeframes or
140 __all__
= ["Error","open","openfp"]
142 class Error(Exception):
145 _AIFC_version
= 0xA2805140L
# Version 1 of AIFF-C
147 def _read_long(file):
149 return struct
.unpack('>l', file.read(4))[0]
153 def _read_ulong(file):
155 return struct
.unpack('>L', file.read(4))[0]
159 def _read_short(file):
161 return struct
.unpack('>h', file.read(2))[0]
165 def _read_string(file):
166 length
= ord(file.read(1))
170 data
= file.read(length
)
175 _HUGE_VAL
= 1.79769313486231e+308 # See <limits.h>
177 def _read_float(f
): # 10 bytes
178 expon
= _read_short(f
) # 2 bytes
182 expon
= expon
+ 0x8000
183 himant
= _read_ulong(f
) # 4 bytes
184 lomant
= _read_ulong(f
) # 4 bytes
185 if expon
== himant
== lomant
== 0:
187 elif expon
== 0x7FFF:
190 expon
= expon
- 16383
191 f
= (himant
* 0x100000000L
+ lomant
) * pow(2.0, expon
- 63)
194 def _write_short(f
, x
):
195 f
.write(struct
.pack('>h', x
))
197 def _write_long(f
, x
):
198 f
.write(struct
.pack('>L', x
))
200 def _write_string(f
, s
):
202 raise ValueError("string exceeds maximum pstring length")
208 def _write_float(f
, x
):
220 fmant
, expon
= math
.frexp(x
)
221 if expon
> 16384 or fmant
>= 1: # Infinity or NaN
226 expon
= expon
+ 16382
227 if expon
< 0: # denormalized
228 fmant
= math
.ldexp(fmant
, expon
)
231 fmant
= math
.ldexp(fmant
, 32)
232 fsmant
= math
.floor(fmant
)
233 himant
= long(fsmant
)
234 fmant
= math
.ldexp(fmant
- fsmant
, 32)
235 fsmant
= math
.floor(fmant
)
236 lomant
= long(fsmant
)
237 _write_short(f
, expon
)
238 _write_long(f
, himant
)
239 _write_long(f
, lomant
)
241 from chunk
import Chunk
244 # Variables used in this class:
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()
265 # _soundpos -- the position in the audio stream
266 # available through the tell() method, set through the
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
279 def initfp(self
, file):
287 if chunk
.getname() != 'FORM':
288 raise Error
, 'file does not start with FORM id'
289 formdata
= chunk
.read(4)
290 if formdata
== 'AIFF':
292 elif formdata
== 'AIFC':
295 raise Error
, 'not an AIFF or AIFF-C file'
296 self
._comm
_chunk
_read
= 0
298 self
._ssnd
_seek
_needed
= 1
300 chunk
= Chunk(self
._file
)
303 chunkname
= chunk
.getname()
304 if chunkname
== 'COMM':
305 self
._read
_comm
_chunk
(chunk
)
306 self
._comm
_chunk
_read
= 1
307 elif chunkname
== 'SSND':
308 self
._ssnd
_chunk
= chunk
309 dummy
= chunk
.read(8)
310 self
._ssnd
_seek
_needed
= 0
311 elif chunkname
== 'FVER':
312 self
._version
= _read_ulong(chunk
)
313 elif chunkname
== 'MARK':
314 self
._readmark
(chunk
)
316 if not self
._comm
_chunk
_read
or not self
._ssnd
_chunk
:
317 raise Error
, 'COMM chunk and/or SSND chunk missing'
318 if self
._aifc
and self
._decomp
:
320 params
= [cl
.ORIGINAL_FORMAT
, 0,
321 cl
.BITS_PER_COMPONENT
, self
._sampwidth
* 8,
322 cl
.FRAME_RATE
, self
._framerate
]
323 if self
._nchannels
== 1:
325 elif self
._nchannels
== 2:
326 params
[1] = cl
.STEREO_INTERLEAVED
328 raise Error
, 'cannot compress more than 2 channels'
329 self
._decomp
.SetParams(params
)
331 def __init__(self
, f
):
332 if type(f
) == type(''):
333 f
= __builtin__
.open(f
, 'rb')
334 # else, assume it is an open file object already
338 # User visible methods.
344 self
._ssnd
_seek
_needed
= 1
349 self
._decomp
.CloseDecompressor()
354 return self
._soundpos
356 def getnchannels(self
):
357 return self
._nchannels
359 def getnframes(self
):
362 def getsampwidth(self
):
363 return self
._sampwidth
365 def getframerate(self
):
366 return self
._framerate
368 def getcomptype(self
):
369 return self
._comptype
371 def getcompname(self
):
372 return self
._compname
374 ## def getversion(self):
375 ## return self._version
378 return self
.getnchannels(), self
.getsampwidth(), \
379 self
.getframerate(), self
.getnframes(), \
380 self
.getcomptype(), self
.getcompname()
382 def getmarkers(self
):
383 if len(self
._markers
) == 0:
387 def getmark(self
, id):
388 for marker
in self
._markers
:
391 raise Error
, 'marker %r does not exist' % (id,)
393 def setpos(self
, pos
):
394 if pos
< 0 or pos
> self
._nframes
:
395 raise Error
, 'position not in range'
397 self
._ssnd
_seek
_needed
= 1
399 def readframes(self
, nframes
):
400 if self
._ssnd
_seek
_needed
:
401 self
._ssnd
_chunk
.seek(0)
402 dummy
= self
._ssnd
_chunk
.read(8)
403 pos
= self
._soundpos
* self
._framesize
405 self
._ssnd
_chunk
.seek(pos
+ 8)
406 self
._ssnd
_seek
_needed
= 0
409 data
= self
._ssnd
_chunk
.read(nframes
* self
._framesize
)
410 if self
._convert
and data
:
411 data
= self
._convert
(data
)
412 self
._soundpos
= self
._soundpos
+ len(data
) // (self
._nchannels
* self
._sampwidth
)
419 def _decomp_data(self
, data
):
421 dummy
= self
._decomp
.SetParam(cl
.FRAME_BUFFER_SIZE
,
423 return self
._decomp
.Decompress(len(data
) // self
._nchannels
,
426 def _ulaw2lin(self
, data
):
428 return audioop
.ulaw2lin(data
, 2)
430 def _adpcm2lin(self
, data
):
432 if not hasattr(self
, '_adpcmstate'):
434 self
._adpcmstate
= None
435 data
, self
._adpcmstate
= audioop
.adpcm2lin(data
, 2,
439 def _read_comm_chunk(self
, chunk
):
440 self
._nchannels
= _read_short(chunk
)
441 self
._nframes
= _read_long(chunk
)
442 self
._sampwidth
= (_read_short(chunk
) + 7) // 8
443 self
._framerate
= int(_read_float(chunk
))
444 self
._framesize
= self
._nchannels
* self
._sampwidth
446 #DEBUG: SGI's soundeditor produces a bad size :-(
448 if chunk
.chunksize
== 18:
450 print 'Warning: bad COMM chunk size'
453 self
._comptype
= chunk
.read(4)
456 length
= ord(chunk
.file.read(1))
459 chunk
.chunksize
= chunk
.chunksize
+ length
460 chunk
.file.seek(-1, 1)
462 self
._compname
= _read_string(chunk
)
463 if self
._comptype
!= 'NONE':
464 if self
._comptype
== 'G722':
470 self
._convert
= self
._adpcm
2lin
471 self
._framesize
= self
._framesize
// 4
473 # for ULAW and ALAW try Compression Library
477 if self
._comptype
== 'ULAW':
480 self
._convert
= self
._ulaw
2lin
481 self
._framesize
= self
._framesize
// 2
485 raise Error
, 'cannot read compressed AIFF-C files'
486 if self
._comptype
== 'ULAW':
487 scheme
= cl
.G711_ULAW
488 self
._framesize
= self
._framesize
// 2
489 elif self
._comptype
== 'ALAW':
490 scheme
= cl
.G711_ALAW
491 self
._framesize
= self
._framesize
// 2
493 raise Error
, 'unsupported compression type'
494 self
._decomp
= cl
.OpenDecompressor(scheme
)
495 self
._convert
= self
._decomp
_data
497 self
._comptype
= 'NONE'
498 self
._compname
= 'not compressed'
500 def _readmark(self
, chunk
):
501 nmarkers
= _read_short(chunk
)
502 # Some files appear to contain invalid counts.
503 # Cope with this by testing for EOF.
505 for i
in range(nmarkers
):
506 id = _read_short(chunk
)
507 pos
= _read_long(chunk
)
508 name
= _read_string(chunk
)
510 # some files appear to have
511 # dummy markers consisting of
512 # a position 0 and name ''
513 self
._markers
.append((id, pos
, name
))
515 print 'Warning: MARK chunk contains only',
516 print len(self
._markers
),
517 if len(self
._markers
) == 1: print 'marker',
518 else: print 'markers',
519 print 'instead of', nmarkers
522 # Variables used in this class:
524 # These variables are user settable through appropriate methods
526 # _file -- the open file with methods write(), close(), tell(), seek()
527 # set through the __init__() method
528 # _comptype -- the AIFF-C compression type ('NONE' in AIFF)
529 # set through the setcomptype() or setparams() method
530 # _compname -- the human-readable AIFF-C compression type
531 # set through the setcomptype() or setparams() method
532 # _nchannels -- the number of audio channels
533 # set through the setnchannels() or setparams() method
534 # _sampwidth -- the number of bytes per audio sample
535 # set through the setsampwidth() or setparams() method
536 # _framerate -- the sampling frequency
537 # set through the setframerate() or setparams() method
538 # _nframes -- the number of audio frames written to the header
539 # set through the setnframes() or setparams() method
540 # _aifc -- whether we're writing an AIFF-C file or an AIFF file
541 # set through the aifc() method, reset through the
544 # These variables are used internally only:
545 # _version -- the AIFF-C version number
546 # _comp -- the compressor from builtin module cl
547 # _nframeswritten -- the number of audio frames actually written
548 # _datalength -- the size of the audio samples written to the header
549 # _datawritten -- the size of the audio samples actually written
551 def __init__(self
, f
):
552 if type(f
) == type(''):
554 f
= __builtin__
.open(f
, 'wb')
556 # else, assume it is an open file object already
559 if filename
[-5:] == '.aiff':
564 def initfp(self
, file):
566 self
._version
= _AIFC_version
567 self
._comptype
= 'NONE'
568 self
._compname
= 'not compressed'
575 self
._nframeswritten
= 0
576 self
._datawritten
= 0
580 self
._aifc
= 1 # AIFF-C is default
587 # User visible methods.
590 if self
._nframeswritten
:
591 raise Error
, 'cannot change parameters after starting to write'
595 if self
._nframeswritten
:
596 raise Error
, 'cannot change parameters after starting to write'
599 def setnchannels(self
, nchannels
):
600 if self
._nframeswritten
:
601 raise Error
, 'cannot change parameters after starting to write'
603 raise Error
, 'bad # of channels'
604 self
._nchannels
= nchannels
606 def getnchannels(self
):
607 if not self
._nchannels
:
608 raise Error
, 'number of channels not set'
609 return self
._nchannels
611 def setsampwidth(self
, sampwidth
):
612 if self
._nframeswritten
:
613 raise Error
, 'cannot change parameters after starting to write'
614 if sampwidth
< 1 or sampwidth
> 4:
615 raise Error
, 'bad sample width'
616 self
._sampwidth
= sampwidth
618 def getsampwidth(self
):
619 if not self
._sampwidth
:
620 raise Error
, 'sample width not set'
621 return self
._sampwidth
623 def setframerate(self
, framerate
):
624 if self
._nframeswritten
:
625 raise Error
, 'cannot change parameters after starting to write'
627 raise Error
, 'bad frame rate'
628 self
._framerate
= framerate
630 def getframerate(self
):
631 if not self
._framerate
:
632 raise Error
, 'frame rate not set'
633 return self
._framerate
635 def setnframes(self
, nframes
):
636 if self
._nframeswritten
:
637 raise Error
, 'cannot change parameters after starting to write'
638 self
._nframes
= nframes
640 def getnframes(self
):
641 return self
._nframeswritten
643 def setcomptype(self
, comptype
, compname
):
644 if self
._nframeswritten
:
645 raise Error
, 'cannot change parameters after starting to write'
646 if comptype
not in ('NONE', 'ULAW', 'ALAW', 'G722'):
647 raise Error
, 'unsupported compression type'
648 self
._comptype
= comptype
649 self
._compname
= compname
651 def getcomptype(self
):
652 return self
._comptype
654 def getcompname(self
):
655 return self
._compname
657 ## def setversion(self, version):
658 ## if self._nframeswritten:
659 ## raise Error, 'cannot change parameters after starting to write'
660 ## self._version = version
662 def setparams(self
, info
):
663 nchannels
, sampwidth
, framerate
, nframes
, comptype
, compname
= info
664 if self
._nframeswritten
:
665 raise Error
, 'cannot change parameters after starting to write'
666 if comptype
not in ('NONE', 'ULAW', 'ALAW', 'G722'):
667 raise Error
, 'unsupported compression type'
668 self
.setnchannels(nchannels
)
669 self
.setsampwidth(sampwidth
)
670 self
.setframerate(framerate
)
671 self
.setnframes(nframes
)
672 self
.setcomptype(comptype
, compname
)
675 if not self
._nchannels
or not self
._sampwidth
or not self
._framerate
:
676 raise Error
, 'not all parameters set'
677 return self
._nchannels
, self
._sampwidth
, self
._framerate
, \
678 self
._nframes
, self
._comptype
, self
._compname
680 def setmark(self
, id, pos
, name
):
682 raise Error
, 'marker ID must be > 0'
684 raise Error
, 'marker position must be >= 0'
685 if type(name
) != type(''):
686 raise Error
, 'marker name must be a string'
687 for i
in range(len(self
._markers
)):
688 if id == self
._markers
[i
][0]:
689 self
._markers
[i
] = id, pos
, name
691 self
._markers
.append((id, pos
, name
))
693 def getmark(self
, id):
694 for marker
in self
._markers
:
697 raise Error
, 'marker %r does not exist' % (id,)
699 def getmarkers(self
):
700 if len(self
._markers
) == 0:
705 return self
._nframeswritten
707 def writeframesraw(self
, data
):
708 self
._ensure
_header
_written
(len(data
))
709 nframes
= len(data
) // (self
._sampwidth
* self
._nchannels
)
711 data
= self
._convert
(data
)
712 self
._file
.write(data
)
713 self
._nframeswritten
= self
._nframeswritten
+ nframes
714 self
._datawritten
= self
._datawritten
+ len(data
)
716 def writeframes(self
, data
):
717 self
.writeframesraw(data
)
718 if self
._nframeswritten
!= self
._nframes
or \
719 self
._datalength
!= self
._datawritten
:
723 self
._ensure
_header
_written
(0)
724 if self
._datawritten
& 1:
725 # quick pad to even size
726 self
._file
.write(chr(0))
727 self
._datawritten
= self
._datawritten
+ 1
729 if self
._nframeswritten
!= self
._nframes
or \
730 self
._datalength
!= self
._datawritten
or \
734 self
._comp
.CloseCompressor()
744 def _comp_data(self
, data
):
746 dummy
= self
._comp
.SetParam(cl
.FRAME_BUFFER_SIZE
, len(data
))
747 dummy
= self
._comp
.SetParam(cl
.COMPRESSED_BUFFER_SIZE
, len(data
))
748 return self
._comp
.Compress(self
._nframes
, data
)
750 def _lin2ulaw(self
, data
):
752 return audioop
.lin2ulaw(data
, 2)
754 def _lin2adpcm(self
, data
):
756 if not hasattr(self
, '_adpcmstate'):
757 self
._adpcmstate
= None
758 data
, self
._adpcmstate
= audioop
.lin2adpcm(data
, 2,
762 def _ensure_header_written(self
, datasize
):
763 if not self
._nframeswritten
:
764 if self
._comptype
in ('ULAW', 'ALAW'):
765 if not self
._sampwidth
:
767 if self
._sampwidth
!= 2:
768 raise Error
, 'sample width must be 2 when compressing with ULAW or ALAW'
769 if self
._comptype
== 'G722':
770 if not self
._sampwidth
:
772 if self
._sampwidth
!= 2:
773 raise Error
, 'sample width must be 2 when compressing with G7.22 (ADPCM)'
774 if not self
._nchannels
:
775 raise Error
, '# channels not specified'
776 if not self
._sampwidth
:
777 raise Error
, 'sample width not specified'
778 if not self
._framerate
:
779 raise Error
, 'sampling rate not specified'
780 self
._write
_header
(datasize
)
782 def _init_compression(self
):
783 if self
._comptype
== 'G722':
784 self
._convert
= self
._lin
2adpcm
789 if self
._comptype
== 'ULAW':
792 self
._convert
= self
._lin
2ulaw
796 raise Error
, 'cannot write compressed AIFF-C files'
797 if self
._comptype
== 'ULAW':
798 scheme
= cl
.G711_ULAW
799 elif self
._comptype
== 'ALAW':
800 scheme
= cl
.G711_ALAW
802 raise Error
, 'unsupported compression type'
803 self
._comp
= cl
.OpenCompressor(scheme
)
804 params
= [cl
.ORIGINAL_FORMAT
, 0,
805 cl
.BITS_PER_COMPONENT
, self
._sampwidth
* 8,
806 cl
.FRAME_RATE
, self
._framerate
,
807 cl
.FRAME_BUFFER_SIZE
, 100,
808 cl
.COMPRESSED_BUFFER_SIZE
, 100]
809 if self
._nchannels
== 1:
811 elif self
._nchannels
== 2:
812 params
[1] = cl
.STEREO_INTERLEAVED
814 raise Error
, 'cannot compress more than 2 channels'
815 self
._comp
.SetParams(params
)
816 # the compressor produces a header which we ignore
817 dummy
= self
._comp
.Compress(0, '')
818 self
._convert
= self
._comp
_data
820 def _write_header(self
, initlength
):
821 if self
._aifc
and self
._comptype
!= 'NONE':
822 self
._init
_compression
()
823 self
._file
.write('FORM')
824 if not self
._nframes
:
825 self
._nframes
= initlength
// (self
._nchannels
* self
._sampwidth
)
826 self
._datalength
= self
._nframes
* self
._nchannels
* self
._sampwidth
827 if self
._datalength
& 1:
828 self
._datalength
= self
._datalength
+ 1
830 if self
._comptype
in ('ULAW', 'ALAW'):
831 self
._datalength
= self
._datalength
// 2
832 if self
._datalength
& 1:
833 self
._datalength
= self
._datalength
+ 1
834 elif self
._comptype
== 'G722':
835 self
._datalength
= (self
._datalength
+ 3) // 4
836 if self
._datalength
& 1:
837 self
._datalength
= self
._datalength
+ 1
838 self
._form
_length
_pos
= self
._file
.tell()
839 commlength
= self
._write
_form
_length
(self
._datalength
)
841 self
._file
.write('AIFC')
842 self
._file
.write('FVER')
843 _write_long(self
._file
, 4)
844 _write_long(self
._file
, self
._version
)
846 self
._file
.write('AIFF')
847 self
._file
.write('COMM')
848 _write_long(self
._file
, commlength
)
849 _write_short(self
._file
, self
._nchannels
)
850 self
._nframes
_pos
= self
._file
.tell()
851 _write_long(self
._file
, self
._nframes
)
852 _write_short(self
._file
, self
._sampwidth
* 8)
853 _write_float(self
._file
, self
._framerate
)
855 self
._file
.write(self
._comptype
)
856 _write_string(self
._file
, self
._compname
)
857 self
._file
.write('SSND')
858 self
._ssnd
_length
_pos
= self
._file
.tell()
859 _write_long(self
._file
, self
._datalength
+ 8)
860 _write_long(self
._file
, 0)
861 _write_long(self
._file
, 0)
863 def _write_form_length(self
, datalength
):
865 commlength
= 18 + 5 + len(self
._compname
)
867 commlength
= commlength
+ 1
872 _write_long(self
._file
, 4 + verslength
+ self
._marklength
+ \
873 8 + commlength
+ 16 + datalength
)
876 def _patchheader(self
):
877 curpos
= self
._file
.tell()
878 if self
._datawritten
& 1:
879 datalength
= self
._datawritten
+ 1
880 self
._file
.write(chr(0))
882 datalength
= self
._datawritten
883 if datalength
== self
._datalength
and \
884 self
._nframes
== self
._nframeswritten
and \
885 self
._marklength
== 0:
886 self
._file
.seek(curpos
, 0)
888 self
._file
.seek(self
._form
_length
_pos
, 0)
889 dummy
= self
._write
_form
_length
(datalength
)
890 self
._file
.seek(self
._nframes
_pos
, 0)
891 _write_long(self
._file
, self
._nframeswritten
)
892 self
._file
.seek(self
._ssnd
_length
_pos
, 0)
893 _write_long(self
._file
, datalength
+ 8)
894 self
._file
.seek(curpos
, 0)
895 self
._nframes
= self
._nframeswritten
896 self
._datalength
= datalength
898 def _writemarkers(self
):
899 if len(self
._markers
) == 0:
901 self
._file
.write('MARK')
903 for marker
in self
._markers
:
904 id, pos
, name
= marker
905 length
= length
+ len(name
) + 1 + 6
906 if len(name
) & 1 == 0:
908 _write_long(self
._file
, length
)
909 self
._marklength
= length
+ 8
910 _write_short(self
._file
, len(self
._markers
))
911 for marker
in self
._markers
:
912 id, pos
, name
= marker
913 _write_short(self
._file
, id)
914 _write_long(self
._file
, pos
)
915 _write_string(self
._file
, name
)
917 def open(f
, mode
=None):
919 if hasattr(f
, 'mode'):
923 if mode
in ('r', 'rb'):
925 elif mode
in ('w', 'wb'):
928 raise Error
, "mode must be 'r', 'rb', 'w', or 'wb'"
930 openfp
= open # B/W compatibility
932 if __name__
== '__main__':
935 sys
.argv
.append('/usr/demos/data/audio/bach.aiff')
939 print "nchannels =", f
.getnchannels()
940 print "nframes =", f
.getnframes()
941 print "sampwidth =", f
.getsampwidth()
942 print "framerate =", f
.getframerate()
943 print "comptype =", f
.getcomptype()
944 print "compname =", f
.getcompname()
949 g
.setparams(f
.getparams())
951 data
= f
.readframes(1024)