blob: b56395ead700732925ea538a56352ad3d0b44694 [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
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
90
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):
Tim Peterscfc41782000-10-09 23:43:55 +0000189 if self._i_opened_the_file:
190 self._i_opened_the_file.close()
191 self._i_opened_the_file = None
Guido van Rossume7b146f2000-02-04 15:28:42 +0000192 self._file = None
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000193
Guido van Rossume7b146f2000-02-04 15:28:42 +0000194 def tell(self):
195 return self._soundpos
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000196
Guido van Rossume7b146f2000-02-04 15:28:42 +0000197 def getnchannels(self):
198 return self._nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000199
Guido van Rossume7b146f2000-02-04 15:28:42 +0000200 def getnframes(self):
201 return self._nframes
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000202
Guido van Rossume7b146f2000-02-04 15:28:42 +0000203 def getsampwidth(self):
204 return self._sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000205
Guido van Rossume7b146f2000-02-04 15:28:42 +0000206 def getframerate(self):
207 return self._framerate
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000208
Guido van Rossume7b146f2000-02-04 15:28:42 +0000209 def getcomptype(self):
210 return self._comptype
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000211
Guido van Rossume7b146f2000-02-04 15:28:42 +0000212 def getcompname(self):
213 return self._compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000214
Guido van Rossume7b146f2000-02-04 15:28:42 +0000215 def getparams(self):
Serhiy Storchaka4c6a0202013-09-04 00:28:43 +0300216 return _wave_params(self.getnchannels(), self.getsampwidth(),
R David Murray671cd322013-04-10 12:31:43 -0400217 self.getframerate(), self.getnframes(),
218 self.getcomptype(), self.getcompname())
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000219
Guido van Rossume7b146f2000-02-04 15:28:42 +0000220 def getmarkers(self):
221 return None
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000222
Guido van Rossume7b146f2000-02-04 15:28:42 +0000223 def getmark(self, id):
Collin Winterce36ad82007-08-30 01:19:48 +0000224 raise Error('no marks')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000225
Guido van Rossume7b146f2000-02-04 15:28:42 +0000226 def setpos(self, pos):
227 if pos < 0 or pos > self._nframes:
Collin Winterce36ad82007-08-30 01:19:48 +0000228 raise Error('position not in range')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000229 self._soundpos = pos
230 self._data_seek_needed = 1
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000231
Guido van Rossume7b146f2000-02-04 15:28:42 +0000232 def readframes(self, nframes):
233 if self._data_seek_needed:
234 self._data_chunk.seek(0, 0)
235 pos = self._soundpos * self._framesize
236 if pos:
237 self._data_chunk.seek(pos, 0)
238 self._data_seek_needed = 0
239 if nframes == 0:
Guido van Rossum51a883b2007-07-23 21:28:30 +0000240 return b''
Serhiy Storchaka3062c9a2013-11-23 22:26:01 +0200241 data = self._data_chunk.read(nframes * self._framesize)
242 if self._sampwidth != 1 and sys.byteorder == 'big':
243 data = audioop.byteswap(data, self._sampwidth)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000244 if self._convert and data:
245 data = self._convert(data)
Guido van Rossum54e54c62001-09-04 19:14:14 +0000246 self._soundpos = self._soundpos + len(data) // (self._nchannels * self._sampwidth)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000247 return data
248
249 #
250 # Internal methods.
251 #
252
253 def _read_fmt_chunk(self, chunk):
Jesus Ceae4b863982012-11-17 03:41:54 +0100254 wFormatTag, self._nchannels, self._framerate, dwAvgBytesPerSec, wBlockAlign = struct.unpack_from('<HHLLH', chunk.read(14))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000255 if wFormatTag == WAVE_FORMAT_PCM:
Jesus Ceae4b863982012-11-17 03:41:54 +0100256 sampwidth = struct.unpack_from('<H', chunk.read(2))[0]
Guido van Rossum54e54c62001-09-04 19:14:14 +0000257 self._sampwidth = (sampwidth + 7) // 8
Guido van Rossume7b146f2000-02-04 15:28:42 +0000258 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000259 raise Error('unknown format: %r' % (wFormatTag,))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000260 self._framesize = self._nchannels * self._sampwidth
261 self._comptype = 'NONE'
262 self._compname = 'not compressed'
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000263
264class Wave_write:
Guido van Rossume7b146f2000-02-04 15:28:42 +0000265 """Variables used in this class:
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000266
Guido van Rossume7b146f2000-02-04 15:28:42 +0000267 These variables are user settable through appropriate methods
268 of this class:
269 _file -- the open file with methods write(), close(), tell(), seek()
270 set through the __init__() method
271 _comptype -- the AIFF-C compression type ('NONE' in AIFF)
272 set through the setcomptype() or setparams() method
273 _compname -- the human-readable AIFF-C compression type
274 set through the setcomptype() or setparams() method
275 _nchannels -- the number of audio channels
276 set through the setnchannels() or setparams() method
277 _sampwidth -- the number of bytes per audio sample
278 set through the setsampwidth() or setparams() method
279 _framerate -- the sampling frequency
280 set through the setframerate() or setparams() method
281 _nframes -- the number of audio frames written to the header
282 set through the setnframes() or setparams() method
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000283
Guido van Rossume7b146f2000-02-04 15:28:42 +0000284 These variables are used internally only:
285 _datalength -- the size of the audio samples written to the header
286 _nframeswritten -- the number of frames actually written
287 _datawritten -- the size of the audio samples actually written
288 """
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000289
Guido van Rossume7b146f2000-02-04 15:28:42 +0000290 def __init__(self, f):
Tim Peterscfc41782000-10-09 23:43:55 +0000291 self._i_opened_the_file = None
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000292 if isinstance(f, str):
Georg Brandl1a3284e2007-12-02 09:40:06 +0000293 f = builtins.open(f, 'wb')
Tim Peterscfc41782000-10-09 23:43:55 +0000294 self._i_opened_the_file = f
Guido van Rossumd8faa362007-04-27 19:54:29 +0000295 try:
296 self.initfp(f)
297 except:
298 if self._i_opened_the_file:
299 f.close()
300 raise
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000301
Guido van Rossume7b146f2000-02-04 15:28:42 +0000302 def initfp(self, file):
303 self._file = file
304 self._convert = None
305 self._nchannels = 0
306 self._sampwidth = 0
307 self._framerate = 0
308 self._nframes = 0
309 self._nframeswritten = 0
310 self._datawritten = 0
311 self._datalength = 0
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000312 self._headerwritten = False
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000313
Guido van Rossume7b146f2000-02-04 15:28:42 +0000314 def __del__(self):
Tim Peterscfc41782000-10-09 23:43:55 +0000315 self.close()
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000316
R David Murrayc91d5ee2013-07-31 13:46:08 -0400317 def __enter__(self):
318 return self
319
320 def __exit__(self, *args):
321 self.close()
322
Guido van Rossume7b146f2000-02-04 15:28:42 +0000323 #
324 # User visible methods.
325 #
326 def setnchannels(self, nchannels):
327 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000328 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000329 if nchannels < 1:
Collin Winterce36ad82007-08-30 01:19:48 +0000330 raise Error('bad # of channels')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000331 self._nchannels = nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000332
Guido van Rossume7b146f2000-02-04 15:28:42 +0000333 def getnchannels(self):
334 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000335 raise Error('number of channels not set')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000336 return self._nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000337
Guido van Rossume7b146f2000-02-04 15:28:42 +0000338 def setsampwidth(self, sampwidth):
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 sampwidth < 1 or sampwidth > 4:
Collin Winterce36ad82007-08-30 01:19:48 +0000342 raise Error('bad sample width')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000343 self._sampwidth = sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000344
Guido van Rossume7b146f2000-02-04 15:28:42 +0000345 def getsampwidth(self):
346 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000347 raise Error('sample width not set')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000348 return self._sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000349
Guido van Rossume7b146f2000-02-04 15:28:42 +0000350 def setframerate(self, framerate):
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 framerate <= 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000354 raise Error('bad frame rate')
Mark Dickinson64a38c02010-08-28 17:22:16 +0000355 self._framerate = int(round(framerate))
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000356
Guido van Rossume7b146f2000-02-04 15:28:42 +0000357 def getframerate(self):
358 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000359 raise Error('frame rate not set')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000360 return self._framerate
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000361
Guido van Rossume7b146f2000-02-04 15:28:42 +0000362 def setnframes(self, nframes):
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 self._nframes = nframes
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000366
Guido van Rossume7b146f2000-02-04 15:28:42 +0000367 def getnframes(self):
368 return self._nframeswritten
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000369
Guido van Rossume7b146f2000-02-04 15:28:42 +0000370 def setcomptype(self, comptype, compname):
371 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000372 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000373 if comptype not in ('NONE',):
Collin Winterce36ad82007-08-30 01:19:48 +0000374 raise Error('unsupported compression type')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000375 self._comptype = comptype
376 self._compname = compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000377
Guido van Rossume7b146f2000-02-04 15:28:42 +0000378 def getcomptype(self):
379 return self._comptype
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000380
Guido van Rossume7b146f2000-02-04 15:28:42 +0000381 def getcompname(self):
382 return self._compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000383
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000384 def setparams(self, params):
385 nchannels, sampwidth, framerate, nframes, comptype, compname = params
Guido van Rossume7b146f2000-02-04 15:28:42 +0000386 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000387 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000388 self.setnchannels(nchannels)
389 self.setsampwidth(sampwidth)
390 self.setframerate(framerate)
391 self.setnframes(nframes)
392 self.setcomptype(comptype, compname)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000393
Guido van Rossume7b146f2000-02-04 15:28:42 +0000394 def getparams(self):
395 if not self._nchannels or not self._sampwidth or not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000396 raise Error('not all parameters set')
Serhiy Storchaka4c6a0202013-09-04 00:28:43 +0300397 return _wave_params(self._nchannels, self._sampwidth, self._framerate,
R David Murray671cd322013-04-10 12:31:43 -0400398 self._nframes, self._comptype, self._compname)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000399
Guido van Rossume7b146f2000-02-04 15:28:42 +0000400 def setmark(self, id, pos, name):
Collin Winterce36ad82007-08-30 01:19:48 +0000401 raise Error('setmark() not supported')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000402
Guido van Rossume7b146f2000-02-04 15:28:42 +0000403 def getmark(self, id):
Collin Winterce36ad82007-08-30 01:19:48 +0000404 raise Error('no marks')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000405
Guido van Rossume7b146f2000-02-04 15:28:42 +0000406 def getmarkers(self):
407 return None
Tim Peterse1190062001-01-15 03:34:38 +0000408
Guido van Rossume7b146f2000-02-04 15:28:42 +0000409 def tell(self):
410 return self._nframeswritten
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000411
Guido van Rossume7b146f2000-02-04 15:28:42 +0000412 def writeframesraw(self, data):
Serhiy Storchaka452bab42013-11-16 14:01:31 +0200413 if not isinstance(data, (bytes, bytearray)):
414 data = memoryview(data).cast('B')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000415 self._ensure_header_written(len(data))
Guido van Rossum54e54c62001-09-04 19:14:14 +0000416 nframes = len(data) // (self._sampwidth * self._nchannels)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000417 if self._convert:
418 data = self._convert(data)
Serhiy Storchaka3062c9a2013-11-23 22:26:01 +0200419 if self._sampwidth != 1 and sys.byteorder == 'big':
420 data = audioop.byteswap(data, self._sampwidth)
421 self._file.write(data)
422 self._datawritten += len(data)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000423 self._nframeswritten = self._nframeswritten + nframes
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000424
Guido van Rossume7b146f2000-02-04 15:28:42 +0000425 def writeframes(self, data):
426 self.writeframesraw(data)
427 if self._datalength != self._datawritten:
428 self._patchheader()
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000429
Guido van Rossume7b146f2000-02-04 15:28:42 +0000430 def close(self):
Tim Peterscfc41782000-10-09 23:43:55 +0000431 if self._file:
R David Murray536ffe12013-07-31 20:48:26 -0400432 try:
433 self._ensure_header_written(0)
434 if self._datalength != self._datawritten:
435 self._patchheader()
436 self._file.flush()
437 finally:
438 self._file = None
Tim Peterscfc41782000-10-09 23:43:55 +0000439 if self._i_opened_the_file:
440 self._i_opened_the_file.close()
441 self._i_opened_the_file = None
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000442
Guido van Rossume7b146f2000-02-04 15:28:42 +0000443 #
444 # Internal methods.
445 #
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000446
Guido van Rossume7b146f2000-02-04 15:28:42 +0000447 def _ensure_header_written(self, datasize):
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000448 if not self._headerwritten:
Guido van Rossume7b146f2000-02-04 15:28:42 +0000449 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000450 raise Error('# channels not specified')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000451 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000452 raise Error('sample width not specified')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000453 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000454 raise Error('sampling rate not specified')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000455 self._write_header(datasize)
456
457 def _write_header(self, initlength):
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000458 assert not self._headerwritten
Guido van Rossum09549f42007-08-27 20:40:10 +0000459 self._file.write(b'RIFF')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000460 if not self._nframes:
Benjamin Peterson5efea042010-01-13 03:49:50 +0000461 self._nframes = initlength // (self._nchannels * self._sampwidth)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000462 self._datalength = self._nframes * self._nchannels * self._sampwidth
Serhiy Storchaka7714ebb2013-11-16 13:04:00 +0200463 try:
464 self._form_length_pos = self._file.tell()
465 except (AttributeError, OSError):
466 self._form_length_pos = None
Jesus Ceae4b863982012-11-17 03:41:54 +0100467 self._file.write(struct.pack('<L4s4sLHHLLHH4s',
Victor Stinnerda9ec992010-12-28 13:26:42 +0000468 36 + self._datalength, b'WAVE', b'fmt ', 16,
Guido van Rossume7b146f2000-02-04 15:28:42 +0000469 WAVE_FORMAT_PCM, self._nchannels, self._framerate,
470 self._nchannels * self._framerate * self._sampwidth,
471 self._nchannels * self._sampwidth,
Victor Stinnerda9ec992010-12-28 13:26:42 +0000472 self._sampwidth * 8, b'data'))
Serhiy Storchaka7714ebb2013-11-16 13:04:00 +0200473 if self._form_length_pos is not None:
474 self._data_length_pos = self._file.tell()
Jesus Ceae4b863982012-11-17 03:41:54 +0100475 self._file.write(struct.pack('<L', self._datalength))
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000476 self._headerwritten = True
Guido van Rossume7b146f2000-02-04 15:28:42 +0000477
478 def _patchheader(self):
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000479 assert self._headerwritten
Guido van Rossume7b146f2000-02-04 15:28:42 +0000480 if self._datawritten == self._datalength:
481 return
482 curpos = self._file.tell()
483 self._file.seek(self._form_length_pos, 0)
Jesus Ceae4b863982012-11-17 03:41:54 +0100484 self._file.write(struct.pack('<L', 36 + self._datawritten))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000485 self._file.seek(self._data_length_pos, 0)
Jesus Ceae4b863982012-11-17 03:41:54 +0100486 self._file.write(struct.pack('<L', self._datawritten))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000487 self._file.seek(curpos, 0)
488 self._datalength = self._datawritten
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000489
Fred Drakef9607821999-06-17 15:18:47 +0000490def open(f, mode=None):
Guido van Rossume7b146f2000-02-04 15:28:42 +0000491 if mode is None:
492 if hasattr(f, 'mode'):
493 mode = f.mode
494 else:
495 mode = 'rb'
496 if mode in ('r', 'rb'):
497 return Wave_read(f)
498 elif mode in ('w', 'wb'):
499 return Wave_write(f)
500 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000501 raise Error("mode must be 'r', 'rb', 'w', or 'wb'")
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000502
503openfp = open # B/W compatibility