1
"""Stuff to parse AIFF-C and AIFF files.
3
Unless explicitly stated otherwise, the description below is true
4
both for AIFF-C files and AIFF files.
6
An AIFF-C file has the following structure.
21
An AIFF file has the string "AIFF" instead of "AIFC".
23
A chunk consists of an identifier (4 bytes) followed by a size (4 bytes,
24
big endian order), followed by the data. The size field does not include
25
the size of the 8 byte header.
27
The following chunk types are recognized.
30
<version number of AIFF-C defining document> (AIFF-C only).
32
<# of markers> (2 bytes)
34
<marker ID> (2 bytes, must be > 0)
36
<marker name> ("pstring")
38
<# of channels> (2 bytes)
39
<# of sound frames> (4 bytes)
40
<size of the samples> (2 bytes)
41
<sampling frequency> (10 bytes, IEEE 80-bit extended
44
<compression type> (4 bytes)
45
<human-readable version of compression type> ("pstring")
47
<offset> (4 bytes, not used by this program)
48
<blocksize> (4 bytes, not used by this program)
51
A pstring consists of 1 byte length, a string of characters, and 0 or 1
52
byte pad to make the total length even.
57
f = aifc.open(file, 'r')
58
where file is either the name of a file or an open file pointer.
59
The open file pointer must have methods read(), seek(), and close().
60
In some types of audio files, if the setpos() method is not used,
61
the seek() method is not necessary.
63
This returns an instance of a class with the following public methods:
64
getnchannels() -- returns number of audio channels (1 for
66
getsampwidth() -- returns sample width in bytes
67
getframerate() -- returns sampling frequency
68
getnframes() -- returns number of audio frames
69
getcomptype() -- returns compression type ('NONE' for AIFF files)
70
getcompname() -- returns human-readable version of
71
compression type ('not compressed' for AIFF files)
72
getparams() -- returns a tuple consisting of all of the
73
above in the above order
74
getmarkers() -- get the list of marks in the audio file or None
76
getmark(id) -- get mark with the specified id (raises an error
77
if the mark does not exist)
78
readframes(n) -- returns at most n frames of audio
79
rewind() -- rewind to the beginning of the audio stream
80
setpos(pos) -- seek to the specified position
81
tell() -- return the current position
82
close() -- close the instance (make it unusable)
83
The position returned by tell(), the position given to setpos() and
84
the position of marks are all compatible and have nothing to do with
85
the actual position in the file.
86
The close() method is called automatically when the class instance
90
f = aifc.open(file, 'w')
91
where file is either the name of a file or an open file pointer.
92
The open file pointer must have methods write(), tell(), seek(), and
95
This returns an instance of a class with the following public methods:
96
aiff() -- create an AIFF file (AIFF-C default)
97
aifc() -- create an AIFF-C file
98
setnchannels(n) -- set the number of channels
99
setsampwidth(n) -- set the sample width
100
setframerate(n) -- set the frame rate
101
setnframes(n) -- set the number of frames
102
setcomptype(type, name)
103
-- set the compression type and the
104
human-readable compression type
106
-- set all parameters at once
107
setmark(id, pos, name)
108
-- add specified mark to the list of marks
109
tell() -- return current position in output file (useful
110
in combination with setmark())
112
-- write audio frames without pathing up the
115
-- write audio frames and patch up the file header
116
close() -- patch up the file header and close the
118
You should set the parameters before the first writeframesraw or
119
writeframes. The total number of frames does not need to be set,
120
but when it is set to the correct value, the header does not have to
122
It is best to first set all parameters, perhaps possibly the
123
compression type, and then write audio frames using writeframesraw.
124
When all frames have been written, either call writeframes('') or
125
close() to patch up the sizes in the header.
126
Marks can be added anytime. If there are any marks, ypu must call
127
close() after all frames have been written.
128
The close() method is called automatically when the class instance
131
When a file is opened with the extension '.aiff', an AIFF file is
132
written, otherwise an AIFF-C file is written. This default can be
133
changed by calling aiff() or aifc() before the first writeframes or
140
__all__ = ["Error","open","openfp"]
142
class Error(Exception):
145
_AIFC_version = 0xA2805140L # Version 1 of AIFF-C
147
_skiplist = 'COMT', 'INST', 'MIDI', 'AESD', \
148
'APPL', 'NAME', 'AUTH', '(c) ', 'ANNO'
150
def _read_long(file):
152
return struct.unpack('>l', file.read(4))[0]
156
def _read_ulong(file):
158
return struct.unpack('>L', file.read(4))[0]
162
def _read_short(file):
164
return struct.unpack('>h', file.read(2))[0]
168
def _read_string(file):
169
length = ord(file.read(1))
173
data = file.read(length)
178
_HUGE_VAL = 1.79769313486231e+308 # See <limits.h>
180
def _read_float(f): # 10 bytes
181
expon = _read_short(f) # 2 bytes
185
expon = expon + 0x8000
186
himant = _read_ulong(f) # 4 bytes
187
lomant = _read_ulong(f) # 4 bytes
188
if expon == himant == lomant == 0:
190
elif expon == 0x7FFF:
193
expon = expon - 16383
194
f = (himant * 0x100000000L + lomant) * pow(2.0, expon - 63)
197
def _write_short(f, x):
198
f.write(struct.pack('>h', x))
200
def _write_long(f, x):
201
f.write(struct.pack('>L', x))
203
def _write_string(f, s):
205
raise ValueError("string exceeds maximum pstring length")
211
def _write_float(f, x):
223
fmant, expon = math.frexp(x)
224
if expon > 16384 or fmant >= 1: # Infinity or NaN
229
expon = expon + 16382
230
if expon < 0: # denormalized
231
fmant = math.ldexp(fmant, expon)
234
fmant = math.ldexp(fmant, 32)
235
fsmant = math.floor(fmant)
236
himant = long(fsmant)
237
fmant = math.ldexp(fmant - fsmant, 32)
238
fsmant = math.floor(fmant)
239
lomant = long(fsmant)
240
_write_short(f, expon)
241
_write_long(f, himant)
242
_write_long(f, lomant)
244
from chunk import Chunk
247
# Variables used in this class:
249
# These variables are available to the user though appropriate
250
# methods of this class:
251
# _file -- the open file with methods read(), close(), and seek()
252
# set through the __init__() method
253
# _nchannels -- the number of audio channels
254
# available through the getnchannels() method
255
# _nframes -- the number of audio frames
256
# available through the getnframes() method
257
# _sampwidth -- the number of bytes per audio sample
258
# available through the getsampwidth() method
259
# _framerate -- the sampling frequency
260
# available through the getframerate() method
261
# _comptype -- the AIFF-C compression type ('NONE' if AIFF)
262
# available through the getcomptype() method
263
# _compname -- the human-readable AIFF-C compression type
264
# available through the getcomptype() method
265
# _markers -- the marks in the audio file
266
# available through the getmarkers() and getmark()
268
# _soundpos -- the position in the audio stream
269
# available through the tell() method, set through the
272
# These variables are used internally only:
273
# _version -- the AIFF-C version number
274
# _decomp -- the decompressor from builtin module cl
275
# _comm_chunk_read -- 1 iff the COMM chunk has been read
276
# _aifc -- 1 iff reading an AIFF-C file
277
# _ssnd_seek_needed -- 1 iff positioned correctly in audio
278
# file for readframes()
279
# _ssnd_chunk -- instantiation of a chunk class for the SSND chunk
280
# _framesize -- size of one frame in the file
282
def initfp(self, file):
288
self._file = Chunk(file)
289
if self._file.getname() != 'FORM':
290
raise Error, 'file does not start with FORM id'
291
formdata = self._file.read(4)
292
if formdata == 'AIFF':
294
elif formdata == 'AIFC':
297
raise Error, 'not an AIFF or AIFF-C file'
298
self._comm_chunk_read = 0
300
self._ssnd_seek_needed = 1
302
chunk = Chunk(self._file)
305
chunkname = chunk.getname()
306
if chunkname == 'COMM':
307
self._read_comm_chunk(chunk)
308
self._comm_chunk_read = 1
309
elif chunkname == 'SSND':
310
self._ssnd_chunk = chunk
311
dummy = chunk.read(8)
312
self._ssnd_seek_needed = 0
313
elif chunkname == 'FVER':
314
self._version = _read_ulong(chunk)
315
elif chunkname == 'MARK':
316
self._readmark(chunk)
317
elif chunkname in _skiplist:
320
raise Error, 'unrecognized chunk type '+chunk.chunkname
322
if not self._comm_chunk_read or not self._ssnd_chunk:
323
raise Error, 'COMM chunk and/or SSND chunk missing'
324
if self._aifc and self._decomp:
326
params = [cl.ORIGINAL_FORMAT, 0,
327
cl.BITS_PER_COMPONENT, self._sampwidth * 8,
328
cl.FRAME_RATE, self._framerate]
329
if self._nchannels == 1:
331
elif self._nchannels == 2:
332
params[1] = cl.STEREO_INTERLEAVED
334
raise Error, 'cannot compress more than 2 channels'
335
self._decomp.SetParams(params)
337
def __init__(self, f):
338
if type(f) == type(''):
339
f = __builtin__.open(f, 'rb')
340
# else, assume it is an open file object already
344
# User visible methods.
350
self._ssnd_seek_needed = 1
355
self._decomp.CloseDecompressor()
360
return self._soundpos
362
def getnchannels(self):
363
return self._nchannels
365
def getnframes(self):
368
def getsampwidth(self):
369
return self._sampwidth
371
def getframerate(self):
372
return self._framerate
374
def getcomptype(self):
375
return self._comptype
377
def getcompname(self):
378
return self._compname
380
## def getversion(self):
381
## return self._version
384
return self.getnchannels(), self.getsampwidth(), \
385
self.getframerate(), self.getnframes(), \
386
self.getcomptype(), self.getcompname()
388
def getmarkers(self):
389
if len(self._markers) == 0:
393
def getmark(self, id):
394
for marker in self._markers:
397
raise Error, 'marker %r does not exist' % (id,)
399
def setpos(self, pos):
400
if pos < 0 or pos > self._nframes:
401
raise Error, 'position not in range'
403
self._ssnd_seek_needed = 1
405
def readframes(self, nframes):
406
if self._ssnd_seek_needed:
407
self._ssnd_chunk.seek(0)
408
dummy = self._ssnd_chunk.read(8)
409
pos = self._soundpos * self._framesize
411
self._ssnd_chunk.seek(pos + 8)
412
self._ssnd_seek_needed = 0
415
data = self._ssnd_chunk.read(nframes * self._framesize)
416
if self._convert and data:
417
data = self._convert(data)
418
self._soundpos = self._soundpos + len(data) / (self._nchannels * self._sampwidth)
425
def _decomp_data(self, data):
427
dummy = self._decomp.SetParam(cl.FRAME_BUFFER_SIZE,
429
return self._decomp.Decompress(len(data) / self._nchannels,
432
def _ulaw2lin(self, data):
434
return audioop.ulaw2lin(data, 2)
436
def _adpcm2lin(self, data):
438
if not hasattr(self, '_adpcmstate'):
440
self._adpcmstate = None
441
data, self._adpcmstate = audioop.adpcm2lin(data, 2,
445
def _read_comm_chunk(self, chunk):
446
self._nchannels = _read_short(chunk)
447
self._nframes = _read_long(chunk)
448
self._sampwidth = (_read_short(chunk) + 7) / 8
449
self._framerate = int(_read_float(chunk))
450
self._framesize = self._nchannels * self._sampwidth
452
#DEBUG: SGI's soundeditor produces a bad size :-(
454
if chunk.chunksize == 18:
456
print 'Warning: bad COMM chunk size'
459
self._comptype = chunk.read(4)
462
length = ord(chunk.file.read(1))
465
chunk.chunksize = chunk.chunksize + length
466
chunk.file.seek(-1, 1)
468
self._compname = _read_string(chunk)
469
if self._comptype != 'NONE':
470
if self._comptype == 'G722':
476
self._convert = self._adpcm2lin
477
self._framesize = self._framesize / 4
479
# for ULAW and ALAW try Compression Library
483
if self._comptype == 'ULAW':
486
self._convert = self._ulaw2lin
487
self._framesize = self._framesize / 2
491
raise Error, 'cannot read compressed AIFF-C files'
492
if self._comptype == 'ULAW':
493
scheme = cl.G711_ULAW
494
self._framesize = self._framesize / 2
495
elif self._comptype == 'ALAW':
496
scheme = cl.G711_ALAW
497
self._framesize = self._framesize / 2
499
raise Error, 'unsupported compression type'
500
self._decomp = cl.OpenDecompressor(scheme)
501
self._convert = self._decomp_data
503
self._comptype = 'NONE'
504
self._compname = 'not compressed'
506
def _readmark(self, chunk):
507
nmarkers = _read_short(chunk)
508
# Some files appear to contain invalid counts.
509
# Cope with this by testing for EOF.
511
for i in range(nmarkers):
512
id = _read_short(chunk)
513
pos = _read_long(chunk)
514
name = _read_string(chunk)
516
# some files appear to have
517
# dummy markers consisting of
518
# a position 0 and name ''
519
self._markers.append((id, pos, name))
521
print 'Warning: MARK chunk contains only',
522
print len(self._markers),
523
if len(self._markers) == 1: print 'marker',
524
else: print 'markers',
525
print 'instead of', nmarkers
528
# Variables used in this class:
530
# These variables are user settable through appropriate methods
532
# _file -- the open file with methods write(), close(), tell(), seek()
533
# set through the __init__() method
534
# _comptype -- the AIFF-C compression type ('NONE' in AIFF)
535
# set through the setcomptype() or setparams() method
536
# _compname -- the human-readable AIFF-C compression type
537
# set through the setcomptype() or setparams() method
538
# _nchannels -- the number of audio channels
539
# set through the setnchannels() or setparams() method
540
# _sampwidth -- the number of bytes per audio sample
541
# set through the setsampwidth() or setparams() method
542
# _framerate -- the sampling frequency
543
# set through the setframerate() or setparams() method
544
# _nframes -- the number of audio frames written to the header
545
# set through the setnframes() or setparams() method
546
# _aifc -- whether we're writing an AIFF-C file or an AIFF file
547
# set through the aifc() method, reset through the
550
# These variables are used internally only:
551
# _version -- the AIFF-C version number
552
# _comp -- the compressor from builtin module cl
553
# _nframeswritten -- the number of audio frames actually written
554
# _datalength -- the size of the audio samples written to the header
555
# _datawritten -- the size of the audio samples actually written
557
def __init__(self, f):
558
if type(f) == type(''):
560
f = __builtin__.open(f, 'wb')
562
# else, assume it is an open file object already
565
if filename[-5:] == '.aiff':
570
def initfp(self, file):
572
self._version = _AIFC_version
573
self._comptype = 'NONE'
574
self._compname = 'not compressed'
581
self._nframeswritten = 0
582
self._datawritten = 0
586
self._aifc = 1 # AIFF-C is default
593
# User visible methods.
596
if self._nframeswritten:
597
raise Error, 'cannot change parameters after starting to write'
601
if self._nframeswritten:
602
raise Error, 'cannot change parameters after starting to write'
605
def setnchannels(self, nchannels):
606
if self._nframeswritten:
607
raise Error, 'cannot change parameters after starting to write'
609
raise Error, 'bad # of channels'
610
self._nchannels = nchannels
612
def getnchannels(self):
613
if not self._nchannels:
614
raise Error, 'number of channels not set'
615
return self._nchannels
617
def setsampwidth(self, sampwidth):
618
if self._nframeswritten:
619
raise Error, 'cannot change parameters after starting to write'
620
if sampwidth < 1 or sampwidth > 4:
621
raise Error, 'bad sample width'
622
self._sampwidth = sampwidth
624
def getsampwidth(self):
625
if not self._sampwidth:
626
raise Error, 'sample width not set'
627
return self._sampwidth
629
def setframerate(self, framerate):
630
if self._nframeswritten:
631
raise Error, 'cannot change parameters after starting to write'
633
raise Error, 'bad frame rate'
634
self._framerate = framerate
636
def getframerate(self):
637
if not self._framerate:
638
raise Error, 'frame rate not set'
639
return self._framerate
641
def setnframes(self, nframes):
642
if self._nframeswritten:
643
raise Error, 'cannot change parameters after starting to write'
644
self._nframes = nframes
646
def getnframes(self):
647
return self._nframeswritten
649
def setcomptype(self, comptype, compname):
650
if self._nframeswritten:
651
raise Error, 'cannot change parameters after starting to write'
652
if comptype not in ('NONE', 'ULAW', 'ALAW', 'G722'):
653
raise Error, 'unsupported compression type'
654
self._comptype = comptype
655
self._compname = compname
657
def getcomptype(self):
658
return self._comptype
660
def getcompname(self):
661
return self._compname
663
## def setversion(self, version):
664
## if self._nframeswritten:
665
## raise Error, 'cannot change parameters after starting to write'
666
## self._version = version
668
def setparams(self, info):
669
nchannels, sampwidth, framerate, nframes, comptype, compname = info
670
if self._nframeswritten:
671
raise Error, 'cannot change parameters after starting to write'
672
if comptype not in ('NONE', 'ULAW', 'ALAW', 'G722'):
673
raise Error, 'unsupported compression type'
674
self.setnchannels(nchannels)
675
self.setsampwidth(sampwidth)
676
self.setframerate(framerate)
677
self.setnframes(nframes)
678
self.setcomptype(comptype, compname)
681
if not self._nchannels or not self._sampwidth or not self._framerate:
682
raise Error, 'not all parameters set'
683
return self._nchannels, self._sampwidth, self._framerate, \
684
self._nframes, self._comptype, self._compname
686
def setmark(self, id, pos, name):
688
raise Error, 'marker ID must be > 0'
690
raise Error, 'marker position must be >= 0'
691
if type(name) != type(''):
692
raise Error, 'marker name must be a string'
693
for i in range(len(self._markers)):
694
if id == self._markers[i][0]:
695
self._markers[i] = id, pos, name
697
self._markers.append((id, pos, name))
699
def getmark(self, id):
700
for marker in self._markers:
703
raise Error, 'marker %r does not exist' % (id,)
705
def getmarkers(self):
706
if len(self._markers) == 0:
711
return self._nframeswritten
713
def writeframesraw(self, data):
714
self._ensure_header_written(len(data))
715
nframes = len(data) / (self._sampwidth * self._nchannels)
717
data = self._convert(data)
718
self._file.write(data)
719
self._nframeswritten = self._nframeswritten + nframes
720
self._datawritten = self._datawritten + len(data)
722
def writeframes(self, data):
723
self.writeframesraw(data)
724
if self._nframeswritten != self._nframes or \
725
self._datalength != self._datawritten:
729
self._ensure_header_written(0)
730
if self._datawritten & 1:
731
# quick pad to even size
732
self._file.write(chr(0))
733
self._datawritten = self._datawritten + 1
735
if self._nframeswritten != self._nframes or \
736
self._datalength != self._datawritten or \
740
self._comp.CloseCompressor()
749
def _comp_data(self, data):
751
dummy = self._comp.SetParam(cl.FRAME_BUFFER_SIZE, len(data))
752
dummy = self._comp.SetParam(cl.COMPRESSED_BUFFER_SIZE, len(data))
753
return self._comp.Compress(self._nframes, data)
755
def _lin2ulaw(self, data):
757
return audioop.lin2ulaw(data, 2)
759
def _lin2adpcm(self, data):
761
if not hasattr(self, '_adpcmstate'):
762
self._adpcmstate = None
763
data, self._adpcmstate = audioop.lin2adpcm(data, 2,
767
def _ensure_header_written(self, datasize):
768
if not self._nframeswritten:
769
if self._comptype in ('ULAW', 'ALAW'):
770
if not self._sampwidth:
772
if self._sampwidth != 2:
773
raise Error, 'sample width must be 2 when compressing with ULAW or ALAW'
774
if self._comptype == 'G722':
775
if not self._sampwidth:
777
if self._sampwidth != 2:
778
raise Error, 'sample width must be 2 when compressing with G7.22 (ADPCM)'
779
if not self._nchannels:
780
raise Error, '# channels not specified'
781
if not self._sampwidth:
782
raise Error, 'sample width not specified'
783
if not self._framerate:
784
raise Error, 'sampling rate not specified'
785
self._write_header(datasize)
787
def _init_compression(self):
788
if self._comptype == 'G722':
789
self._convert = self._lin2adpcm
794
if self._comptype == 'ULAW':
797
self._convert = self._lin2ulaw
801
raise Error, 'cannot write compressed AIFF-C files'
802
if self._comptype == 'ULAW':
803
scheme = cl.G711_ULAW
804
elif self._comptype == 'ALAW':
805
scheme = cl.G711_ALAW
807
raise Error, 'unsupported compression type'
808
self._comp = cl.OpenCompressor(scheme)
809
params = [cl.ORIGINAL_FORMAT, 0,
810
cl.BITS_PER_COMPONENT, self._sampwidth * 8,
811
cl.FRAME_RATE, self._framerate,
812
cl.FRAME_BUFFER_SIZE, 100,
813
cl.COMPRESSED_BUFFER_SIZE, 100]
814
if self._nchannels == 1:
816
elif self._nchannels == 2:
817
params[1] = cl.STEREO_INTERLEAVED
819
raise Error, 'cannot compress more than 2 channels'
820
self._comp.SetParams(params)
821
# the compressor produces a header which we ignore
822
dummy = self._comp.Compress(0, '')
823
self._convert = self._comp_data
825
def _write_header(self, initlength):
826
if self._aifc and self._comptype != 'NONE':
827
self._init_compression()
828
self._file.write('FORM')
829
if not self._nframes:
830
self._nframes = initlength / (self._nchannels * self._sampwidth)
831
self._datalength = self._nframes * self._nchannels * self._sampwidth
832
if self._datalength & 1:
833
self._datalength = self._datalength + 1
835
if self._comptype in ('ULAW', 'ALAW'):
836
self._datalength = self._datalength / 2
837
if self._datalength & 1:
838
self._datalength = self._datalength + 1
839
elif self._comptype == 'G722':
840
self._datalength = (self._datalength + 3) / 4
841
if self._datalength & 1:
842
self._datalength = self._datalength + 1
843
self._form_length_pos = self._file.tell()
844
commlength = self._write_form_length(self._datalength)
846
self._file.write('AIFC')
847
self._file.write('FVER')
848
_write_long(self._file, 4)
849
_write_long(self._file, self._version)
851
self._file.write('AIFF')
852
self._file.write('COMM')
853
_write_long(self._file, commlength)
854
_write_short(self._file, self._nchannels)
855
self._nframes_pos = self._file.tell()
856
_write_long(self._file, self._nframes)
857
_write_short(self._file, self._sampwidth * 8)
858
_write_float(self._file, self._framerate)
860
self._file.write(self._comptype)
861
_write_string(self._file, self._compname)
862
self._file.write('SSND')
863
self._ssnd_length_pos = self._file.tell()
864
_write_long(self._file, self._datalength + 8)
865
_write_long(self._file, 0)
866
_write_long(self._file, 0)
868
def _write_form_length(self, datalength):
870
commlength = 18 + 5 + len(self._compname)
872
commlength = commlength + 1
877
_write_long(self._file, 4 + verslength + self._marklength + \
878
8 + commlength + 16 + datalength)
881
def _patchheader(self):
882
curpos = self._file.tell()
883
if self._datawritten & 1:
884
datalength = self._datawritten + 1
885
self._file.write(chr(0))
887
datalength = self._datawritten
888
if datalength == self._datalength and \
889
self._nframes == self._nframeswritten and \
890
self._marklength == 0:
891
self._file.seek(curpos, 0)
893
self._file.seek(self._form_length_pos, 0)
894
dummy = self._write_form_length(datalength)
895
self._file.seek(self._nframes_pos, 0)
896
_write_long(self._file, self._nframeswritten)
897
self._file.seek(self._ssnd_length_pos, 0)
898
_write_long(self._file, datalength + 8)
899
self._file.seek(curpos, 0)
900
self._nframes = self._nframeswritten
901
self._datalength = datalength
903
def _writemarkers(self):
904
if len(self._markers) == 0:
906
self._file.write('MARK')
908
for marker in self._markers:
909
id, pos, name = marker
910
length = length + len(name) + 1 + 6
911
if len(name) & 1 == 0:
913
_write_long(self._file, length)
914
self._marklength = length + 8
915
_write_short(self._file, len(self._markers))
916
for marker in self._markers:
917
id, pos, name = marker
918
_write_short(self._file, id)
919
_write_long(self._file, pos)
920
_write_string(self._file, name)
922
def open(f, mode=None):
924
if hasattr(f, 'mode'):
928
if mode in ('r', 'rb'):
930
elif mode in ('w', 'wb'):
933
raise Error, "mode must be 'r', 'rb', 'w', or 'wb'"
935
openfp = open # B/W compatibility
937
if __name__ == '__main__':
940
sys.argv.append('/usr/demos/data/audio/bach.aiff')
944
print "nchannels =", f.getnchannels()
945
print "nframes =", f.getnframes()
946
print "sampwidth =", f.getsampwidth()
947
print "framerate =", f.getframerate()
948
print "comptype =", f.getcomptype()
949
print "compname =", f.getcompname()
954
g.setparams(f.getparams())
956
data = f.readframes(1024)