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 postion 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
142 _AIFC_version
= 0xA2805140 # Version 1 of AIFF-C
144 _skiplist
= 'COMT', 'INST', 'MIDI', 'AESD', \
145 'APPL', 'NAME', 'AUTH', '(c) ', 'ANNO'
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
179 expon
= _read_short(f
) # 2 bytes
183 expon
= expon
+ 0x8000
184 himant
= _read_ulong(f
) # 4 bytes
185 lomant
= _read_ulong(f
) # 4 bytes
186 if expon
== himant
== lomant
== 0:
188 elif expon
== 0x7FFF:
191 expon
= expon
- 16383
192 f
= (himant
* 0x100000000L
+ lomant
) * pow(2.0, expon
- 63)
195 def _write_short(f
, x
):
196 f
.write(struct
.pack('>h', x
))
198 def _write_long(f
, x
):
199 f
.write(struct
.pack('>L', x
))
201 def _write_string(f
, s
):
207 def _write_float(f
, x
):
219 fmant
, expon
= math
.frexp(x
)
220 if expon
> 16384 or fmant
>= 1: # Infinity or NaN
225 expon
= expon
+ 16382
226 if expon
< 0: # denormalized
227 fmant
= math
.ldexp(fmant
, expon
)
230 fmant
= math
.ldexp(fmant
, 32)
231 fsmant
= math
.floor(fmant
)
232 himant
= long(fsmant
)
233 fmant
= math
.ldexp(fmant
- fsmant
, 32)
234 fsmant
= math
.floor(fmant
)
235 lomant
= long(fsmant
)
236 _write_short(f
, expon
)
237 _write_long(f
, himant
)
238 _write_long(f
, lomant
)
240 from chunk
import Chunk
243 # Variables used in this class:
245 # These variables are available to the user though appropriate
246 # methods of this class:
247 # _file -- the open file with methods read(), close(), and seek()
248 # set through the __init__() method
249 # _nchannels -- the number of audio channels
250 # available through the getnchannels() method
251 # _nframes -- the number of audio frames
252 # available through the getnframes() method
253 # _sampwidth -- the number of bytes per audio sample
254 # available through the getsampwidth() method
255 # _framerate -- the sampling frequency
256 # available through the getframerate() method
257 # _comptype -- the AIFF-C compression type ('NONE' if AIFF)
258 # available through the getcomptype() method
259 # _compname -- the human-readable AIFF-C compression type
260 # available through the getcomptype() method
261 # _markers -- the marks in the audio file
262 # available through the getmarkers() and getmark()
264 # _soundpos -- the position in the audio stream
265 # available through the tell() method, set through the
268 # These variables are used internally only:
269 # _version -- the AIFF-C version number
270 # _decomp -- the decompressor from builtin module cl
271 # _comm_chunk_read -- 1 iff the COMM chunk has been read
272 # _aifc -- 1 iff reading an AIFF-C file
273 # _ssnd_seek_needed -- 1 iff positioned correctly in audio
274 # file for readframes()
275 # _ssnd_chunk -- instantiation of a chunk class for the SSND chunk
276 # _framesize -- size of one frame in the file
278 def initfp(self
, file):
284 self
._file
= Chunk(file)
285 if self
._file
.getname() != 'FORM':
286 raise Error
, 'file does not start with FORM id'
287 formdata
= self
._file
.read(4)
288 if formdata
== 'AIFF':
290 elif formdata
== 'AIFC':
293 raise Error
, 'not an AIFF or AIFF-C file'
294 self
._comm
_chunk
_read
= 0
296 self
._ssnd
_seek
_needed
= 1
298 chunk
= Chunk(self
._file
)
301 chunkname
= chunk
.getname()
302 if chunkname
== 'COMM':
303 self
._read
_comm
_chunk
(chunk
)
304 self
._comm
_chunk
_read
= 1
305 elif chunkname
== 'SSND':
306 self
._ssnd
_chunk
= chunk
307 dummy
= chunk
.read(8)
308 self
._ssnd
_seek
_needed
= 0
309 elif chunkname
== 'FVER':
310 self
._version
= _read_long(chunk
)
311 elif chunkname
== 'MARK':
312 self
._readmark
(chunk
)
313 elif chunkname
in _skiplist
:
316 raise Error
, 'unrecognized chunk type '+chunk
.chunkname
318 if not self
._comm
_chunk
_read
or not self
._ssnd
_chunk
:
319 raise Error
, 'COMM chunk and/or SSND chunk missing'
320 if self
._aifc
and self
._decomp
:
322 params
= [cl
.ORIGINAL_FORMAT
, 0,
323 cl
.BITS_PER_COMPONENT
, self
._sampwidth
* 8,
324 cl
.FRAME_RATE
, self
._framerate
]
325 if self
._nchannels
== 1:
327 elif self
._nchannels
== 2:
328 params
[1] = cl
.STEREO_INTERLEAVED
330 raise Error
, 'cannot compress more than 2 channels'
331 self
._decomp
.SetParams(params
)
333 def __init__(self
, f
):
334 if type(f
) == type(''):
335 f
= __builtin__
.open(f
, 'rb')
336 # else, assume it is an open file object already
340 # User visible methods.
346 self
._ssnd
_seek
_needed
= 1
351 self
._decomp
.CloseDecompressor()
356 return self
._soundpos
358 def getnchannels(self
):
359 return self
._nchannels
361 def getnframes(self
):
364 def getsampwidth(self
):
365 return self
._sampwidth
367 def getframerate(self
):
368 return self
._framerate
370 def getcomptype(self
):
371 return self
._comptype
373 def getcompname(self
):
374 return self
._compname
376 ## def getversion(self):
377 ## return self._version
380 return self
.getnchannels(), self
.getsampwidth(), \
381 self
.getframerate(), self
.getnframes(), \
382 self
.getcomptype(), self
.getcompname()
384 def getmarkers(self
):
385 if len(self
._markers
) == 0:
389 def getmark(self
, id):
390 for marker
in self
._markers
:
393 raise Error
, 'marker ' + `
id`
+ ' does not exist'
395 def setpos(self
, pos
):
396 if pos
< 0 or pos
> self
._nframes
:
397 raise Error
, 'position not in range'
399 self
._ssnd
_seek
_needed
= 1
401 def readframes(self
, nframes
):
402 if self
._ssnd
_seek
_needed
:
403 self
._ssnd
_chunk
.seek(0)
404 dummy
= self
._ssnd
_chunk
.read(8)
405 pos
= self
._soundpos
* self
._framesize
407 self
._ssnd
_chunk
.setpos(pos
+ 8)
408 self
._ssnd
_seek
_needed
= 0
411 data
= self
._ssnd
_chunk
.read(nframes
* self
._framesize
)
412 if self
._convert
and data
:
413 data
= self
._convert
(data
)
414 self
._soundpos
= self
._soundpos
+ len(data
) / (self
._nchannels
* self
._sampwidth
)
421 def _decomp_data(self
, data
):
423 dummy
= self
._decomp
.SetParam(cl
.FRAME_BUFFER_SIZE
,
425 return self
._decomp
.Decompress(len(data
) / self
._nchannels
,
428 def _ulaw2lin(self
, data
):
430 return audioop
.ulaw2lin(data
, 2)
432 def _adpcm2lin(self
, data
):
434 if not hasattr(self
, '_adpcmstate'):
436 self
._adpcmstate
= None
437 data
, self
._adpcmstate
= audioop
.adpcm2lin(data
, 2,
441 def _read_comm_chunk(self
, chunk
):
442 self
._nchannels
= _read_short(chunk
)
443 self
._nframes
= _read_long(chunk
)
444 self
._sampwidth
= (_read_short(chunk
) + 7) / 8
445 self
._framerate
= int(_read_float(chunk
))
446 self
._framesize
= self
._nchannels
* self
._sampwidth
448 #DEBUG: SGI's soundeditor produces a bad size :-(
450 if chunk
.chunksize
== 18:
452 print 'Warning: bad COMM chunk size'
455 self
._comptype
= chunk
.read(4)
458 length
= ord(chunk
.file.read(1))
461 chunk
.chunksize
= chunk
.chunksize
+ length
462 chunk
.file.seek(-1, 1)
464 self
._compname
= _read_string(chunk
)
465 if self
._comptype
!= 'NONE':
466 if self
._comptype
== 'G722':
472 self
._convert
= self
._adpcm
2lin
473 self
._framesize
= self
._framesize
/ 4
475 # for ULAW and ALAW try Compression Library
479 if self
._comptype
== 'ULAW':
482 self
._convert
= self
._ulaw
2lin
483 self
._framesize
= self
._framesize
/ 2
487 raise Error
, 'cannot read compressed AIFF-C files'
488 if self
._comptype
== 'ULAW':
489 scheme
= cl
.G711_ULAW
490 self
._framesize
= self
._framesize
/ 2
491 elif self
._comptype
== 'ALAW':
492 scheme
= cl
.G711_ALAW
493 self
._framesize
= self
._framesize
/ 2
495 raise Error
, 'unsupported compression type'
496 self
._decomp
= cl
.OpenDecompressor(scheme
)
497 self
._convert
= self
._decomp
_data
499 self
._comptype
= 'NONE'
500 self
._compname
= 'not compressed'
502 def _readmark(self
, chunk
):
503 nmarkers
= _read_short(chunk
)
504 # Some files appear to contain invalid counts.
505 # Cope with this by testing for EOF.
507 for i
in range(nmarkers
):
508 id = _read_short(chunk
)
509 pos
= _read_long(chunk
)
510 name
= _read_string(chunk
)
512 # some files appear to have
513 # dummy markers consisting of
514 # a position 0 and name ''
515 self
._markers
.append((id, pos
, name
))
517 print 'Warning: MARK chunk contains only',
518 print len(self
._markers
),
519 if len(self
._markers
) == 1: print 'marker',
520 else: print 'markers',
521 print 'instead of', nmarkers
524 # Variables used in this class:
526 # These variables are user settable through appropriate methods
528 # _file -- the open file with methods write(), close(), tell(), seek()
529 # set through the __init__() method
530 # _comptype -- the AIFF-C compression type ('NONE' in AIFF)
531 # set through the setcomptype() or setparams() method
532 # _compname -- the human-readable AIFF-C compression type
533 # set through the setcomptype() or setparams() method
534 # _nchannels -- the number of audio channels
535 # set through the setnchannels() or setparams() method
536 # _sampwidth -- the number of bytes per audio sample
537 # set through the setsampwidth() or setparams() method
538 # _framerate -- the sampling frequency
539 # set through the setframerate() or setparams() method
540 # _nframes -- the number of audio frames written to the header
541 # set through the setnframes() or setparams() method
542 # _aifc -- whether we're writing an AIFF-C file or an AIFF file
543 # set through the aifc() method, reset through the
546 # These variables are used internally only:
547 # _version -- the AIFF-C version number
548 # _comp -- the compressor from builtin module cl
549 # _nframeswritten -- the number of audio frames actually written
550 # _datalength -- the size of the audio samples written to the header
551 # _datawritten -- the size of the audio samples actually written
553 def __init__(self
, f
):
554 if type(f
) == type(''):
556 f
= __builtin__
.open(f
, 'wb')
558 # else, assume it is an open file object already
561 if filename
[-5:] == '.aiff':
566 def initfp(self
, file):
568 self
._version
= _AIFC_version
569 self
._comptype
= 'NONE'
570 self
._compname
= 'not compressed'
577 self
._nframeswritten
= 0
578 self
._datawritten
= 0
582 self
._aifc
= 1 # AIFF-C is default
589 # User visible methods.
592 if self
._nframeswritten
:
593 raise Error
, 'cannot change parameters after starting to write'
597 if self
._nframeswritten
:
598 raise Error
, 'cannot change parameters after starting to write'
601 def setnchannels(self
, nchannels
):
602 if self
._nframeswritten
:
603 raise Error
, 'cannot change parameters after starting to write'
605 raise Error
, 'bad # of channels'
606 self
._nchannels
= nchannels
608 def getnchannels(self
):
609 if not self
._nchannels
:
610 raise Error
, 'number of channels not set'
611 return self
._nchannels
613 def setsampwidth(self
, sampwidth
):
614 if self
._nframeswritten
:
615 raise Error
, 'cannot change parameters after starting to write'
616 if sampwidth
< 1 or sampwidth
> 4:
617 raise Error
, 'bad sample width'
618 self
._sampwidth
= sampwidth
620 def getsampwidth(self
):
621 if not self
._sampwidth
:
622 raise Error
, 'sample width not set'
623 return self
._sampwidth
625 def setframerate(self
, framerate
):
626 if self
._nframeswritten
:
627 raise Error
, 'cannot change parameters after starting to write'
629 raise Error
, 'bad frame rate'
630 self
._framerate
= framerate
632 def getframerate(self
):
633 if not self
._framerate
:
634 raise Error
, 'frame rate not set'
635 return self
._framerate
637 def setnframes(self
, nframes
):
638 if self
._nframeswritten
:
639 raise Error
, 'cannot change parameters after starting to write'
640 self
._nframes
= nframes
642 def getnframes(self
):
643 return self
._nframeswritten
645 def setcomptype(self
, comptype
, compname
):
646 if self
._nframeswritten
:
647 raise Error
, 'cannot change parameters after starting to write'
648 if comptype
not in ('NONE', 'ULAW', 'ALAW', 'G722'):
649 raise Error
, 'unsupported compression type'
650 self
._comptype
= comptype
651 self
._compname
= compname
653 def getcomptype(self
):
654 return self
._comptype
656 def getcompname(self
):
657 return self
._compname
659 ## def setversion(self, version):
660 ## if self._nframeswritten:
661 ## raise Error, 'cannot change parameters after starting to write'
662 ## self._version = version
664 def setparams(self
, (nchannels
, sampwidth
, framerate
, nframes
, comptype
, compname
)):
665 if self
._nframeswritten
:
666 raise Error
, 'cannot change parameters after starting to write'
667 if comptype
not in ('NONE', 'ULAW', 'ALAW', 'G722'):
668 raise Error
, 'unsupported compression type'
669 self
.setnchannels(nchannels
)
670 self
.setsampwidth(sampwidth
)
671 self
.setframerate(framerate
)
672 self
.setnframes(nframes
)
673 self
.setcomptype(comptype
, compname
)
676 if not self
._nchannels
or not self
._sampwidth
or not self
._framerate
:
677 raise Error
, 'not all parameters set'
678 return self
._nchannels
, self
._sampwidth
, self
._framerate
, \
679 self
._nframes
, self
._comptype
, self
._compname
681 def setmark(self
, id, pos
, name
):
683 raise Error
, 'marker ID must be > 0'
685 raise Error
, 'marker position must be >= 0'
686 if type(name
) != type(''):
687 raise Error
, 'marker name must be a string'
688 for i
in range(len(self
._markers
)):
689 if id == self
._markers
[i
][0]:
690 self
._markers
[i
] = id, pos
, name
692 self
._markers
.append((id, pos
, name
))
694 def getmark(self
, id):
695 for marker
in self
._markers
:
698 raise Error
, 'marker ' + `
id`
+ ' does not exist'
700 def getmarkers(self
):
701 if len(self
._markers
) == 0:
706 return self
._nframeswritten
708 def writeframesraw(self
, data
):
709 self
._ensure
_header
_written
(len(data
))
710 nframes
= len(data
) / (self
._sampwidth
* self
._nchannels
)
712 data
= self
._convert
(data
)
713 self
._file
.write(data
)
714 self
._nframeswritten
= self
._nframeswritten
+ nframes
715 self
._datawritten
= self
._datawritten
+ len(data
)
717 def writeframes(self
, data
):
718 self
.writeframesraw(data
)
719 if self
._nframeswritten
!= self
._nframes
or \
720 self
._datalength
!= self
._datawritten
:
724 self
._ensure
_header
_written
(0)
725 if self
._datawritten
& 1:
726 # quick pad to even size
727 self
._file
.write(chr(0))
728 self
._datawritten
= self
._datawritten
+ 1
730 if self
._nframeswritten
!= self
._nframes
or \
731 self
._datalength
!= self
._datawritten
or \
735 self
._comp
.CloseCompressor()
744 def _comp_data(self
, data
):
746 dum
= self
._comp
.SetParam(cl
.FRAME_BUFFER_SIZE
, len(data
))
747 dum
= 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':
785 self
._convert
= self
._lin
2adpcm
790 if self
._comptype
== 'ULAW':
793 self
._convert
= self
._lin
2ulaw
797 raise Error
, 'cannot write compressed AIFF-C files'
798 if self
._comptype
== 'ULAW':
799 scheme
= cl
.G711_ULAW
800 elif self
._comptype
== 'ALAW':
801 scheme
= cl
.G711_ALAW
803 raise Error
, 'unsupported compression type'
804 self
._comp
= cl
.OpenCompressor(scheme
)
805 params
= [cl
.ORIGINAL_FORMAT
, 0,
806 cl
.BITS_PER_COMPONENT
, self
._sampwidth
* 8,
807 cl
.FRAME_RATE
, self
._framerate
,
808 cl
.FRAME_BUFFER_SIZE
, 100,
809 cl
.COMPRESSED_BUFFER_SIZE
, 100]
810 if self
._nchannels
== 1:
812 elif self
._nchannels
== 2:
813 params
[1] = cl
.STEREO_INTERLEAVED
815 raise Error
, 'cannot compress more than 2 channels'
816 self
._comp
.SetParams(params
)
817 # the compressor produces a header which we ignore
818 dummy
= self
._comp
.Compress(0, '')
819 self
._convert
= self
._comp
_data
821 def _write_header(self
, initlength
):
822 if self
._aifc
and self
._comptype
!= 'NONE':
823 self
._init
_compression
()
824 self
._file
.write('FORM')
825 if not self
._nframes
:
826 self
._nframes
= initlength
/ (self
._nchannels
* self
._sampwidth
)
827 self
._datalength
= self
._nframes
* self
._nchannels
* self
._sampwidth
828 if self
._datalength
& 1:
829 self
._datalength
= self
._datalength
+ 1
831 if self
._comptype
in ('ULAW', 'ALAW'):
832 self
._datalength
= self
._datalength
/ 2
833 if self
._datalength
& 1:
834 self
._datalength
= self
._datalength
+ 1
835 elif self
._comptype
== 'G722':
836 self
._datalength
= (self
._datalength
+ 3) / 4
837 if self
._datalength
& 1:
838 self
._datalength
= self
._datalength
+ 1
839 self
._form
_length
_pos
= self
._file
.tell()
840 commlength
= self
._write
_form
_length
(self
._datalength
)
842 self
._file
.write('AIFC')
843 self
._file
.write('FVER')
844 _write_long(self
._file
, 4)
845 _write_long(self
._file
, self
._version
)
847 self
._file
.write('AIFF')
848 self
._file
.write('COMM')
849 _write_long(self
._file
, commlength
)
850 _write_short(self
._file
, self
._nchannels
)
851 self
._nframes
_pos
= self
._file
.tell()
852 _write_long(self
._file
, self
._nframes
)
853 _write_short(self
._file
, self
._sampwidth
* 8)
854 _write_float(self
._file
, self
._framerate
)
856 self
._file
.write(self
._comptype
)
857 _write_string(self
._file
, self
._compname
)
858 self
._file
.write('SSND')
859 self
._ssnd
_length
_pos
= self
._file
.tell()
860 _write_long(self
._file
, self
._datalength
+ 8)
861 _write_long(self
._file
, 0)
862 _write_long(self
._file
, 0)
864 def _write_form_length(self
, datalength
):
866 commlength
= 18 + 5 + len(self
._compname
)
868 commlength
= commlength
+ 1
873 _write_long(self
._file
, 4 + verslength
+ self
._marklength
+ \
874 8 + commlength
+ 16 + datalength
)
877 def _patchheader(self
):
878 curpos
= self
._file
.tell()
879 if self
._datawritten
& 1:
880 datalength
= self
._datawritten
+ 1
881 self
._file
.write(chr(0))
883 datalength
= self
._datawritten
884 if datalength
== self
._datalength
and \
885 self
._nframes
== self
._nframeswritten
and \
886 self
._marklength
== 0:
887 self
._file
.seek(curpos
, 0)
889 self
._file
.seek(self
._form
_length
_pos
, 0)
890 dummy
= self
._write
_form
_length
(datalength
)
891 self
._file
.seek(self
._nframes
_pos
, 0)
892 _write_long(self
._file
, self
._nframeswritten
)
893 self
._file
.seek(self
._ssnd
_length
_pos
, 0)
894 _write_long(self
._file
, datalength
+ 8)
895 self
._file
.seek(curpos
, 0)
896 self
._nframes
= self
._nframeswritten
897 self
._datalength
= datalength
899 def _writemarkers(self
):
900 if len(self
._markers
) == 0:
902 self
._file
.write('MARK')
904 for marker
in self
._markers
:
905 id, pos
, name
= marker
906 length
= length
+ len(name
) + 1 + 6
907 if len(name
) & 1 == 0:
909 _write_long(self
._file
, length
)
910 self
._marklength
= length
+ 8
911 _write_short(self
._file
, len(self
._markers
))
912 for marker
in self
._markers
:
913 id, pos
, name
= marker
914 _write_short(self
._file
, id)
915 _write_long(self
._file
, pos
)
916 _write_string(self
._file
, name
)
918 def open(f
, mode
=None):
920 if hasattr(f
, 'mode'):
924 if mode
in ('r', 'rb'):
926 elif mode
in ('w', 'wb'):
929 raise Error
, "mode must be 'r', 'rb', 'w', or 'wb'"
931 openfp
= open # B/W compatibility
933 if __name__
== '__main__':
936 sys
.argv
.append('/usr/demos/data/audio/bach.aiff')
940 print "nchannels =", f
.getnchannels()
941 print "nframes =", f
.getnframes()
942 print "sampwidth =", f
.getsampwidth()
943 print "framerate =", f
.getframerate()
944 print "comptype =", f
.getcomptype()
945 print "compname =", f
.getcompname()
950 g
.setparams(f
.getparams())
952 data
= f
.readframes(1024)