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)
35 # <position> (4 bytes)
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
43 # in AIFF-C files only:
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
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)
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
105 # setparams(nchannels, sampwidth, framerate, nframes, comptype, compname)
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
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
145 _AIFC_version
= 0xA2805140 # Version 1 of AIFF-C
147 _skiplist
= 'COMT', 'INST', 'MIDI', 'AESD', \
148 'APPL', 'NAME', 'AUTH', '(c) ', 'ANNO'
150 _nchannelslist
= [(1, AL
.MONO
), (2, AL
.STEREO
)]
151 _sampwidthlist
= [(8, AL
.SAMPLE_8
), (16, AL
.SAMPLE_16
), (24, AL
.SAMPLE_24
)]
152 _frameratelist
= [(48000, AL
.RATE_48000
),
153 (44100, AL
.RATE_44100
),
154 (32000, AL
.RATE_32000
),
155 (22050, AL
.RATE_22050
),
156 (16000, AL
.RATE_16000
),
157 (11025, AL
.RATE_11025
),
158 ( 8000, AL
.RATE_8000
)]
160 def _convert1(value
, list):
164 raise Error
, 'unknown parameter value'
166 def _convert2(value
, list):
170 raise Error
, 'unknown parameter value'
172 def _read_long(file):
178 x
= x
*256 + ord(byte
)
183 def _read_ulong(file):
189 x
= x
*256 + ord(byte
)
192 def _read_short(file):
198 x
= x
*256 + ord(byte
)
203 def _read_string(file):
204 length
= ord(file.read(1))
205 data
= file.read(length
)
210 _HUGE_VAL
= 1.79769313486231e+308 # See <limits.h>
212 def _read_float(f
): # 10 bytes
214 expon
= _read_short(f
) # 2 bytes
218 expon
= expon
+ 0x8000
219 himant
= _read_ulong(f
) # 4 bytes
220 lomant
= _read_ulong(f
) # 4 bytes
221 if expon
== himant
== lomant
== 0:
223 elif expon
== 0x7FFF:
226 expon
= expon
- 16383
227 f
= (himant
* 0x100000000L
+ lomant
) * pow(2.0, expon
- 63)
230 def _write_short(f
, x
):
231 d
, m
= divmod(x
, 256)
235 def _write_long(f
, x
):
240 d
, m
= divmod(x
, 256)
244 f
.write(chr(int(data
[i
])))
246 def _write_string(f
, s
):
252 def _write_float(f
, x
):
264 fmant
, expon
= math
.frexp(x
)
265 if expon
> 16384 or fmant
>= 1: # Infinity or NaN
270 expon
= expon
+ 16382
271 if expon
< 0: # denormalized
272 fmant
= math
.ldexp(fmant
, expon
)
275 fmant
= math
.ldexp(fmant
, 32)
276 fsmant
= math
.floor(fmant
)
277 himant
= long(fsmant
)
278 fmant
= math
.ldexp(fmant
- fsmant
, 32)
279 fsmant
= math
.floor(fmant
)
280 lomant
= long(fsmant
)
281 _write_short(f
, expon
)
282 _write_long(f
, himant
)
283 _write_long(f
, lomant
)
286 def __init__(self
, file):
288 self
.chunkname
= self
.file.read(4)
289 if len(self
.chunkname
) < 4:
291 self
.chunksize
= _read_long(self
.file)
293 self
.offset
= self
.file.tell()
296 self
.file.seek(self
.offset
, 0)
299 def setpos(self
, pos
):
300 if pos
< 0 or pos
> self
.chunksize
:
302 self
.file.seek(self
.offset
+ pos
, 0)
305 def read(self
, length
):
306 if self
.size_read
>= self
.chunksize
:
308 if length
> self
.chunksize
- self
.size_read
:
309 length
= self
.chunksize
- self
.size_read
310 data
= self
.file.read(length
)
311 self
.size_read
= self
.size_read
+ len(data
)
316 self
.file.seek(self
.chunksize
- self
.size_read
, 1)
318 while self
.size_read
< self
.chunksize
:
319 dummy
= self
.read(8192)
322 if self
.chunksize
& 1:
326 # Variables used in this class:
328 # These variables are available to the user though appropriate
329 # methods of this class:
330 # _file -- the open file with methods read(), close(), and seek()
331 # set through the __init__() method
332 # _nchannels -- the number of audio channels
333 # available through the getnchannels() method
334 # _nframes -- the number of audio frames
335 # available through the getnframes() method
336 # _sampwidth -- the number of bytes per audio sample
337 # available through the getsampwidth() method
338 # _framerate -- the sampling frequency
339 # available through the getframerate() method
340 # _comptype -- the AIFF-C compression type ('NONE' if AIFF)
341 # available through the getcomptype() method
342 # _compname -- the human-readable AIFF-C compression type
343 # available through the getcomptype() method
344 # _markers -- the marks in the audio file
345 # available through the getmarkers() and getmark()
347 # _soundpos -- the position in the audio stream
348 # available through the tell() method, set through the
351 # These variables are used internally only:
352 # _version -- the AIFF-C version number
353 # _decomp -- the decompressor from builtin module cl
354 # _comm_chunk_read -- 1 iff the COMM chunk has been read
355 # _aifc -- 1 iff reading an AIFF-C file
356 # _ssnd_seek_needed -- 1 iff positioned correctly in audio
357 # file for readframes()
358 # _ssnd_chunk -- instantiation of a chunk class for the SSND chunk
359 # _framesize -- size of one frame in the file
361 access _file
, _nchannels
, _nframes
, _sampwidth
, _framerate
, \
362 _comptype
, _compname
, _markers
, _soundpos
, _version
, \
363 _decomp
, _comm_chunk_read
, __aifc
, _ssnd_seek_needed
, \
364 _ssnd_chunk
, _framesize
: private
366 def initfp(self
, file):
373 form
= self
._file
.read(4)
375 raise Error
, 'file does not start with FORM id'
376 formlength
= _read_long(self
._file
)
378 raise Error
, 'invalid FORM chunk data size'
379 formdata
= self
._file
.read(4)
380 formlength
= formlength
- 4
381 if formdata
== 'AIFF':
383 elif formdata
== 'AIFC':
386 raise Error
, 'not an AIFF or AIFF-C file'
387 self
._comm
_chunk
_read
= 0
388 while formlength
> 0:
389 self
._ssnd
_seek
_needed
= 1
390 #DEBUG: SGI's soundfiler has a bug. There should
391 # be no need to check for EOF here.
393 chunk
= Chunk(self
._file
)
396 print 'Warning: FORM chunk size too large'
399 raise EOFError # different error, raise exception
400 if chunk
.chunkname
== 'COMM':
401 self
._read
_comm
_chunk
(chunk
)
402 self
._comm
_chunk
_read
= 1
403 elif chunk
.chunkname
== 'SSND':
404 self
._ssnd
_chunk
= chunk
405 dummy
= chunk
.read(8)
406 self
._ssnd
_seek
_needed
= 0
407 elif chunk
.chunkname
== 'FVER':
408 self
._version
= _read_long(chunk
)
409 elif chunk
.chunkname
== 'MARK':
410 self
._readmark
(chunk
)
411 elif chunk
.chunkname
in _skiplist
:
414 raise Error
, 'unrecognized chunk type '+chunk
.chunkname
415 formlength
= formlength
- 8 - chunk
.chunksize
416 if chunk
.chunksize
& 1:
417 formlength
= formlength
- 1
420 if not self
._comm
_chunk
_read
or not self
._ssnd
_chunk
:
421 raise Error
, 'COMM chunk and/or SSND chunk missing'
422 if self
._aifc
and self
._decomp
:
423 params
= [CL
.ORIGINAL_FORMAT
, 0, \
424 CL
.BITS_PER_COMPONENT
, 0, \
425 CL
.FRAME_RATE
, self
._framerate
]
426 if self
._nchannels
== AL
.MONO
:
429 params
[1] = CL
.STEREO_INTERLEAVED
430 if self
._sampwidth
== AL
.SAMPLE_8
:
432 elif self
._sampwidth
== AL
.SAMPLE_16
:
436 self
._decomp
.SetParams(params
)
438 def __init__(self
, f
):
439 if type(f
) == type(''):
440 f
= builtin
.open(f
, 'r')
441 # else, assume it is an open file object already
449 # User visible methods.
455 self
._ssnd
_seek
_needed
= 1
460 self
._decomp
.CloseDecompressor()
465 return self
._soundpos
467 def getnchannels(self
):
468 return self
._nchannels
470 def getnframes(self
):
473 def getsampwidth(self
):
474 return self
._sampwidth
476 def getframerate(self
):
477 return self
._framerate
479 def getcomptype(self
):
480 return self
._comptype
482 def getcompname(self
):
483 return self
._compname
485 ## def getversion(self):
486 ## return self._version
489 return self
.getnchannels(), self
.getsampwidth(), \
490 self
.getframerate(), self
.getnframes(), \
491 self
.getcomptype(), self
.getcompname()
493 def getmarkers(self
):
494 if len(self
._markers
) == 0:
498 def getmark(self
, id):
499 for marker
in self
._markers
:
502 raise Error
, 'marker ' + `
id`
+ ' does not exist'
504 def setpos(self
, pos
):
505 if pos
< 0 or pos
> self
._nframes
:
506 raise Error
, 'position not in range'
508 self
._ssnd
_seek
_needed
= 1
510 def readframes(self
, nframes
):
511 if self
._ssnd
_seek
_needed
:
512 self
._ssnd
_chunk
.rewind()
513 dummy
= self
._ssnd
_chunk
.read(8)
514 pos
= self
._soundpos
* self
._framesize
516 self
._ssnd
_chunk
.setpos(pos
+ 8)
517 self
._ssnd
_seek
_needed
= 0
520 data
= self
._ssnd
_chunk
.read(nframes
* self
._framesize
)
521 if self
._convert
and data
:
522 data
= self
._convert
(data
)
523 self
._soundpos
= self
._soundpos
+ len(data
) / (self
._nchannels
* self
._sampwidth
)
531 def _decomp_data(self
, data
):
532 dummy
= self
._decomp
.SetParam(CL
.FRAME_BUFFER_SIZE
,
534 return self
._decomp
.Decompress(len(data
) / self
._nchannels
,
537 def _ulaw2lin(self
, data
):
539 return audioop
.ulaw2lin(data
, 2)
541 def _read_comm_chunk(self
, chunk
):
542 nchannels
= _read_short(chunk
)
543 self
._nchannels
= _convert1(nchannels
, _nchannelslist
)
544 self
._nframes
= _read_long(chunk
)
545 sampwidth
= _read_short(chunk
)
546 self
._sampwidth
= _convert1(sampwidth
, _sampwidthlist
)
547 framerate
= _read_float(chunk
)
548 self
._framerate
= _convert1(framerate
, _frameratelist
)
549 self
._framesize
= self
._nchannels
* self
._sampwidth
551 #DEBUG: SGI's soundeditor produces a bad size :-(
553 if chunk
.chunksize
== 18:
555 print 'Warning: bad COMM chunk size'
558 self
._comptype
= chunk
.read(4)
561 length
= ord(chunk
.file.read(1))
564 chunk
.chunksize
= chunk
.chunksize
+ length
565 chunk
.file.seek(-1, 1)
567 self
._compname
= _read_string(chunk
)
568 if self
._comptype
!= 'NONE':
572 if self
._comptype
== 'ULAW':
575 self
._convert
= self
._ulaw
2lin
576 self
._framesize
= self
._framesize
/ 2
580 raise Error
, 'cannot read compressed AIFF-C files'
581 if self
._comptype
== 'ULAW':
582 scheme
= CL
.G711_ULAW
583 self
._framesize
= self
._framesize
/ 2
584 elif self
._comptype
== 'ALAW':
585 scheme
= CL
.G711_ALAW
586 self
._framesize
= self
._framesize
/ 2
588 raise Error
, 'unsupported compression type'
589 self
._decomp
= cl
.OpenDecompressor(scheme
)
590 self
._convert
= self
._decomp
_data
592 self
._comptype
= 'NONE'
593 self
._compname
= 'not compressed'
595 def _readmark(self
, chunk
):
596 nmarkers
= _read_short(chunk
)
597 # Some files appear to contain invalid counts.
598 # Cope with this by testing for EOF.
600 for i
in range(nmarkers
):
601 id = _read_short(chunk
)
602 pos
= _read_long(chunk
)
603 name
= _read_string(chunk
)
604 self
._markers
.append((id, pos
, name
))
606 print 'Warning: MARK chunk contains only',
607 print len(self
._markers
),
608 if len(self
._markers
) == 1: print 'marker',
609 else: print 'markers',
610 print 'instead of', nmarkers
613 # Variables used in this class:
615 # These variables are user settable through appropriate methods
617 # _file -- the open file with methods write(), close(), tell(), seek()
618 # set through the __init__() method
619 # _comptype -- the AIFF-C compression type ('NONE' in AIFF)
620 # set through the setcomptype() or setparams() method
621 # _compname -- the human-readable AIFF-C compression type
622 # set through the setcomptype() or setparams() method
623 # _nchannels -- the number of audio channels
624 # set through the setnchannels() or setparams() method
625 # _sampwidth -- the number of bytes per audio sample
626 # set through the setsampwidth() or setparams() method
627 # _framerate -- the sampling frequency
628 # set through the setframerate() or setparams() method
629 # _nframes -- the number of audio frames written to the header
630 # set through the setnframes() or setparams() method
631 # _aifc -- whether we're writing an AIFF-C file or an AIFF file
632 # set through the aifc() method, reset through the
635 # These variables are used internally only:
636 # _version -- the AIFF-C version number
637 # _comp -- the compressor from builtin module cl
638 # _nframeswritten -- the number of audio frames actually written
639 # _datalength -- the size of the audio samples written to the header
640 # _datawritten -- the size of the audio samples actually written
642 access _file
, _comptype
, _compname
, _nchannels
, _sampwidth
, \
643 _framerate
, _nframes
, _aifc
, _version
, _comp
, \
644 _nframeswritten
, _datalength
, _datawritten
: private
646 def __init__(self
, f
):
647 if type(f
) == type(''):
649 f
= builtin
.open(f
, 'w')
651 # else, assume it is an open file object already
654 if filename
[-5:] == '.aiff':
659 def initfp(self
, file):
661 self
._version
= _AIFC_version
662 self
._comptype
= 'NONE'
663 self
._compname
= 'not compressed'
670 self
._nframeswritten
= 0
671 self
._datawritten
= 0
675 self
._aifc
= 1 # AIFF-C is default
682 # User visible methods.
685 if self
._nframeswritten
:
686 raise Error
, 'cannot change parameters after starting to write'
690 if self
._nframeswritten
:
691 raise Error
, 'cannot change parameters after starting to write'
694 def setnchannels(self
, nchannels
):
695 if self
._nframeswritten
:
696 raise Error
, 'cannot change parameters after starting to write'
697 dummy
= _convert2(nchannels
, _nchannelslist
)
698 self
._nchannels
= nchannels
700 def getnchannels(self
):
701 if not self
._nchannels
:
702 raise Error
, 'number of channels not set'
703 return self
._nchannels
705 def setsampwidth(self
, sampwidth
):
706 if self
._nframeswritten
:
707 raise Error
, 'cannot change parameters after starting to write'
708 dummy
= _convert2(sampwidth
, _sampwidthlist
)
709 self
._sampwidth
= sampwidth
711 def getsampwidth(self
):
712 if not self
._sampwidth
:
713 raise Error
, 'sample width not set'
714 return self
._sampwidth
716 def setframerate(self
, framerate
):
717 if self
._nframeswritten
:
718 raise Error
, 'cannot change parameters after starting to write'
719 dummy
= _convert2(framerate
, _frameratelist
)
720 self
._framerate
= framerate
722 def getframerate(self
):
723 if not self
._framerate
:
724 raise Error
, 'frame rate not set'
725 return self
._framerate
727 def setnframes(self
, nframes
):
728 if self
._nframeswritten
:
729 raise Error
, 'cannot change parameters after starting to write'
730 self
._nframes
= nframes
732 def getnframes(self
):
733 return self
._nframeswritten
735 def setcomptype(self
, comptype
, compname
):
736 if self
._nframeswritten
:
737 raise Error
, 'cannot change parameters after starting to write'
738 if comptype
not in ('NONE', 'ULAW', 'ALAW'):
739 raise Error
, 'unsupported compression type'
740 self
._comptype
= comptype
741 self
._compname
= compname
743 def getcomptype(self
):
744 return self
._comptype
746 def getcompname(self
):
747 return self
._compname
749 ## def setversion(self, version):
750 ## if self._nframeswritten:
751 ## raise Error, 'cannot change parameters after starting to write'
752 ## self._version = version
754 def setparams(self
, (nchannels
, sampwidth
, framerate
, nframes
, comptype
, compname
)):
755 if self
._nframeswritten
:
756 raise Error
, 'cannot change parameters after starting to write'
757 if comptype
not in ('NONE', 'ULAW', 'ALAW'):
758 raise Error
, 'unsupported compression type'
759 dummy
= _convert2(nchannels
, _nchannelslist
)
760 dummy
= _convert2(sampwidth
, _sampwidthlist
)
761 dummy
= _convert2(framerate
, _frameratelist
)
762 self
._nchannels
= nchannels
763 self
._sampwidth
= sampwidth
764 self
._framerate
= framerate
765 self
._nframes
= nframes
766 self
._comptype
= comptype
767 self
._compname
= compname
770 if not self
._nchannels
or not self
._sampwidth
or not self
._framerate
:
771 raise Error
, 'not all parameters set'
772 return self
._nchannels
, self
._sampwidth
, self
._framerate
, \
773 self
._nframes
, self
._comptype
, self
._compname
775 def setmark(self
, id, pos
, name
):
777 raise Error
, 'marker ID must be > 0'
779 raise Error
, 'marker position must be >= 0'
780 if type(name
) != type(''):
781 raise Error
, 'marker name must be a string'
782 for i
in range(len(self
._markers
)):
783 if id == self
._markers
[i
][0]:
784 self
._markers
[i
] = id, pos
, name
786 self
._markers
.append((id, pos
, name
))
788 def getmark(self
, id):
789 for marker
in self
._markers
:
792 raise Error
, 'marker ' + `
id`
+ ' does not exist'
794 def getmarkers(self
):
795 if len(self
._markers
) == 0:
800 return self
._nframeswritten
802 def writeframesraw(self
, data
):
803 self
._ensure
_header
_written
(len(data
))
804 nframes
= len(data
) / (self
._sampwidth
* self
._nchannels
)
806 data
= self
._convert
(data
)
807 self
._file
.write(data
)
808 self
._nframeswritten
= self
._nframeswritten
+ nframes
809 self
._datawritten
= self
._datawritten
+ len(data
)
811 def writeframes(self
, data
):
812 self
.writeframesraw(data
)
813 if self
._nframeswritten
!= self
._nframes
or \
814 self
._datalength
!= self
._datawritten
:
818 self
._ensure
_header
_written
(0)
819 if self
._datawritten
& 1:
820 # quick pad to even size
821 self
._file
.write(chr(0))
822 self
._datawritten
= self
._datawritten
+ 1
824 if self
._nframeswritten
!= self
._nframes
or \
825 self
._datalength
!= self
._datawritten
or \
829 self
._comp
.CloseCompressor()
839 def _comp_data(self
, data
):
840 dum
= self
._comp
.SetParam(CL
.FRAME_BUFFER_SIZE
, len(data
))
841 dum
= self
._comp
.SetParam(CL
.COMPRESSED_BUFFER_SIZE
, len(data
))
842 return self
._comp
.Compress(nframes
, data
)
844 def _lin2ulaw(self
, data
):
846 return audioop
.lin2ulaw(data
, 2)
848 def _ensure_header_written(self
, datasize
):
849 if not self
._nframeswritten
:
850 if self
._comptype
in ('ULAW', 'ALAW'):
851 if not self
._sampwidth
:
852 self
._sampwidth
= AL
.SAMPLE_16
853 if self
._sampwidth
!= AL
.SAMPLE_16
:
854 raise Error
, 'sample width must be 2 when compressing with ULAW or ALAW'
855 if not self
._nchannels
:
856 raise Error
, '# channels not specified'
857 if not self
._sampwidth
:
858 raise Error
, 'sample width not specified'
859 if not self
._framerate
:
860 raise Error
, 'sampling rate not specified'
861 self
._write
_header
(datasize
)
863 def _init_compression(self
):
867 if self
._comptype
== 'ULAW':
870 self
._convert
= self
._lin
2ulaw
874 raise Error
, 'cannot write compressed AIFF-C files'
875 if self
._comptype
== 'ULAW':
876 scheme
= CL
.G711_ULAW
877 elif self
._comptype
== 'ALAW':
878 scheme
= CL
.G711_ALAW
880 raise Error
, 'unsupported compression type'
881 self
._comp
= cl
.OpenCompressor(scheme
)
882 params
= [CL
.ORIGINAL_FORMAT
, 0, \
883 CL
.BITS_PER_COMPONENT
, 0, \
884 CL
.FRAME_RATE
, self
._framerate
, \
885 CL
.FRAME_BUFFER_SIZE
, 100, \
886 CL
.COMPRESSED_BUFFER_SIZE
, 100]
887 if self
._nchannels
== AL
.MONO
:
890 params
[1] = CL
.STEREO_INTERLEAVED
891 if self
._sampwidth
== AL
.SAMPLE_8
:
893 elif self
._sampwidth
== AL
.SAMPLE_16
:
897 self
._comp
.SetParams(params
)
898 # the compressor produces a header which we ignore
899 dummy
= self
._comp
.Compress(0, '')
900 self
._convert
= self
._comp
_data
902 def _write_header(self
, initlength
):
903 if self
._aifc
and self
._comptype
!= 'NONE':
904 self
._init
_compression
()
905 self
._file
.write('FORM')
906 if not self
._nframes
:
907 self
._nframes
= initlength
/ (self
._nchannels
* self
._sampwidth
)
908 self
._datalength
= self
._nframes
* self
._nchannels
* self
._sampwidth
909 if self
._datalength
& 1:
910 self
._datalength
= self
._datalength
+ 1
911 if self
._aifc
and self
._comptype
in ('ULAW', 'ALAW'):
912 self
._datalength
= self
._datalength
/ 2
913 if self
._datalength
& 1:
914 self
._datalength
= self
._datalength
+ 1
915 self
._form
_length
_pos
= self
._file
.tell()
916 commlength
= self
._write
_form
_length
(self
._datalength
)
918 self
._file
.write('AIFC')
919 self
._file
.write('FVER')
920 _write_long(self
._file
, 4)
921 _write_long(self
._file
, self
._version
)
923 self
._file
.write('AIFF')
924 self
._file
.write('COMM')
925 _write_long(self
._file
, commlength
)
926 _write_short(self
._file
, _convert2(self
._nchannels
, _nchannelslist
))
927 self
._nframes
_pos
= self
._file
.tell()
928 _write_long(self
._file
, self
._nframes
)
929 _write_short(self
._file
, _convert2(self
._sampwidth
, _sampwidthlist
))
930 _write_float(self
._file
, _convert2(self
._framerate
, _frameratelist
))
932 self
._file
.write(self
._comptype
)
933 _write_string(self
._file
, self
._compname
)
934 self
._file
.write('SSND')
935 self
._ssnd
_length
_pos
= self
._file
.tell()
936 _write_long(self
._file
, self
._datalength
+ 8)
937 _write_long(self
._file
, 0)
938 _write_long(self
._file
, 0)
940 def _write_form_length(self
, datalength
):
942 commlength
= 18 + 5 + len(self
._compname
)
944 commlength
= commlength
+ 1
949 _write_long(self
._file
, 4 + verslength
+ self
._marklength
+ \
950 8 + commlength
+ 16 + datalength
)
953 def _patchheader(self
):
954 curpos
= self
._file
.tell()
955 if self
._datawritten
& 1:
956 datalength
= self
._datawritten
+ 1
957 self
._file
.write(chr(0))
959 datalength
= self
._datawritten
960 if datalength
== self
._datalength
and \
961 self
._nframes
== self
._nframeswritten
and \
962 self
._marklength
== 0:
963 self
._file
.seek(curpos
, 0)
965 self
._file
.seek(self
._form
_length
_pos
, 0)
966 dummy
= self
._write
_form
_length
(datalength
)
967 self
._file
.seek(self
._nframes
_pos
, 0)
968 _write_long(self
._file
, self
._nframeswritten
)
969 self
._file
.seek(self
._ssnd
_length
_pos
, 0)
970 _write_long(self
._file
, datalength
+ 8)
971 self
._file
.seek(curpos
, 0)
972 self
._nframes
= self
._nframeswritten
973 self
._datalength
= datalength
975 def _writemarkers(self
):
976 if len(self
._markers
) == 0:
978 self
._file
.write('MARK')
980 for marker
in self
._markers
:
981 id, pos
, name
= marker
982 length
= length
+ len(name
) + 1 + 6
983 if len(name
) & 1 == 0:
985 _write_long(self
._file
, length
)
986 self
._marklength
= length
+ 8
987 _write_short(self
._file
, len(self
._markers
))
988 for marker
in self
._markers
:
989 id, pos
, name
= marker
990 _write_short(self
._file
, id)
991 _write_long(self
._file
, pos
)
992 _write_string(self
._file
, name
)
1000 raise Error
, "mode must be 'r' or 'w'"
1002 openfp
= open # B/W compatibility