Skip to content

Commit 3601e88

Browse files
committed
Sjoerd Mullender writes:
""" Extended chunk so that it can also handle formats that are almost according to EA IFF 85. In particular, added options to handle little-endian and to handle formats that include the header size in the chunk size value. Fixed a bug where the header size was included in the chunk size, which it isn't according to EA IFF 85. Added a new method getsize() to get the size of the chunk (excluding header). Fixed chunk documentation (TIFF doesn't look like it uses chunks). Converted wave to use chunk. Wave uses EA IFF 85 chunks except that it uses little-endian encoding of integer data. Removed __del__ methods from aifc and wave since I got an AttributeError there upon exit. """
1 parent 2900ff9 commit 3601e88

3 files changed

Lines changed: 50 additions & 147 deletions

File tree

Lib/aifc.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -293,8 +293,6 @@ def initfp(self, file):
293293
self._comm_chunk_read = 0
294294
while 1:
295295
self._ssnd_seek_needed = 1
296-
#DEBUG: SGI's soundfiler has a bug. There should
297-
# be no need to check for EOF here.
298296
try:
299297
chunk = Chunk(self._file)
300298
except EOFError:
@@ -337,10 +335,6 @@ def __init__(self, f):
337335
# else, assume it is an open file object already
338336
self.initfp(f)
339337

340-
def __del__(self):
341-
if self._file:
342-
self.close()
343-
344338
#
345339
# User visible methods.
346340
#

