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
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
141 _AIFC_version
= 0xA2805140 # Version 1 of AIFF-C
143 _skiplist
= 'COMT', 'INST', 'MIDI', 'AESD', \
144 'APPL', 'NAME', 'AUTH', '(c) ', 'ANNO'
146 def _read_long(file):
148 return struct
.unpack('>l', file.read(4))[0]
152 def _read_ulong(file):
154 return struct
.unpack('>L', file.read(4))[0]
158 def _read_short(file):
160 return struct
.unpack('>h', file.read(2))[0]
164 def _read_string(file):
165 length
= ord(file.read(1))
169 data
= file.read(length
)
174 _HUGE_VAL
= 1.79769313486231e+308 # See <limits.h>
176 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
):
206 def _write_float(f
, x
):
218 fmant
, expon
= math
.frexp(x
)
219 if expon
> 16384 or fmant
>= 1: # Infinity or NaN
224 expon
= expon
+ 16382
225 if expon
< 0: # denormalized
226 fmant
= math
.ldexp(fmant
, expon
)
229 fmant
= math
.ldexp(fmant
, 32)
230 fsmant
= math
.floor(fmant
)
231 himant
= long(fsmant
)
232 fmant
= math
.ldexp(fmant
- fsmant
, 32)
233 fsmant
= math
.floor(fmant
)
234 lomant
= long(fsmant
)
235 _write_short(f
, expon
)
236 _write_long(f
, himant
)
237 _write_long(f
, lomant
)
240 def __init__(self
, file):
242 self
.chunkname
= self
.file.read(4)
243 if len(self
.chunkname
) < 4:
245 self
.chunksize
= _read_long(self
.file)
247 self
.offset
= self
.file.tell()
250 self
.file.seek(self
.offset
, 0)
253 def setpos(self
, pos
):
254 if pos
< 0 or pos
> self
.chunksize
:
256 self
.file.seek(self
.offset
+ pos
, 0)
259 def read(self
, length
):
260 if self
.size_read
>= self
.chunksize
:
262 if length
> self
.chunksize
- self
.size_read
:
263 length
= self
.chunksize
- self
.size_read
264 data
= self
.file.read(length
)
265 self
.size_read
= self
.size_read
+ len(data
)
270 self
.file.seek(self
.chunksize
- self
.size_read
, 1)
272 while self
.size_read
< self
.chunksize
:
273 dummy
= self
.read(8192)
276 if self
.chunksize
& 1:
280 # Variables used in this class:
282 # These variables are available to the user though appropriate
283 # methods of this class:
284 # _file -- the open file with methods read(), close(), and seek()
285 # set through the __init__() method
286 # _nchannels -- the number of audio channels
287 # available through the getnchannels() method
288 # _nframes -- the number of audio frames
289 # available through the getnframes() method
290 # _sampwidth -- the number of bytes per audio sample
291 # available through the getsampwidth() method
292 # _framerate -- the sampling frequency
293 # available through the getframerate() method
294 # _comptype -- the AIFF-C compression type ('NONE' if AIFF)
295 # available through the getcomptype() method
296 # _compname -- the human-readable AIFF-C compression type
297 # available through the getcomptype() method
298 # _markers -- the marks in the audio file
299 # available through the getmarkers() and getmark()
301 # _soundpos -- the position in the audio stream
302 # available through the tell() method, set through the
305 # These variables are used internally only:
306 # _version -- the AIFF-C version number
307 # _decomp -- the decompressor from builtin module cl
308 # _comm_chunk_read -- 1 iff the COMM chunk has been read
309 # _aifc -- 1 iff reading an AIFF-C file
310 # _ssnd_seek_needed -- 1 iff positioned correctly in audio
311 # file for readframes()
312 # _ssnd_chunk -- instantiation of a chunk class for the SSND chunk
313 # _framesize -- size of one frame in the file
315 ## if 0: access _file, _nchannels, _nframes, _sampwidth, _framerate, \
316 ## _comptype, _compname, _markers, _soundpos, _version, \
317 ## _decomp, _comm_chunk_read, __aifc, _ssnd_seek_needed, \
318 ## _ssnd_chunk, _framesize: private
320 def initfp(self
, file):
327 form
= self
._file
.read(4)
329 raise Error
, 'file does not start with FORM id'
330 formlength
= _read_long(self
._file
)
332 raise Error
, 'invalid FORM chunk data size'
333 formdata
= self
._file
.read(4)
334 formlength
= formlength
- 4
335 if formdata
== 'AIFF':
337 elif formdata
== 'AIFC':
340 raise Error
, 'not an AIFF or AIFF-C file'
341 self
._comm
_chunk
_read
= 0
342 while formlength
> 0:
343 self
._ssnd
_seek
_needed
= 1
344 #DEBUG: SGI's soundfiler has a bug. There should
345 # be no need to check for EOF here.
347 chunk
= Chunk(self
._file
)
350 print 'Warning: FORM chunk size too large'
353 raise EOFError # different error, raise exception
354 if chunk
.chunkname
== 'COMM':
355 self
._read
_comm
_chunk
(chunk
)
356 self
._comm
_chunk
_read
= 1
357 elif chunk
.chunkname
== 'SSND':
358 self
._ssnd
_chunk
= chunk
359 dummy
= chunk
.read(8)
360 self
._ssnd
_seek
_needed
= 0
361 elif chunk
.chunkname
== 'FVER':
362 self
._version
= _read_long(chunk
)
363 elif chunk
.chunkname
== 'MARK':
364 self
._readmark
(chunk
)
365 elif chunk
.chunkname
in _skiplist
:
368 raise Error
, 'unrecognized chunk type '+chunk
.chunkname
369 formlength
= formlength
- 8 - chunk
.chunksize
370 if chunk
.chunksize
& 1:
371 formlength
= formlength
- 1
374 if not self
._comm
_chunk
_read
or not self
._ssnd
_chunk
:
375 raise Error
, 'COMM chunk and/or SSND chunk missing'
376 if self
._aifc
and self
._decomp
:
378 params
= [cl
.ORIGINAL_FORMAT
, 0,
379 cl
.BITS_PER_COMPONENT
, self
._sampwidth
* 8,
380 cl
.FRAME_RATE
, self
._framerate
]
381 if self
._nchannels
== 1:
383 elif self
._nchannels
== 2:
384 params
[1] = cl
.STEREO_INTERLEAVED
386 raise Error
, 'cannot compress more than 2 channels'
387 self
._decomp
.SetParams(params
)
389 def __init__(self
, f
):
390 if type(f
) == type(''):
391 f
= __builtin__
.open(f
, 'rb')
392 # else, assume it is an open file object already
400 # User visible methods.
406 self
._ssnd
_seek
_needed
= 1
411 self
._decomp
.CloseDecompressor()
416 return self
._soundpos
418 def getnchannels(self
):
419 return self
._nchannels
421 def getnframes(self
):
424 def getsampwidth(self
):
425 return self
._sampwidth
427 def getframerate(self
):
428 return self
._framerate
430 def getcomptype(self
):
431 return self
._comptype
433 def getcompname(self
):
434 return self
._compname
436 ## def getversion(self):
437 ## return self._version
440 return self
.getnchannels(), self
.getsampwidth(), \
441 self
.getframerate(), self
.getnframes(), \
442 self
.getcomptype(), self
.getcompname()
444 def getmarkers(self
):
445 if len(self
._markers
) == 0:
449 def getmark(self
, id):
450 for marker
in self
._markers
:
453 raise Error
, 'marker ' + `
id`
+ ' does not exist'
455 def setpos(self
, pos
):
456 if pos
< 0 or pos
> self
._nframes
:
457 raise Error
, 'position not in range'
459 self
._ssnd
_seek
_needed
= 1
461 def readframes(self
, nframes
):
462 if self
._ssnd
_seek
_needed
:
463 self
._ssnd
_chunk
.rewind()
464 dummy
= self
._ssnd
_chunk
.read(8)
465 pos
= self
._soundpos
* self
._framesize
467 self
._ssnd
_chunk
.setpos(pos
+ 8)
468 self
._ssnd
_seek
_needed
= 0
471 data
= self
._ssnd
_chunk
.read(nframes
* self
._framesize
)
472 if self
._convert
and data
:
473 data
= self
._convert
(data
)
474 self
._soundpos
= self
._soundpos
+ len(data
) / (self
._nchannels
* self
._sampwidth
)
480 ## if 0: access *: private
482 def _decomp_data(self
, data
):
484 dummy
= self
._decomp
.SetParam(cl
.FRAME_BUFFER_SIZE
,
486 return self
._decomp
.Decompress(len(data
) / self
._nchannels
,
489 def _ulaw2lin(self
, data
):
491 return audioop
.ulaw2lin(data
, 2)
493 def _adpcm2lin(self
, data
):
495 if not hasattr(self
, '_adpcmstate'):
497 self
._adpcmstate
= None
498 data
, self
._adpcmstate
= audioop
.adpcm2lin(data
, 2,
502 def _read_comm_chunk(self
, chunk
):
503 self
._nchannels
= _read_short(chunk
)
504 self
._nframes
= _read_long(chunk
)
505 self
._sampwidth
= (_read_short(chunk
) + 7) / 8
506 self
._framerate
= int(_read_float(chunk
))
507 self
._framesize
= self
._nchannels
* self
._sampwidth
509 #DEBUG: SGI's soundeditor produces a bad size :-(
511 if chunk
.chunksize
== 18:
513 print 'Warning: bad COMM chunk size'
516 self
._comptype
= chunk
.read(4)
519 length
= ord(chunk
.file.read(1))
522 chunk
.chunksize
= chunk
.chunksize
+ length
523 chunk
.file.seek(-1, 1)
525 self
._compname
= _read_string(chunk
)
526 if self
._comptype
!= 'NONE':
527 if self
._comptype
== 'G722':
533 self
._convert
= self
._adpcm
2lin
534 self
._framesize
= self
._framesize
/ 4
536 # for ULAW and ALAW try Compression Library
540 if self
._comptype
== 'ULAW':
543 self
._convert
= self
._ulaw
2lin
544 self
._framesize
= self
._framesize
/ 2
548 raise Error
, 'cannot read compressed AIFF-C files'
549 if self
._comptype
== 'ULAW':
550 scheme
= cl
.G711_ULAW
551 self
._framesize
= self
._framesize
/ 2
552 elif self
._comptype
== 'ALAW':
553 scheme
= cl
.G711_ALAW
554 self
._framesize
= self
._framesize
/ 2
556 raise Error
, 'unsupported compression type'
557 self
._decomp
= cl
.OpenDecompressor(scheme
)
558 self
._convert
= self
._decomp
_data
560 self
._comptype
= 'NONE'
561 self
._compname
= 'not compressed'
563 def _readmark(self
, chunk
):
564 nmarkers
= _read_short(chunk
)
565 # Some files appear to contain invalid counts.
566 # Cope with this by testing for EOF.
568 for i
in range(nmarkers
):
569 id = _read_short(chunk
)
570 pos
= _read_long(chunk
)
571 name
= _read_string(chunk
)
573 # some files appear to have
574 # dummy markers consisting of
575 # a position 0 and name ''
576 self
._markers
.append((id, pos
, name
))
578 print 'Warning: MARK chunk contains only',
579 print len(self
._markers
),
580 if len(self
._markers
) == 1: print 'marker',
581 else: print 'markers',
582 print 'instead of', nmarkers
585 # Variables used in this class:
587 # These variables are user settable through appropriate methods
589 # _file -- the open file with methods write(), close(), tell(), seek()
590 # set through the __init__() method
591 # _comptype -- the AIFF-C compression type ('NONE' in AIFF)
592 # set through the setcomptype() or setparams() method
593 # _compname -- the human-readable AIFF-C compression type
594 # set through the setcomptype() or setparams() method
595 # _nchannels -- the number of audio channels
596 # set through the setnchannels() or setparams() method
597 # _sampwidth -- the number of bytes per audio sample
598 # set through the setsampwidth() or setparams() method
599 # _framerate -- the sampling frequency
600 # set through the setframerate() or setparams() method
601 # _nframes -- the number of audio frames written to the header
602 # set through the setnframes() or setparams() method
603 # _aifc -- whether we're writing an AIFF-C file or an AIFF file
604 # set through the aifc() method, reset through the
607 # These variables are used internally only:
608 # _version -- the AIFF-C version number
609 # _comp -- the compressor from builtin module cl
610 # _nframeswritten -- the number of audio frames actually written
611 # _datalength -- the size of the audio samples written to the header
612 # _datawritten -- the size of the audio samples actually written
614 ## if 0: access _file, _comptype, _compname, _nchannels, _sampwidth, \
615 ## _framerate, _nframes, _aifc, _version, _comp, \
616 ## _nframeswritten, _datalength, _datawritten: private
618 def __init__(self
, f
):
619 if type(f
) == type(''):
621 f
= __builtin__
.open(f
, 'wb')
623 # else, assume it is an open file object already
626 if filename
[-5:] == '.aiff':
631 def initfp(self
, file):
633 self
._version
= _AIFC_version
634 self
._comptype
= 'NONE'
635 self
._compname
= 'not compressed'
642 self
._nframeswritten
= 0
643 self
._datawritten
= 0
647 self
._aifc
= 1 # AIFF-C is default
654 # User visible methods.
657 if self
._nframeswritten
:
658 raise Error
, 'cannot change parameters after starting to write'
662 if self
._nframeswritten
:
663 raise Error
, 'cannot change parameters after starting to write'
666 def setnchannels(self
, nchannels
):
667 if self
._nframeswritten
:
668 raise Error
, 'cannot change parameters after starting to write'
670 raise Error
, 'bad # of channels'
671 self
._nchannels
= nchannels
673 def getnchannels(self
):
674 if not self
._nchannels
:
675 raise Error
, 'number of channels not set'
676 return self
._nchannels
678 def setsampwidth(self
, sampwidth
):
679 if self
._nframeswritten
:
680 raise Error
, 'cannot change parameters after starting to write'
681 if sampwidth
< 1 or sampwidth
> 4:
682 raise Error
, 'bad sample width'
683 self
._sampwidth
= sampwidth
685 def getsampwidth(self
):
686 if not self
._sampwidth
:
687 raise Error
, 'sample width not set'
688 return self
._sampwidth
690 def setframerate(self
, framerate
):
691 if self
._nframeswritten
:
692 raise Error
, 'cannot change parameters after starting to write'
694 raise Error
, 'bad frame rate'
695 self
._framerate
= framerate
697 def getframerate(self
):
698 if not self
._framerate
:
699 raise Error
, 'frame rate not set'
700 return self
._framerate
702 def setnframes(self
, nframes
):
703 if self
._nframeswritten
:
704 raise Error
, 'cannot change parameters after starting to write'
705 self
._nframes
= nframes
707 def getnframes(self
):
708 return self
._nframeswritten
710 def setcomptype(self
, comptype
, compname
):
711 if self
._nframeswritten
:
712 raise Error
, 'cannot change parameters after starting to write'
713 if comptype
not in ('NONE', 'ULAW', 'ALAW', 'G722'):
714 raise Error
, 'unsupported compression type'
715 self
._comptype
= comptype
716 self
._compname
= compname
718 def getcomptype(self
):
719 return self
._comptype
721 def getcompname(self
):
722 return self
._compname
724 ## def setversion(self, version):
725 ## if self._nframeswritten:
726 ## raise Error, 'cannot change parameters after starting to write'
727 ## self._version = version
729 def setparams(self
, (nchannels
, sampwidth
, framerate
, nframes
, comptype
, compname
)):
730 if self
._nframeswritten
:
731 raise Error
, 'cannot change parameters after starting to write'
732 if comptype
not in ('NONE', 'ULAW', 'ALAW', 'G722'):
733 raise Error
, 'unsupported compression type'
734 self
.setnchannels(nchannels
)
735 self
.setsampwidth(sampwidth
)
736 self
.setframerate(framerate
)
737 self
.setnframes(nframes
)
738 self
.setcomptype(comptype
, compname
)
741 if not self
._nchannels
or not self
._sampwidth
or not self
._framerate
:
742 raise Error
, 'not all parameters set'
743 return self
._nchannels
, self
._sampwidth
, self
._framerate
, \
744 self
._nframes
, self
._comptype
, self
._compname
746 def setmark(self
, id, pos
, name
):
748 raise Error
, 'marker ID must be > 0'
750 raise Error
, 'marker position must be >= 0'
751 if type(name
) != type(''):
752 raise Error
, 'marker name must be a string'
753 for i
in range(len(self
._markers
)):
754 if id == self
._markers
[i
][0]:
755 self
._markers
[i
] = id, pos
, name
757 self
._markers
.append((id, pos
, name
))
759 def getmark(self
, id):
760 for marker
in self
._markers
:
763 raise Error
, 'marker ' + `
id`
+ ' does not exist'
765 def getmarkers(self
):
766 if len(self
._markers
) == 0:
771 return self
._nframeswritten
773 def writeframesraw(self
, data
):
774 self
._ensure
_header
_written
(len(data
))
775 nframes
= len(data
) / (self
._sampwidth
* self
._nchannels
)
777 data
= self
._convert
(data
)
778 self
._file
.write(data
)
779 self
._nframeswritten
= self
._nframeswritten
+ nframes
780 self
._datawritten
= self
._datawritten
+ len(data
)
782 def writeframes(self
, data
):
783 self
.writeframesraw(data
)
784 if self
._nframeswritten
!= self
._nframes
or \
785 self
._datalength
!= self
._datawritten
:
789 self
._ensure
_header
_written
(0)
790 if self
._datawritten
& 1:
791 # quick pad to even size
792 self
._file
.write(chr(0))
793 self
._datawritten
= self
._datawritten
+ 1
795 if self
._nframeswritten
!= self
._nframes
or \
796 self
._datalength
!= self
._datawritten
or \
800 self
._comp
.CloseCompressor()
808 ## if 0: access *: private
810 def _comp_data(self
, data
):
812 dum
= self
._comp
.SetParam(cl
.FRAME_BUFFER_SIZE
, len(data
))
813 dum
= self
._comp
.SetParam(cl
.COMPRESSED_BUFFER_SIZE
, len(data
))
814 return self
._comp
.Compress(nframes
, data
)
816 def _lin2ulaw(self
, data
):
818 return audioop
.lin2ulaw(data
, 2)
820 def _lin2adpcm(self
, data
):
822 if not hasattr(self
, '_adpcmstate'):
823 self
._adpcmstate
= None
824 data
, self
._adpcmstate
= audioop
.lin2adpcm(data
, 2,
828 def _ensure_header_written(self
, datasize
):
829 if not self
._nframeswritten
:
830 if self
._comptype
in ('ULAW', 'ALAW'):
831 if not self
._sampwidth
:
833 if self
._sampwidth
!= 2:
834 raise Error
, 'sample width must be 2 when compressing with ULAW or ALAW'
835 if self
._comptype
== 'G722':
836 if not self
._sampwidth
:
838 if self
._sampwidth
!= 2:
839 raise Error
, 'sample width must be 2 when compressing with G7.22 (ADPCM)'
840 if not self
._nchannels
:
841 raise Error
, '# channels not specified'
842 if not self
._sampwidth
:
843 raise Error
, 'sample width not specified'
844 if not self
._framerate
:
845 raise Error
, 'sampling rate not specified'
846 self
._write
_header
(datasize
)
848 def _init_compression(self
):
849 if self
._comptype
== 'G722':
851 self
._convert
= self
._lin
2adpcm
856 if self
._comptype
== 'ULAW':
859 self
._convert
= self
._lin
2ulaw
863 raise Error
, 'cannot write compressed AIFF-C files'
864 if self
._comptype
== 'ULAW':
865 scheme
= cl
.G711_ULAW
866 elif self
._comptype
== 'ALAW':
867 scheme
= cl
.G711_ALAW
869 raise Error
, 'unsupported compression type'
870 self
._comp
= cl
.OpenCompressor(scheme
)
871 params
= [cl
.ORIGINAL_FORMAT
, 0,
872 cl
.BITS_PER_COMPONENT
, self
._sampwidth
* 8,
873 cl
.FRAME_RATE
, self
._framerate
,
874 cl
.FRAME_BUFFER_SIZE
, 100,
875 cl
.COMPRESSED_BUFFER_SIZE
, 100]
876 if self
._nchannels
== 1:
878 elif self
._nchannels
== 2:
879 params
[1] = cl
.STEREO_INTERLEAVED
881 raise Error
, 'cannot compress more than 2 channels'
882 self
._comp
.SetParams(params
)
883 # the compressor produces a header which we ignore
884 dummy
= self
._comp
.Compress(0, '')
885 self
._convert
= self
._comp
_data
887 def _write_header(self
, initlength
):
888 if self
._aifc
and self
._comptype
!= 'NONE':
889 self
._init
_compression
()
890 self
._file
.write('FORM')
891 if not self
._nframes
:
892 self
._nframes
= initlength
/ (self
._nchannels
* self
._sampwidth
)
893 self
._datalength
= self
._nframes
* self
._nchannels
* self
._sampwidth
894 if self
._datalength
& 1:
895 self
._datalength
= self
._datalength
+ 1
897 if self
._comptype
in ('ULAW', 'ALAW'):
898 self
._datalength
= self
._datalength
/ 2
899 if self
._datalength
& 1:
900 self
._datalength
= self
._datalength
+ 1
901 elif self
._comptype
== 'G722':
902 self
._datalength
= (self
._datalength
+ 3) / 4
903 if self
._datalength
& 1:
904 self
._datalength
= self
._datalength
+ 1
905 self
._form
_length
_pos
= self
._file
.tell()
906 commlength
= self
._write
_form
_length
(self
._datalength
)
908 self
._file
.write('AIFC')
909 self
._file
.write('FVER')
910 _write_long(self
._file
, 4)
911 _write_long(self
._file
, self
._version
)
913 self
._file
.write('AIFF')
914 self
._file
.write('COMM')
915 _write_long(self
._file
, commlength
)
916 _write_short(self
._file
, self
._nchannels
)
917 self
._nframes
_pos
= self
._file
.tell()
918 _write_long(self
._file
, self
._nframes
)
919 _write_short(self
._file
, self
._sampwidth
* 8)
920 _write_float(self
._file
, self
._framerate
)
922 self
._file
.write(self
._comptype
)
923 _write_string(self
._file
, self
._compname
)
924 self
._file
.write('SSND')
925 self
._ssnd
_length
_pos
= self
._file
.tell()
926 _write_long(self
._file
, self
._datalength
+ 8)
927 _write_long(self
._file
, 0)
928 _write_long(self
._file
, 0)
930 def _write_form_length(self
, datalength
):
932 commlength
= 18 + 5 + len(self
._compname
)
934 commlength
= commlength
+ 1
939 _write_long(self
._file
, 4 + verslength
+ self
._marklength
+ \
940 8 + commlength
+ 16 + datalength
)
943 def _patchheader(self
):
944 curpos
= self
._file
.tell()
945 if self
._datawritten
& 1:
946 datalength
= self
._datawritten
+ 1
947 self
._file
.write(chr(0))
949 datalength
= self
._datawritten
950 if datalength
== self
._datalength
and \
951 self
._nframes
== self
._nframeswritten
and \
952 self
._marklength
== 0:
953 self
._file
.seek(curpos
, 0)
955 self
._file
.seek(self
._form
_length
_pos
, 0)
956 dummy
= self
._write
_form
_length
(datalength
)
957 self
._file
.seek(self
._nframes
_pos
, 0)
958 _write_long(self
._file
, self
._nframeswritten
)
959 self
._file
.seek(self
._ssnd
_length
_pos
, 0)
960 _write_long(self
._file
, datalength
+ 8)
961 self
._file
.seek(curpos
, 0)
962 self
._nframes
= self
._nframeswritten
963 self
._datalength
= datalength
965 def _writemarkers(self
):
966 if len(self
._markers
) == 0:
968 self
._file
.write('MARK')
970 for marker
in self
._markers
:
971 id, pos
, name
= marker
972 length
= length
+ len(name
) + 1 + 6
973 if len(name
) & 1 == 0:
975 _write_long(self
._file
, length
)
976 self
._marklength
= length
+ 8
977 _write_short(self
._file
, len(self
._markers
))
978 for marker
in self
._markers
:
979 id, pos
, name
= marker
980 _write_short(self
._file
, id)
981 _write_long(self
._file
, pos
)
982 _write_string(self
._file
, name
)
990 raise Error
, "mode must be 'r' or 'w'"
992 openfp
= open # B/W compatibility
994 if __name__
== '__main__':
997 sys
.argv
.append('/usr/demos/data/audio/bach.aiff')
1001 print "nchannels =", f
.getnchannels()
1002 print "nframes =", f
.getnframes()
1003 print "sampwidth =", f
.getsampwidth()
1004 print "framerate =", f
.getframerate()
1005 print "comptype =", f
.getcomptype()
1006 print "compname =", f
.getcompname()
1011 g
.setparams(f
.getparams())
1013 data
= f
.readframes(1024)