blob: 0e6628b087a13687781e88ad2f9f4b5ff1f485d6 [file] [log] [blame]
Guido van Rossume7b146f2000-02-04 15:28:42 +00001"""Stuff to parse WAVE files.
2
3Usage.
4
5Reading WAVE files:
6 f = wave.open(file, 'r')
7where file is either the name of a file or an open file pointer.
8The open file pointer must have methods read(), seek(), and close().
9When the setpos() and rewind() methods are not used, the seek()
10method is not necessary.
11
12This returns an instance of a class with the following public methods:
13 getnchannels() -- returns number of audio channels (1 for
14 mono, 2 for stereo)
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)
R David Murray671cd322013-04-10 12:31:43 -040021 getparams() -- returns a namedtuple consisting of all of the
Guido van Rossume7b146f2000-02-04 15:28:42 +000022 above in the above order
23 getmarkers() -- returns None (for compatibility with the
24 aifc module)
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)
32The position returned by tell() and the position given to setpos()
Thomas Wouters7e474022000-07-16 12:04:32 +000033are compatible and have nothing to do with the actual position in the
Guido van Rossume7b146f2000-02-04 15:28:42 +000034file.
35The close() method is called automatically when the class instance
36is destroyed.
37
38Writing WAVE files:
39 f = wave.open(file, 'w')
40where file is either the name of a file or an open file pointer.
41The open file pointer must have methods write(), tell(), seek(), and
42close().
43
44This 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
52 setparams(tuple)
53 -- set all parameters at once
54 tell() -- return current position in output file
55 writeframesraw(data)
56 -- write audio frames without pathing up the
57 file header
58 writeframes(data)
59 -- write audio frames and patch up the file header
60 close() -- patch up the file header and close the
61 output file
62You should set the parameters before the first writeframesraw or
63writeframes. The total number of frames does not need to be set,
64but when it is set to the correct value, the header does not have to
65be patched up.
66It is best to first set all parameters, perhaps possibly the
67compression type, and then write audio frames using writeframesraw.
68When all frames have been written, either call writeframes('') or
69close() to patch up the sizes in the header.
70The close() method is called automatically when the class instance
71is destroyed.
72"""
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000073
Georg Brandl1a3284e2007-12-02 09:40:06 +000074import builtins
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000075
Skip Montanaro40fc1602001-03-01 04:27:19 +000076__all__ = ["open", "openfp", "Error"]
77
Fred Drake9b8d8012000-08-17 04:45:13 +000078class Error(Exception):
79 pass
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000080
81WAVE_FORMAT_PCM = 0x0001
82
Serhiy Storchakad3b75052013-10-17 23:04:04 +030083_array_fmts = None, 'b', 'h', None, 'i'
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000084
Guido van Rossumebb9c921999-02-05 22:28:17 +000085import struct
Serhiy Storchakad739bda2013-05-29 23:38:00 +030086import sys
Guido van Rossum3601e881999-08-26 15:50:43 +000087from chunk import Chunk
R David Murray671cd322013-04-10 12:31:43 -040088from collections import namedtuple
89
Serhiy Storchaka4c6a0202013-09-04 00:28:43 +030090_wave_params = namedtuple('_wave_params',
R David Murray671cd322013-04-10 12:31:43 -040091 'nchannels sampwidth framerate nframes comptype compname')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000092
93class Wave_read:
Guido van Rossume7b146f2000-02-04 15:28:42 +000094 """Variables used in this class:
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000095
Guido van Rossume7b146f2000-02-04 15:28:42 +000096 These variables are available to the user though appropriate
97 methods of this class:
98 _file -- the open file with methods read(), close(), and seek()
99 set through the __init__() method
100 _nchannels -- the number of audio channels
101 available through the getnchannels() method
102 _nframes -- the number of audio frames
103 available through the getnframes() method
104 _sampwidth -- the number of bytes per audio sample
105 available through the getsampwidth() method
106 _framerate -- the sampling frequency
107 available through the getframerate() method
108 _comptype -- the AIFF-C compression type ('NONE' if AIFF)
109 available through the getcomptype() method
110 _compname -- the human-readable AIFF-C compression type
111 available through the getcomptype() method
112 _soundpos -- the position in the audio stream
113 available through the tell() method, set through the
114 setpos() method
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000115
Guido van Rossume7b146f2000-02-04 15:28:42 +0000116 These variables are used internally only:
117 _fmt_chunk_read -- 1 iff the FMT chunk has been read
118 _data_seek_needed -- 1 iff positioned correctly in audio
119 file for readframes()
120 _data_chunk -- instantiation of a chunk class for the DATA chunk
121 _framesize -- size of one frame in the file
122 """
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000123
Guido van Rossume7b146f2000-02-04 15:28:42 +0000124 def initfp(self, file):
125 self._convert = None
126 self._soundpos = 0
127 self._file = Chunk(file, bigendian = 0)
Guido van Rossum51a883b2007-07-23 21:28:30 +0000128 if self._file.getname() != b'RIFF':
Collin Winterce36ad82007-08-30 01:19:48 +0000129 raise Error('file does not start with RIFF id')
Guido van Rossum51a883b2007-07-23 21:28:30 +0000130 if self._file.read(4) != b'WAVE':
Collin Winterce36ad82007-08-30 01:19:48 +0000131 raise Error('not a WAVE file')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000132 self._fmt_chunk_read = 0
133 self._data_chunk = None
134 while 1:
135 self._data_seek_needed = 1
136 try:
137 chunk = Chunk(self._file, bigendian = 0)
138 except EOFError:
139 break
140 chunkname = chunk.getname()
Guido van Rossum51a883b2007-07-23 21:28:30 +0000141 if chunkname == b'fmt ':
Guido van Rossume7b146f2000-02-04 15:28:42 +0000142 self._read_fmt_chunk(chunk)
143 self._fmt_chunk_read = 1
Guido van Rossum51a883b2007-07-23 21:28:30 +0000144 elif chunkname == b'data':
Guido van Rossume7b146f2000-02-04 15:28:42 +0000145 if not self._fmt_chunk_read:
Collin Winterce36ad82007-08-30 01:19:48 +0000146 raise Error('data chunk before fmt chunk')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000147 self._data_chunk = chunk
Guido van Rossum54e54c62001-09-04 19:14:14 +0000148 self._nframes = chunk.chunksize // self._framesize
Guido van Rossume7b146f2000-02-04 15:28:42 +0000149 self._data_seek_needed = 0
150 break
151 chunk.skip()
152 if not self._fmt_chunk_read or not self._data_chunk:
Collin Winterce36ad82007-08-30 01:19:48 +0000153 raise Error('fmt chunk and/or data chunk missing')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000154
Guido van Rossume7b146f2000-02-04 15:28:42 +0000155 def __init__(self, f):
Tim Peterscfc41782000-10-09 23:43:55 +0000156 self._i_opened_the_file = None
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000157 if isinstance(f, str):
Georg Brandl1a3284e2007-12-02 09:40:06 +0000158 f = builtins.open(f, 'rb')
Tim Peterscfc41782000-10-09 23:43:55 +0000159 self._i_opened_the_file = f
Guido van Rossume7b146f2000-02-04 15:28:42 +0000160 # else, assume it is an open file object already
Guido van Rossumd8faa362007-04-27 19:54:29 +0000161 try:
162 self.initfp(f)
163 except:
164 if self._i_opened_the_file:
165 f.close()
166 raise
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000167
Tim Peterscfc41782000-10-09 23:43:55 +0000168 def __del__(self):
169 self.close()
R David Murrayc91d5ee2013-07-31 13:46:08 -0400170
171 def __enter__(self):
172 return self
173
174 def __exit__(self, *args):
175 self.close()
176
Guido van Rossume7b146f2000-02-04 15:28:42 +0000177 #
178 # User visible methods.
179 #
180 def getfp(self):
181 return self._file
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000182
Guido van Rossume7b146f2000-02-04 15:28:42 +0000183 def rewind(self):
184 self._data_seek_needed = 1
185 self._soundpos = 0
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000186
Guido van Rossume7b146f2000-02-04 15:28:42 +0000187 def close(self):
Tim Peterscfc41782000-10-09 23:43:55 +0000188 if self._i_opened_the_file:
189 self._i_opened_the_file.close()
190 self._i_opened_the_file = None
Guido van Rossume7b146f2000-02-04 15:28:42 +0000191 self._file = None
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000192
Guido van Rossume7b146f2000-02-04 15:28:42 +0000193 def tell(self):
194 return self._soundpos
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000195
Guido van Rossume7b146f2000-02-04 15:28:42 +0000196 def getnchannels(self):
197 return self._nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000198
Guido van Rossume7b146f2000-02-04 15:28:42 +0000199 def getnframes(self):
200 return self._nframes
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000201
Guido van Rossume7b146f2000-02-04 15:28:42 +0000202 def getsampwidth(self):
203 return self._sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000204
Guido van Rossume7b146f2000-02-04 15:28:42 +0000205 def getframerate(self):
206 return self._framerate
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000207
Guido van Rossume7b146f2000-02-04 15:28:42 +0000208 def getcomptype(self):
209 return self._comptype
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000210
Guido van Rossume7b146f2000-02-04 15:28:42 +0000211 def getcompname(self):
212 return self._compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000213
Guido van Rossume7b146f2000-02-04 15:28:42 +0000214 def getparams(self):
Serhiy Storchaka4c6a0202013-09-04 00:28:43 +0300215 return _wave_params(self.getnchannels(), self.getsampwidth(),
R David Murray671cd322013-04-10 12:31:43 -0400216 self.getframerate(), self.getnframes(),
217 self.getcomptype(), self.getcompname())
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000218
Guido van Rossume7b146f2000-02-04 15:28:42 +0000219 def getmarkers(self):
220 return None
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000221
Guido van Rossume7b146f2000-02-04 15:28:42 +0000222 def getmark(self, id):
Collin Winterce36ad82007-08-30 01:19:48 +0000223 raise Error('no marks')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000224
Guido van Rossume7b146f2000-02-04 15:28:42 +0000225 def setpos(self, pos):
226 if pos < 0 or pos > self._nframes:
Collin Winterce36ad82007-08-30 01:19:48 +0000227 raise Error('position not in range')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000228 self._soundpos = pos
229 self._data_seek_needed = 1
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000230
Guido van Rossume7b146f2000-02-04 15:28:42 +0000231 def readframes(self, nframes):
232 if self._data_seek_needed:
233 self._data_chunk.seek(0, 0)
234 pos = self._soundpos * self._framesize
235 if pos:
236 self._data_chunk.seek(pos, 0)
237 self._data_seek_needed = 0
238 if nframes == 0:
Guido van Rossum51a883b2007-07-23 21:28:30 +0000239 return b''
Serhiy Storchakad739bda2013-05-29 23:38:00 +0300240 if self._sampwidth > 1 and sys.byteorder == 'big':
Guido van Rossume7b146f2000-02-04 15:28:42 +0000241 # unfortunately the fromfile() method does not take
242 # something that only looks like a file object, so
243 # we have to reach into the innards of the chunk object
244 import array
245 chunk = self._data_chunk
246 data = array.array(_array_fmts[self._sampwidth])
Serhiy Storchakad3b75052013-10-17 23:04:04 +0300247 assert data.itemsize == self._sampwidth
Guido van Rossume7b146f2000-02-04 15:28:42 +0000248 nitems = nframes * self._nchannels
249 if nitems * self._sampwidth > chunk.chunksize - chunk.size_read:
Benjamin Peterson5efea042010-01-13 03:49:50 +0000250 nitems = (chunk.chunksize - chunk.size_read) // self._sampwidth
Guido van Rossume7b146f2000-02-04 15:28:42 +0000251 data.fromfile(chunk.file.file, nitems)
252 # "tell" data chunk how much was read
253 chunk.size_read = chunk.size_read + nitems * self._sampwidth
254 # do the same for the outermost chunk
255 chunk = chunk.file
256 chunk.size_read = chunk.size_read + nitems * self._sampwidth
257 data.byteswap()
Antoine Pitrou1ce3eb52010-09-01 20:29:34 +0000258 data = data.tobytes()
Guido van Rossume7b146f2000-02-04 15:28:42 +0000259 else:
260 data = self._data_chunk.read(nframes * self._framesize)
261 if self._convert and data:
262 data = self._convert(data)
Guido van Rossum54e54c62001-09-04 19:14:14 +0000263 self._soundpos = self._soundpos + len(data) // (self._nchannels * self._sampwidth)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000264 return data
265
266 #
267 # Internal methods.
268 #
269
270 def _read_fmt_chunk(self, chunk):
Jesus Ceae4b863982012-11-17 03:41:54 +0100271 wFormatTag, self._nchannels, self._framerate, dwAvgBytesPerSec, wBlockAlign = struct.unpack_from('<HHLLH', chunk.read(14))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000272 if wFormatTag == WAVE_FORMAT_PCM:
Jesus Ceae4b863982012-11-17 03:41:54 +0100273 sampwidth = struct.unpack_from('<H', chunk.read(2))[0]
Guido van Rossum54e54c62001-09-04 19:14:14 +0000274 self._sampwidth = (sampwidth + 7) // 8
Guido van Rossume7b146f2000-02-04 15:28:42 +0000275 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000276 raise Error('unknown format: %r' % (wFormatTag,))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000277 self._framesize = self._nchannels * self._sampwidth
278 self._comptype = 'NONE'
279 self._compname = 'not compressed'
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000280
281class Wave_write:
Guido van Rossume7b146f2000-02-04 15:28:42 +0000282 """Variables used in this class:
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000283
Guido van Rossume7b146f2000-02-04 15:28:42 +0000284 These variables are user settable through appropriate methods
285 of this class:
286 _file -- the open file with methods write(), close(), tell(), seek()
287 set through the __init__() method
288 _comptype -- the AIFF-C compression type ('NONE' in AIFF)
289 set through the setcomptype() or setparams() method
290 _compname -- the human-readable AIFF-C compression type
291 set through the setcomptype() or setparams() method
292 _nchannels -- the number of audio channels
293 set through the setnchannels() or setparams() method
294 _sampwidth -- the number of bytes per audio sample
295 set through the setsampwidth() or setparams() method
296 _framerate -- the sampling frequency
297 set through the setframerate() or setparams() method
298 _nframes -- the number of audio frames written to the header
299 set through the setnframes() or setparams() method
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000300
Guido van Rossume7b146f2000-02-04 15:28:42 +0000301 These variables are used internally only:
302 _datalength -- the size of the audio samples written to the header
303 _nframeswritten -- the number of frames actually written
304 _datawritten -- the size of the audio samples actually written
305 """
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000306
Guido van Rossume7b146f2000-02-04 15:28:42 +0000307 def __init__(self, f):
Tim Peterscfc41782000-10-09 23:43:55 +0000308 self._i_opened_the_file = None
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000309 if isinstance(f, str):
Georg Brandl1a3284e2007-12-02 09:40:06 +0000310 f = builtins.open(f, 'wb')
Tim Peterscfc41782000-10-09 23:43:55 +0000311 self._i_opened_the_file = f
Guido van Rossumd8faa362007-04-27 19:54:29 +0000312 try:
313 self.initfp(f)
314 except:
315 if self._i_opened_the_file:
316 f.close()
317 raise
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000318
Guido van Rossume7b146f2000-02-04 15:28:42 +0000319 def initfp(self, file):
320 self._file = file
321 self._convert = None
322 self._nchannels = 0
323 self._sampwidth = 0
324 self._framerate = 0
325 self._nframes = 0
326 self._nframeswritten = 0
327 self._datawritten = 0
328 self._datalength = 0
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000329 self._headerwritten = False
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000330
Guido van Rossume7b146f2000-02-04 15:28:42 +0000331 def __del__(self):
Tim Peterscfc41782000-10-09 23:43:55 +0000332 self.close()
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000333
R David Murrayc91d5ee2013-07-31 13:46:08 -0400334 def __enter__(self):
335 return self
336
337 def __exit__(self, *args):
338 self.close()
339
Guido van Rossume7b146f2000-02-04 15:28:42 +0000340 #
341 # User visible methods.
342 #
343 def setnchannels(self, nchannels):
344 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000345 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000346 if nchannels < 1:
Collin Winterce36ad82007-08-30 01:19:48 +0000347 raise Error('bad # of channels')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000348 self._nchannels = nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000349
Guido van Rossume7b146f2000-02-04 15:28:42 +0000350 def getnchannels(self):
351 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000352 raise Error('number of channels not set')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000353 return self._nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000354
Guido van Rossume7b146f2000-02-04 15:28:42 +0000355 def setsampwidth(self, sampwidth):
356 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000357 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000358 if sampwidth < 1 or sampwidth > 4:
Collin Winterce36ad82007-08-30 01:19:48 +0000359 raise Error('bad sample width')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000360 self._sampwidth = sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000361
Guido van Rossume7b146f2000-02-04 15:28:42 +0000362 def getsampwidth(self):
363 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000364 raise Error('sample width not set')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000365 return self._sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000366
Guido van Rossume7b146f2000-02-04 15:28:42 +0000367 def setframerate(self, framerate):
368 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000369 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000370 if framerate <= 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000371 raise Error('bad frame rate')
Mark Dickinson64a38c02010-08-28 17:22:16 +0000372 self._framerate = int(round(framerate))
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000373
Guido van Rossume7b146f2000-02-04 15:28:42 +0000374 def getframerate(self):
375 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000376 raise Error('frame rate not set')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000377 return self._framerate
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000378
Guido van Rossume7b146f2000-02-04 15:28:42 +0000379 def setnframes(self, nframes):
380 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000381 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000382 self._nframes = nframes
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000383
Guido van Rossume7b146f2000-02-04 15:28:42 +0000384 def getnframes(self):
385 return self._nframeswritten
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000386
Guido van Rossume7b146f2000-02-04 15:28:42 +0000387 def setcomptype(self, comptype, compname):
388 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000389 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000390 if comptype not in ('NONE',):
Collin Winterce36ad82007-08-30 01:19:48 +0000391 raise Error('unsupported compression type')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000392 self._comptype = comptype
393 self._compname = compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000394
Guido van Rossume7b146f2000-02-04 15:28:42 +0000395 def getcomptype(self):
396 return self._comptype
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000397
Guido van Rossume7b146f2000-02-04 15:28:42 +0000398 def getcompname(self):
399 return self._compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000400
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000401 def setparams(self, params):
402 nchannels, sampwidth, framerate, nframes, comptype, compname = params
Guido van Rossume7b146f2000-02-04 15:28:42 +0000403 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000404 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000405 self.setnchannels(nchannels)
406 self.setsampwidth(sampwidth)
407 self.setframerate(framerate)
408 self.setnframes(nframes)
409 self.setcomptype(comptype, compname)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000410
Guido van Rossume7b146f2000-02-04 15:28:42 +0000411 def getparams(self):
412 if not self._nchannels or not self._sampwidth or not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000413 raise Error('not all parameters set')
Serhiy Storchaka4c6a0202013-09-04 00:28:43 +0300414 return _wave_params(self._nchannels, self._sampwidth, self._framerate,
R David Murray671cd322013-04-10 12:31:43 -0400415 self._nframes, self._comptype, self._compname)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000416
Guido van Rossume7b146f2000-02-04 15:28:42 +0000417 def setmark(self, id, pos, name):
Collin Winterce36ad82007-08-30 01:19:48 +0000418 raise Error('setmark() not supported')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000419
Guido van Rossume7b146f2000-02-04 15:28:42 +0000420 def getmark(self, id):
Collin Winterce36ad82007-08-30 01:19:48 +0000421 raise Error('no marks')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000422
Guido van Rossume7b146f2000-02-04 15:28:42 +0000423 def getmarkers(self):
424 return None
Tim Peterse1190062001-01-15 03:34:38 +0000425
Guido van Rossume7b146f2000-02-04 15:28:42 +0000426 def tell(self):
427 return self._nframeswritten
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000428
Guido van Rossume7b146f2000-02-04 15:28:42 +0000429 def writeframesraw(self, data):
430 self._ensure_header_written(len(data))
Guido van Rossum54e54c62001-09-04 19:14:14 +0000431 nframes = len(data) // (self._sampwidth * self._nchannels)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000432 if self._convert:
433 data = self._convert(data)
Serhiy Storchakad739bda2013-05-29 23:38:00 +0300434 if self._sampwidth > 1 and sys.byteorder == 'big':
Guido van Rossume7b146f2000-02-04 15:28:42 +0000435 import array
436 data = array.array(_array_fmts[self._sampwidth], data)
Serhiy Storchakad3b75052013-10-17 23:04:04 +0300437 assert data.itemsize == self._sampwidth
Guido van Rossume7b146f2000-02-04 15:28:42 +0000438 data.byteswap()
439 data.tofile(self._file)
440 self._datawritten = self._datawritten + len(data) * self._sampwidth
441 else:
442 self._file.write(data)
443 self._datawritten = self._datawritten + len(data)
444 self._nframeswritten = self._nframeswritten + nframes
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000445
Guido van Rossume7b146f2000-02-04 15:28:42 +0000446 def writeframes(self, data):
447 self.writeframesraw(data)
448 if self._datalength != self._datawritten:
449 self._patchheader()
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000450
Guido van Rossume7b146f2000-02-04 15:28:42 +0000451 def close(self):
Tim Peterscfc41782000-10-09 23:43:55 +0000452 if self._file:
R David Murray536ffe12013-07-31 20:48:26 -0400453 try:
454 self._ensure_header_written(0)
455 if self._datalength != self._datawritten:
456 self._patchheader()
457 self._file.flush()
458 finally:
459 self._file = None
Tim Peterscfc41782000-10-09 23:43:55 +0000460 if self._i_opened_the_file:
461 self._i_opened_the_file.close()
462 self._i_opened_the_file = None
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000463
Guido van Rossume7b146f2000-02-04 15:28:42 +0000464 #
465 # Internal methods.
466 #
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000467
Guido van Rossume7b146f2000-02-04 15:28:42 +0000468 def _ensure_header_written(self, datasize):
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000469 if not self._headerwritten:
Guido van Rossume7b146f2000-02-04 15:28:42 +0000470 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000471 raise Error('# channels not specified')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000472 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000473 raise Error('sample width not specified')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000474 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000475 raise Error('sampling rate not specified')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000476 self._write_header(datasize)
477
478 def _write_header(self, initlength):
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000479 assert not self._headerwritten
Guido van Rossum09549f42007-08-27 20:40:10 +0000480 self._file.write(b'RIFF')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000481 if not self._nframes:
Benjamin Peterson5efea042010-01-13 03:49:50 +0000482 self._nframes = initlength // (self._nchannels * self._sampwidth)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000483 self._datalength = self._nframes * self._nchannels * self._sampwidth
484 self._form_length_pos = self._file.tell()
Jesus Ceae4b863982012-11-17 03:41:54 +0100485 self._file.write(struct.pack('<L4s4sLHHLLHH4s',
Victor Stinnerda9ec992010-12-28 13:26:42 +0000486 36 + self._datalength, b'WAVE', b'fmt ', 16,
Guido van Rossume7b146f2000-02-04 15:28:42 +0000487 WAVE_FORMAT_PCM, self._nchannels, self._framerate,
488 self._nchannels * self._framerate * self._sampwidth,
489 self._nchannels * self._sampwidth,
Victor Stinnerda9ec992010-12-28 13:26:42 +0000490 self._sampwidth * 8, b'data'))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000491 self._data_length_pos = self._file.tell()
Jesus Ceae4b863982012-11-17 03:41:54 +0100492 self._file.write(struct.pack('<L', self._datalength))
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000493 self._headerwritten = True
Guido van Rossume7b146f2000-02-04 15:28:42 +0000494
495 def _patchheader(self):
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000496 assert self._headerwritten
Guido van Rossume7b146f2000-02-04 15:28:42 +0000497 if self._datawritten == self._datalength:
498 return
499 curpos = self._file.tell()
500 self._file.seek(self._form_length_pos, 0)
Jesus Ceae4b863982012-11-17 03:41:54 +0100501 self._file.write(struct.pack('<L', 36 + self._datawritten))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000502 self._file.seek(self._data_length_pos, 0)
Jesus Ceae4b863982012-11-17 03:41:54 +0100503 self._file.write(struct.pack('<L', self._datawritten))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000504 self._file.seek(curpos, 0)
505 self._datalength = self._datawritten
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000506
Fred Drakef9607821999-06-17 15:18:47 +0000507def open(f, mode=None):
Guido van Rossume7b146f2000-02-04 15:28:42 +0000508 if mode is None:
509 if hasattr(f, 'mode'):
510 mode = f.mode
511 else:
512 mode = 'rb'
513 if mode in ('r', 'rb'):
514 return Wave_read(f)
515 elif mode in ('w', 'wb'):
516 return Wave_write(f)
517 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000518 raise Error("mode must be 'r', 'rb', 'w', or 'wb'")
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000519
520openfp = open # B/W compatibility