blob: ea410c12d783ba2ff083f0819232c2e1f66695fc [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 +000085import struct
Serhiy Storchakad739bda2013-05-29 23:38:00 +030086import sys
Guido van Rossum3601e881999-08-26 15:50:43 +000087from chunk import Chunk
R David Murray671cd322013-04-10 12:31:43 -040088from collections import namedtuple
89
90_result = namedtuple('params',
91 'nchannels sampwidth framerate nframes comptype compname')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000092
93class Wave_read:
Guido van Rossume7b146f2000-02-04 15:28:42 +000094 """Variables used in this class:
Guido van Rossum3ed23cc1994-02-15 15:57:15 +000095
Guido van Rossume7b146f2000-02-04 15:28:42 +000096 These variables are available to the user though appropriate
97 methods of this class:
98 _file -- the open file with methods read(), close(), and seek()
99 set through the __init__() method
100 _nchannels -- the number of audio channels
101 available through the getnchannels() method
102 _nframes -- the number of audio frames
103 available through the getnframes() method
104 _sampwidth -- the number of bytes per audio sample
105 available through the getsampwidth() method
106 _framerate -- the sampling frequency
107 available through the getframerate() method
108 _comptype -- the AIFF-C compression type ('NONE' if AIFF)
109 available through the getcomptype() method
110 _compname -- the human-readable AIFF-C compression type
111 available through the getcomptype() method
112 _soundpos -- the position in the audio stream
113 available through the tell() method, set through the
114 setpos() method
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000115
Guido van Rossume7b146f2000-02-04 15:28:42 +0000116 These variables are used internally only:
117 _fmt_chunk_read -- 1 iff the FMT chunk has been read
118 _data_seek_needed -- 1 iff positioned correctly in audio
119 file for readframes()
120 _data_chunk -- instantiation of a chunk class for the DATA chunk
121 _framesize -- size of one frame in the file
122 """
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000123
Guido van Rossume7b146f2000-02-04 15:28:42 +0000124 def initfp(self, file):
125 self._convert = None
126 self._soundpos = 0
127 self._file = Chunk(file, bigendian = 0)
Guido van Rossum51a883b2007-07-23 21:28:30 +0000128 if self._file.getname() != b'RIFF':
Collin Winterce36ad82007-08-30 01:19:48 +0000129 raise Error('file does not start with RIFF id')
Guido van Rossum51a883b2007-07-23 21:28:30 +0000130 if self._file.read(4) != b'WAVE':
Collin Winterce36ad82007-08-30 01:19:48 +0000131 raise Error('not a WAVE file')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000132 self._fmt_chunk_read = 0
133 self._data_chunk = None
134 while 1:
135 self._data_seek_needed = 1
136 try:
137 chunk = Chunk(self._file, bigendian = 0)
138 except EOFError:
139 break
140 chunkname = chunk.getname()
Guido van Rossum51a883b2007-07-23 21:28:30 +0000141 if chunkname == b'fmt ':
Guido van Rossume7b146f2000-02-04 15:28:42 +0000142 self._read_fmt_chunk(chunk)
143 self._fmt_chunk_read = 1
Guido van Rossum51a883b2007-07-23 21:28:30 +0000144 elif chunkname == b'data':
Guido van Rossume7b146f2000-02-04 15:28:42 +0000145 if not self._fmt_chunk_read:
Collin Winterce36ad82007-08-30 01:19:48 +0000146 raise Error('data chunk before fmt chunk')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000147 self._data_chunk = chunk
Guido van Rossum54e54c62001-09-04 19:14:14 +0000148 self._nframes = chunk.chunksize // self._framesize
Guido van Rossume7b146f2000-02-04 15:28:42 +0000149 self._data_seek_needed = 0
150 break
151 chunk.skip()
152 if not self._fmt_chunk_read or not self._data_chunk:
Collin Winterce36ad82007-08-30 01:19:48 +0000153 raise Error('fmt chunk and/or data chunk missing')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000154
Guido van Rossume7b146f2000-02-04 15:28:42 +0000155 def __init__(self, f):
Tim Peterscfc41782000-10-09 23:43:55 +0000156 self._i_opened_the_file = None
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000157 if isinstance(f, str):
Georg Brandl1a3284e2007-12-02 09:40:06 +0000158 f = builtins.open(f, 'rb')
Tim Peterscfc41782000-10-09 23:43:55 +0000159 self._i_opened_the_file = f
Guido van Rossume7b146f2000-02-04 15:28:42 +0000160 # else, assume it is an open file object already
Guido van Rossumd8faa362007-04-27 19:54:29 +0000161 try:
162 self.initfp(f)
163 except:
164 if self._i_opened_the_file:
165 f.close()
166 raise
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000167
Tim Peterscfc41782000-10-09 23:43:55 +0000168 def __del__(self):
169 self.close()
Guido van Rossume7b146f2000-02-04 15:28:42 +0000170 #
171 # User visible methods.
172 #
173 def getfp(self):
174 return self._file
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000175
Guido van Rossume7b146f2000-02-04 15:28:42 +0000176 def rewind(self):
177 self._data_seek_needed = 1
178 self._soundpos = 0
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000179
Guido van Rossume7b146f2000-02-04 15:28:42 +0000180 def close(self):
Tim Peterscfc41782000-10-09 23:43:55 +0000181 if self._i_opened_the_file:
182 self._i_opened_the_file.close()
183 self._i_opened_the_file = None
Guido van Rossume7b146f2000-02-04 15:28:42 +0000184 self._file = None
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000185
Guido van Rossume7b146f2000-02-04 15:28:42 +0000186 def tell(self):
187 return self._soundpos
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000188
Guido van Rossume7b146f2000-02-04 15:28:42 +0000189 def getnchannels(self):
190 return self._nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000191
Guido van Rossume7b146f2000-02-04 15:28:42 +0000192 def getnframes(self):
193 return self._nframes
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000194
Guido van Rossume7b146f2000-02-04 15:28:42 +0000195 def getsampwidth(self):
196 return self._sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000197
Guido van Rossume7b146f2000-02-04 15:28:42 +0000198 def getframerate(self):
199 return self._framerate
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000200
Guido van Rossume7b146f2000-02-04 15:28:42 +0000201 def getcomptype(self):
202 return self._comptype
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000203
Guido van Rossume7b146f2000-02-04 15:28:42 +0000204 def getcompname(self):
205 return self._compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000206
Guido van Rossume7b146f2000-02-04 15:28:42 +0000207 def getparams(self):
R David Murray671cd322013-04-10 12:31:43 -0400208 return _result(self.getnchannels(), self.getsampwidth(),
209 self.getframerate(), self.getnframes(),
210 self.getcomptype(), self.getcompname())
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000211
Guido van Rossume7b146f2000-02-04 15:28:42 +0000212 def getmarkers(self):
213 return None
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000214
Guido van Rossume7b146f2000-02-04 15:28:42 +0000215 def getmark(self, id):
Collin Winterce36ad82007-08-30 01:19:48 +0000216 raise Error('no marks')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000217
Guido van Rossume7b146f2000-02-04 15:28:42 +0000218 def setpos(self, pos):
219 if pos < 0 or pos > self._nframes:
Collin Winterce36ad82007-08-30 01:19:48 +0000220 raise Error('position not in range')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000221 self._soundpos = pos
222 self._data_seek_needed = 1
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000223
Guido van Rossume7b146f2000-02-04 15:28:42 +0000224 def readframes(self, nframes):
225 if self._data_seek_needed:
226 self._data_chunk.seek(0, 0)
227 pos = self._soundpos * self._framesize
228 if pos:
229 self._data_chunk.seek(pos, 0)
230 self._data_seek_needed = 0
231 if nframes == 0:
Guido van Rossum51a883b2007-07-23 21:28:30 +0000232 return b''
Serhiy Storchakad739bda2013-05-29 23:38:00 +0300233 if self._sampwidth > 1 and sys.byteorder == 'big':
Guido van Rossume7b146f2000-02-04 15:28:42 +0000234 # unfortunately the fromfile() method does not take
235 # something that only looks like a file object, so
236 # we have to reach into the innards of the chunk object
237 import array
238 chunk = self._data_chunk
239 data = array.array(_array_fmts[self._sampwidth])
240 nitems = nframes * self._nchannels
241 if nitems * self._sampwidth > chunk.chunksize - chunk.size_read:
Benjamin Peterson5efea042010-01-13 03:49:50 +0000242 nitems = (chunk.chunksize - chunk.size_read) // self._sampwidth
Guido van Rossume7b146f2000-02-04 15:28:42 +0000243 data.fromfile(chunk.file.file, nitems)
244 # "tell" data chunk how much was read
245 chunk.size_read = chunk.size_read + nitems * self._sampwidth
246 # do the same for the outermost chunk
247 chunk = chunk.file
248 chunk.size_read = chunk.size_read + nitems * self._sampwidth
249 data.byteswap()
Antoine Pitrou1ce3eb52010-09-01 20:29:34 +0000250 data = data.tobytes()
Guido van Rossume7b146f2000-02-04 15:28:42 +0000251 else:
252 data = self._data_chunk.read(nframes * self._framesize)
253 if self._convert and data:
254 data = self._convert(data)
Guido van Rossum54e54c62001-09-04 19:14:14 +0000255 self._soundpos = self._soundpos + len(data) // (self._nchannels * self._sampwidth)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000256 return data
257
258 #
259 # Internal methods.
260 #
261
262 def _read_fmt_chunk(self, chunk):
Jesus Ceae4b863982012-11-17 03:41:54 +0100263 wFormatTag, self._nchannels, self._framerate, dwAvgBytesPerSec, wBlockAlign = struct.unpack_from('<HHLLH', chunk.read(14))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000264 if wFormatTag == WAVE_FORMAT_PCM:
Jesus Ceae4b863982012-11-17 03:41:54 +0100265 sampwidth = struct.unpack_from('<H', chunk.read(2))[0]
Guido van Rossum54e54c62001-09-04 19:14:14 +0000266 self._sampwidth = (sampwidth + 7) // 8
Guido van Rossume7b146f2000-02-04 15:28:42 +0000267 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000268 raise Error('unknown format: %r' % (wFormatTag,))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000269 self._framesize = self._nchannels * self._sampwidth
270 self._comptype = 'NONE'
271 self._compname = 'not compressed'
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000272
273class Wave_write:
Guido van Rossume7b146f2000-02-04 15:28:42 +0000274 """Variables used in this class:
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000275
Guido van Rossume7b146f2000-02-04 15:28:42 +0000276 These variables are user settable through appropriate methods
277 of this class:
278 _file -- the open file with methods write(), close(), tell(), seek()
279 set through the __init__() method
280 _comptype -- the AIFF-C compression type ('NONE' in AIFF)
281 set through the setcomptype() or setparams() method
282 _compname -- the human-readable AIFF-C compression type
283 set through the setcomptype() or setparams() method
284 _nchannels -- the number of audio channels
285 set through the setnchannels() or setparams() method
286 _sampwidth -- the number of bytes per audio sample
287 set through the setsampwidth() or setparams() method
288 _framerate -- the sampling frequency
289 set through the setframerate() or setparams() method
290 _nframes -- the number of audio frames written to the header
291 set through the setnframes() or setparams() method
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000292
Guido van Rossume7b146f2000-02-04 15:28:42 +0000293 These variables are used internally only:
294 _datalength -- the size of the audio samples written to the header
295 _nframeswritten -- the number of frames actually written
296 _datawritten -- the size of the audio samples actually written
297 """
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000298
Guido van Rossume7b146f2000-02-04 15:28:42 +0000299 def __init__(self, f):
Tim Peterscfc41782000-10-09 23:43:55 +0000300 self._i_opened_the_file = None
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000301 if isinstance(f, str):
Georg Brandl1a3284e2007-12-02 09:40:06 +0000302 f = builtins.open(f, 'wb')
Tim Peterscfc41782000-10-09 23:43:55 +0000303 self._i_opened_the_file = f
Guido van Rossumd8faa362007-04-27 19:54:29 +0000304 try:
305 self.initfp(f)
306 except:
307 if self._i_opened_the_file:
308 f.close()
309 raise
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000310
Guido van Rossume7b146f2000-02-04 15:28:42 +0000311 def initfp(self, file):
312 self._file = file
313 self._convert = None
314 self._nchannels = 0
315 self._sampwidth = 0
316 self._framerate = 0
317 self._nframes = 0
318 self._nframeswritten = 0
319 self._datawritten = 0
320 self._datalength = 0
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000321 self._headerwritten = False
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000322
Guido van Rossume7b146f2000-02-04 15:28:42 +0000323 def __del__(self):
Tim Peterscfc41782000-10-09 23:43:55 +0000324 self.close()
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000325
Guido van Rossume7b146f2000-02-04 15:28:42 +0000326 #
327 # User visible methods.
328 #
329 def setnchannels(self, nchannels):
330 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000331 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000332 if nchannels < 1:
Collin Winterce36ad82007-08-30 01:19:48 +0000333 raise Error('bad # of channels')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000334 self._nchannels = nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000335
Guido van Rossume7b146f2000-02-04 15:28:42 +0000336 def getnchannels(self):
337 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000338 raise Error('number of channels not set')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000339 return self._nchannels
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000340
Guido van Rossume7b146f2000-02-04 15:28:42 +0000341 def setsampwidth(self, sampwidth):
342 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000343 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000344 if sampwidth < 1 or sampwidth > 4:
Collin Winterce36ad82007-08-30 01:19:48 +0000345 raise Error('bad sample width')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000346 self._sampwidth = sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000347
Guido van Rossume7b146f2000-02-04 15:28:42 +0000348 def getsampwidth(self):
349 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000350 raise Error('sample width not set')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000351 return self._sampwidth
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000352
Guido van Rossume7b146f2000-02-04 15:28:42 +0000353 def setframerate(self, framerate):
354 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000355 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000356 if framerate <= 0:
Collin Winterce36ad82007-08-30 01:19:48 +0000357 raise Error('bad frame rate')
Mark Dickinson64a38c02010-08-28 17:22:16 +0000358 self._framerate = int(round(framerate))
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000359
Guido van Rossume7b146f2000-02-04 15:28:42 +0000360 def getframerate(self):
361 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000362 raise Error('frame rate not set')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000363 return self._framerate
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000364
Guido van Rossume7b146f2000-02-04 15:28:42 +0000365 def setnframes(self, nframes):
366 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000367 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000368 self._nframes = nframes
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000369
Guido van Rossume7b146f2000-02-04 15:28:42 +0000370 def getnframes(self):
371 return self._nframeswritten
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000372
Guido van Rossume7b146f2000-02-04 15:28:42 +0000373 def setcomptype(self, comptype, compname):
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 if comptype not in ('NONE',):
Collin Winterce36ad82007-08-30 01:19:48 +0000377 raise Error('unsupported compression type')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000378 self._comptype = comptype
379 self._compname = compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000380
Guido van Rossume7b146f2000-02-04 15:28:42 +0000381 def getcomptype(self):
382 return self._comptype
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000383
Guido van Rossume7b146f2000-02-04 15:28:42 +0000384 def getcompname(self):
385 return self._compname
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000386
Guido van Rossum1bc535d2007-05-15 18:46:22 +0000387 def setparams(self, params):
388 nchannels, sampwidth, framerate, nframes, comptype, compname = params
Guido van Rossume7b146f2000-02-04 15:28:42 +0000389 if self._datawritten:
Collin Winterce36ad82007-08-30 01:19:48 +0000390 raise Error('cannot change parameters after starting to write')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000391 self.setnchannels(nchannels)
392 self.setsampwidth(sampwidth)
393 self.setframerate(framerate)
394 self.setnframes(nframes)
395 self.setcomptype(comptype, compname)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000396
Guido van Rossume7b146f2000-02-04 15:28:42 +0000397 def getparams(self):
398 if not self._nchannels or not self._sampwidth or not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000399 raise Error('not all parameters set')
R David Murray671cd322013-04-10 12:31:43 -0400400 return _result(self._nchannels, self._sampwidth, self._framerate,
401 self._nframes, self._comptype, self._compname)
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000402
Guido van Rossume7b146f2000-02-04 15:28:42 +0000403 def setmark(self, id, pos, name):
Collin Winterce36ad82007-08-30 01:19:48 +0000404 raise Error('setmark() not supported')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000405
Guido van Rossume7b146f2000-02-04 15:28:42 +0000406 def getmark(self, id):
Collin Winterce36ad82007-08-30 01:19:48 +0000407 raise Error('no marks')
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000408
Guido van Rossume7b146f2000-02-04 15:28:42 +0000409 def getmarkers(self):
410 return None
Tim Peterse1190062001-01-15 03:34:38 +0000411
Guido van Rossume7b146f2000-02-04 15:28:42 +0000412 def tell(self):
413 return self._nframeswritten
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000414
Guido van Rossume7b146f2000-02-04 15:28:42 +0000415 def writeframesraw(self, data):
416 self._ensure_header_written(len(data))
Guido van Rossum54e54c62001-09-04 19:14:14 +0000417 nframes = len(data) // (self._sampwidth * self._nchannels)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000418 if self._convert:
419 data = self._convert(data)
Serhiy Storchakad739bda2013-05-29 23:38:00 +0300420 if self._sampwidth > 1 and sys.byteorder == 'big':
Guido van Rossume7b146f2000-02-04 15:28:42 +0000421 import array
422 data = array.array(_array_fmts[self._sampwidth], data)
423 data.byteswap()
424 data.tofile(self._file)
425 self._datawritten = self._datawritten + len(data) * self._sampwidth
426 else:
427 self._file.write(data)
428 self._datawritten = self._datawritten + len(data)
429 self._nframeswritten = self._nframeswritten + nframes
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000430
Guido van Rossume7b146f2000-02-04 15:28:42 +0000431 def writeframes(self, data):
432 self.writeframesraw(data)
433 if self._datalength != self._datawritten:
434 self._patchheader()
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000435
Guido van Rossume7b146f2000-02-04 15:28:42 +0000436 def close(self):
Tim Peterscfc41782000-10-09 23:43:55 +0000437 if self._file:
438 self._ensure_header_written(0)
439 if self._datalength != self._datawritten:
440 self._patchheader()
441 self._file.flush()
442 self._file = None
443 if self._i_opened_the_file:
444 self._i_opened_the_file.close()
445 self._i_opened_the_file = None
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000446
Guido van Rossume7b146f2000-02-04 15:28:42 +0000447 #
448 # Internal methods.
449 #
Guido van Rossum3ed23cc1994-02-15 15:57:15 +0000450
Guido van Rossume7b146f2000-02-04 15:28:42 +0000451 def _ensure_header_written(self, datasize):
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000452 if not self._headerwritten:
Guido van Rossume7b146f2000-02-04 15:28:42 +0000453 if not self._nchannels:
Collin Winterce36ad82007-08-30 01:19:48 +0000454 raise Error('# channels not specified')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000455 if not self._sampwidth:
Collin Winterce36ad82007-08-30 01:19:48 +0000456 raise Error('sample width not specified')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000457 if not self._framerate:
Collin Winterce36ad82007-08-30 01:19:48 +0000458 raise Error('sampling rate not specified')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000459 self._write_header(datasize)
460
461 def _write_header(self, initlength):
Georg Brandlcc2adbc2010-10-30 08:29:28 +0000462 assert not self._headerwritten
Guido van Rossum09549f42007-08-27 20:40:10 +0000463 self._file.write(b'RIFF')
Guido van Rossume7b146f2000-02-04 15:28:42 +0000464 if not self._nframes:
Benjamin Peterson5efea042010-01-13 03:49:50 +0000465 self._nframes = initlength // (self._nchannels * self._sampwidth)
Guido van Rossume7b146f2000-02-04 15:28:42 +0000466 self._datalength = self._nframes * self._nchannels * self._sampwidth
467 self._form_length_pos = self._file.tell()
Jesus Ceae4b863982012-11-17 03:41:54 +0100468 self._file.write(struct.pack('<L4s4sLHHLLHH4s',
Victor Stinnerda9ec992010-12-28 13:26:42 +0000469 36 + self._datalength, b'WAVE', b'fmt ', 16,
Guido van Rossume7b146f2000-02-04 15:28:42 +0000470 WAVE_FORMAT_PCM, self._nchannels, self._framerate,
471 self._nchannels * self._framerate * self._sampwidth,
472 self._nchannels * self._sampwidth,
Victor Stinnerda9ec992010-12-28 13:26:42 +0000473 self._sampwidth * 8, b'data'))
Guido van Rossume7b146f2000-02-04 15:28:42 +0000474 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