blob: 3d01817afd0b0fa15c68532dbdf0778f7e581c37 [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 Storchakaa44372f2013-11-09 23:12:06 +020090def _byteswap3(data):
91 ba = bytearray(data)
92 ba[::3] = data[2::3]
93 ba[2::3] = data[::3]
94 return bytes(ba)
95
Serhiy Storchaka4c6a0202013-09-04 00:28:43 +030096_wave_params = namedtuple('_wave_params',
R David Murray671cd322013-04-10 12:31:43 -040097 'nchannels sampwidth framerate nframes comptype compname')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000098
99class Wave_read:
Guido van Rossume7b146f2000-02-04 15:28:42 +0000100 """Variables used in this class:
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000101
Guido van Rossume7b146f2000-02-04 15:28:42 +0000102 These variables are available to the user though appropriate
103 methods of this class:
104 _file -- the open file with methods read(), close(), and seek()
105 set through the __init__() method
106 _nchannels -- the number of audio channels
107 available through the getnchannels() method
108 _nframes -- the number of audio frames
109 available through the getnframes() method
110 _sampwidth -- the number of bytes per audio sample
111 available through the getsampwidth() method
112 _framerate -- the sampling frequency
113 available through the getframerate() method
114 _comptype -- the AIFF-C compression type ('NONE' if AIFF)
115 available through the getcomptype() method
116 _compname -- the human-readable AIFF-C compression type
117 available through the getcomptype() method
118 _soundpos -- the position in the audio stream
119 available through the tell() method, set through the
120 setpos() method
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000121
Guido van Rossume7b146f2000-02-04 15:28:42 +0000122 These variables are used internally only:
123 _fmt_chunk_read -- 1 iff the FMT chunk has been read
124 _data_seek_needed -- 1 iff positioned correctly in audio
125 file for readframes()
126 _data_chunk -- instantiation of a chunk class for the DATA chunk
127 _framesize -- size of one frame in the file
128 """
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000129
Guido van Rossume7b146f2000-02-04 15:28:42 +0000130 def initfp(self, file):
131 self._convert = None
132 self._soundpos = 0
133 self._file = Chunk(file, bigendian = 0)
Guido van Rossum51a883b2007-07-23 21:28:30 +0000134 if self._file.getname() != b'RIFF':
Collin Winterce36ad82007-08-30 01:19:48 +0000135 raise Error('file does not start with RIFF id')
Guido van Rossum51a883b2007-07-23 21:28:30 +0000136 if self._file.read(4) != b'WAVE':
Collin Winterce36ad82007-08-30 01:19:48 +0000137 raise Error('not a WAVE file')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000138 self._fmt_chunk_read = 0
139 self._data_chunk = None
140 while 1:
141 self._data_seek_needed = 1
142 try:
143 chunk = Chunk(self._file, bigendian = 0)
144 except EOFError:
145 break
146 chunkname = chunk.getname()
Guido van Rossum51a883b2007-07-23 21:28:30 +0000147 if chunkname == b'fmt ':
Guido van Rossume7b146f2000-02-04 15:28:42 +0000148 self._read_fmt_chunk(chunk)
149 self._fmt_chunk_read = 1
Guido van Rossum51a883b2007-07-23 21:28:30 +0000150 elif chunkname == b'data':
Guido van Rossume7b146f2000-02-04 15:28:42 +0000151 if not self._fmt_chunk_read:
Collin Winterce36ad82007-08-30 01:19:48 +0000152 raise Error('data chunk before fmt chunk')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000153 self._data_chunk = chunk
Guido van Rossum54e54c62001-09-04 19:14:14 +0000154 self._nframes = chunk.chunksize // self._framesize
Guido van Rossume7b146f2000-02-04 15:28:42 +0000155 self._data_seek_needed = 0
156 break
157 chunk.skip()
158 if not self._fmt_chunk_read or not self._data_chunk:
Collin Winterce36ad82007-08-30 01:19:48 +0000159 raise Error('fmt chunk and/or data chunk missing')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000160
Guido van Rossume7b146f2000-02-04 15:28:42 +0000161 def __init__(self, f):
Tim Peterscfc41782000-10-09 23:43:55 +0000162 self._i_opened_the_file = None
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000163 if isinstance(f, str):
Georg Brandl1a3284e2007-12-02 09:40:06 +0000164 f = builtins.open(f, 'rb')
Tim Peterscfc41782000-10-09 23:43:55 +0000165 self._i_opened_the_file = f
Guido van Rossume7b146f2000-02-04 15:28:42 +0000166 # else, assume it is an open file object already
Guido van Rossumd8faa362007-04-27 19:54:29 +0000167 try:
168 self.initfp(f)
169 except:
170 if self._i_opened_the_file:
171 f.close()
172 raise
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000173
Tim Peterscfc41782000-10-09 23:43:55 +0000174 def __del__(self):
175 self.close()
R David Murrayc91d5ee2013-07-31 13:46:08 -0400176
177 def __enter__(self):
178 return self
179
180 def __exit__(self, *args):
181 self.close()
182
Guido van Rossume7b146f2000-02-04 15:28:42 +0000183 #
184 # User visible methods.
185 #
186 def getfp(self):
187 return self._file
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000188
Guido van Rossume7b146f2000-02-04 15:28:42 +0000189 def rewind(self):
190 self._data_seek_needed = 1
191 self._soundpos = 0
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000192
Guido van Rossume7b146f2000-02-04 15:28:42 +0000193 def close(self):
Tim Peterscfc41782000-10-09 23:43:55 +0000194 if self._i_opened_the_file:
195 self._i_opened_the_file.close()
196 self._i_opened_the_file = None
Guido van Rossume7b146f2000-02-04 15:28:42 +0000197 self._file = None
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000198
Guido van Rossume7b146f2000-02-04 15:28:42 +0000199 def tell(self):
200 return self._soundpos
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000201
Guido van Rossume7b146f2000-02-04 15:28:42 +0000202 def getnchannels(self):
203 return self._nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000204
Guido van Rossume7b146f2000-02-04 15:28:42 +0000205 def getnframes(self):
206 return self._nframes
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000207
Guido van Rossume7b146f2000-02-04 15:28:42 +0000208 def getsampwidth(self):
209 return self._sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000210
Guido van Rossume7b146f2000-02-04 15:28:42 +0000211 def getframerate(self):
212 return self._framerate
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000213
Guido van Rossume7b146f2000-02-04 15:28:42 +0000214 def getcomptype(self):
215 return self._comptype
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000216
Guido van Rossume7b146f2000-02-04 15:28:42 +0000217 def getcompname(self):
218 return self._compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000219
Guido van Rossume7b146f2000-02-04 15:28:42 +0000220 def getparams(self):
Serhiy Storchaka4c6a0202013-09-04 00:28:43 +0300221 return _wave_params(self.getnchannels(), self.getsampwidth(),
R David Murray671cd322013-04-10 12:31:43 -0400222 self.getframerate(), self.getnframes(),
223 self.getcomptype(), self.getcompname())
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000224
Guido van Rossume7b146f2000-02-04 15:28:42 +0000225 def getmarkers(self):
226 return None
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000227
Guido van Rossume7b146f2000-02-04 15:28:42 +0000228 def getmark(self, id):
Collin Winterce36ad82007-08-30 01:19:48 +0000229 raise Error('no marks')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000230
Guido van Rossume7b146f2000-02-04 15:28:42 +0000231 def setpos(self, pos):
232 if pos < 0 or pos > self._nframes:
Collin Winterce36ad82007-08-30 01:19:48 +0000233 raise Error('position not in range')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000234 self._soundpos = pos
235 self._data_seek_needed = 1
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000236
Guido van Rossume7b146f2000-02-04 15:28:42 +0000237 def readframes(self, nframes):
238 if self._data_seek_needed:
239 self._data_chunk.seek(0, 0)
240 pos = self._soundpos * self._framesize
241 if pos:
242 self._data_chunk.seek(pos, 0)
243 self._data_seek_needed = 0
244 if nframes == 0:
Guido van Rossum51a883b2007-07-23 21:28:30 +0000245 return b''
Serhiy Storchakaa44372f2013-11-09 23:12:06 +0200246 if self._sampwidth in (2, 4) and sys.byteorder == 'big':
Guido van Rossume7b146f2000-02-04 15:28:42 +0000247 # unfortunately the fromfile() method does not take
248 # something that only looks like a file object, so
249 # we have to reach into the innards of the chunk object
250 import array
251 chunk = self._data_chunk
252 data = array.array(_array_fmts[self._sampwidth])
Serhiy Storchakad3b75052013-10-17 23:04:04 +0300253 assert data.itemsize == self._sampwidth
Guido van Rossume7b146f2000-02-04 15:28:42 +0000254 nitems = nframes * self._nchannels
255 if nitems * self._sampwidth > chunk.chunksize - chunk.size_read:
Benjamin Peterson5efea042010-01-13 03:49:50 +0000256 nitems = (chunk.chunksize - chunk.size_read) // self._sampwidth
Guido van Rossume7b146f2000-02-04 15:28:42 +0000257 data.fromfile(chunk.file.file, nitems)
258 # "tell" data chunk how much was read
259 chunk.size_read = chunk.size_read + nitems * self._sampwidth
260 # do the same for the outermost chunk
261 chunk = chunk.file
262 chunk.size_read = chunk.size_read + nitems * self._sampwidth
263 data.byteswap()
Antoine Pitrou1ce3eb52010-09-01 20:29:34 +0000264 data = data.tobytes()
Guido van Rossume7b146f2000-02-04 15:28:42 +0000265 else:
266 data = self._data_chunk.read(nframes * self._framesize)
Serhiy Storchakaa44372f2013-11-09 23:12:06 +0200267 if self._sampwidth == 3 and sys.byteorder == 'big':
268 data = _byteswap3(data)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000269 if self._convert and data:
270 data = self._convert(data)
Guido van Rossum54e54c62001-09-04 19:14:14 +0000271 self._soundpos = self._soundpos + len(data) // (self._nchannels * self._sampwidth)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000272 return data
273
274 #
275 # Internal methods.
276 #
277
278 def _read_fmt_chunk(self, chunk):
Jesus Ceae4b863982012-11-17 03:41:54 +0100279 wFormatTag, self._nchannels, self._framerate, dwAvgBytesPerSec, wBlockAlign = struct.unpack_from('<HHLLH', chunk.read(14))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000280 if wFormatTag == WAVE_FORMAT_PCM:
Jesus Ceae4b863982012-11-17 03:41:54 +0100281 sampwidth = struct.unpack_from('<H', chunk.read(2))[0]
Guido van Rossum54e54c62001-09-04 19:14:14 +0000282 self._sampwidth = (sampwidth + 7) // 8
Guido van Rossume7b146f2000-02-04 15:28:42 +0000283 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000284 raise Error('unknown format: %r' % (wFormatTag,))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000285 self._framesize = self._nchannels * self._sampwidth
286 self._comptype = 'NONE'
287 self._compname = 'not compressed'
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000288
289class Wave_write:
Guido van Rossume7b146f2000-02-04 15:28:42 +0000290 """Variables used in this class:
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000291
Guido van Rossume7b146f2000-02-04 15:28:42 +0000292 These variables are user settable through appropriate methods
293 of this class:
294 _file -- the open file with methods write(), close(), tell(), seek()
295 set through the __init__() method
296 _comptype -- the AIFF-C compression type ('NONE' in AIFF)
297 set through the setcomptype() or setparams() method
298 _compname -- the human-readable AIFF-C compression type
299 set through the setcomptype() or setparams() method
300 _nchannels -- the number of audio channels
301 set through the setnchannels() or setparams() method
302 _sampwidth -- the number of bytes per audio sample
303 set through the setsampwidth() or setparams() method
304 _framerate -- the sampling frequency
305 set through the setframerate() or setparams() method
306 _nframes -- the number of audio frames written to the header
307 set through the setnframes() or setparams() method
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000308
Guido van Rossume7b146f2000-02-04 15:28:42 +0000309 These variables are used internally only:
310 _datalength -- the size of the audio samples written to the header
311 _nframeswritten -- the number of frames actually written
312 _datawritten -- the size of the audio samples actually written
313 """
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000314
Guido van Rossume7b146f2000-02-04 15:28:42 +0000315 def __init__(self, f):
Tim Peterscfc41782000-10-09 23:43:55 +0000316 self._i_opened_the_file = None
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000317 if isinstance(f, str):
Georg Brandl1a3284e2007-12-02 09:40:06 +0000318 f = builtins.open(f, 'wb')
Tim Peterscfc41782000-10-09 23:43:55 +0000319 self._i_opened_the_file = f
Guido van Rossumd8faa362007-04-27 19:54:29 +0000320 try:
321 self.initfp(f)
322 except:
323 if self._i_opened_the_file:
324 f.close()
325 raise
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000326
Guido van Rossume7b146f2000-02-04 15:28:42 +0000327 def initfp(self, file):
328 self._file = file
329 self._convert = None
330 self._nchannels = 0
331 self._sampwidth = 0
332 self._framerate = 0
333 self._nframes = 0
334 self._nframeswritten = 0
335 self._datawritten = 0
336 self._datalength = 0
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000337 self._headerwritten = False
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000338
Guido van Rossume7b146f2000-02-04 15:28:42 +0000339 def __del__(self):
Tim Peterscfc41782000-10-09 23:43:55 +0000340 self.close()
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000341
R David Murrayc91d5ee2013-07-31 13:46:08 -0400342 def __enter__(self):
343 return self
344
345 def __exit__(self, *args):
346 self.close()
347
Guido van Rossume7b146f2000-02-04 15:28:42 +0000348 #
349 # User visible methods.
350 #
351 def setnchannels(self, nchannels):
352 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000353 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000354 if nchannels < 1:
Collin Winterce36ad82007-08-30 01:19:48 +0000355 raise Error('bad # of channels')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000356 self._nchannels = nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000357
Guido van Rossume7b146f2000-02-04 15:28:42 +0000358 def getnchannels(self):
359 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000360 raise Error('number of channels not set')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000361 return self._nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000362
Guido van Rossume7b146f2000-02-04 15:28:42 +0000363 def setsampwidth(self, sampwidth):
364 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000365 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000366 if sampwidth < 1 or sampwidth > 4:
Collin Winterce36ad82007-08-30 01:19:48 +0000367 raise Error('bad sample width')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000368 self._sampwidth = sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000369
Guido van Rossume7b146f2000-02-04 15:28:42 +0000370 def getsampwidth(self):
371 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000372 raise Error('sample width not set')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000373 return self._sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000374
Guido van Rossume7b146f2000-02-04 15:28:42 +0000375 def setframerate(self, framerate):
376 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000377 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000378 if framerate <= 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000379 raise Error('bad frame rate')
Mark Dickinson64a38c02010-08-28 17:22:16 +0000380 self._framerate = int(round(framerate))
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000381
Guido van Rossume7b146f2000-02-04 15:28:42 +0000382 def getframerate(self):
383 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000384 raise Error('frame rate not set')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000385 return self._framerate
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000386
Guido van Rossume7b146f2000-02-04 15:28:42 +0000387 def setnframes(self, nframes):
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 self._nframes = nframes
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000391
Guido van Rossume7b146f2000-02-04 15:28:42 +0000392 def getnframes(self):
393 return self._nframeswritten
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000394
Guido van Rossume7b146f2000-02-04 15:28:42 +0000395 def setcomptype(self, comptype, compname):
396 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000397 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000398 if comptype not in ('NONE',):
Collin Winterce36ad82007-08-30 01:19:48 +0000399 raise Error('unsupported compression type')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000400 self._comptype = comptype
401 self._compname = compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000402
Guido van Rossume7b146f2000-02-04 15:28:42 +0000403 def getcomptype(self):
404 return self._comptype
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000405
Guido van Rossume7b146f2000-02-04 15:28:42 +0000406 def getcompname(self):
407 return self._compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000408
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000409 def setparams(self, params):
410 nchannels, sampwidth, framerate, nframes, comptype, compname = params
Guido van Rossume7b146f2000-02-04 15:28:42 +0000411 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000412 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000413 self.setnchannels(nchannels)
414 self.setsampwidth(sampwidth)
415 self.setframerate(framerate)
416 self.setnframes(nframes)
417 self.setcomptype(comptype, compname)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000418
Guido van Rossume7b146f2000-02-04 15:28:42 +0000419 def getparams(self):
420 if not self._nchannels or not self._sampwidth or not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000421 raise Error('not all parameters set')
Serhiy Storchaka4c6a0202013-09-04 00:28:43 +0300422 return _wave_params(self._nchannels, self._sampwidth, self._framerate,
R David Murray671cd322013-04-10 12:31:43 -0400423 self._nframes, self._comptype, self._compname)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000424
Guido van Rossume7b146f2000-02-04 15:28:42 +0000425 def setmark(self, id, pos, name):
Collin Winterce36ad82007-08-30 01:19:48 +0000426 raise Error('setmark() not supported')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000427
Guido van Rossume7b146f2000-02-04 15:28:42 +0000428 def getmark(self, id):
Collin Winterce36ad82007-08-30 01:19:48 +0000429 raise Error('no marks')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000430
Guido van Rossume7b146f2000-02-04 15:28:42 +0000431 def getmarkers(self):
432 return None
Tim Peterse1190062001-01-15 03:34:38 +0000433
Guido van Rossume7b146f2000-02-04 15:28:42 +0000434 def tell(self):
435 return self._nframeswritten
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000436
Guido van Rossume7b146f2000-02-04 15:28:42 +0000437 def writeframesraw(self, data):
438 self._ensure_header_written(len(data))
Guido van Rossum54e54c62001-09-04 19:14:14 +0000439 nframes = len(data) // (self._sampwidth * self._nchannels)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000440 if self._convert:
441 data = self._convert(data)
Serhiy Storchakaa44372f2013-11-09 23:12:06 +0200442 if self._sampwidth in (2, 4) and sys.byteorder == 'big':
Guido van Rossume7b146f2000-02-04 15:28:42 +0000443 import array
444 data = array.array(_array_fmts[self._sampwidth], data)
Serhiy Storchakad3b75052013-10-17 23:04:04 +0300445 assert data.itemsize == self._sampwidth
Guido van Rossume7b146f2000-02-04 15:28:42 +0000446 data.byteswap()
447 data.tofile(self._file)
448 self._datawritten = self._datawritten + len(data) * self._sampwidth
449 else:
Serhiy Storchakaa44372f2013-11-09 23:12:06 +0200450 if self._sampwidth == 3 and sys.byteorder == 'big':
451 data = _byteswap3(data)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000452 self._file.write(data)
453 self._datawritten = self._datawritten + len(data)
454 self._nframeswritten = self._nframeswritten + nframes
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000455
Guido van Rossume7b146f2000-02-04 15:28:42 +0000456 def writeframes(self, data):
457 self.writeframesraw(data)
458 if self._datalength != self._datawritten:
459 self._patchheader()
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000460
Guido van Rossume7b146f2000-02-04 15:28:42 +0000461 def close(self):
Tim Peterscfc41782000-10-09 23:43:55 +0000462 if self._file:
R David Murray536ffe12013-07-31 20:48:26 -0400463 try:
464 self._ensure_header_written(0)
465 if self._datalength != self._datawritten:
466 self._patchheader()
467 self._file.flush()
468 finally:
469 self._file = None
Tim Peterscfc41782000-10-09 23:43:55 +0000470 if self._i_opened_the_file:
471 self._i_opened_the_file.close()
472 self._i_opened_the_file = None
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000473
Guido van Rossume7b146f2000-02-04 15:28:42 +0000474 #
475 # Internal methods.
476 #
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000477
Guido van Rossume7b146f2000-02-04 15:28:42 +0000478 def _ensure_header_written(self, datasize):
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000479 if not self._headerwritten:
Guido van Rossume7b146f2000-02-04 15:28:42 +0000480 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000481 raise Error('# channels not specified')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000482 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000483 raise Error('sample width not specified')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000484 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000485 raise Error('sampling rate not specified')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000486 self._write_header(datasize)
487
488 def _write_header(self, initlength):
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000489 assert not self._headerwritten
Guido van Rossum09549f42007-08-27 20:40:10 +0000490 self._file.write(b'RIFF')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000491 if not self._nframes:
Benjamin Peterson5efea042010-01-13 03:49:50 +0000492 self._nframes = initlength // (self._nchannels * self._sampwidth)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000493 self._datalength = self._nframes * self._nchannels * self._sampwidth
Serhiy Storchaka7714ebb2013-11-16 13:04:00 +0200494 try:
495 self._form_length_pos = self._file.tell()
496 except (AttributeError, OSError):
497 self._form_length_pos = None
Jesus Ceae4b863982012-11-17 03:41:54 +0100498 self._file.write(struct.pack('<L4s4sLHHLLHH4s',
Victor Stinnerda9ec992010-12-28 13:26:42 +0000499 36 + self._datalength, b'WAVE', b'fmt ', 16,
Guido van Rossume7b146f2000-02-04 15:28:42 +0000500 WAVE_FORMAT_PCM, self._nchannels, self._framerate,
501 self._nchannels * self._framerate * self._sampwidth,
502 self._nchannels * self._sampwidth,
Victor Stinnerda9ec992010-12-28 13:26:42 +0000503 self._sampwidth * 8, b'data'))
Serhiy Storchaka7714ebb2013-11-16 13:04:00 +0200504 if self._form_length_pos is not None:
505 self._data_length_pos = self._file.tell()
Jesus Ceae4b863982012-11-17 03:41:54 +0100506 self._file.write(struct.pack('<L', self._datalength))
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000507 self._headerwritten = True
Guido van Rossume7b146f2000-02-04 15:28:42 +0000508
509 def _patchheader(self):
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000510 assert self._headerwritten
Guido van Rossume7b146f2000-02-04 15:28:42 +0000511 if self._datawritten == self._datalength:
512 return
513 curpos = self._file.tell()
514 self._file.seek(self._form_length_pos, 0)
Jesus Ceae4b863982012-11-17 03:41:54 +0100515 self._file.write(struct.pack('<L', 36 + self._datawritten))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000516 self._file.seek(self._data_length_pos, 0)
Jesus Ceae4b863982012-11-17 03:41:54 +0100517 self._file.write(struct.pack('<L', self._datawritten))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000518 self._file.seek(curpos, 0)
519 self._datalength = self._datawritten
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000520
Fred Drakef9607821999-06-17 15:18:47 +0000521def open(f, mode=None):
Guido van Rossume7b146f2000-02-04 15:28:42 +0000522 if mode is None:
523 if hasattr(f, 'mode'):
524 mode = f.mode
525 else:
526 mode = 'rb'
527 if mode in ('r', 'rb'):
528 return Wave_read(f)
529 elif mode in ('w', 'wb'):
530 return Wave_write(f)
531 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000532 raise Error("mode must be 'r', 'rb', 'w', or 'wb'")
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000533
534openfp = open # B/W compatibility