blob: 2877137add96c181e5d7d52db40b0fb7822e46f6 [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)
21 getparams() -- returns a tuple consisting of all of the
22 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
83_array_fmts = None, 'b', 'h', None, 'l'
84
Guido van Rossumebb9c921999-02-05 22:28:17 +000085# Determine endian-ness
86import struct
Jeremy Hyltonda3f2282007-08-29 17:26:34 +000087if struct.pack("h", 1) == b"\000\001":
Guido van Rossume7b146f2000-02-04 15:28:42 +000088 big_endian = 1
Guido van Rossumebb9c921999-02-05 22:28:17 +000089else:
Guido van Rossume7b146f2000-02-04 15:28:42 +000090 big_endian = 0
Guido van Rossumebb9c921999-02-05 22:28:17 +000091
Guido van Rossum3601e881999-08-26 15:50:43 +000092from chunk import Chunk
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()
Guido van Rossume7b146f2000-02-04 15:28:42 +0000171 #
172 # User visible methods.
173 #
174 def getfp(self):
175 return self._file
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000176
Guido van Rossume7b146f2000-02-04 15:28:42 +0000177 def rewind(self):
178 self._data_seek_needed = 1
179 self._soundpos = 0
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000180
Guido van Rossume7b146f2000-02-04 15:28:42 +0000181 def close(self):
Tim Peterscfc41782000-10-09 23:43:55 +0000182 if self._i_opened_the_file:
183 self._i_opened_the_file.close()
184 self._i_opened_the_file = None
Guido van Rossume7b146f2000-02-04 15:28:42 +0000185 self._file = None
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000186
Guido van Rossume7b146f2000-02-04 15:28:42 +0000187 def tell(self):
188 return self._soundpos
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000189
Guido van Rossume7b146f2000-02-04 15:28:42 +0000190 def getnchannels(self):
191 return self._nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000192
Guido van Rossume7b146f2000-02-04 15:28:42 +0000193 def getnframes(self):
194 return self._nframes
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000195
Guido van Rossume7b146f2000-02-04 15:28:42 +0000196 def getsampwidth(self):
197 return self._sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000198
Guido van Rossume7b146f2000-02-04 15:28:42 +0000199 def getframerate(self):
200 return self._framerate
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000201
Guido van Rossume7b146f2000-02-04 15:28:42 +0000202 def getcomptype(self):
203 return self._comptype
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000204
Guido van Rossume7b146f2000-02-04 15:28:42 +0000205 def getcompname(self):
206 return self._compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000207
Guido van Rossume7b146f2000-02-04 15:28:42 +0000208 def getparams(self):
209 return self.getnchannels(), self.getsampwidth(), \
210 self.getframerate(), self.getnframes(), \
211 self.getcomptype(), self.getcompname()
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000212
Guido van Rossume7b146f2000-02-04 15:28:42 +0000213 def getmarkers(self):
214 return None
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000215
Guido van Rossume7b146f2000-02-04 15:28:42 +0000216 def getmark(self, id):
Collin Winterce36ad82007-08-30 01:19:48 +0000217 raise Error('no marks')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000218
Guido van Rossume7b146f2000-02-04 15:28:42 +0000219 def setpos(self, pos):
220 if pos < 0 or pos > self._nframes:
Collin Winterce36ad82007-08-30 01:19:48 +0000221 raise Error('position not in range')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000222 self._soundpos = pos
223 self._data_seek_needed = 1
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000224
Guido van Rossume7b146f2000-02-04 15:28:42 +0000225 def readframes(self, nframes):
226 if self._data_seek_needed:
227 self._data_chunk.seek(0, 0)
228 pos = self._soundpos * self._framesize
229 if pos:
230 self._data_chunk.seek(pos, 0)
231 self._data_seek_needed = 0
232 if nframes == 0:
Guido van Rossum51a883b2007-07-23 21:28:30 +0000233 return b''
Guido van Rossume7b146f2000-02-04 15:28:42 +0000234 if self._sampwidth > 1 and big_endian:
235 # unfortunately the fromfile() method does not take
236 # something that only looks like a file object, so
237 # we have to reach into the innards of the chunk object
238 import array
239 chunk = self._data_chunk
240 data = array.array(_array_fmts[self._sampwidth])
241 nitems = nframes * self._nchannels
242 if nitems * self._sampwidth > chunk.chunksize - chunk.size_read:
Benjamin Peterson5efea042010-01-13 03:49:50 +0000243 nitems = (chunk.chunksize - chunk.size_read) // self._sampwidth
Guido van Rossume7b146f2000-02-04 15:28:42 +0000244 data.fromfile(chunk.file.file, nitems)
245 # "tell" data chunk how much was read
246 chunk.size_read = chunk.size_read + nitems * self._sampwidth
247 # do the same for the outermost chunk
248 chunk = chunk.file
249 chunk.size_read = chunk.size_read + nitems * self._sampwidth
250 data.byteswap()
Antoine Pitrou1ce3eb52010-09-01 20:29:34 +0000251 data = data.tobytes()
Guido van Rossume7b146f2000-02-04 15:28:42 +0000252 else:
253 data = self._data_chunk.read(nframes * self._framesize)
254 if self._convert and data:
255 data = self._convert(data)
Guido van Rossum54e54c62001-09-04 19:14:14 +0000256 self._soundpos = self._soundpos + len(data) // (self._nchannels * self._sampwidth)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000257 return data
258
259 #
260 # Internal methods.
261 #
262
263 def _read_fmt_chunk(self, chunk):
Guido van Rossumdc0b1a12007-04-12 22:55:07 +0000264 wFormatTag, self._nchannels, self._framerate, dwAvgBytesPerSec, wBlockAlign = struct.unpack_from('<hhllh', chunk.read(14))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000265 if wFormatTag == WAVE_FORMAT_PCM:
Guido van Rossumdc0b1a12007-04-12 22:55:07 +0000266 sampwidth = struct.unpack_from('<h', chunk.read(2))[0]
Guido van Rossum54e54c62001-09-04 19:14:14 +0000267 self._sampwidth = (sampwidth + 7) // 8
Guido van Rossume7b146f2000-02-04 15:28:42 +0000268 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000269 raise Error('unknown format: %r' % (wFormatTag,))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000270 self._framesize = self._nchannels * self._sampwidth
271 self._comptype = 'NONE'
272 self._compname = 'not compressed'
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000273
274class Wave_write:
Guido van Rossume7b146f2000-02-04 15:28:42 +0000275 """Variables used in this class:
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000276
Guido van Rossume7b146f2000-02-04 15:28:42 +0000277 These variables are user settable through appropriate methods
278 of this class:
279 _file -- the open file with methods write(), close(), tell(), seek()
280 set through the __init__() method
281 _comptype -- the AIFF-C compression type ('NONE' in AIFF)
282 set through the setcomptype() or setparams() method
283 _compname -- the human-readable AIFF-C compression type
284 set through the setcomptype() or setparams() method
285 _nchannels -- the number of audio channels
286 set through the setnchannels() or setparams() method
287 _sampwidth -- the number of bytes per audio sample
288 set through the setsampwidth() or setparams() method
289 _framerate -- the sampling frequency
290 set through the setframerate() or setparams() method
291 _nframes -- the number of audio frames written to the header
292 set through the setnframes() or setparams() method
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000293
Guido van Rossume7b146f2000-02-04 15:28:42 +0000294 These variables are used internally only:
295 _datalength -- the size of the audio samples written to the header
296 _nframeswritten -- the number of frames actually written
297 _datawritten -- the size of the audio samples actually written
298 """
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000299
Guido van Rossume7b146f2000-02-04 15:28:42 +0000300 def __init__(self, f):
Tim Peterscfc41782000-10-09 23:43:55 +0000301 self._i_opened_the_file = None
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000302 if isinstance(f, str):
Georg Brandl1a3284e2007-12-02 09:40:06 +0000303 f = builtins.open(f, 'wb')
Tim Peterscfc41782000-10-09 23:43:55 +0000304 self._i_opened_the_file = f
Guido van Rossumd8faa362007-04-27 19:54:29 +0000305 try:
306 self.initfp(f)
307 except:
308 if self._i_opened_the_file:
309 f.close()
310 raise
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000311
Guido van Rossume7b146f2000-02-04 15:28:42 +0000312 def initfp(self, file):
313 self._file = file
314 self._convert = None
315 self._nchannels = 0
316 self._sampwidth = 0
317 self._framerate = 0
318 self._nframes = 0
319 self._nframeswritten = 0
320 self._datawritten = 0
321 self._datalength = 0
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000322 self._headerwritten = False
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000323
Guido van Rossume7b146f2000-02-04 15:28:42 +0000324 def __del__(self):
Tim Peterscfc41782000-10-09 23:43:55 +0000325 self.close()
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000326
Guido van Rossume7b146f2000-02-04 15:28:42 +0000327 #
328 # User visible methods.
329 #
330 def setnchannels(self, nchannels):
331 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000332 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000333 if nchannels < 1:
Collin Winterce36ad82007-08-30 01:19:48 +0000334 raise Error('bad # of channels')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000335 self._nchannels = nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000336
Guido van Rossume7b146f2000-02-04 15:28:42 +0000337 def getnchannels(self):
338 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000339 raise Error('number of channels not set')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000340 return self._nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000341
Guido van Rossume7b146f2000-02-04 15:28:42 +0000342 def setsampwidth(self, sampwidth):
343 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000344 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000345 if sampwidth < 1 or sampwidth > 4:
Collin Winterce36ad82007-08-30 01:19:48 +0000346 raise Error('bad sample width')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000347 self._sampwidth = sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000348
Guido van Rossume7b146f2000-02-04 15:28:42 +0000349 def getsampwidth(self):
350 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000351 raise Error('sample width not set')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000352 return self._sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000353
Guido van Rossume7b146f2000-02-04 15:28:42 +0000354 def setframerate(self, framerate):
355 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000356 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000357 if framerate <= 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000358 raise Error('bad frame rate')
Mark Dickinson64a38c02010-08-28 17:22:16 +0000359 self._framerate = int(round(framerate))
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000360
Guido van Rossume7b146f2000-02-04 15:28:42 +0000361 def getframerate(self):
362 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000363 raise Error('frame rate not set')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000364 return self._framerate
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000365
Guido van Rossume7b146f2000-02-04 15:28:42 +0000366 def setnframes(self, nframes):
367 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000368 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000369 self._nframes = nframes
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000370
Guido van Rossume7b146f2000-02-04 15:28:42 +0000371 def getnframes(self):
372 return self._nframeswritten
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000373
Guido van Rossume7b146f2000-02-04 15:28:42 +0000374 def setcomptype(self, comptype, compname):
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 if comptype not in ('NONE',):
Collin Winterce36ad82007-08-30 01:19:48 +0000378 raise Error('unsupported compression type')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000379 self._comptype = comptype
380 self._compname = compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000381
Guido van Rossume7b146f2000-02-04 15:28:42 +0000382 def getcomptype(self):
383 return self._comptype
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000384
Guido van Rossume7b146f2000-02-04 15:28:42 +0000385 def getcompname(self):
386 return self._compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000387
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000388 def setparams(self, params):
389 nchannels, sampwidth, framerate, nframes, comptype, compname = params
Guido van Rossume7b146f2000-02-04 15:28:42 +0000390 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000391 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000392 self.setnchannels(nchannels)
393 self.setsampwidth(sampwidth)
394 self.setframerate(framerate)
395 self.setnframes(nframes)
396 self.setcomptype(comptype, compname)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000397
Guido van Rossume7b146f2000-02-04 15:28:42 +0000398 def getparams(self):
399 if not self._nchannels or not self._sampwidth or not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000400 raise Error('not all parameters set')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000401 return self._nchannels, self._sampwidth, self._framerate, \
402 self._nframes, self._comptype, self._compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000403
Guido van Rossume7b146f2000-02-04 15:28:42 +0000404 def setmark(self, id, pos, name):
Collin Winterce36ad82007-08-30 01:19:48 +0000405 raise Error('setmark() not supported')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000406
Guido van Rossume7b146f2000-02-04 15:28:42 +0000407 def getmark(self, id):
Collin Winterce36ad82007-08-30 01:19:48 +0000408 raise Error('no marks')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000409
Guido van Rossume7b146f2000-02-04 15:28:42 +0000410 def getmarkers(self):
411 return None
Tim Peterse1190062001-01-15 03:34:38 +0000412
Guido van Rossume7b146f2000-02-04 15:28:42 +0000413 def tell(self):
414 return self._nframeswritten
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000415
Guido van Rossume7b146f2000-02-04 15:28:42 +0000416 def writeframesraw(self, data):
417 self._ensure_header_written(len(data))
Guido van Rossum54e54c62001-09-04 19:14:14 +0000418 nframes = len(data) // (self._sampwidth * self._nchannels)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000419 if self._convert:
420 data = self._convert(data)
421 if self._sampwidth > 1 and big_endian:
422 import array
423 data = array.array(_array_fmts[self._sampwidth], data)
424 data.byteswap()
425 data.tofile(self._file)
426 self._datawritten = self._datawritten + len(data) * self._sampwidth
427 else:
428 self._file.write(data)
429 self._datawritten = self._datawritten + len(data)
430 self._nframeswritten = self._nframeswritten + nframes
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000431
Guido van Rossume7b146f2000-02-04 15:28:42 +0000432 def writeframes(self, data):
433 self.writeframesraw(data)
434 if self._datalength != self._datawritten:
435 self._patchheader()
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000436
Guido van Rossume7b146f2000-02-04 15:28:42 +0000437 def close(self):
Tim Peterscfc41782000-10-09 23:43:55 +0000438 if self._file:
439 self._ensure_header_written(0)
440 if self._datalength != self._datawritten:
441 self._patchheader()
442 self._file.flush()
443 self._file = None
444 if self._i_opened_the_file:
445 self._i_opened_the_file.close()
446 self._i_opened_the_file = None
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000447
Guido van Rossume7b146f2000-02-04 15:28:42 +0000448 #
449 # Internal methods.
450 #
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000451
Guido van Rossume7b146f2000-02-04 15:28:42 +0000452 def _ensure_header_written(self, datasize):
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000453 if not self._headerwritten:
Guido van Rossume7b146f2000-02-04 15:28:42 +0000454 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000455 raise Error('# channels not specified')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000456 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000457 raise Error('sample width not specified')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000458 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000459 raise Error('sampling rate not specified')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000460 self._write_header(datasize)
461
462 def _write_header(self, initlength):
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000463 assert not self._headerwritten
Guido van Rossum09549f42007-08-27 20:40:10 +0000464 self._file.write(b'RIFF')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000465 if not self._nframes:
Benjamin Peterson5efea042010-01-13 03:49:50 +0000466 self._nframes = initlength // (self._nchannels * self._sampwidth)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000467 self._datalength = self._nframes * self._nchannels * self._sampwidth
468 self._form_length_pos = self._file.tell()
Guido van Rossumeca576c2000-10-09 20:01:53 +0000469 self._file.write(struct.pack('<l4s4slhhllhh4s',
Victor Stinnerda9ec992010-12-28 13:26:42 +0000470 36 + self._datalength, b'WAVE', b'fmt ', 16,
Guido van Rossume7b146f2000-02-04 15:28:42 +0000471 WAVE_FORMAT_PCM, self._nchannels, self._framerate,
472 self._nchannels * self._framerate * self._sampwidth,
473 self._nchannels * self._sampwidth,
Victor Stinnerda9ec992010-12-28 13:26:42 +0000474 self._sampwidth * 8, b'data'))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000475 self._data_length_pos = self._file.tell()
476 self._file.write(struct.pack('<l', self._datalength))
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000477 self._headerwritten = True
Guido van Rossume7b146f2000-02-04 15:28:42 +0000478
479 def _patchheader(self):
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000480 assert self._headerwritten
Guido van Rossume7b146f2000-02-04 15:28:42 +0000481 if self._datawritten == self._datalength:
482 return
483 curpos = self._file.tell()
484 self._file.seek(self._form_length_pos, 0)
485 self._file.write(struct.pack('<l', 36 + self._datawritten))
486 self._file.seek(self._data_length_pos, 0)
487 self._file.write(struct.pack('<l', self._datawritten))
488 self._file.seek(curpos, 0)
489 self._datalength = self._datawritten
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000490
Fred Drakef9607821999-06-17 15:18:47 +0000491def open(f, mode=None):
Guido van Rossume7b146f2000-02-04 15:28:42 +0000492 if mode is None:
493 if hasattr(f, 'mode'):
494 mode = f.mode
495 else:
496 mode = 'rb'
497 if mode in ('r', 'rb'):
498 return Wave_read(f)
499 elif mode in ('w', 'wb'):
500 return Wave_write(f)
501 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000502 raise Error("mode must be 'r', 'rb', 'w', or 'wb'")
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000503
504openfp = open # B/W compatibility