Lib/chunk.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,19 +49,24 @@
4949
"""
5050

5151
class Chunk:
52-
def __init__(self, file, align = 1):
52+
def __init__(self, file, align = 1, bigendian = 1, inclheader = 0):
5353
import struct
5454
self.closed = 0
5555
self.align = align # whether to align to word (2-byte) boundaries
56+
if bigendian:
57+
strflag = '>'
58+
else:
59+
strflag = '<'
5660
self.file = file
5761
self.chunkname = file.read(4)
5862
if len(self.chunkname) < 4:
5963
raise EOFError
6064
try:
61-
self.chunksize = struct.unpack('>l', file.read(4))[0]
65+
self.chunksize = struct.unpack(strflag+'l', file.read(4))[0]
6266
except struct.error:
6367
raise EOFError
64-
self.chunksize = self.chunksize - 8 # subtract header
68+
if inclheader:
69+
self.chunksize = self.chunksize - 8 # subtract header
6570
self.size_read = 0
6671
try:
6772
self.offset = self.file.tell()
@@ -74,6 +79,10 @@ def getname(self):
7479
"""Return the name (ID) of the current chunk."""
7580
return self.chunkname
7681

82+
def getsize(self):
83+
"""Return the size of the current chunk."""
84+
return self.chunksize
85+
7786
def close(self):
7887
if not self.closed:
7988
self.skip()

Lib/wave.py

Lines changed: 38 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -85,87 +85,7 @@
8585
else:
8686
big_endian = 0
8787

88-
def _read_long(file):
89-
x = 0L
90-
for i in range(4):
91-
byte = file.read(1)
92-
if byte == '':
93-
raise EOFError
94-
x = x + (ord(byte) << (8 * i))
95-
if x >= 0x80000000L:
96-
x = x - 0x100000000L
97-
return int(x)
98-
99-
def _read_ulong(file):
100-
x = 0L
101-
for i in range(4):
102-
byte = file.read(1)
103-
if byte == '':
104-
raise EOFError
105-
x = x + (ord(byte) << (8 * i))
106-
return x
107-
108-
def _read_short(file):
109-
x = 0
110-
for i in range(2):
111-
byte = file.read(1)
112-
if byte == '':
113-
raise EOFError
114-
x = x + (ord(byte) << (8 * i))
115-
if x >= 0x8000:
116-
x = x - 0x10000
117-
return x
118-
119-
def _write_short(f, x):
120-
d, m = divmod(x, 256)
121-
f.write(chr(m))
122-
f.write(chr(d))
123-
124-
def _write_long(f, x):
125-
if x < 0:
126-
x = x + 0x100000000L
127-
for i in range(4):
128-
d, m = divmod(x, 256)
129-
f.write(chr(int(m)))
130-
x = d
131-
132-
class Chunk:
133-
def __init__(self, file):
134-
self.file = file
135-
self.chunkname = self.file.read(4)
136-
if len(self.chunkname) < 4:
137-
raise EOFError
138-
self.chunksize = _read_long(self.file)
139-
self.size_read = 0
140-
self.offset = self.file.tell()
141-
142-
def rewind(self):
143-
self.file.seek(self.offset, 0)
144-
self.size_read = 0
145-
146-
def setpos(self, pos):
147-
if pos < 0 or pos > self.chunksize:
148-
raise RuntimeError
149-
self.file.seek(self.offset + pos, 0)
150-
self.size_read = pos
151-
152-
def read(self, length):
153-
if self.size_read >= self.chunksize:
154-
return ''
155-
if length > self.chunksize - self.size_read:
156-
length = self.chunksize - self.size_read
157-
data = self.file.read(length)
158-
self.size_read = self.size_read + len(data)
159-
return data
160-
161-
def skip(self):
162-
try:
163-
self.file.seek(self.chunksize - self.size_read, 1)
164-
except RuntimeError:
165-
while self.size_read < self.chunksize:
166-
dummy = self.read(8192)
167-
if not dummy:
168-
raise EOFError
88+
from chunk import Chunk
16989

17090
class Wave_read:
17191
# Variables used in this class:
@@ -197,41 +117,34 @@ class Wave_read:
197117
# _data_chunk -- instantiation of a chunk class for the DATA chunk
198118
# _framesize -- size of one frame in the file
199119

200-
## access _file, _nchannels, _nframes, _sampwidth, _framerate, \
201-
## _comptype, _compname, _soundpos, \
202-
## _fmt_chunk_read, _data_seek_needed, \
203-
## _data_chunk, _framesize: private
204-
205120
def initfp(self, file):
206-
self._file = file
207121
self._convert = None
208122
self._soundpos = 0
209-
form = self._file.read(4)
210-
if form != 'RIFF':
123+
self._file = Chunk(file, bigendian = 0)
124+
if self._file.getname() != 'RIFF':
211125
raise Error, 'file does not start with RIFF id'
212-
formlength = _read_long(self._file)
213-
if formlength <= 0:
214-
raise Error, 'invalid FORM chunk data size'
215-
formdata = self._file.read(4)
216-
formlength = formlength - 4
217-
if formdata != 'WAVE':
126+
if self._file.read(4) != 'WAVE':
218127
raise Error, 'not a WAVE file'
219128
self._fmt_chunk_read = 0
220-
while formlength > 0:
129+
self._data_chunk = None
130+
while 1:
221131
self._data_seek_needed = 1
222-
chunk = Chunk(self._file)
223-
if chunk.chunkname == 'fmt ':
132+
try:
133+
chunk = Chunk(self._file, bigendian = 0)
134+
except EOFError:
135+
break
136+
chunkname = chunk.getname()
137+
if chunkname == 'fmt ':
224138
self._read_fmt_chunk(chunk)
225139
self._fmt_chunk_read = 1
226-
elif chunk.chunkname == 'data':
140+
elif chunkname == 'data':
227141
if not self._fmt_chunk_read:
228142
raise Error, 'data chunk before fmt chunk'
229143
self._data_chunk = chunk
230144
self._nframes = chunk.chunksize / self._framesize
231145
self._data_seek_needed = 0
232-
formlength = formlength - 8 - chunk.chunksize
233-
if formlength > 0:
234-
chunk.skip()
146+
break
147+
chunk.skip()
235148
if not self._fmt_chunk_read or not self._data_chunk:
236149
raise Error, 'fmt chunk and/or data chunk missing'
237150

@@ -241,10 +154,6 @@ def __init__(self, f):
241154
# else, assume it is an open file object already
242155
self.initfp(f)
243156

244-
def __del__(self):
245-
if self._file:
246-
self.close()
247-
248157
#
249158
# User visible methods.
250159
#
@@ -298,10 +207,10 @@ def setpos(self, pos):
298207

299208
def readframes(self, nframes):
300209
if self._data_seek_needed:
301-
self._data_chunk.rewind()
210+
self._data_chunk.seek(0, 0)
302211
pos = self._soundpos * self._framesize
303212
if pos:
304-
self._data_chunk.setpos(pos)
213+
self._data_chunk.seek(pos, 0)
305214
self._data_seek_needed = 0
306215
if nframes == 0:
307216
return ''
@@ -310,12 +219,17 @@ def readframes(self, nframes):
310219
# something that only looks like a file object, so
311220
# we have to reach into the innards of the chunk object
312221
import array
222+
chunk = self._data_chunk
313223
data = array.array(_array_fmts[self._sampwidth])
314224
nitems = nframes * self._nchannels
315-
if nitems * self._sampwidth > self._data_chunk.chunksize - self._data_chunk.size_read:
316-
nitems = (self._data_chunk.chunksize - self._data_chunk.size_read) / self._sampwidth
317-
data.fromfile(self._data_chunk.file, nitems)
318-
self._data_chunk.size_read = self._data_chunk.size_read + nitems * self._sampwidth
225+
if nitems * self._sampwidth > chunk.chunksize - chunk.size_read:
226+
nitems = (chunk.chunksize - chunk.size_read) / self._sampwidth
227+
data.fromfile(chunk.file.file, nitems)
228+
# "tell" data chunk how much was read
229+
chunk.size_read = chunk.size_read + nitems * self._sampwidth
230+
# do the same for the outermost chunk
231+
chunk = chunk.file
232+
chunk.size_read = chunk.size_read + nitems * self._sampwidth
319233
data.byteswap()
320234
data = data.tostring()
321235
else:
@@ -328,16 +242,12 @@ def readframes(self, nframes):
328242
#
329243
# Internal methods.
330244
#
331-
## access *: private
332245

333246
def _read_fmt_chunk(self, chunk):
334-
wFormatTag = _read_short(chunk)
335-
self._nchannels = _read_short(chunk)
336-
self._framerate = _read_long(chunk)
337-
dwAvgBytesPerSec = _read_long(chunk)
338-
wBlockAlign = _read_short(chunk)
247+
wFormatTag, self._nchannels, self._framerate, dwAvgBytesPerSec, wBlockAlign = struct.unpack('<hhllh', chunk.read(14))
339248
if wFormatTag == WAVE_FORMAT_PCM:
340-
self._sampwidth = (_read_short(chunk) + 7) / 8
249+
sampwidth = struct.unpack('<h', chunk.read(2))[0]
250+
self._sampwidth = (sampwidth + 7) / 8
341251
else:
342252
raise Error, 'unknown format: ' + `wFormatTag`
343253
self._framesize = self._nchannels * self._sampwidth
@@ -369,10 +279,6 @@ class Wave_write:
369279
# _nframeswritten -- the number of frames actually written
370280
# _datawritten -- the size of the audio samples actually written
371281

372-
## access _file, _comptype, _compname, _nchannels, _sampwidth, \
373-
## _framerate, _nframes, _nframeswritten, \
374-
## _datalength, _datawritten: private
375-
376282
def __init__(self, f):
377283
if type(f) == type(''):
378284
f = __builtin__.open(f, 'wb')
@@ -512,7 +418,6 @@ def close(self):
512418
#
513419
# Internal methods.
514420
#
515-
## access *: private
516421

517422
def _ensure_header_written(self, datasize):
518423
if not self._datawritten:
@@ -530,28 +435,23 @@ def _write_header(self, initlength):
530435
self._nframes = initlength / (self._nchannels * self._sampwidth)
531436
self._datalength = self._nframes * self._nchannels * self._sampwidth
532437
self._form_length_pos = self._file.tell()
533-
_write_long(self._file, 36 + self._datalength)
534-
self._file.write('WAVE')
535-
self._file.write('fmt ')
536-
_write_long(self._file, 16)
537-
_write_short(self._file, WAVE_FORMAT_PCM)
538-
_write_short(self._file, self._nchannels)
539-
_write_long(self._file, self._framerate)
540-
_write_long(self._file, self._nchannels * self._framerate * self._sampwidth)
541-
_write_short(self._file, self._nchannels * self._sampwidth)
542-
_write_short(self._file, self._sampwidth * 8)
543-
self._file.write('data')
438+
self._file.write(struct.pack('<lsslhhllhhs',
439+
36 + self._datalength, 'WAVE', 'fmt ', 16,
440+
WAVE_FORMAT_PCM, self._nchannels, self._framerate,
441+
self._nchannels * self._framerate * self._sampwidth,
442+
self._nchannels * self._sampwidth,
443+
self._sampwidth * 8, 'data'))
544444
self._data_length_pos = self._file.tell()
545-
_write_long(self._file, self._datalength)
445+
self._file.write(struct.pack('<l', self._datalength))
546446

547447
def _patchheader(self):
548448
if self._datawritten == self._datalength:
549449
return
550450
curpos = self._file.tell()
551451
self._file.seek(self._form_length_pos, 0)
552-
_write_long(self._file, 36 + self._datawritten)
452+
self._file.write(struct.pack('<l', 36 + self._datawritten))
553453
self._file.seek(self._data_length_pos, 0)
554-
_write_long(self._file, self._datawritten)
454+
self._file.write(struct.pack('<l', self._datawritten))
555455
self._file.seek(curpos, 0)
556456
self._datalength = self._datawritten
557457

0 commit comments

Comments
 (0)