1 """Stuff to parse WAVE files.
6 f = wave.open(file, 'r')
7 where file is either the name of a file or an open file pointer.
8 The open file pointer must have methods read(), seek(), and close().
9 When the setpos() and rewind() methods are not used, the seek()
10 method is not necessary.
12 This returns an instance of a class with the following public methods:
13 getnchannels() -- returns number of audio channels (1 for
15 getsampwidth() -- returns sample width in bytes
16 getframerate() -- returns sampling frequency
17 getnframes() -- returns number of audio frames
18 getcomptype() -- returns compression type ('NONE' for linear samples)
19 getcompname() -- returns human-readable version of
20 compression type ('not compressed' linear samples)
21 getparams() -- returns a tuple consisting of all of the
22 above in the above order
23 getmarkers() -- returns None (for compatibility with the
25 getmark(id) -- raises an error since the mark does not
26 exist (for compatibility with the aifc module)
27 readframes(n) -- returns at most n frames of audio
28 rewind() -- rewind to the beginning of the audio stream
29 setpos(pos) -- seek to the specified position
30 tell() -- return the current position
31 close() -- close the instance (make it unusable)
32 The position returned by tell() and the position given to setpos()
33 are compatible and have nothing to do with the actual postion in the
35 The close() method is called automatically when the class instance
39 f = wave.open(file, 'w')
40 where file is either the name of a file or an open file pointer.
41 The open file pointer must have methods write(), tell(), seek(), and
44 This returns an instance of a class with the following public methods:
45 setnchannels(n) -- set the number of channels
46 setsampwidth(n) -- set the sample width
47 setframerate(n) -- set the frame rate
48 setnframes(n) -- set the number of frames
49 setcomptype(type, name)
50 -- set the compression type and the
51 human-readable compression type
53 -- set all parameters at once
54 tell() -- return current position in output file
56 -- write audio frames without pathing up the
59 -- write audio frames and patch up the file header
60 close() -- patch up the file header and close the
62 You should set the parameters before the first writeframesraw or
63 writeframes. The total number of frames does not need to be set,
64 but when it is set to the correct value, the header does not have to
66 It is best to first set all parameters, perhaps possibly the
67 compression type, and then write audio frames using writeframesraw.
68 When all frames have been written, either call writeframes('') or
69 close() to patch up the sizes in the header.
70 The close() method is called automatically when the class instance
78 WAVE_FORMAT_PCM
= 0x0001
80 _array_fmts
= None, 'b', 'h', None, 'l'
82 # Determine endian-ness
84 if struct
.pack("h", 1) == "\000\001":
89 from chunk
import Chunk
92 """Variables used in this class:
94 These variables are available to the user though appropriate
95 methods of this class:
96 _file -- the open file with methods read(), close(), and seek()
97 set through the __init__() method
98 _nchannels -- the number of audio channels
99 available through the getnchannels() method
100 _nframes -- the number of audio frames
101 available through the getnframes() method
102 _sampwidth -- the number of bytes per audio sample
103 available through the getsampwidth() method
104 _framerate -- the sampling frequency
105 available through the getframerate() method
106 _comptype -- the AIFF-C compression type ('NONE' if AIFF)
107 available through the getcomptype() method
108 _compname -- the human-readable AIFF-C compression type
109 available through the getcomptype() method
110 _soundpos -- the position in the audio stream
111 available through the tell() method, set through the
114 These variables are used internally only:
115 _fmt_chunk_read -- 1 iff the FMT chunk has been read
116 _data_seek_needed -- 1 iff positioned correctly in audio
117 file for readframes()
118 _data_chunk -- instantiation of a chunk class for the DATA chunk
119 _framesize -- size of one frame in the file
122 def initfp(self
, file):
125 self
._file
= Chunk(file, bigendian
= 0)
126 if self
._file
.getname() != 'RIFF':
127 raise Error
, 'file does not start with RIFF id'
128 if self
._file
.read(4) != 'WAVE':
129 raise Error
, 'not a WAVE file'
130 self
._fmt
_chunk
_read
= 0
131 self
._data
_chunk
= None
133 self
._data
_seek
_needed
= 1
135 chunk
= Chunk(self
._file
, bigendian
= 0)
138 chunkname
= chunk
.getname()
139 if chunkname
== 'fmt ':
140 self
._read
_fmt
_chunk
(chunk
)
141 self
._fmt
_chunk
_read
= 1
142 elif chunkname
== 'data':
143 if not self
._fmt
_chunk
_read
:
144 raise Error
, 'data chunk before fmt chunk'
145 self
._data
_chunk
= chunk
146 self
._nframes
= chunk
.chunksize
/ self
._framesize
147 self
._data
_seek
_needed
= 0
150 if not self
._fmt
_chunk
_read
or not self
._data
_chunk
:
151 raise Error
, 'fmt chunk and/or data chunk missing'
153 def __init__(self
, f
):
154 if type(f
) == type(''):
155 f
= __builtin__
.open(f
, 'rb')
156 # else, assume it is an open file object already
160 # User visible methods.
166 self
._data
_seek
_needed
= 1
173 return self
._soundpos
175 def getnchannels(self
):
176 return self
._nchannels
178 def getnframes(self
):
181 def getsampwidth(self
):
182 return self
._sampwidth
184 def getframerate(self
):
185 return self
._framerate
187 def getcomptype(self
):
188 return self
._comptype
190 def getcompname(self
):
191 return self
._compname
194 return self
.getnchannels(), self
.getsampwidth(), \
195 self
.getframerate(), self
.getnframes(), \
196 self
.getcomptype(), self
.getcompname()
198 def getmarkers(self
):
201 def getmark(self
, id):
202 raise Error
, 'no marks'
204 def setpos(self
, pos
):
205 if pos
< 0 or pos
> self
._nframes
:
206 raise Error
, 'position not in range'
208 self
._data
_seek
_needed
= 1
210 def readframes(self
, nframes
):
211 if self
._data
_seek
_needed
:
212 self
._data
_chunk
.seek(0, 0)
213 pos
= self
._soundpos
* self
._framesize
215 self
._data
_chunk
.seek(pos
, 0)
216 self
._data
_seek
_needed
= 0
219 if self
._sampwidth
> 1 and big_endian
:
220 # unfortunately the fromfile() method does not take
221 # something that only looks like a file object, so
222 # we have to reach into the innards of the chunk object
224 chunk
= self
._data
_chunk
225 data
= array
.array(_array_fmts
[self
._sampwidth
])
226 nitems
= nframes
* self
._nchannels
227 if nitems
* self
._sampwidth
> chunk
.chunksize
- chunk
.size_read
:
228 nitems
= (chunk
.chunksize
- chunk
.size_read
) / self
._sampwidth
229 data
.fromfile(chunk
.file.file, nitems
)
230 # "tell" data chunk how much was read
231 chunk
.size_read
= chunk
.size_read
+ nitems
* self
._sampwidth
232 # do the same for the outermost chunk
234 chunk
.size_read
= chunk
.size_read
+ nitems
* self
._sampwidth
236 data
= data
.tostring()
238 data
= self
._data
_chunk
.read(nframes
* self
._framesize
)
239 if self
._convert
and data
:
240 data
= self
._convert
(data
)
241 self
._soundpos
= self
._soundpos
+ len(data
) / (self
._nchannels
* self
._sampwidth
)
248 def _read_fmt_chunk(self
, chunk
):
249 wFormatTag
, self
._nchannels
, self
._framerate
, dwAvgBytesPerSec
, wBlockAlign
= struct
.unpack('<hhllh', chunk
.read(14))
250 if wFormatTag
== WAVE_FORMAT_PCM
:
251 sampwidth
= struct
.unpack('<h', chunk
.read(2))[0]
252 self
._sampwidth
= (sampwidth
+ 7) / 8
254 raise Error
, 'unknown format: ' + `wFormatTag`
255 self
._framesize
= self
._nchannels
* self
._sampwidth
256 self
._comptype
= 'NONE'
257 self
._compname
= 'not compressed'
260 """Variables used in this class:
262 These variables are user settable through appropriate methods
264 _file -- the open file with methods write(), close(), tell(), seek()
265 set through the __init__() method
266 _comptype -- the AIFF-C compression type ('NONE' in AIFF)
267 set through the setcomptype() or setparams() method
268 _compname -- the human-readable AIFF-C compression type
269 set through the setcomptype() or setparams() method
270 _nchannels -- the number of audio channels
271 set through the setnchannels() or setparams() method
272 _sampwidth -- the number of bytes per audio sample
273 set through the setsampwidth() or setparams() method
274 _framerate -- the sampling frequency
275 set through the setframerate() or setparams() method
276 _nframes -- the number of audio frames written to the header
277 set through the setnframes() or setparams() method
279 These variables are used internally only:
280 _datalength -- the size of the audio samples written to the header
281 _nframeswritten -- the number of frames actually written
282 _datawritten -- the size of the audio samples actually written
285 def __init__(self
, f
):
286 if type(f
) == type(''):
287 f
= __builtin__
.open(f
, 'wb')
290 def initfp(self
, file):
297 self
._nframeswritten
= 0
298 self
._datawritten
= 0
306 # User visible methods.
308 def setnchannels(self
, nchannels
):
309 if self
._datawritten
:
310 raise Error
, 'cannot change parameters after starting to write'
312 raise Error
, 'bad # of channels'
313 self
._nchannels
= nchannels
315 def getnchannels(self
):
316 if not self
._nchannels
:
317 raise Error
, 'number of channels not set'
318 return self
._nchannels
320 def setsampwidth(self
, sampwidth
):
321 if self
._datawritten
:
322 raise Error
, 'cannot change parameters after starting to write'
323 if sampwidth
< 1 or sampwidth
> 4:
324 raise Error
, 'bad sample width'
325 self
._sampwidth
= sampwidth
327 def getsampwidth(self
):
328 if not self
._sampwidth
:
329 raise Error
, 'sample width not set'
330 return self
._sampwidth
332 def setframerate(self
, framerate
):
333 if self
._datawritten
:
334 raise Error
, 'cannot change parameters after starting to write'
336 raise Error
, 'bad frame rate'
337 self
._framerate
= framerate
339 def getframerate(self
):
340 if not self
._framerate
:
341 raise Error
, 'frame rate not set'
342 return self
._framerate
344 def setnframes(self
, nframes
):
345 if self
._datawritten
:
346 raise Error
, 'cannot change parameters after starting to write'
347 self
._nframes
= nframes
349 def getnframes(self
):
350 return self
._nframeswritten
352 def setcomptype(self
, comptype
, compname
):
353 if self
._datawritten
:
354 raise Error
, 'cannot change parameters after starting to write'
355 if comptype
not in ('NONE',):
356 raise Error
, 'unsupported compression type'
357 self
._comptype
= comptype
358 self
._compname
= compname
360 def getcomptype(self
):
361 return self
._comptype
363 def getcompname(self
):
364 return self
._compname
366 def setparams(self
, (nchannels
, sampwidth
, framerate
, nframes
, comptype
, compname
)):
367 if self
._datawritten
:
368 raise Error
, 'cannot change parameters after starting to write'
369 self
.setnchannels(nchannels
)
370 self
.setsampwidth(sampwidth
)
371 self
.setframerate(framerate
)
372 self
.setnframes(nframes
)
373 self
.setcomptype(comptype
, compname
)
376 if not self
._nchannels
or not self
._sampwidth
or not self
._framerate
:
377 raise Error
, 'not all parameters set'
378 return self
._nchannels
, self
._sampwidth
, self
._framerate
, \
379 self
._nframes
, self
._comptype
, self
._compname
381 def setmark(self
, id, pos
, name
):
382 raise Error
, 'setmark() not supported'
384 def getmark(self
, id):
385 raise Error
, 'no marks'
387 def getmarkers(self
):
391 return self
._nframeswritten
393 def writeframesraw(self
, data
):
394 self
._ensure
_header
_written
(len(data
))
395 nframes
= len(data
) / (self
._sampwidth
* self
._nchannels
)
397 data
= self
._convert
(data
)
398 if self
._sampwidth
> 1 and big_endian
:
400 data
= array
.array(_array_fmts
[self
._sampwidth
], data
)
402 data
.tofile(self
._file
)
403 self
._datawritten
= self
._datawritten
+ len(data
) * self
._sampwidth
405 self
._file
.write(data
)
406 self
._datawritten
= self
._datawritten
+ len(data
)
407 self
._nframeswritten
= self
._nframeswritten
+ nframes
409 def writeframes(self
, data
):
410 self
.writeframesraw(data
)
411 if self
._datalength
!= self
._datawritten
:
415 self
._ensure
_header
_written
(0)
416 if self
._datalength
!= self
._datawritten
:
425 def _ensure_header_written(self
, datasize
):
426 if not self
._datawritten
:
427 if not self
._nchannels
:
428 raise Error
, '# channels not specified'
429 if not self
._sampwidth
:
430 raise Error
, 'sample width not specified'
431 if not self
._framerate
:
432 raise Error
, 'sampling rate not specified'
433 self
._write
_header
(datasize
)
435 def _write_header(self
, initlength
):
436 self
._file
.write('RIFF')
437 if not self
._nframes
:
438 self
._nframes
= initlength
/ (self
._nchannels
* self
._sampwidth
)
439 self
._datalength
= self
._nframes
* self
._nchannels
* self
._sampwidth
440 self
._form
_length
_pos
= self
._file
.tell()
441 self
._file
.write(struct
.pack('<lsslhhllhhs',
442 36 + self
._datalength
, 'WAVE', 'fmt ', 16,
443 WAVE_FORMAT_PCM
, self
._nchannels
, self
._framerate
,
444 self
._nchannels
* self
._framerate
* self
._sampwidth
,
445 self
._nchannels
* self
._sampwidth
,
446 self
._sampwidth
* 8, 'data'))
447 self
._data
_length
_pos
= self
._file
.tell()
448 self
._file
.write(struct
.pack('<l', self
._datalength
))
450 def _patchheader(self
):
451 if self
._datawritten
== self
._datalength
:
453 curpos
= self
._file
.tell()
454 self
._file
.seek(self
._form
_length
_pos
, 0)
455 self
._file
.write(struct
.pack('<l', 36 + self
._datawritten
))
456 self
._file
.seek(self
._data
_length
_pos
, 0)
457 self
._file
.write(struct
.pack('<l', self
._datawritten
))
458 self
._file
.seek(curpos
, 0)
459 self
._datalength
= self
._datawritten
461 def open(f
, mode
=None):
463 if hasattr(f
, 'mode'):
467 if mode
in ('r', 'rb'):
469 elif mode
in ('w', 'wb'):
472 raise Error
, "mode must be 'r', 'rb', 'w', or 'wb'"
474 openfp
= open # B/W compatibility