Skip to content

Commit

Permalink
WIP: read(): Don't ignore "frames" argument
Browse files Browse the repository at this point in the history
Partial solution to #210, but this breaks blocks()!

The tests can be run without testing blocks():

    python3 -m pytest -k"not blocks"
  • Loading branch information
mgeier committed Nov 14, 2017
1 parent 969b742 commit f3a08f2
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 22 deletions.
49 changes: 29 additions & 20 deletions soundfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,10 @@ def read(file, frames=-1, start=0, stop=None, dtype='float64', always_2d=False,
array anyway.
If `out` was specified, it is returned. If `out` has more
frames than available in the file (or if `frames` is smaller
than the length of `out`) and no `fill_value` is given, then
only a part of `out` is overwritten and a view containing all
valid frames is returned.
frames than available in the file and no `fill_value` is given,
or if `frames` is smaller than the length of `out`, then only a
part of `out` is overwritten and a view containing all valid
frames is returned.
samplerate : int
The sample rate of the audio file.
Expand Down Expand Up @@ -806,12 +806,11 @@ def read(self, frames=-1, dtype='float64', always_2d=False,
one-dimensional array is returned. Use ``always_2d=True``
to return a two-dimensional array anyway.
If `out` was specified, it is returned. If `out` has more
frames than available in the file (or if `frames` is
smaller than the length of `out`) and no `fill_value` is
given, then only a part of `out` is overwritten and a view
containing all valid frames is returned. numpy.ndarray or
type(out)
If `out` was specified, it is returned. If `out` has more
frames than available in the file and no `fill_value` is
given, or if `frames` is smaller than the length of `out`,
then only a part of `out` is overwritten and a view
containing all valid frames is returned.
Other Parameters
----------------
Expand Down Expand Up @@ -850,18 +849,25 @@ def read(self, frames=-1, dtype='float64', always_2d=False,
buffer_read, .write
"""
explicit_frames = frames >= 0
if out is not None and (frames < 0 or frames > len(out)):
frames = len(out)
max_frames = self._check_frames(frames, fill_value)
if out is None:
frames = self._check_frames(frames, fill_value)
out = self._create_empty_array(frames, always_2d, dtype)
else:
if frames < 0 or frames > len(out):
frames = len(out)
frames = self._array_io('read', out, frames)
if len(out) > frames:
out = self._create_empty_array(max_frames, always_2d, dtype)
read_frames = self._array_io('read', out, max_frames)
if read_frames < max_frames:
if fill_value is None:
out = out[:frames]
# NB: This can only happen in non-seekable files
assert not self.seekable()
out = out[:read_frames]
else:
out[read_frames:max_frames] = fill_value
if max_frames < len(out):
if explicit_frames or fill_value is None:
out = out[:max_frames]
else:
out[frames:] = fill_value
out[max_frames:] = fill_value
return out

def buffer_read(self, frames=-1, dtype=None):
Expand Down Expand Up @@ -1332,13 +1338,16 @@ def _prepare_read(self, start, stop, frames):
if frames >= 0 and stop is not None:
raise TypeError("Only one of {frames, stop} may be used")

explicit_stop = stop is not None
start, stop, _ = slice(start, stop).indices(self.frames)
if stop < start:
stop = start
if frames < 0:
if explicit_stop:
frames = stop - start
if self.seekable():
self.seek(start, SEEK_SET)
if frames < 0:
frames = self.frames - start
return frames


Expand Down
33 changes: 31 additions & 2 deletions tests/test_pysoundfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,8 +364,7 @@ def test_blocks_with_frames(file_stereo_r):
def test_blocks_with_frames_and_fill_value(file_stereo_r):
blocks = list(
sf.blocks(file_stereo_r, blocksize=2, frames=3, fill_value=0))
last_block = np.row_stack((data_stereo[2:3], np.zeros((1, 2))))
assert_equal_list_of_arrays(blocks, [data_stereo[0:2], last_block])
assert_equal_list_of_arrays(blocks, [data_stereo[0:2], data_stereo[2:3]])


def test_blocks_with_out(file_stereo_r):
Expand Down Expand Up @@ -742,6 +741,36 @@ def test_read_into_out_over_end_with_fill_should_return_full_data_and_write_into
assert out.shape == (4, sf_stereo_r.channels)


def test_read_into_out_with_frames_and_fill_value(sf_stereo_r):
out = np.ones((8, sf_stereo_r.channels), dtype='float64')
data = sf_stereo_r.read(3, out=out, fill_value=0)
assert len(data) == 3
assert np.all(data == data_stereo[:3])
assert np.all(data == out[:3])
assert data.base is out
assert np.all(out[3:] == 1)


def test_read_into_out_over_end_with_frames_and_fill_value(sf_stereo_r):
out = np.ones((8, sf_stereo_r.channels), dtype='float64')
data = sf_stereo_r.read(6, out=out, fill_value=0)
assert len(data) == 6
assert np.all(out[:4] == data_stereo)
assert np.all(out[4:6] == 0)
assert np.all(out[6:8] == 1)
assert np.all(data == out[:6])
assert data.base is out


def test_read_into_out_over_end_with_too_large_frames_and_fill_value(sf_stereo_r):
out = np.ones((8, sf_stereo_r.channels), dtype='float64')
data = sf_stereo_r.read(99, out=out, fill_value=0)
assert len(data) == 8
assert np.all(out[:4] == data_stereo)
assert np.all(out[4:8] == 0)
assert data is out


# -----------------------------------------------------------------------------
# Test buffer read
# -----------------------------------------------------------------------------
Expand Down

0 comments on commit f3a08f2

Please sign in to comment.