Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove "out" from blocks() #213

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 33 additions & 65 deletions soundfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,8 +313,8 @@ def write(file, data, samplerate, subtype=None, endian=None, format=None,
f.write(data)


def blocks(file, blocksize=None, overlap=0, frames=-1, start=0, stop=None,
dtype='float64', always_2d=False, fill_value=None, out=None,
def blocks(file, blocksize, overlap=0, frames=-1, start=0, stop=None,
dtype='float64', always_2d=False, fill_value=None,
samplerate=None, channels=None,
format=None, subtype=None, endian=None, closefd=True):
"""Return a generator for block-wise reading.
Expand All @@ -334,26 +334,26 @@ def blocks(file, blocksize=None, overlap=0, frames=-1, start=0, stop=None,
The file to read from. See :class:`SoundFile` for details.
blocksize : int
The number of frames to read per block.
Either this or `out` must be given.
overlap : int, optional
The number of frames to rewind between each block.

Yields
------
numpy.ndarray or type(out)
numpy.ndarray
Blocks of audio data.
If `out` was given, and the requested frames are not an integer
multiple of the length of `out`, and no `fill_value` was given,
the last block will be a smaller view into `out`.

Other Parameters
----------------
frames, start, stop
frames
See :meth:`SoundFile.blocks`.
start, stop
See :func:`read`.
dtype : {'float64', 'float32', 'int32', 'int16'}, optional
dtype
See :func:`read`.
always_2d, fill_value, out
always_2d
See :func:`read`.
fill_value
See :meth:`SoundFile.blocks`.
samplerate, channels, format, subtype, endian, closefd
See :class:`SoundFile`.

Expand All @@ -368,7 +368,7 @@ def blocks(file, blocksize=None, overlap=0, frames=-1, start=0, stop=None,
subtype, endian, format, closefd) as f:
frames = f._prepare_read(start, stop, frames)
for block in f.blocks(blocksize, overlap, frames,
dtype, always_2d, fill_value, out):
dtype, always_2d, fill_value):
yield block


Expand Down Expand Up @@ -1012,8 +1012,8 @@ def buffer_write(self, data, dtype):
assert written == frames
self._update_frames(written)

def blocks(self, blocksize=None, overlap=0, frames=-1, dtype='float64',
always_2d=False, fill_value=None, out=None):
def blocks(self, blocksize, overlap=0, frames=-1, dtype='float64',
always_2d=False, fill_value=None):
"""Return a generator for block-wise reading.

By default, the generator yields blocks of the given
Expand All @@ -1023,36 +1023,29 @@ def blocks(self, blocksize=None, overlap=0, frames=-1, dtype='float64',
Parameters
----------
blocksize : int
The number of frames to read per block. Either this or `out`
must be given.
The number of frames to read per block.
overlap : int, optional
The number of frames to rewind between each block.
frames : int, optional
The number of frames to read.
If ``frames < 0``, the file is read until the end.
If not specified, or if larger than the remaining number of
frames, the file is read until the end.
dtype : {'float64', 'float32', 'int32', 'int16'}, optional
See :meth:`.read`.
fill_value : float, optional
The last generated block may be smaller than *blocksize*.
If *fill_value* is specified, it will be used to fill the
remaining space and all blocks will have the same size.

Yields
------
numpy.ndarray or type(out)
numpy.ndarray
Blocks of audio data.
If `out` was given, and the requested frames are not an
integer multiple of the length of `out`, and no
`fill_value` was given, the last block will be a smaller
view into `out`.


Other Parameters
----------------
always_2d, fill_value, out
See :meth:`.read`.
fill_value : float, optional
always_2d
See :meth:`.read`.
out : numpy.ndarray or subclass, optional
If `out` is specified, the data is written into the given
array instead of creating a new array. In this case, the
arguments `dtype` and `always_2d` are silently ignored!

Examples
--------
Expand All @@ -1062,47 +1055,22 @@ def blocks(self, blocksize=None, overlap=0, frames=-1, dtype='float64',
>>> pass # do something with 'block'

"""
import numpy as np

if 'r' not in self.mode and '+' not in self.mode:
raise RuntimeError("blocks() is not allowed in write-only mode")

if out is None:
if blocksize is None:
raise TypeError("One of {blocksize, out} must be specified")
out = self._create_empty_array(blocksize, always_2d, dtype)
copy_out = True
else:
if blocksize is not None:
raise TypeError(
"Only one of {blocksize, out} may be specified")
blocksize = len(out)
copy_out = False

overlap_memory = None
frames = self._check_frames(frames, fill_value)
frames = self._check_frames(frames, fill_value=None)
buffer = self._create_empty_array(blocksize, always_2d, dtype)
read_target = buffer
while frames > 0:
if overlap_memory is None:
output_offset = 0
else:
output_offset = len(overlap_memory)
out[:output_offset] = overlap_memory

toread = min(blocksize - output_offset, frames)
self.read(toread, dtype, always_2d, fill_value, out[output_offset:])

read_result = self.read(frames, fill_value=fill_value,
out=read_target)
out_frames = blocksize - len(read_target) + len(read_result)
yield buffer[:out_frames].copy(order='K')
frames -= len(read_result)
if overlap:
if overlap_memory is None:
overlap_memory = np.copy(out[-overlap:])
else:
overlap_memory[:] = out[-overlap:]

if blocksize > frames + overlap and fill_value is None:
block = out[:frames + overlap]
else:
block = out
yield np.copy(block) if copy_out else block
frames -= toread
# Copy the end of the block to the beginning of the next
buffer[:overlap] = buffer[-overlap:]
read_target = buffer[overlap:]

def truncate(self, frames=None):
"""Truncate the file to a given number of frames.
Expand Down
2 changes: 1 addition & 1 deletion tests/test_argspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,4 @@ def test_order_of_blocks_arguments():
meth_args = list(signature(sf.SoundFile.blocks).parameters)[1:]
meth_args[3:3] = ['start', 'stop']
func_args = list(signature(sf.blocks).parameters)
assert func_args[:10] == ['file'] + meth_args
assert func_args[:9] == ['file'] + meth_args
33 changes: 10 additions & 23 deletions tests/test_pysoundfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,36 +361,23 @@ def test_blocks_with_frames(file_stereo_r):
assert_equal_list_of_arrays(blocks, [data_stereo[0:2], data_stereo[2:3]])


def test_blocks_with_too_large_frames(file_stereo_r):
blocks = list(sf.blocks(file_stereo_r, blocksize=2, frames=99))
assert_equal_list_of_arrays(blocks, [data_stereo[0:2], data_stereo[2:4]])


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])


def test_blocks_with_out(file_stereo_r):
out = np.empty((3, 2))
blocks = list(sf.blocks(file_stereo_r, out=out))
assert blocks[0] is out
# First frame was overwritten by second block:
assert np.all(blocks[0] == data_stereo[[3, 1, 2]])

assert blocks[1].base is out
assert np.all(blocks[1] == data_stereo[[3]])

with pytest.raises(TypeError):
list(sf.blocks(filename_stereo, blocksize=3, out=out))


def test_blocks_inplace_modification(file_stereo_r):
out = np.empty((3, 2))
blocks = []
for block in sf.blocks(file_stereo_r, out=out, overlap=1):
blocks.append(np.copy(block))
block *= 2

expected_blocks = [data_stereo[0:3], data_stereo[2:5]]
assert_equal_list_of_arrays(blocks, expected_blocks)
def test_blocks_with_too_large_frames_and_fill_value(file_stereo_r):
blocks = list(
sf.blocks(file_stereo_r, blocksize=3, frames=3000, fill_value=0))
last_block = np.row_stack((data_stereo[3:4], np.zeros((2, 2))))
assert_equal_list_of_arrays(blocks, [data_stereo[0:3], last_block])


def test_blocks_mono():
Expand Down