blob: f155879a9a76aba7a0c1c5d87b5674fe4bc23c8d [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.
Serhiy Storchakae0fd7ef2015-07-10 22:13:40 +030068When all frames have been written, either call writeframes(b'') or
Guido van Rossume7b146f2000-02-04 15:28:42 +000069close() 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
Martin Panter19e69c52015-11-14 12:46:42 +000076__all__ = ["open", "openfp", "Error", "Wave_read", "Wave_write"]
Skip Montanaro40fc1602001-03-01 04:27:19 +000077
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
Serhiy Storchaka3062c9a2013-11-23 22:26:01 +020085import audioop
Guido van Rossumebb9c921999-02-05 22:28:17 +000086import struct
Serhiy Storchakad739bda2013-05-29 23:38:00 +030087import sys
Guido van Rossum3601e881999-08-26 15:50:43 +000088from chunk import Chunk
R David Murray671cd322013-04-10 12:31:43 -040089from collections import namedtuple
Brian Curtin9f914a02017-11-10 11:38:25 -050090import warnings
R David Murray671cd322013-04-10 12:31:43 -040091
Serhiy Storchaka4c6a0202013-09-04 00:28:43 +030092_wave_params = namedtuple('_wave_params',
R David Murray671cd322013-04-10 12:31:43 -040093 'nchannels sampwidth framerate nframes comptype compname')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000094
95class Wave_read:
Guido van Rossume7b146f2000-02-04 15:28:42 +000096 """Variables used in this class:
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000097
Guido van Rossume7b146f2000-02-04 15:28:42 +000098 These variables are available to the user though appropriate
99 methods of this class:
100 _file -- the open file with methods read(), close(), and seek()
101 set through the __init__() method
102 _nchannels -- the number of audio channels
103 available through the getnchannels() method
104 _nframes -- the number of audio frames
105 available through the getnframes() method
106 _sampwidth -- the number of bytes per audio sample
107 available through the getsampwidth() method
108 _framerate -- the sampling frequency
109 available through the getframerate() method
110 _comptype -- the AIFF-C compression type ('NONE' if AIFF)
111 available through the getcomptype() method
112 _compname -- the human-readable AIFF-C compression type
113 available through the getcomptype() method
114 _soundpos -- the position in the audio stream
115 available through the tell() method, set through the
116 setpos() method
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000117
Guido van Rossume7b146f2000-02-04 15:28:42 +0000118 These variables are used internally only:
119 _fmt_chunk_read -- 1 iff the FMT chunk has been read
120 _data_seek_needed -- 1 iff positioned correctly in audio
121 file for readframes()
122 _data_chunk -- instantiation of a chunk class for the DATA chunk
123 _framesize -- size of one frame in the file
124 """
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000125
Guido van Rossume7b146f2000-02-04 15:28:42 +0000126 def initfp(self, file):
127 self._convert = None
128 self._soundpos = 0
129 self._file = Chunk(file, bigendian = 0)
Guido van Rossum51a883b2007-07-23 21:28:30 +0000130 if self._file.getname() != b'RIFF':
Collin Winterce36ad82007-08-30 01:19:48 +0000131 raise Error('file does not start with RIFF id')
Guido van Rossum51a883b2007-07-23 21:28:30 +0000132 if self._file.read(4) != b'WAVE':
Collin Winterce36ad82007-08-30 01:19:48 +0000133 raise Error('not a WAVE file')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000134 self._fmt_chunk_read = 0
135 self._data_chunk = None
136 while 1:
137 self._data_seek_needed = 1
138 try:
139 chunk = Chunk(self._file, bigendian = 0)
140 except EOFError:
141 break
142 chunkname = chunk.getname()
Guido van Rossum51a883b2007-07-23 21:28:30 +0000143 if chunkname == b'fmt ':
Guido van Rossume7b146f2000-02-04 15:28:42 +0000144 self._read_fmt_chunk(chunk)
145 self._fmt_chunk_read = 1
Guido van Rossum51a883b2007-07-23 21:28:30 +0000146 elif chunkname == b'data':
Guido van Rossume7b146f2000-02-04 15:28:42 +0000147 if not self._fmt_chunk_read:
Collin Winterce36ad82007-08-30 01:19:48 +0000148 raise Error('data chunk before fmt chunk')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000149 self._data_chunk = chunk
Guido van Rossum54e54c62001-09-04 19:14:14 +0000150 self._nframes = chunk.chunksize // self._framesize
Guido van Rossume7b146f2000-02-04 15:28:42 +0000151 self._data_seek_needed = 0
152 break
153 chunk.skip()
154 if not self._fmt_chunk_read or not self._data_chunk:
Collin Winterce36ad82007-08-30 01:19:48 +0000155 raise Error('fmt chunk and/or data chunk missing')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000156
Guido van Rossume7b146f2000-02-04 15:28:42 +0000157 def __init__(self, f):
Tim Peterscfc41782000-10-09 23:43:55 +0000158 self._i_opened_the_file = None
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000159 if isinstance(f, str):
Georg Brandl1a3284e2007-12-02 09:40:06 +0000160 f = builtins.open(f, 'rb')
Tim Peterscfc41782000-10-09 23:43:55 +0000161 self._i_opened_the_file = f
Guido van Rossume7b146f2000-02-04 15:28:42 +0000162 # else, assume it is an open file object already
Guido van Rossumd8faa362007-04-27 19:54:29 +0000163 try:
164 self.initfp(f)
165 except:
166 if self._i_opened_the_file:
167 f.close()
168 raise
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000169
Tim Peterscfc41782000-10-09 23:43:55 +0000170 def __del__(self):
171 self.close()
R David Murrayc91d5ee2013-07-31 13:46:08 -0400172
173 def __enter__(self):
174 return self
175
176 def __exit__(self, *args):
177 self.close()
178
Guido van Rossume7b146f2000-02-04 15:28:42 +0000179 #
180 # User visible methods.
181 #
182 def getfp(self):
183 return self._file
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000184
Guido van Rossume7b146f2000-02-04 15:28:42 +0000185 def rewind(self):
186 self._data_seek_needed = 1
187 self._soundpos = 0
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000188
Guido van Rossume7b146f2000-02-04 15:28:42 +0000189 def close(self):
190 self._file = None
Serhiy Storchaka7e7a3db2015-04-10 13:24:41 +0300191 file = self._i_opened_the_file
192 if file:
193 self._i_opened_the_file = None
194 file.close()
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000195
Guido van Rossume7b146f2000-02-04 15:28:42 +0000196 def tell(self):
197 return self._soundpos
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000198
Guido van Rossume7b146f2000-02-04 15:28:42 +0000199 def getnchannels(self):
200 return self._nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000201
Guido van Rossume7b146f2000-02-04 15:28:42 +0000202 def getnframes(self):
203 return self._nframes
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000204
Guido van Rossume7b146f2000-02-04 15:28:42 +0000205 def getsampwidth(self):
206 return self._sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000207
Guido van Rossume7b146f2000-02-04 15:28:42 +0000208 def getframerate(self):
209 return self._framerate
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000210
Guido van Rossume7b146f2000-02-04 15:28:42 +0000211 def getcomptype(self):
212 return self._comptype
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000213
Guido van Rossume7b146f2000-02-04 15:28:42 +0000214 def getcompname(self):
215 return self._compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000216
Guido van Rossume7b146f2000-02-04 15:28:42 +0000217 def getparams(self):
Serhiy Storchaka4c6a0202013-09-04 00:28:43 +0300218 return _wave_params(self.getnchannels(), self.getsampwidth(),
R David Murray671cd322013-04-10 12:31:43 -0400219 self.getframerate(), self.getnframes(),
220 self.getcomptype(), self.getcompname())
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000221
Guido van Rossume7b146f2000-02-04 15:28:42 +0000222 def getmarkers(self):
223 return None
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000224
Guido van Rossume7b146f2000-02-04 15:28:42 +0000225 def getmark(self, id):
Collin Winterce36ad82007-08-30 01:19:48 +0000226 raise Error('no marks')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000227
Guido van Rossume7b146f2000-02-04 15:28:42 +0000228 def setpos(self, pos):
229 if pos < 0 or pos > self._nframes:
Collin Winterce36ad82007-08-30 01:19:48 +0000230 raise Error('position not in range')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000231 self._soundpos = pos
232 self._data_seek_needed = 1
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000233
Guido van Rossume7b146f2000-02-04 15:28:42 +0000234 def readframes(self, nframes):
235 if self._data_seek_needed:
236 self._data_chunk.seek(0, 0)
237 pos = self._soundpos * self._framesize
238 if pos:
239 self._data_chunk.seek(pos, 0)
240 self._data_seek_needed = 0
241 if nframes == 0:
Guido van Rossum51a883b2007-07-23 21:28:30 +0000242 return b''
Serhiy Storchaka3062c9a2013-11-23 22:26:01 +0200243 data = self._data_chunk.read(nframes * self._framesize)
244 if self._sampwidth != 1 and sys.byteorder == 'big':
245 data = audioop.byteswap(data, self._sampwidth)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000246 if self._convert and data:
247 data = self._convert(data)
Guido van Rossum54e54c62001-09-04 19:14:14 +0000248 self._soundpos = self._soundpos + len(data) // (self._nchannels * self._sampwidth)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000249 return data
250
251 #
252 # Internal methods.
253 #
254
255 def _read_fmt_chunk(self, chunk):
Serhiy Storchaka134cb012018-03-18 09:55:53 +0200256 try:
257 wFormatTag, self._nchannels, self._framerate, dwAvgBytesPerSec, wBlockAlign = struct.unpack_from('<HHLLH', chunk.read(14))
258 except struct.error:
259 raise EOFError from None
Guido van Rossume7b146f2000-02-04 15:28:42 +0000260 if wFormatTag == WAVE_FORMAT_PCM:
Serhiy Storchaka134cb012018-03-18 09:55:53 +0200261 try:
262 sampwidth = struct.unpack_from('<H', chunk.read(2))[0]
263 except struct.error:
264 raise EOFError from None
Guido van Rossum54e54c62001-09-04 19:14:14 +0000265 self._sampwidth = (sampwidth + 7) // 8
Serhiy Storchaka134cb012018-03-18 09:55:53 +0200266 if not self._sampwidth:
267 raise Error('bad sample width')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000268 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000269 raise Error('unknown format: %r' % (wFormatTag,))
Serhiy Storchaka134cb012018-03-18 09:55:53 +0200270 if not self._nchannels:
271 raise Error('bad # of channels')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000272 self._framesize = self._nchannels * self._sampwidth
273 self._comptype = 'NONE'
274 self._compname = 'not compressed'
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000275
276class Wave_write:
Guido van Rossume7b146f2000-02-04 15:28:42 +0000277 """Variables used in this class:
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000278
Guido van Rossume7b146f2000-02-04 15:28:42 +0000279 These variables are user settable through appropriate methods
280 of this class:
281 _file -- the open file with methods write(), close(), tell(), seek()
282 set through the __init__() method
283 _comptype -- the AIFF-C compression type ('NONE' in AIFF)
284 set through the setcomptype() or setparams() method
285 _compname -- the human-readable AIFF-C compression type
286 set through the setcomptype() or setparams() method
287 _nchannels -- the number of audio channels
288 set through the setnchannels() or setparams() method
289 _sampwidth -- the number of bytes per audio sample
290 set through the setsampwidth() or setparams() method
291 _framerate -- the sampling frequency
292 set through the setframerate() or setparams() method
293 _nframes -- the number of audio frames written to the header
294 set through the setnframes() or setparams() method
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000295
Guido van Rossume7b146f2000-02-04 15:28:42 +0000296 These variables are used internally only:
297 _datalength -- the size of the audio samples written to the header
298 _nframeswritten -- the number of frames actually written
299 _datawritten -- the size of the audio samples actually written
300 """
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000301
Guido van Rossume7b146f2000-02-04 15:28:42 +0000302 def __init__(self, f):
Tim Peterscfc41782000-10-09 23:43:55 +0000303 self._i_opened_the_file = None
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000304 if isinstance(f, str):
Georg Brandl1a3284e2007-12-02 09:40:06 +0000305 f = builtins.open(f, 'wb')
Tim Peterscfc41782000-10-09 23:43:55 +0000306 self._i_opened_the_file = f
Guido van Rossumd8faa362007-04-27 19:54:29 +0000307 try:
308 self.initfp(f)
309 except:
310 if self._i_opened_the_file:
311 f.close()
312 raise
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000313
Guido van Rossume7b146f2000-02-04 15:28:42 +0000314 def initfp(self, file):
315 self._file = file
316 self._convert = None
317 self._nchannels = 0
318 self._sampwidth = 0
319 self._framerate = 0
320 self._nframes = 0
321 self._nframeswritten = 0
322 self._datawritten = 0
323 self._datalength = 0
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000324 self._headerwritten = False
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000325
Guido van Rossume7b146f2000-02-04 15:28:42 +0000326 def __del__(self):
Tim Peterscfc41782000-10-09 23:43:55 +0000327 self.close()
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000328
R David Murrayc91d5ee2013-07-31 13:46:08 -0400329 def __enter__(self):
330 return self
331
332 def __exit__(self, *args):
333 self.close()
334
Guido van Rossume7b146f2000-02-04 15:28:42 +0000335 #
336 # User visible methods.
337 #
338 def setnchannels(self, nchannels):
339 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000340 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000341 if nchannels < 1:
Collin Winterce36ad82007-08-30 01:19:48 +0000342 raise Error('bad # of channels')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000343 self._nchannels = nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000344
Guido van Rossume7b146f2000-02-04 15:28:42 +0000345 def getnchannels(self):
346 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000347 raise Error('number of channels not set')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000348 return self._nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000349
Guido van Rossume7b146f2000-02-04 15:28:42 +0000350 def setsampwidth(self, sampwidth):
351 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000352 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000353 if sampwidth < 1 or sampwidth > 4:
Collin Winterce36ad82007-08-30 01:19:48 +0000354 raise Error('bad sample width')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000355 self._sampwidth = sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000356
Guido van Rossume7b146f2000-02-04 15:28:42 +0000357 def getsampwidth(self):
358 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000359 raise Error('sample width not set')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000360 return self._sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000361
Guido van Rossume7b146f2000-02-04 15:28:42 +0000362 def setframerate(self, framerate):
363 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000364 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000365 if framerate <= 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000366 raise Error('bad frame rate')
Mark Dickinson64a38c02010-08-28 17:22:16 +0000367 self._framerate = int(round(framerate))
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000368
Guido van Rossume7b146f2000-02-04 15:28:42 +0000369 def getframerate(self):
370 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000371 raise Error('frame rate not set')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000372 return self._framerate
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000373
Guido van Rossume7b146f2000-02-04 15:28:42 +0000374 def setnframes(self, nframes):
375 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000376 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000377 self._nframes = nframes
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000378
Guido van Rossume7b146f2000-02-04 15:28:42 +0000379 def getnframes(self):
380 return self._nframeswritten
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000381
Guido van Rossume7b146f2000-02-04 15:28:42 +0000382 def setcomptype(self, comptype, compname):
383 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000384 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000385 if comptype not in ('NONE',):
Collin Winterce36ad82007-08-30 01:19:48 +0000386 raise Error('unsupported compression type')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000387 self._comptype = comptype
388 self._compname = compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000389
Guido van Rossume7b146f2000-02-04 15:28:42 +0000390 def getcomptype(self):
391 return self._comptype
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000392
Guido van Rossume7b146f2000-02-04 15:28:42 +0000393 def getcompname(self):
394 return self._compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000395
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000396 def setparams(self, params):
397 nchannels, sampwidth, framerate, nframes, comptype, compname = params
Guido van Rossume7b146f2000-02-04 15:28:42 +0000398 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000399 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000400 self.setnchannels(nchannels)
401 self.setsampwidth(sampwidth)
402 self.setframerate(framerate)
403 self.setnframes(nframes)
404 self.setcomptype(comptype, compname)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000405
Guido van Rossume7b146f2000-02-04 15:28:42 +0000406 def getparams(self):
407 if not self._nchannels or not self._sampwidth or not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000408 raise Error('not all parameters set')
Serhiy Storchaka4c6a0202013-09-04 00:28:43 +0300409 return _wave_params(self._nchannels, self._sampwidth, self._framerate,
R David Murray671cd322013-04-10 12:31:43 -0400410 self._nframes, self._comptype, self._compname)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000411
Guido van Rossume7b146f2000-02-04 15:28:42 +0000412 def setmark(self, id, pos, name):
Collin Winterce36ad82007-08-30 01:19:48 +0000413 raise Error('setmark() not supported')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000414
Guido van Rossume7b146f2000-02-04 15:28:42 +0000415 def getmark(self, id):
Collin Winterce36ad82007-08-30 01:19:48 +0000416 raise Error('no marks')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000417
Guido van Rossume7b146f2000-02-04 15:28:42 +0000418 def getmarkers(self):
419 return None
Tim Peterse1190062001-01-15 03:34:38 +0000420
Guido van Rossume7b146f2000-02-04 15:28:42 +0000421 def tell(self):
422 return self._nframeswritten
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000423
Guido van Rossume7b146f2000-02-04 15:28:42 +0000424 def writeframesraw(self, data):
Serhiy Storchaka452bab42013-11-16 14:01:31 +0200425 if not isinstance(data, (bytes, bytearray)):
426 data = memoryview(data).cast('B')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000427 self._ensure_header_written(len(data))
Guido van Rossum54e54c62001-09-04 19:14:14 +0000428 nframes = len(data) // (self._sampwidth * self._nchannels)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000429 if self._convert:
430 data = self._convert(data)
Serhiy Storchaka3062c9a2013-11-23 22:26:01 +0200431 if self._sampwidth != 1 and sys.byteorder == 'big':
432 data = audioop.byteswap(data, self._sampwidth)
433 self._file.write(data)
434 self._datawritten += len(data)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000435 self._nframeswritten = self._nframeswritten + nframes
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000436
Guido van Rossume7b146f2000-02-04 15:28:42 +0000437 def writeframes(self, data):
438 self.writeframesraw(data)
439 if self._datalength != self._datawritten:
440 self._patchheader()
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000441
Guido van Rossume7b146f2000-02-04 15:28:42 +0000442 def close(self):
Serhiy Storchaka7e7a3db2015-04-10 13:24:41 +0300443 try:
444 if self._file:
R David Murray536ffe12013-07-31 20:48:26 -0400445 self._ensure_header_written(0)
446 if self._datalength != self._datawritten:
447 self._patchheader()
448 self._file.flush()
Serhiy Storchaka7e7a3db2015-04-10 13:24:41 +0300449 finally:
450 self._file = None
451 file = self._i_opened_the_file
452 if file:
453 self._i_opened_the_file = None
454 file.close()
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000455
Guido van Rossume7b146f2000-02-04 15:28:42 +0000456 #
457 # Internal methods.
458 #
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000459
Guido van Rossume7b146f2000-02-04 15:28:42 +0000460 def _ensure_header_written(self, datasize):
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000461 if not self._headerwritten:
Guido van Rossume7b146f2000-02-04 15:28:42 +0000462 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000463 raise Error('# channels not specified')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000464 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000465 raise Error('sample width not specified')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000466 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000467 raise Error('sampling rate not specified')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000468 self._write_header(datasize)
469
470 def _write_header(self, initlength):
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000471 assert not self._headerwritten
Guido van Rossum09549f42007-08-27 20:40:10 +0000472 self._file.write(b'RIFF')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000473 if not self._nframes:
Benjamin Peterson5efea042010-01-13 03:49:50 +0000474 self._nframes = initlength // (self._nchannels * self._sampwidth)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000475 self._datalength = self._nframes * self._nchannels * self._sampwidth
Serhiy Storchaka7714ebb2013-11-16 13:04:00 +0200476 try:
477 self._form_length_pos = self._file.tell()
478 except (AttributeError, OSError):
479 self._form_length_pos = None
Jesus Ceae4b863982012-11-17 03:41:54 +0100480 self._file.write(struct.pack('<L4s4sLHHLLHH4s',
Victor Stinnerda9ec992010-12-28 13:26:42 +0000481 36 + self._datalength, b'WAVE', b'fmt ', 16,
Guido van Rossume7b146f2000-02-04 15:28:42 +0000482 WAVE_FORMAT_PCM, self._nchannels, self._framerate,
483 self._nchannels * self._framerate * self._sampwidth,
484 self._nchannels * self._sampwidth,
Victor Stinnerda9ec992010-12-28 13:26:42 +0000485 self._sampwidth * 8, b'data'))
Serhiy Storchaka7714ebb2013-11-16 13:04:00 +0200486 if self._form_length_pos is not None:
487 self._data_length_pos = self._file.tell()
Jesus Ceae4b863982012-11-17 03:41:54 +0100488 self._file.write(struct.pack('<L', self._datalength))
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000489 self._headerwritten = True
Guido van Rossume7b146f2000-02-04 15:28:42 +0000490
491 def _patchheader(self):
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000492 assert self._headerwritten
Guido van Rossume7b146f2000-02-04 15:28:42 +0000493 if self._datawritten == self._datalength:
494 return
495 curpos = self._file.tell()
496 self._file.seek(self._form_length_pos, 0)
Jesus Ceae4b863982012-11-17 03:41:54 +0100497 self._file.write(struct.pack('<L', 36 + self._datawritten))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000498 self._file.seek(self._data_length_pos, 0)
Jesus Ceae4b863982012-11-17 03:41:54 +0100499 self._file.write(struct.pack('<L', self._datawritten))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000500 self._file.seek(curpos, 0)
501 self._datalength = self._datawritten
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000502
Fred Drakef9607821999-06-17 15:18:47 +0000503def open(f, mode=None):
Guido van Rossume7b146f2000-02-04 15:28:42 +0000504 if mode is None:
505 if hasattr(f, 'mode'):
506 mode = f.mode
507 else:
508 mode = 'rb'
509 if mode in ('r', 'rb'):
510 return Wave_read(f)
511 elif mode in ('w', 'wb'):
512 return Wave_write(f)
513 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000514 raise Error("mode must be 'r', 'rb', 'w', or 'wb'")
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000515
Brian Curtin9f914a02017-11-10 11:38:25 -0500516def openfp(f, mode=None):
517 warnings.warn("wave.openfp is deprecated since Python 3.7. "
518 "Use wave.open instead.", DeprecationWarning, stacklevel=2)
519 return open(f, mode=mode)