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
= 0xA2805140 # 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
* 0x100000000 + 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")
203 f
.write(struct
.pack('b', len(s
)))
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
)
234 fmant
= math
.ldexp(fmant
- fsmant
, 32)
235 fsmant
= math
.floor(fmant
)
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):
286 if chunk
.getname() != b
'FORM':
287 raise Error('file does not start with FORM id')
288 formdata
= chunk
.read(4)
289 if formdata
== b
'AIFF':
291 elif formdata
== b
'AIFC':
294 raise Error('not an AIFF or AIFF-C file')
295 self
._comm
_chunk
_read
= 0
297 self
._ssnd
_seek
_needed
= 1
299 chunk
= Chunk(self
._file
)
302 chunkname
= chunk
.getname()
303 if chunkname
== b
'COMM':
304 self
._read
_comm
_chunk
(chunk
)
305 self
._comm
_chunk
_read
= 1
306 elif chunkname
== b
'SSND':
307 self
._ssnd
_chunk
= chunk
308 dummy
= chunk
.read(8)
309 self
._ssnd
_seek
_needed
= 0
310 elif chunkname
== b
'FVER':
311 self
._version
= _read_ulong(chunk
)
312 elif chunkname
== b
'MARK':
313 self
._readmark
(chunk
)
315 if not self
._comm
_chunk
_read
or not self
._ssnd
_chunk
:
316 raise Error('COMM chunk and/or SSND chunk missing')
318 def __init__(self
, f
):
319 if isinstance(f
, str):
320 f
= builtins
.open(f
, 'rb')
321 # else, assume it is an open file object already
325 # User visible methods.
331 self
._ssnd
_seek
_needed
= 1
338 return self
._soundpos
340 def getnchannels(self
):
341 return self
._nchannels
343 def getnframes(self
):
346 def getsampwidth(self
):
347 return self
._sampwidth
349 def getframerate(self
):
350 return self
._framerate
352 def getcomptype(self
):
353 return self
._comptype
355 def getcompname(self
):
356 return self
._compname
358 ## def getversion(self):
359 ## return self._version
362 return self
.getnchannels(), self
.getsampwidth(), \
363 self
.getframerate(), self
.getnframes(), \
364 self
.getcomptype(), self
.getcompname()
366 def getmarkers(self
):
367 if len(self
._markers
) == 0:
371 def getmark(self
, id):
372 for marker
in self
._markers
:
375 raise Error('marker {0!r} does not exist'.format(id))
377 def setpos(self
, pos
):
378 if pos
< 0 or pos
> self
._nframes
:
379 raise Error('position not in range')
381 self
._ssnd
_seek
_needed
= 1
383 def readframes(self
, nframes
):
384 if self
._ssnd
_seek
_needed
:
385 self
._ssnd
_chunk
.seek(0)
386 dummy
= self
._ssnd
_chunk
.read(8)
387 pos
= self
._soundpos
* self
._framesize
389 self
._ssnd
_chunk
.seek(pos
+ 8)
390 self
._ssnd
_seek
_needed
= 0
393 data
= self
._ssnd
_chunk
.read(nframes
* self
._framesize
)
394 if self
._convert
and data
:
395 data
= self
._convert
(data
)
396 self
._soundpos
= self
._soundpos
+ len(data
) // (self
._nchannels
404 def _alaw2lin(self
, data
):
406 return audioop
.alaw2lin(data
, 2)
408 def _ulaw2lin(self
, data
):
410 return audioop
.ulaw2lin(data
, 2)
412 def _adpcm2lin(self
, data
):
414 if not hasattr(self
, '_adpcmstate'):
416 self
._adpcmstate
= None
417 data
, self
._adpcmstate
= audioop
.adpcm2lin(data
, 2, self
._adpcmstate
)
420 def _read_comm_chunk(self
, chunk
):
421 self
._nchannels
= _read_short(chunk
)
422 self
._nframes
= _read_long(chunk
)
423 self
._sampwidth
= (_read_short(chunk
) + 7) // 8
424 self
._framerate
= int(_read_float(chunk
))
425 self
._framesize
= self
._nchannels
* self
._sampwidth
427 #DEBUG: SGI's soundeditor produces a bad size :-(
429 if chunk
.chunksize
== 18:
431 print('Warning: bad COMM chunk size')
434 self
._comptype
= chunk
.read(4)
437 length
= ord(chunk
.file.read(1))
440 chunk
.chunksize
= chunk
.chunksize
+ length
441 chunk
.file.seek(-1, 1)
443 self
._compname
= _read_string(chunk
)
444 if self
._comptype
!= b
'NONE':
445 if self
._comptype
== b
'G722':
446 self
._convert
= self
._adpcm
2lin
447 self
._framesize
= self
._framesize
// 4
448 elif self
._comptype
in (b
'ulaw', b
'ULAW'):
449 self
._convert
= self
._ulaw
2lin
450 self
._framesize
= self
._framesize
// 2
451 elif self
._comptype
in (b
'alaw', b
'ALAW'):
452 self
._convert
= self
._alaw
2lin
453 self
._framesize
= self
._framesize
// 2
455 raise Error('unsupported compression type')
457 self
._comptype
= b
'NONE'
458 self
._compname
= b
'not compressed'
460 def _readmark(self
, chunk
):
461 nmarkers
= _read_short(chunk
)
462 # Some files appear to contain invalid counts.
463 # Cope with this by testing for EOF.
465 for i
in range(nmarkers
):
466 id = _read_short(chunk
)
467 pos
= _read_long(chunk
)
468 name
= _read_string(chunk
)
470 # some files appear to have
471 # dummy markers consisting of
472 # a position 0 and name ''
473 self
._markers
.append((id, pos
, name
))
475 print('Warning: MARK chunk contains only', end
=' ')
476 print(len(self
._markers
), end
=' ')
477 if len(self
._markers
) == 1: print('marker', end
=' ')
478 else: print('markers', end
=' ')
479 print('instead of', nmarkers
)
482 # Variables used in this class:
484 # These variables are user settable through appropriate methods
486 # _file -- the open file with methods write(), close(), tell(), seek()
487 # set through the __init__() method
488 # _comptype -- the AIFF-C compression type ('NONE' in AIFF)
489 # set through the setcomptype() or setparams() method
490 # _compname -- the human-readable AIFF-C compression type
491 # set through the setcomptype() or setparams() method
492 # _nchannels -- the number of audio channels
493 # set through the setnchannels() or setparams() method
494 # _sampwidth -- the number of bytes per audio sample
495 # set through the setsampwidth() or setparams() method
496 # _framerate -- the sampling frequency
497 # set through the setframerate() or setparams() method
498 # _nframes -- the number of audio frames written to the header
499 # set through the setnframes() or setparams() method
500 # _aifc -- whether we're writing an AIFF-C file or an AIFF file
501 # set through the aifc() method, reset through the
504 # These variables are used internally only:
505 # _version -- the AIFF-C version number
506 # _comp -- the compressor from builtin module cl
507 # _nframeswritten -- the number of audio frames actually written
508 # _datalength -- the size of the audio samples written to the header
509 # _datawritten -- the size of the audio samples actually written
511 def __init__(self
, f
):
512 if isinstance(f
, str):
514 f
= builtins
.open(f
, 'wb')
516 # else, assume it is an open file object already
519 if filename
[-5:] == '.aiff':
524 def initfp(self
, file):
526 self
._version
= _AIFC_version
527 self
._comptype
= b
'NONE'
528 self
._compname
= b
'not compressed'
534 self
._nframeswritten
= 0
535 self
._datawritten
= 0
539 self
._aifc
= 1 # AIFF-C is default
546 # User visible methods.
549 if self
._nframeswritten
:
550 raise Error('cannot change parameters after starting to write')
554 if self
._nframeswritten
:
555 raise Error('cannot change parameters after starting to write')
558 def setnchannels(self
, nchannels
):
559 if self
._nframeswritten
:
560 raise Error('cannot change parameters after starting to write')
562 raise Error('bad # of channels')
563 self
._nchannels
= nchannels
565 def getnchannels(self
):
566 if not self
._nchannels
:
567 raise Error('number of channels not set')
568 return self
._nchannels
570 def setsampwidth(self
, sampwidth
):
571 if self
._nframeswritten
:
572 raise Error('cannot change parameters after starting to write')
573 if sampwidth
< 1 or sampwidth
> 4:
574 raise Error('bad sample width')
575 self
._sampwidth
= sampwidth
577 def getsampwidth(self
):
578 if not self
._sampwidth
:
579 raise Error('sample width not set')
580 return self
._sampwidth
582 def setframerate(self
, framerate
):
583 if self
._nframeswritten
:
584 raise Error('cannot change parameters after starting to write')
586 raise Error('bad frame rate')
587 self
._framerate
= framerate
589 def getframerate(self
):
590 if not self
._framerate
:
591 raise Error('frame rate not set')
592 return self
._framerate
594 def setnframes(self
, nframes
):
595 if self
._nframeswritten
:
596 raise Error('cannot change parameters after starting to write')
597 self
._nframes
= nframes
599 def getnframes(self
):
600 return self
._nframeswritten
602 def setcomptype(self
, comptype
, compname
):
603 if self
._nframeswritten
:
604 raise Error('cannot change parameters after starting to write')
605 if comptype
not in (b
'NONE', b
'ulaw', b
'ULAW',
606 b
'alaw', b
'ALAW', b
'G722'):
607 raise Error('unsupported compression type')
608 self
._comptype
= comptype
609 self
._compname
= compname
611 def getcomptype(self
):
612 return self
._comptype
614 def getcompname(self
):
615 return self
._compname
617 ## def setversion(self, version):
618 ## if self._nframeswritten:
619 ## raise Error, 'cannot change parameters after starting to write'
620 ## self._version = version
622 def setparams(self
, params
):
623 nchannels
, sampwidth
, framerate
, nframes
, comptype
, compname
= params
624 if self
._nframeswritten
:
625 raise Error('cannot change parameters after starting to write')
626 if comptype
not in (b
'NONE', b
'ulaw', b
'ULAW',
627 b
'alaw', b
'ALAW', b
'G722'):
628 raise Error('unsupported compression type')
629 self
.setnchannels(nchannels
)
630 self
.setsampwidth(sampwidth
)
631 self
.setframerate(framerate
)
632 self
.setnframes(nframes
)
633 self
.setcomptype(comptype
, compname
)
636 if not self
._nchannels
or not self
._sampwidth
or not self
._framerate
:
637 raise Error('not all parameters set')
638 return self
._nchannels
, self
._sampwidth
, self
._framerate
, \
639 self
._nframes
, self
._comptype
, self
._compname
641 def setmark(self
, id, pos
, name
):
643 raise Error('marker ID must be > 0')
645 raise Error('marker position must be >= 0')
646 if not isinstance(name
, str):
647 raise Error('marker name must be a string')
648 for i
in range(len(self
._markers
)):
649 if id == self
._markers
[i
][0]:
650 self
._markers
[i
] = id, pos
, name
652 self
._markers
.append((id, pos
, name
))
654 def getmark(self
, id):
655 for marker
in self
._markers
:
658 raise Error('marker {0!r} does not exist'.format(id))
660 def getmarkers(self
):
661 if len(self
._markers
) == 0:
666 return self
._nframeswritten
668 def writeframesraw(self
, data
):
669 self
._ensure
_header
_written
(len(data
))
670 nframes
= len(data
) // (self
._sampwidth
* self
._nchannels
)
672 data
= self
._convert
(data
)
673 self
._file
.write(data
)
674 self
._nframeswritten
= self
._nframeswritten
+ nframes
675 self
._datawritten
= self
._datawritten
+ len(data
)
677 def writeframes(self
, data
):
678 self
.writeframesraw(data
)
679 if self
._nframeswritten
!= self
._nframes
or \
680 self
._datalength
!= self
._datawritten
:
684 self
._ensure
_header
_written
(0)
685 if self
._datawritten
& 1:
686 # quick pad to even size
687 self
._file
.write(b
'\x00')
688 self
._datawritten
= self
._datawritten
+ 1
690 if self
._nframeswritten
!= self
._nframes
or \
691 self
._datalength
!= self
._datawritten
or \
702 def _lin2alaw(self
, data
):
704 return audioop
.lin2alaw(data
, 2)
706 def _lin2ulaw(self
, data
):
708 return audioop
.lin2ulaw(data
, 2)
710 def _lin2adpcm(self
, data
):
712 if not hasattr(self
, '_adpcmstate'):
713 self
._adpcmstate
= None
714 data
, self
._adpcmstate
= audioop
.lin2adpcm(data
, 2, self
._adpcmstate
)
717 def _ensure_header_written(self
, datasize
):
718 if not self
._nframeswritten
:
719 if self
._comptype
in (b
'ULAW', b
'ALAW'):
720 if not self
._sampwidth
:
722 if self
._sampwidth
!= 2:
723 raise Error('sample width must be 2 when compressing '
724 'with ulaw/ULAW or alaw/ALAW')
725 if self
._comptype
== b
'G722':
726 if not self
._sampwidth
:
728 if self
._sampwidth
!= 2:
729 raise Error('sample width must be 2 when compressing '
730 'with G7.22 (ADPCM)')
731 if not self
._nchannels
:
732 raise Error('# channels not specified')
733 if not self
._sampwidth
:
734 raise Error('sample width not specified')
735 if not self
._framerate
:
736 raise Error('sampling rate not specified')
737 self
._write
_header
(datasize
)
739 def _init_compression(self
):
740 if self
._comptype
== b
'G722':
741 self
._convert
= self
._lin
2adpcm
742 elif self
._comptype
in (b
'ulaw', b
'ULAW'):
743 self
._convert
= self
._lin
2ulaw
744 elif self
._comptype
in (b
'alaw', b
'ALAW'):
745 self
._convert
= self
._lin
2alaw
747 raise Error('unsupported compression type')
749 def _write_header(self
, initlength
):
750 if self
._aifc
and self
._comptype
!= b
'NONE':
751 self
._init
_compression
()
752 self
._file
.write(b
'FORM')
753 if not self
._nframes
:
754 self
._nframes
= initlength
// (self
._nchannels
* self
._sampwidth
)
755 self
._datalength
= self
._nframes
* self
._nchannels
* self
._sampwidth
756 if self
._datalength
& 1:
757 self
._datalength
= self
._datalength
+ 1
759 if self
._comptype
in (b
'ulaw', b
'ULAW', b
'alaw', b
'ALAW'):
760 self
._datalength
= self
._datalength
// 2
761 if self
._datalength
& 1:
762 self
._datalength
= self
._datalength
+ 1
763 elif self
._comptype
== b
'G722':
764 self
._datalength
= (self
._datalength
+ 3) // 4
765 if self
._datalength
& 1:
766 self
._datalength
= self
._datalength
+ 1
767 self
._form
_length
_pos
= self
._file
.tell()
768 commlength
= self
._write
_form
_length
(self
._datalength
)
770 self
._file
.write(b
'AIFC')
771 self
._file
.write(b
'FVER')
772 _write_long(self
._file
, 4)
773 _write_long(self
._file
, self
._version
)
775 self
._file
.write(b
'AIFF')
776 self
._file
.write(b
'COMM')
777 _write_long(self
._file
, commlength
)
778 _write_short(self
._file
, self
._nchannels
)
779 self
._nframes
_pos
= self
._file
.tell()
780 _write_long(self
._file
, self
._nframes
)
781 _write_short(self
._file
, self
._sampwidth
* 8)
782 _write_float(self
._file
, self
._framerate
)
784 self
._file
.write(self
._comptype
)
785 _write_string(self
._file
, self
._compname
)
786 self
._file
.write(b
'SSND')
787 self
._ssnd
_length
_pos
= self
._file
.tell()
788 _write_long(self
._file
, self
._datalength
+ 8)
789 _write_long(self
._file
, 0)
790 _write_long(self
._file
, 0)
792 def _write_form_length(self
, datalength
):
794 commlength
= 18 + 5 + len(self
._compname
)
796 commlength
= commlength
+ 1
801 _write_long(self
._file
, 4 + verslength
+ self
._marklength
+ \
802 8 + commlength
+ 16 + datalength
)
805 def _patchheader(self
):
806 curpos
= self
._file
.tell()
807 if self
._datawritten
& 1:
808 datalength
= self
._datawritten
+ 1
809 self
._file
.write(b
'\x00')
811 datalength
= self
._datawritten
812 if datalength
== self
._datalength
and \
813 self
._nframes
== self
._nframeswritten
and \
814 self
._marklength
== 0:
815 self
._file
.seek(curpos
, 0)
817 self
._file
.seek(self
._form
_length
_pos
, 0)
818 dummy
= self
._write
_form
_length
(datalength
)
819 self
._file
.seek(self
._nframes
_pos
, 0)
820 _write_long(self
._file
, self
._nframeswritten
)
821 self
._file
.seek(self
._ssnd
_length
_pos
, 0)
822 _write_long(self
._file
, datalength
+ 8)
823 self
._file
.seek(curpos
, 0)
824 self
._nframes
= self
._nframeswritten
825 self
._datalength
= datalength
827 def _writemarkers(self
):
828 if len(self
._markers
) == 0:
830 self
._file
.write(b
'MARK')
832 for marker
in self
._markers
:
833 id, pos
, name
= marker
834 length
= length
+ len(name
) + 1 + 6
835 if len(name
) & 1 == 0:
837 _write_long(self
._file
, length
)
838 self
._marklength
= length
+ 8
839 _write_short(self
._file
, len(self
._markers
))
840 for marker
in self
._markers
:
841 id, pos
, name
= marker
842 _write_short(self
._file
, id)
843 _write_long(self
._file
, pos
)
844 _write_string(self
._file
, name
)
846 def open(f
, mode
=None):
848 if hasattr(f
, 'mode'):
852 if mode
in ('r', 'rb'):
854 elif mode
in ('w', 'wb'):
857 raise Error("mode must be 'r', 'rb', 'w', or 'wb'")
859 openfp
= open # B/W compatibility
861 if __name__
== '__main__':
864 sys
.argv
.append('/usr/demos/data/audio/bach.aiff')
868 print("nchannels =", f
.getnchannels())
869 print("nframes =", f
.getnframes())
870 print("sampwidth =", f
.getsampwidth())
871 print("framerate =", f
.getframerate())
872 print("comptype =", f
.getcomptype())
873 print("compname =", f
.getcompname())
878 g
.setparams(f
.getparams())
880 data
= f
.readframes(1024)