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 position 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
76 __all__
= ["open", "openfp", "Error"]
78 class Error(Exception):
81 WAVE_FORMAT_PCM
= 0x0001
83 _array_fmts
= None, 'b', 'h', None, 'l'
85 # Determine endian-ness
87 if struct
.pack("h", 1) == "\000\001":
92 from chunk
import Chunk
95 """Variables used in this class:
97 These variables are available to the user though appropriate
98 methods of this class:
99 _file -- the open file with methods read(), close(), and seek()
100 set through the __init__() method
101 _nchannels -- the number of audio channels
102 available through the getnchannels() method
103 _nframes -- the number of audio frames
104 available through the getnframes() method
105 _sampwidth -- the number of bytes per audio sample
106 available through the getsampwidth() method
107 _framerate -- the sampling frequency
108 available through the getframerate() method
109 _comptype -- the AIFF-C compression type ('NONE' if AIFF)
110 available through the getcomptype() method
111 _compname -- the human-readable AIFF-C compression type
112 available through the getcomptype() method
113 _soundpos -- the position in the audio stream
114 available through the tell() method, set through the
117 These variables are used internally only:
118 _fmt_chunk_read -- 1 iff the FMT chunk has been read
119 _data_seek_needed -- 1 iff positioned correctly in audio
120 file for readframes()
121 _data_chunk -- instantiation of a chunk class for the DATA chunk
122 _framesize -- size of one frame in the file
125 def initfp(self
, file):
128 self
._file
= Chunk(file, bigendian
= 0)
129 if self
._file
.getname() != 'RIFF':
130 raise Error
, 'file does not start with RIFF id'
131 if self
._file
.read(4) != 'WAVE':
132 raise Error
, 'not a WAVE file'
133 self
._fmt
_chunk
_read
= 0
134 self
._data
_chunk
= None
136 self
._data
_seek
_needed
= 1
138 chunk
= Chunk(self
._file
, bigendian
= 0)
141 chunkname
= chunk
.getname()
142 if chunkname
== 'fmt ':
143 self
._read
_fmt
_chunk
(chunk
)
144 self
._fmt
_chunk
_read
= 1
145 elif chunkname
== 'data':
146 if not self
._fmt
_chunk
_read
:
147 raise Error
, 'data chunk before fmt chunk'
148 self
._data
_chunk
= chunk
149 self
._nframes
= chunk
.chunksize
// self
._framesize
150 self
._data
_seek
_needed
= 0
153 if not self
._fmt
_chunk
_read
or not self
._data
_chunk
:
154 raise Error
, 'fmt chunk and/or data chunk missing'
156 def __init__(self
, f
):
157 self
._i
_opened
_the
_file
= None
158 if isinstance(f
, basestring
):
159 f
= __builtin__
.open(f
, 'rb')
160 self
._i
_opened
_the
_file
= f
161 # else, assume it is an open file object already
165 if self
._i
_opened
_the
_file
:
172 # User visible methods.
178 self
._data
_seek
_needed
= 1
182 if self
._i
_opened
_the
_file
:
183 self
._i
_opened
_the
_file
.close()
184 self
._i
_opened
_the
_file
= None
188 return self
._soundpos
190 def getnchannels(self
):
191 return self
._nchannels
193 def getnframes(self
):
196 def getsampwidth(self
):
197 return self
._sampwidth
199 def getframerate(self
):
200 return self
._framerate
202 def getcomptype(self
):
203 return self
._comptype
205 def getcompname(self
):
206 return self
._compname
209 return self
.getnchannels(), self
.getsampwidth(), \
210 self
.getframerate(), self
.getnframes(), \
211 self
.getcomptype(), self
.getcompname()
213 def getmarkers(self
):
216 def getmark(self
, id):
217 raise Error
, 'no marks'
219 def setpos(self
, pos
):
220 if pos
< 0 or pos
> self
._nframes
:
221 raise Error
, 'position not in range'
223 self
._data
_seek
_needed
= 1
225 def readframes(self
, nframes
):
226 if self
._data
_seek
_needed
:
227 self
._data
_chunk
.seek(0, 0)
228 pos
= self
._soundpos
* self
._framesize
230 self
._data
_chunk
.seek(pos
, 0)
231 self
._data
_seek
_needed
= 0
234 if self
._sampwidth
> 1 and big_endian
:
235 # unfortunately the fromfile() method does not take
236 # something that only looks like a file object, so
237 # we have to reach into the innards of the chunk object
239 chunk
= self
._data
_chunk
240 data
= array
.array(_array_fmts
[self
._sampwidth
])
241 nitems
= nframes
* self
._nchannels
242 if nitems
* self
._sampwidth
> chunk
.chunksize
- chunk
.size_read
:
243 nitems
= (chunk
.chunksize
- chunk
.size_read
) / self
._sampwidth
244 data
.fromfile(chunk
.file.file, nitems
)
245 # "tell" data chunk how much was read
246 chunk
.size_read
= chunk
.size_read
+ nitems
* self
._sampwidth
247 # do the same for the outermost chunk
249 chunk
.size_read
= chunk
.size_read
+ nitems
* self
._sampwidth
251 data
= data
.tostring()
253 data
= self
._data
_chunk
.read(nframes
* self
._framesize
)
254 if self
._convert
and data
:
255 data
= self
._convert
(data
)
256 self
._soundpos
= self
._soundpos
+ len(data
) // (self
._nchannels
* self
._sampwidth
)
263 def _read_fmt_chunk(self
, chunk
):
264 wFormatTag
, self
._nchannels
, self
._framerate
, dwAvgBytesPerSec
, wBlockAlign
= struct
.unpack('<hhllh', chunk
.read(14))
265 if wFormatTag
== WAVE_FORMAT_PCM
:
266 sampwidth
= struct
.unpack('<h', chunk
.read(2))[0]
267 self
._sampwidth
= (sampwidth
+ 7) // 8
269 raise Error
, 'unknown format: %r' % (wFormatTag
,)
270 self
._framesize
= self
._nchannels
* self
._sampwidth
271 self
._comptype
= 'NONE'
272 self
._compname
= 'not compressed'
275 """Variables used in this class:
277 These variables are user settable through appropriate methods
279 _file -- the open file with methods write(), close(), tell(), seek()
280 set through the __init__() method
281 _comptype -- the AIFF-C compression type ('NONE' in AIFF)
282 set through the setcomptype() or setparams() method
283 _compname -- the human-readable AIFF-C compression type
284 set through the setcomptype() or setparams() method
285 _nchannels -- the number of audio channels
286 set through the setnchannels() or setparams() method
287 _sampwidth -- the number of bytes per audio sample
288 set through the setsampwidth() or setparams() method
289 _framerate -- the sampling frequency
290 set through the setframerate() or setparams() method
291 _nframes -- the number of audio frames written to the header
292 set through the setnframes() or setparams() method
294 These variables are used internally only:
295 _datalength -- the size of the audio samples written to the header
296 _nframeswritten -- the number of frames actually written
297 _datawritten -- the size of the audio samples actually written
300 def __init__(self
, f
):
301 self
._i
_opened
_the
_file
= None
302 if isinstance(f
, basestring
):
303 f
= __builtin__
.open(f
, 'wb')
304 self
._i
_opened
_the
_file
= f
308 if self
._i
_opened
_the
_file
:
312 def initfp(self
, file):
319 self
._nframeswritten
= 0
320 self
._datawritten
= 0
327 # User visible methods.
329 def setnchannels(self
, nchannels
):
330 if self
._datawritten
:
331 raise Error
, 'cannot change parameters after starting to write'
333 raise Error
, 'bad # of channels'
334 self
._nchannels
= nchannels
336 def getnchannels(self
):
337 if not self
._nchannels
:
338 raise Error
, 'number of channels not set'
339 return self
._nchannels
341 def setsampwidth(self
, sampwidth
):
342 if self
._datawritten
:
343 raise Error
, 'cannot change parameters after starting to write'
344 if sampwidth
< 1 or sampwidth
> 4:
345 raise Error
, 'bad sample width'
346 self
._sampwidth
= sampwidth
348 def getsampwidth(self
):
349 if not self
._sampwidth
:
350 raise Error
, 'sample width not set'
351 return self
._sampwidth
353 def setframerate(self
, framerate
):
354 if self
._datawritten
:
355 raise Error
, 'cannot change parameters after starting to write'
357 raise Error
, 'bad frame rate'
358 self
._framerate
= framerate
360 def getframerate(self
):
361 if not self
._framerate
:
362 raise Error
, 'frame rate not set'
363 return self
._framerate
365 def setnframes(self
, nframes
):
366 if self
._datawritten
:
367 raise Error
, 'cannot change parameters after starting to write'
368 self
._nframes
= nframes
370 def getnframes(self
):
371 return self
._nframeswritten
373 def setcomptype(self
, comptype
, compname
):
374 if self
._datawritten
:
375 raise Error
, 'cannot change parameters after starting to write'
376 if comptype
not in ('NONE',):
377 raise Error
, 'unsupported compression type'
378 self
._comptype
= comptype
379 self
._compname
= compname
381 def getcomptype(self
):
382 return self
._comptype
384 def getcompname(self
):
385 return self
._compname
387 def setparams(self
, (nchannels
, sampwidth
, framerate
, nframes
, comptype
, compname
)):
388 if self
._datawritten
:
389 raise Error
, 'cannot change parameters after starting to write'
390 self
.setnchannels(nchannels
)
391 self
.setsampwidth(sampwidth
)
392 self
.setframerate(framerate
)
393 self
.setnframes(nframes
)
394 self
.setcomptype(comptype
, compname
)
397 if not self
._nchannels
or not self
._sampwidth
or not self
._framerate
:
398 raise Error
, 'not all parameters set'
399 return self
._nchannels
, self
._sampwidth
, self
._framerate
, \
400 self
._nframes
, self
._comptype
, self
._compname
402 def setmark(self
, id, pos
, name
):
403 raise Error
, 'setmark() not supported'
405 def getmark(self
, id):
406 raise Error
, 'no marks'
408 def getmarkers(self
):
412 return self
._nframeswritten
414 def writeframesraw(self
, data
):
415 self
._ensure
_header
_written
(len(data
))
416 nframes
= len(data
) // (self
._sampwidth
* self
._nchannels
)
418 data
= self
._convert
(data
)
419 if self
._sampwidth
> 1 and big_endian
:
421 data
= array
.array(_array_fmts
[self
._sampwidth
], data
)
423 data
.tofile(self
._file
)
424 self
._datawritten
= self
._datawritten
+ len(data
) * self
._sampwidth
426 self
._file
.write(data
)
427 self
._datawritten
= self
._datawritten
+ len(data
)
428 self
._nframeswritten
= self
._nframeswritten
+ nframes
430 def writeframes(self
, data
):
431 self
.writeframesraw(data
)
432 if self
._datalength
!= self
._datawritten
:
437 self
._ensure
_header
_written
(0)
438 if self
._datalength
!= self
._datawritten
:
442 if self
._i
_opened
_the
_file
:
443 self
._i
_opened
_the
_file
.close()
444 self
._i
_opened
_the
_file
= None
450 def _ensure_header_written(self
, datasize
):
451 if not self
._datawritten
:
452 if not self
._nchannels
:
453 raise Error
, '# channels not specified'
454 if not self
._sampwidth
:
455 raise Error
, 'sample width not specified'
456 if not self
._framerate
:
457 raise Error
, 'sampling rate not specified'
458 self
._write
_header
(datasize
)
460 def _write_header(self
, initlength
):
461 self
._file
.write('RIFF')
462 if not self
._nframes
:
463 self
._nframes
= initlength
/ (self
._nchannels
* self
._sampwidth
)
464 self
._datalength
= self
._nframes
* self
._nchannels
* self
._sampwidth
465 self
._form
_length
_pos
= self
._file
.tell()
466 self
._file
.write(struct
.pack('<l4s4slhhllhh4s',
467 36 + self
._datalength
, 'WAVE', 'fmt ', 16,
468 WAVE_FORMAT_PCM
, self
._nchannels
, self
._framerate
,
469 self
._nchannels
* self
._framerate
* self
._sampwidth
,
470 self
._nchannels
* self
._sampwidth
,
471 self
._sampwidth
* 8, 'data'))
472 self
._data
_length
_pos
= self
._file
.tell()
473 self
._file
.write(struct
.pack('<l', self
._datalength
))
475 def _patchheader(self
):
476 if self
._datawritten
== self
._datalength
:
478 curpos
= self
._file
.tell()
479 self
._file
.seek(self
._form
_length
_pos
, 0)
480 self
._file
.write(struct
.pack('<l', 36 + self
._datawritten
))
481 self
._file
.seek(self
._data
_length
_pos
, 0)
482 self
._file
.write(struct
.pack('<l', self
._datawritten
))
483 self
._file
.seek(curpos
, 0)
484 self
._datalength
= self
._datawritten
486 def open(f
, mode
=None):
488 if hasattr(f
, 'mode'):
492 if mode
in ('r', 'rb'):
494 elif mode
in ('w', 'wb'):
497 raise Error
, "mode must be 'r', 'rb', 'w', or 'wb'"
499 openfp
= open # B/W compatibility