blob: e3bf2af53c322e382539a90b1b18c231ea0108ea [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
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
R David Murray671cd322013-04-10 12:31:43 -040093from collections import namedtuple
94
95_result = namedtuple('params',
96 'nchannels sampwidth framerate nframes comptype compname')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000097
98class Wave_read:
Guido van Rossume7b146f2000-02-04 15:28:42 +000099 """Variables used in this class:
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000100
Guido van Rossume7b146f2000-02-04 15:28:42 +0000101 These variables are available to the user though appropriate
102 methods of this class:
103 _file -- the open file with methods read(), close(), and seek()
104 set through the __init__() method
105 _nchannels -- the number of audio channels
106 available through the getnchannels() method
107 _nframes -- the number of audio frames
108 available through the getnframes() method
109 _sampwidth -- the number of bytes per audio sample
110 available through the getsampwidth() method
111 _framerate -- the sampling frequency
112 available through the getframerate() method
113 _comptype -- the AIFF-C compression type ('NONE' if AIFF)
114 available through the getcomptype() method
115 _compname -- the human-readable AIFF-C compression type
116 available through the getcomptype() method
117 _soundpos -- the position in the audio stream
118 available through the tell() method, set through the
119 setpos() method
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000120
Guido van Rossume7b146f2000-02-04 15:28:42 +0000121 These variables are used internally only:
122 _fmt_chunk_read -- 1 iff the FMT chunk has been read
123 _data_seek_needed -- 1 iff positioned correctly in audio
124 file for readframes()
125 _data_chunk -- instantiation of a chunk class for the DATA chunk
126 _framesize -- size of one frame in the file
127 """
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000128
Guido van Rossume7b146f2000-02-04 15:28:42 +0000129 def initfp(self, file):
130 self._convert = None
131 self._soundpos = 0
132 self._file = Chunk(file, bigendian = 0)
Guido van Rossum51a883b2007-07-23 21:28:30 +0000133 if self._file.getname() != b'RIFF':
Collin Winterce36ad82007-08-30 01:19:48 +0000134 raise Error('file does not start with RIFF id')
Guido van Rossum51a883b2007-07-23 21:28:30 +0000135 if self._file.read(4) != b'WAVE':
Collin Winterce36ad82007-08-30 01:19:48 +0000136 raise Error('not a WAVE file')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000137 self._fmt_chunk_read = 0
138 self._data_chunk = None
139 while 1:
140 self._data_seek_needed = 1
141 try:
142 chunk = Chunk(self._file, bigendian = 0)
143 except EOFError:
144 break
145 chunkname = chunk.getname()
Guido van Rossum51a883b2007-07-23 21:28:30 +0000146 if chunkname == b'fmt ':
Guido van Rossume7b146f2000-02-04 15:28:42 +0000147 self._read_fmt_chunk(chunk)
148 self._fmt_chunk_read = 1
Guido van Rossum51a883b2007-07-23 21:28:30 +0000149 elif chunkname == b'data':
Guido van Rossume7b146f2000-02-04 15:28:42 +0000150 if not self._fmt_chunk_read:
Collin Winterce36ad82007-08-30 01:19:48 +0000151 raise Error('data chunk before fmt chunk')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000152 self._data_chunk = chunk
Guido van Rossum54e54c62001-09-04 19:14:14 +0000153 self._nframes = chunk.chunksize // self._framesize
Guido van Rossume7b146f2000-02-04 15:28:42 +0000154 self._data_seek_needed = 0
155 break
156 chunk.skip()
157 if not self._fmt_chunk_read or not self._data_chunk:
Collin Winterce36ad82007-08-30 01:19:48 +0000158 raise Error('fmt chunk and/or data chunk missing')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000159
Guido van Rossume7b146f2000-02-04 15:28:42 +0000160 def __init__(self, f):
Tim Peterscfc41782000-10-09 23:43:55 +0000161 self._i_opened_the_file = None
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000162 if isinstance(f, str):
Georg Brandl1a3284e2007-12-02 09:40:06 +0000163 f = builtins.open(f, 'rb')
Tim Peterscfc41782000-10-09 23:43:55 +0000164 self._i_opened_the_file = f
Guido van Rossume7b146f2000-02-04 15:28:42 +0000165 # else, assume it is an open file object already
Guido van Rossumd8faa362007-04-27 19:54:29 +0000166 try:
167 self.initfp(f)
168 except:
169 if self._i_opened_the_file:
170 f.close()
171 raise
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000172
Tim Peterscfc41782000-10-09 23:43:55 +0000173 def __del__(self):
174 self.close()
Guido van Rossume7b146f2000-02-04 15:28:42 +0000175 #
176 # User visible methods.
177 #
178 def getfp(self):
179 return self._file
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000180
Guido van Rossume7b146f2000-02-04 15:28:42 +0000181 def rewind(self):
182 self._data_seek_needed = 1
183 self._soundpos = 0
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000184
Guido van Rossume7b146f2000-02-04 15:28:42 +0000185 def close(self):
Tim Peterscfc41782000-10-09 23:43:55 +0000186 if self._i_opened_the_file:
187 self._i_opened_the_file.close()
188 self._i_opened_the_file = None
Guido van Rossume7b146f2000-02-04 15:28:42 +0000189 self._file = None
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000190
Guido van Rossume7b146f2000-02-04 15:28:42 +0000191 def tell(self):
192 return self._soundpos
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000193
Guido van Rossume7b146f2000-02-04 15:28:42 +0000194 def getnchannels(self):
195 return self._nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000196
Guido van Rossume7b146f2000-02-04 15:28:42 +0000197 def getnframes(self):
198 return self._nframes
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000199
Guido van Rossume7b146f2000-02-04 15:28:42 +0000200 def getsampwidth(self):
201 return self._sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000202
Guido van Rossume7b146f2000-02-04 15:28:42 +0000203 def getframerate(self):
204 return self._framerate
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000205
Guido van Rossume7b146f2000-02-04 15:28:42 +0000206 def getcomptype(self):
207 return self._comptype
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000208
Guido van Rossume7b146f2000-02-04 15:28:42 +0000209 def getcompname(self):
210 return self._compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000211
Guido van Rossume7b146f2000-02-04 15:28:42 +0000212 def getparams(self):
R David Murray671cd322013-04-10 12:31:43 -0400213 return _result(self.getnchannels(), self.getsampwidth(),
214 self.getframerate(), self.getnframes(),
215 self.getcomptype(), self.getcompname())
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000216
Guido van Rossume7b146f2000-02-04 15:28:42 +0000217 def getmarkers(self):
218 return None
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000219
Guido van Rossume7b146f2000-02-04 15:28:42 +0000220 def getmark(self, id):
Collin Winterce36ad82007-08-30 01:19:48 +0000221 raise Error('no marks')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000222
Guido van Rossume7b146f2000-02-04 15:28:42 +0000223 def setpos(self, pos):
224 if pos < 0 or pos > self._nframes:
Collin Winterce36ad82007-08-30 01:19:48 +0000225 raise Error('position not in range')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000226 self._soundpos = pos
227 self._data_seek_needed = 1
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000228
Guido van Rossume7b146f2000-02-04 15:28:42 +0000229 def readframes(self, nframes):
230 if self._data_seek_needed:
231 self._data_chunk.seek(0, 0)
232 pos = self._soundpos * self._framesize
233 if pos:
234 self._data_chunk.seek(pos, 0)
235 self._data_seek_needed = 0
236 if nframes == 0:
Guido van Rossum51a883b2007-07-23 21:28:30 +0000237 return b''
Guido van Rossume7b146f2000-02-04 15:28:42 +0000238 if self._sampwidth > 1 and big_endian:
239 # unfortunately the fromfile() method does not take
240 # something that only looks like a file object, so
241 # we have to reach into the innards of the chunk object
242 import array
243 chunk = self._data_chunk
244 data = array.array(_array_fmts[self._sampwidth])
245 nitems = nframes * self._nchannels
246 if nitems * self._sampwidth > chunk.chunksize - chunk.size_read:
Benjamin Peterson5efea042010-01-13 03:49:50 +0000247 nitems = (chunk.chunksize - chunk.size_read) // self._sampwidth
Guido van Rossume7b146f2000-02-04 15:28:42 +0000248 data.fromfile(chunk.file.file, nitems)
249 # "tell" data chunk how much was read
250 chunk.size_read = chunk.size_read + nitems * self._sampwidth
251 # do the same for the outermost chunk
252 chunk = chunk.file
253 chunk.size_read = chunk.size_read + nitems * self._sampwidth
254 data.byteswap()
Antoine Pitrou1ce3eb52010-09-01 20:29:34 +0000255 data = data.tobytes()
Guido van Rossume7b146f2000-02-04 15:28:42 +0000256 else:
257 data = self._data_chunk.read(nframes * self._framesize)
258 if self._convert and data:
259 data = self._convert(data)
Guido van Rossum54e54c62001-09-04 19:14:14 +0000260 self._soundpos = self._soundpos + len(data) // (self._nchannels * self._sampwidth)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000261 return data
262
263 #
264 # Internal methods.
265 #
266
267 def _read_fmt_chunk(self, chunk):
Jesus Ceae4b863982012-11-17 03:41:54 +0100268 wFormatTag, self._nchannels, self._framerate, dwAvgBytesPerSec, wBlockAlign = struct.unpack_from('<HHLLH', chunk.read(14))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000269 if wFormatTag == WAVE_FORMAT_PCM:
Jesus Ceae4b863982012-11-17 03:41:54 +0100270 sampwidth = struct.unpack_from('<H', chunk.read(2))[0]
Guido van Rossum54e54c62001-09-04 19:14:14 +0000271 self._sampwidth = (sampwidth + 7) // 8
Guido van Rossume7b146f2000-02-04 15:28:42 +0000272 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000273 raise Error('unknown format: %r' % (wFormatTag,))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000274 self._framesize = self._nchannels * self._sampwidth
275 self._comptype = 'NONE'
276 self._compname = 'not compressed'
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000277
278class Wave_write:
Guido van Rossume7b146f2000-02-04 15:28:42 +0000279 """Variables used in this class:
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000280
Guido van Rossume7b146f2000-02-04 15:28:42 +0000281 These variables are user settable through appropriate methods
282 of this class:
283 _file -- the open file with methods write(), close(), tell(), seek()
284 set through the __init__() method
285 _comptype -- the AIFF-C compression type ('NONE' in AIFF)
286 set through the setcomptype() or setparams() method
287 _compname -- the human-readable AIFF-C compression type
288 set through the setcomptype() or setparams() method
289 _nchannels -- the number of audio channels
290 set through the setnchannels() or setparams() method
291 _sampwidth -- the number of bytes per audio sample
292 set through the setsampwidth() or setparams() method
293 _framerate -- the sampling frequency
294 set through the setframerate() or setparams() method
295 _nframes -- the number of audio frames written to the header
296 set through the setnframes() or setparams() method
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000297
Guido van Rossume7b146f2000-02-04 15:28:42 +0000298 These variables are used internally only:
299 _datalength -- the size of the audio samples written to the header
300 _nframeswritten -- the number of frames actually written
301 _datawritten -- the size of the audio samples actually written
302 """
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000303
Guido van Rossume7b146f2000-02-04 15:28:42 +0000304 def __init__(self, f):
Tim Peterscfc41782000-10-09 23:43:55 +0000305 self._i_opened_the_file = None
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000306 if isinstance(f, str):
Georg Brandl1a3284e2007-12-02 09:40:06 +0000307 f = builtins.open(f, 'wb')
Tim Peterscfc41782000-10-09 23:43:55 +0000308 self._i_opened_the_file = f
Guido van Rossumd8faa362007-04-27 19:54:29 +0000309 try:
310 self.initfp(f)
311 except:
312 if self._i_opened_the_file:
313 f.close()
314 raise
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000315
Guido van Rossume7b146f2000-02-04 15:28:42 +0000316 def initfp(self, file):
317 self._file = file
318 self._convert = None
319 self._nchannels = 0
320 self._sampwidth = 0
321 self._framerate = 0
322 self._nframes = 0
323 self._nframeswritten = 0
324 self._datawritten = 0
325 self._datalength = 0
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000326 self._headerwritten = False
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000327
Guido van Rossume7b146f2000-02-04 15:28:42 +0000328 def __del__(self):
Tim Peterscfc41782000-10-09 23:43:55 +0000329 self.close()
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000330
Guido van Rossume7b146f2000-02-04 15:28:42 +0000331 #
332 # User visible methods.
333 #
334 def setnchannels(self, nchannels):
335 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000336 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000337 if nchannels < 1:
Collin Winterce36ad82007-08-30 01:19:48 +0000338 raise Error('bad # of channels')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000339 self._nchannels = nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000340
Guido van Rossume7b146f2000-02-04 15:28:42 +0000341 def getnchannels(self):
342 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000343 raise Error('number of channels not set')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000344 return self._nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000345
Guido van Rossume7b146f2000-02-04 15:28:42 +0000346 def setsampwidth(self, sampwidth):
347 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000348 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000349 if sampwidth < 1 or sampwidth > 4:
Collin Winterce36ad82007-08-30 01:19:48 +0000350 raise Error('bad sample width')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000351 self._sampwidth = sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000352
Guido van Rossume7b146f2000-02-04 15:28:42 +0000353 def getsampwidth(self):
354 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000355 raise Error('sample width not set')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000356 return self._sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000357
Guido van Rossume7b146f2000-02-04 15:28:42 +0000358 def setframerate(self, framerate):
359 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000360 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000361 if framerate <= 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000362 raise Error('bad frame rate')
Mark Dickinson64a38c02010-08-28 17:22:16 +0000363 self._framerate = int(round(framerate))
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000364
Guido van Rossume7b146f2000-02-04 15:28:42 +0000365 def getframerate(self):
366 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000367 raise Error('frame rate not set')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000368 return self._framerate
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000369
Guido van Rossume7b146f2000-02-04 15:28:42 +0000370 def setnframes(self, nframes):
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 self._nframes = nframes
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000374
Guido van Rossume7b146f2000-02-04 15:28:42 +0000375 def getnframes(self):
376 return self._nframeswritten
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000377
Guido van Rossume7b146f2000-02-04 15:28:42 +0000378 def setcomptype(self, comptype, compname):
379 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000380 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000381 if comptype not in ('NONE',):
Collin Winterce36ad82007-08-30 01:19:48 +0000382 raise Error('unsupported compression type')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000383 self._comptype = comptype
384 self._compname = compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000385
Guido van Rossume7b146f2000-02-04 15:28:42 +0000386 def getcomptype(self):
387 return self._comptype
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000388
Guido van Rossume7b146f2000-02-04 15:28:42 +0000389 def getcompname(self):
390 return self._compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000391
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000392 def setparams(self, params):
393 nchannels, sampwidth, framerate, nframes, comptype, compname = params
Guido van Rossume7b146f2000-02-04 15:28:42 +0000394 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000395 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000396 self.setnchannels(nchannels)
397 self.setsampwidth(sampwidth)
398 self.setframerate(framerate)
399 self.setnframes(nframes)
400 self.setcomptype(comptype, compname)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000401
Guido van Rossume7b146f2000-02-04 15:28:42 +0000402 def getparams(self):
403 if not self._nchannels or not self._sampwidth or not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000404 raise Error('not all parameters set')
R David Murray671cd322013-04-10 12:31:43 -0400405 return _result(self._nchannels, self._sampwidth, self._framerate,
406 self._nframes, self._comptype, self._compname)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000407
Guido van Rossume7b146f2000-02-04 15:28:42 +0000408 def setmark(self, id, pos, name):
Collin Winterce36ad82007-08-30 01:19:48 +0000409 raise Error('setmark() not supported')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000410
Guido van Rossume7b146f2000-02-04 15:28:42 +0000411 def getmark(self, id):
Collin Winterce36ad82007-08-30 01:19:48 +0000412 raise Error('no marks')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000413
Guido van Rossume7b146f2000-02-04 15:28:42 +0000414 def getmarkers(self):
415 return None
Tim Peterse1190062001-01-15 03:34:38 +0000416
Guido van Rossume7b146f2000-02-04 15:28:42 +0000417 def tell(self):
418 return self._nframeswritten
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000419
Guido van Rossume7b146f2000-02-04 15:28:42 +0000420 def writeframesraw(self, data):
421 self._ensure_header_written(len(data))
Guido van Rossum54e54c62001-09-04 19:14:14 +0000422 nframes = len(data) // (self._sampwidth * self._nchannels)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000423 if self._convert:
424 data = self._convert(data)
425 if self._sampwidth > 1 and big_endian:
426 import array
427 data = array.array(_array_fmts[self._sampwidth], data)
428 data.byteswap()
429 data.tofile(self._file)
430 self._datawritten = self._datawritten + len(data) * self._sampwidth
431 else:
432 self._file.write(data)
433 self._datawritten = self._datawritten + len(data)
434 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):
Tim Peterscfc41782000-10-09 23:43:55 +0000442 if self._file:
443 self._ensure_header_written(0)
444 if self._datalength != self._datawritten:
445 self._patchheader()
446 self._file.flush()
447 self._file = None
448 if self._i_opened_the_file:
449 self._i_opened_the_file.close()
450 self._i_opened_the_file = None
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000451
Guido van Rossume7b146f2000-02-04 15:28:42 +0000452 #
453 # Internal methods.
454 #
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000455
Guido van Rossume7b146f2000-02-04 15:28:42 +0000456 def _ensure_header_written(self, datasize):
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000457 if not self._headerwritten:
Guido van Rossume7b146f2000-02-04 15:28:42 +0000458 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000459 raise Error('# channels not specified')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000460 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000461 raise Error('sample width not specified')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000462 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000463 raise Error('sampling rate not specified')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000464 self._write_header(datasize)
465
466 def _write_header(self, initlength):
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000467 assert not self._headerwritten
Guido van Rossum09549f42007-08-27 20:40:10 +0000468 self._file.write(b'RIFF')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000469 if not self._nframes:
Benjamin Peterson5efea042010-01-13 03:49:50 +0000470 self._nframes = initlength // (self._nchannels * self._sampwidth)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000471 self._datalength = self._nframes * self._nchannels * self._sampwidth
472 self._form_length_pos = self._file.tell()
Jesus Ceae4b863982012-11-17 03:41:54 +0100473 self._file.write(struct.pack('<L4s4sLHHLLHH4s',
Victor Stinnerda9ec992010-12-28 13:26:42 +0000474 36 + self._datalength, b'WAVE', b'fmt ', 16,
Guido van Rossume7b146f2000-02-04 15:28:42 +0000475 WAVE_FORMAT_PCM, self._nchannels, self._framerate,
476 self._nchannels * self._framerate * self._sampwidth,
477 self._nchannels * self._sampwidth,
Victor Stinnerda9ec992010-12-28 13:26:42 +0000478 self._sampwidth * 8, b'data'))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000479 self._data_length_pos = self._file.tell()
Jesus Ceae4b863982012-11-17 03:41:54 +0100480 self._file.write(struct.pack('<L', self._datalength))
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000481 self._headerwritten = True
Guido van Rossume7b146f2000-02-04 15:28:42 +0000482
483 def _patchheader(self):
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000484 assert self._headerwritten
Guido van Rossume7b146f2000-02-04 15:28:42 +0000485 if self._datawritten == self._datalength:
486 return
487 curpos = self._file.tell()
488 self._file.seek(self._form_length_pos, 0)
Jesus Ceae4b863982012-11-17 03:41:54 +0100489 self._file.write(struct.pack('<L', 36 + self._datawritten))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000490 self._file.seek(self._data_length_pos, 0)
Jesus Ceae4b863982012-11-17 03:41:54 +0100491 self._file.write(struct.pack('<L', self._datawritten))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000492 self._file.seek(curpos, 0)
493 self._datalength = self._datawritten
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000494
Fred Drakef9607821999-06-17 15:18:47 +0000495def open(f, mode=None):
Guido van Rossume7b146f2000-02-04 15:28:42 +0000496 if mode is None:
497 if hasattr(f, 'mode'):
498 mode = f.mode
499 else:
500 mode = 'rb'
501 if mode in ('r', 'rb'):
502 return Wave_read(f)
503 elif mode in ('w', 'wb'):
504 return Wave_write(f)
505 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000506 raise Error("mode must be 'r', 'rb', 'w', or 'wb'")
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000507
508openfp = open # B/W compatibility