blob: b7071198e6b8413cea89839a221f613779506714 [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)
Michael Haas25fa3ec2019-11-04 22:32:10 -060056 -- write audio frames without patching up the
Guido van Rossume7b146f2000-02-04 15:28:42 +000057 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
Victor Stinnerac7b1a32019-06-18 00:00:24 +020074from chunk import Chunk
75from collections import namedtuple
76import audioop
Georg Brandl1a3284e2007-12-02 09:40:06 +000077import builtins
Victor Stinnerac7b1a32019-06-18 00:00:24 +020078import struct
79import sys
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000080
Victor Stinnerac7b1a32019-06-18 00:00:24 +020081
82__all__ = ["open", "Error", "Wave_read", "Wave_write"]
Skip Montanaro40fc1602001-03-01 04:27:19 +000083
Fred Drake9b8d8012000-08-17 04:45:13 +000084class Error(Exception):
85 pass
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000086
87WAVE_FORMAT_PCM = 0x0001
88
Serhiy Storchakad3b75052013-10-17 23:04:04 +030089_array_fmts = None, 'b', 'h', None, 'i'
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000090
Serhiy Storchaka4c6a0202013-09-04 00:28:43 +030091_wave_params = namedtuple('_wave_params',
R David Murray671cd322013-04-10 12:31:43 -040092 'nchannels sampwidth framerate nframes comptype compname')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000093
94class Wave_read:
Guido van Rossume7b146f2000-02-04 15:28:42 +000095 """Variables used in this class:
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000096
Guido van Rossume7b146f2000-02-04 15:28:42 +000097 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
115 setpos() method
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000116
Guido van Rossume7b146f2000-02-04 15:28:42 +0000117 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
123 """
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000124
Guido van Rossume7b146f2000-02-04 15:28:42 +0000125 def initfp(self, file):
126 self._convert = None
127 self._soundpos = 0
128 self._file = Chunk(file, bigendian = 0)
Guido van Rossum51a883b2007-07-23 21:28:30 +0000129 if self._file.getname() != b'RIFF':
Collin Winterce36ad82007-08-30 01:19:48 +0000130 raise Error('file does not start with RIFF id')
Guido van Rossum51a883b2007-07-23 21:28:30 +0000131 if self._file.read(4) != b'WAVE':
Collin Winterce36ad82007-08-30 01:19:48 +0000132 raise Error('not a WAVE file')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000133 self._fmt_chunk_read = 0
134 self._data_chunk = None
135 while 1:
136 self._data_seek_needed = 1
137 try:
138 chunk = Chunk(self._file, bigendian = 0)
139 except EOFError:
140 break
141 chunkname = chunk.getname()
Guido van Rossum51a883b2007-07-23 21:28:30 +0000142 if chunkname == b'fmt ':
Guido van Rossume7b146f2000-02-04 15:28:42 +0000143 self._read_fmt_chunk(chunk)
144 self._fmt_chunk_read = 1
Guido van Rossum51a883b2007-07-23 21:28:30 +0000145 elif chunkname == b'data':
Guido van Rossume7b146f2000-02-04 15:28:42 +0000146 if not self._fmt_chunk_read:
Collin Winterce36ad82007-08-30 01:19:48 +0000147 raise Error('data chunk before fmt chunk')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000148 self._data_chunk = chunk
Guido van Rossum54e54c62001-09-04 19:14:14 +0000149 self._nframes = chunk.chunksize // self._framesize
Guido van Rossume7b146f2000-02-04 15:28:42 +0000150 self._data_seek_needed = 0
151 break
152 chunk.skip()
153 if not self._fmt_chunk_read or not self._data_chunk:
Collin Winterce36ad82007-08-30 01:19:48 +0000154 raise Error('fmt chunk and/or data chunk missing')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000155
Guido van Rossume7b146f2000-02-04 15:28:42 +0000156 def __init__(self, f):
Tim Peterscfc41782000-10-09 23:43:55 +0000157 self._i_opened_the_file = None
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000158 if isinstance(f, str):
Georg Brandl1a3284e2007-12-02 09:40:06 +0000159 f = builtins.open(f, 'rb')
Tim Peterscfc41782000-10-09 23:43:55 +0000160 self._i_opened_the_file = f
Guido van Rossume7b146f2000-02-04 15:28:42 +0000161 # else, assume it is an open file object already
Guido van Rossumd8faa362007-04-27 19:54:29 +0000162 try:
163 self.initfp(f)
164 except:
165 if self._i_opened_the_file:
166 f.close()
167 raise
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000168
Tim Peterscfc41782000-10-09 23:43:55 +0000169 def __del__(self):
170 self.close()
R David Murrayc91d5ee2013-07-31 13:46:08 -0400171
172 def __enter__(self):
173 return self
174
175 def __exit__(self, *args):
176 self.close()
177
Guido van Rossume7b146f2000-02-04 15:28:42 +0000178 #
179 # User visible methods.
180 #
181 def getfp(self):
182 return self._file
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000183
Guido van Rossume7b146f2000-02-04 15:28:42 +0000184 def rewind(self):
185 self._data_seek_needed = 1
186 self._soundpos = 0
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000187
Guido van Rossume7b146f2000-02-04 15:28:42 +0000188 def close(self):
189 self._file = None
Serhiy Storchaka7e7a3db2015-04-10 13:24:41 +0300190 file = self._i_opened_the_file
191 if file:
192 self._i_opened_the_file = None
193 file.close()
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000194
Guido van Rossume7b146f2000-02-04 15:28:42 +0000195 def tell(self):
196 return self._soundpos
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000197
Guido van Rossume7b146f2000-02-04 15:28:42 +0000198 def getnchannels(self):
199 return self._nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000200
Guido van Rossume7b146f2000-02-04 15:28:42 +0000201 def getnframes(self):
202 return self._nframes
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000203
Guido van Rossume7b146f2000-02-04 15:28:42 +0000204 def getsampwidth(self):
205 return self._sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000206
Guido van Rossume7b146f2000-02-04 15:28:42 +0000207 def getframerate(self):
208 return self._framerate
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000209
Guido van Rossume7b146f2000-02-04 15:28:42 +0000210 def getcomptype(self):
211 return self._comptype
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000212
Guido van Rossume7b146f2000-02-04 15:28:42 +0000213 def getcompname(self):
214 return self._compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000215
Guido van Rossume7b146f2000-02-04 15:28:42 +0000216 def getparams(self):
Serhiy Storchaka4c6a0202013-09-04 00:28:43 +0300217 return _wave_params(self.getnchannels(), self.getsampwidth(),
R David Murray671cd322013-04-10 12:31:43 -0400218 self.getframerate(), self.getnframes(),
219 self.getcomptype(), self.getcompname())
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000220
Guido van Rossume7b146f2000-02-04 15:28:42 +0000221 def getmarkers(self):
222 return None
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000223
Guido van Rossume7b146f2000-02-04 15:28:42 +0000224 def getmark(self, id):
Collin Winterce36ad82007-08-30 01:19:48 +0000225 raise Error('no marks')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000226
Guido van Rossume7b146f2000-02-04 15:28:42 +0000227 def setpos(self, pos):
228 if pos < 0 or pos > self._nframes:
Collin Winterce36ad82007-08-30 01:19:48 +0000229 raise Error('position not in range')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000230 self._soundpos = pos
231 self._data_seek_needed = 1
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000232
Guido van Rossume7b146f2000-02-04 15:28:42 +0000233 def readframes(self, nframes):
234 if self._data_seek_needed:
235 self._data_chunk.seek(0, 0)
236 pos = self._soundpos * self._framesize
237 if pos:
238 self._data_chunk.seek(pos, 0)
239 self._data_seek_needed = 0
240 if nframes == 0:
Guido van Rossum51a883b2007-07-23 21:28:30 +0000241 return b''
Serhiy Storchaka3062c9a2013-11-23 22:26:01 +0200242 data = self._data_chunk.read(nframes * self._framesize)
243 if self._sampwidth != 1 and sys.byteorder == 'big':
244 data = audioop.byteswap(data, self._sampwidth)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000245 if self._convert and data:
246 data = self._convert(data)
Guido van Rossum54e54c62001-09-04 19:14:14 +0000247 self._soundpos = self._soundpos + len(data) // (self._nchannels * self._sampwidth)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000248 return data
249
250 #
251 # Internal methods.
252 #
253
254 def _read_fmt_chunk(self, chunk):
Serhiy Storchaka134cb012018-03-18 09:55:53 +0200255 try:
256 wFormatTag, self._nchannels, self._framerate, dwAvgBytesPerSec, wBlockAlign = struct.unpack_from('<HHLLH', chunk.read(14))
257 except struct.error:
258 raise EOFError from None
Guido van Rossume7b146f2000-02-04 15:28:42 +0000259 if wFormatTag == WAVE_FORMAT_PCM:
Serhiy Storchaka134cb012018-03-18 09:55:53 +0200260 try:
261 sampwidth = struct.unpack_from('<H', chunk.read(2))[0]
262 except struct.error:
263 raise EOFError from None
Guido van Rossum54e54c62001-09-04 19:14:14 +0000264 self._sampwidth = (sampwidth + 7) // 8
Serhiy Storchaka134cb012018-03-18 09:55:53 +0200265 if not self._sampwidth:
266 raise Error('bad sample width')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000267 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000268 raise Error('unknown format: %r' % (wFormatTag,))
Serhiy Storchaka134cb012018-03-18 09:55:53 +0200269 if not self._nchannels:
270 raise Error('bad # of channels')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000271 self._framesize = self._nchannels * self._sampwidth
272 self._comptype = 'NONE'
273 self._compname = 'not compressed'
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000274
275class Wave_write:
Guido van Rossume7b146f2000-02-04 15:28:42 +0000276 """Variables used in this class:
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000277
Guido van Rossume7b146f2000-02-04 15:28:42 +0000278 These variables are user settable through appropriate methods
279 of this class:
280 _file -- the open file with methods write(), close(), tell(), seek()
281 set through the __init__() method
282 _comptype -- the AIFF-C compression type ('NONE' in AIFF)
283 set through the setcomptype() or setparams() method
284 _compname -- the human-readable AIFF-C compression type
285 set through the setcomptype() or setparams() method
286 _nchannels -- the number of audio channels
287 set through the setnchannels() or setparams() method
288 _sampwidth -- the number of bytes per audio sample
289 set through the setsampwidth() or setparams() method
290 _framerate -- the sampling frequency
291 set through the setframerate() or setparams() method
292 _nframes -- the number of audio frames written to the header
293 set through the setnframes() or setparams() method
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000294
Guido van Rossume7b146f2000-02-04 15:28:42 +0000295 These variables are used internally only:
296 _datalength -- the size of the audio samples written to the header
297 _nframeswritten -- the number of frames actually written
298 _datawritten -- the size of the audio samples actually written
299 """
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000300
Guido van Rossume7b146f2000-02-04 15:28:42 +0000301 def __init__(self, f):
Tim Peterscfc41782000-10-09 23:43:55 +0000302 self._i_opened_the_file = None
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000303 if isinstance(f, str):
Georg Brandl1a3284e2007-12-02 09:40:06 +0000304 f = builtins.open(f, 'wb')
Tim Peterscfc41782000-10-09 23:43:55 +0000305 self._i_opened_the_file = f
Guido van Rossumd8faa362007-04-27 19:54:29 +0000306 try:
307 self.initfp(f)
308 except:
309 if self._i_opened_the_file:
310 f.close()
311 raise
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000312
Guido van Rossume7b146f2000-02-04 15:28:42 +0000313 def initfp(self, file):
314 self._file = file
315 self._convert = None
316 self._nchannels = 0
317 self._sampwidth = 0
318 self._framerate = 0
319 self._nframes = 0
320 self._nframeswritten = 0
321 self._datawritten = 0
322 self._datalength = 0
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000323 self._headerwritten = False
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000324
Guido van Rossume7b146f2000-02-04 15:28:42 +0000325 def __del__(self):
Tim Peterscfc41782000-10-09 23:43:55 +0000326 self.close()
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000327
R David Murrayc91d5ee2013-07-31 13:46:08 -0400328 def __enter__(self):
329 return self
330
331 def __exit__(self, *args):
332 self.close()
333
Guido van Rossume7b146f2000-02-04 15:28:42 +0000334 #
335 # User visible methods.
336 #
337 def setnchannels(self, nchannels):
338 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000339 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000340 if nchannels < 1:
Collin Winterce36ad82007-08-30 01:19:48 +0000341 raise Error('bad # of channels')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000342 self._nchannels = nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000343
Guido van Rossume7b146f2000-02-04 15:28:42 +0000344 def getnchannels(self):
345 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000346 raise Error('number of channels not set')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000347 return self._nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000348
Guido van Rossume7b146f2000-02-04 15:28:42 +0000349 def setsampwidth(self, sampwidth):
350 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000351 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000352 if sampwidth < 1 or sampwidth > 4:
Collin Winterce36ad82007-08-30 01:19:48 +0000353 raise Error('bad sample width')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000354 self._sampwidth = sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000355
Guido van Rossume7b146f2000-02-04 15:28:42 +0000356 def getsampwidth(self):
357 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000358 raise Error('sample width not set')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000359 return self._sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000360
Guido van Rossume7b146f2000-02-04 15:28:42 +0000361 def setframerate(self, framerate):
362 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000363 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000364 if framerate <= 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000365 raise Error('bad frame rate')
Mark Dickinson64a38c02010-08-28 17:22:16 +0000366 self._framerate = int(round(framerate))
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000367
Guido van Rossume7b146f2000-02-04 15:28:42 +0000368 def getframerate(self):
369 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000370 raise Error('frame rate not set')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000371 return self._framerate
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000372
Guido van Rossume7b146f2000-02-04 15:28:42 +0000373 def setnframes(self, nframes):
374 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000375 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000376 self._nframes = nframes
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000377
Guido van Rossume7b146f2000-02-04 15:28:42 +0000378 def getnframes(self):
379 return self._nframeswritten
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000380
Guido van Rossume7b146f2000-02-04 15:28:42 +0000381 def setcomptype(self, comptype, compname):
382 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000383 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000384 if comptype not in ('NONE',):
Collin Winterce36ad82007-08-30 01:19:48 +0000385 raise Error('unsupported compression type')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000386 self._comptype = comptype
387 self._compname = compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000388
Guido van Rossume7b146f2000-02-04 15:28:42 +0000389 def getcomptype(self):
390 return self._comptype
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000391
Guido van Rossume7b146f2000-02-04 15:28:42 +0000392 def getcompname(self):
393 return self._compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000394
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000395 def setparams(self, params):
396 nchannels, sampwidth, framerate, nframes, comptype, compname = params
Guido van Rossume7b146f2000-02-04 15:28:42 +0000397 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000398 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000399 self.setnchannels(nchannels)
400 self.setsampwidth(sampwidth)
401 self.setframerate(framerate)
402 self.setnframes(nframes)
403 self.setcomptype(comptype, compname)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000404
Guido van Rossume7b146f2000-02-04 15:28:42 +0000405 def getparams(self):
406 if not self._nchannels or not self._sampwidth or not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000407 raise Error('not all parameters set')
Serhiy Storchaka4c6a0202013-09-04 00:28:43 +0300408 return _wave_params(self._nchannels, self._sampwidth, self._framerate,
R David Murray671cd322013-04-10 12:31:43 -0400409 self._nframes, self._comptype, self._compname)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000410
Guido van Rossume7b146f2000-02-04 15:28:42 +0000411 def setmark(self, id, pos, name):
Collin Winterce36ad82007-08-30 01:19:48 +0000412 raise Error('setmark() not supported')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000413
Guido van Rossume7b146f2000-02-04 15:28:42 +0000414 def getmark(self, id):
Collin Winterce36ad82007-08-30 01:19:48 +0000415 raise Error('no marks')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000416
Guido van Rossume7b146f2000-02-04 15:28:42 +0000417 def getmarkers(self):
418 return None
Tim Peterse1190062001-01-15 03:34:38 +0000419
Guido van Rossume7b146f2000-02-04 15:28:42 +0000420 def tell(self):
421 return self._nframeswritten
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000422
Guido van Rossume7b146f2000-02-04 15:28:42 +0000423 def writeframesraw(self, data):
Serhiy Storchaka452bab42013-11-16 14:01:31 +0200424 if not isinstance(data, (bytes, bytearray)):
425 data = memoryview(data).cast('B')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000426 self._ensure_header_written(len(data))
Guido van Rossum54e54c62001-09-04 19:14:14 +0000427 nframes = len(data) // (self._sampwidth * self._nchannels)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000428 if self._convert:
429 data = self._convert(data)
Serhiy Storchaka3062c9a2013-11-23 22:26:01 +0200430 if self._sampwidth != 1 and sys.byteorder == 'big':
431 data = audioop.byteswap(data, self._sampwidth)
432 self._file.write(data)
433 self._datawritten += len(data)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000434 self._nframeswritten = self._nframeswritten + nframes
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000435
Guido van Rossume7b146f2000-02-04 15:28:42 +0000436 def writeframes(self, data):
437 self.writeframesraw(data)
438 if self._datalength != self._datawritten:
439 self._patchheader()
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000440
Guido van Rossume7b146f2000-02-04 15:28:42 +0000441 def close(self):
Serhiy Storchaka7e7a3db2015-04-10 13:24:41 +0300442 try:
443 if self._file:
R David Murray536ffe12013-07-31 20:48:26 -0400444 self._ensure_header_written(0)
445 if self._datalength != self._datawritten:
446 self._patchheader()
447 self._file.flush()
Serhiy Storchaka7e7a3db2015-04-10 13:24:41 +0300448 finally:
449 self._file = None
450 file = self._i_opened_the_file
451 if file:
452 self._i_opened_the_file = None
453 file.close()
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000454
Guido van Rossume7b146f2000-02-04 15:28:42 +0000455 #
456 # Internal methods.
457 #
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000458
Guido van Rossume7b146f2000-02-04 15:28:42 +0000459 def _ensure_header_written(self, datasize):
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000460 if not self._headerwritten:
Guido van Rossume7b146f2000-02-04 15:28:42 +0000461 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000462 raise Error('# channels not specified')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000463 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000464 raise Error('sample width not specified')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000465 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000466 raise Error('sampling rate not specified')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000467 self._write_header(datasize)
468
469 def _write_header(self, initlength):
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000470 assert not self._headerwritten
Guido van Rossum09549f42007-08-27 20:40:10 +0000471 self._file.write(b'RIFF')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000472 if not self._nframes:
Benjamin Peterson5efea042010-01-13 03:49:50 +0000473 self._nframes = initlength // (self._nchannels * self._sampwidth)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000474 self._datalength = self._nframes * self._nchannels * self._sampwidth
Serhiy Storchaka7714ebb2013-11-16 13:04:00 +0200475 try:
476 self._form_length_pos = self._file.tell()
477 except (AttributeError, OSError):
478 self._form_length_pos = None
Jesus Ceae4b863982012-11-17 03:41:54 +0100479 self._file.write(struct.pack('<L4s4sLHHLLHH4s',
Victor Stinnerda9ec992010-12-28 13:26:42 +0000480 36 + self._datalength, b'WAVE', b'fmt ', 16,
Guido van Rossume7b146f2000-02-04 15:28:42 +0000481 WAVE_FORMAT_PCM, self._nchannels, self._framerate,
482 self._nchannels * self._framerate * self._sampwidth,
483 self._nchannels * self._sampwidth,
Victor Stinnerda9ec992010-12-28 13:26:42 +0000484 self._sampwidth * 8, b'data'))
Serhiy Storchaka7714ebb2013-11-16 13:04:00 +0200485 if self._form_length_pos is not None:
486 self._data_length_pos = self._file.tell()
Jesus Ceae4b863982012-11-17 03:41:54 +0100487 self._file.write(struct.pack('<L', self._datalength))
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000488 self._headerwritten = True
Guido van Rossume7b146f2000-02-04 15:28:42 +0000489
490 def _patchheader(self):
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000491 assert self._headerwritten
Guido van Rossume7b146f2000-02-04 15:28:42 +0000492 if self._datawritten == self._datalength:
493 return
494 curpos = self._file.tell()
495 self._file.seek(self._form_length_pos, 0)
Jesus Ceae4b863982012-11-17 03:41:54 +0100496 self._file.write(struct.pack('<L', 36 + self._datawritten))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000497 self._file.seek(self._data_length_pos, 0)
Jesus Ceae4b863982012-11-17 03:41:54 +0100498 self._file.write(struct.pack('<L', self._datawritten))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000499 self._file.seek(curpos, 0)
500 self._datalength = self._datawritten
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000501
Fred Drakef9607821999-06-17 15:18:47 +0000502def open(f, mode=None):
Guido van Rossume7b146f2000-02-04 15:28:42 +0000503 if mode is None:
504 if hasattr(f, 'mode'):
505 mode = f.mode
506 else:
507 mode = 'rb'
508 if mode in ('r', 'rb'):
509 return Wave_read(f)
510 elif mode in ('w', 'wb'):
511 return Wave_write(f)
512 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000513 raise Error("mode must be 'r', 'rb', 'w', or 'wb'")