Skip to content

Commit

Permalink
Remove channels_first argument
Browse files Browse the repository at this point in the history
  • Loading branch information
mgeier committed Apr 29, 2014
1 parent 14a8fd1 commit 12294be
Showing 1 changed file with 30 additions and 62 deletions.
92 changes: 30 additions & 62 deletions pysoundfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -586,34 +586,28 @@ def seek(self, frames, whence=SEEK_SET, which=None):
raise ValueError("Invalid which: %s" % repr(which))
return _snd.sf_seek(self._file, frames, whence)

def _create_out_array(self, frames, dtype, channels_first, always_2d):
def _create_out_array(self, frames, dtype, always_2d):
# Helper function for read()
if channels_first:
order = 'C'
if self.channels == 1 and not always_2d:
shape = frames,
else:
shape = frames, self.channels
if self.channels == 1 and not always_2d:
shape = frames,
else:
order = 'F'
shape = self.channels, frames
return _np.empty(shape, dtype, order)
shape = frames, self.channels
return _np.empty(shape, dtype, order='C')

def read(self, frames=-1, dtype='float64', channels_first=True,
always_2d=True, out=None, fill_value=None):
def read(self, frames=-1, dtype='float64', always_2d=True,
out=None, fill_value=None):
"""Read a number of frames from the file.
Reads the given number of frames in the given data format from
the current read position. This also advances the read
position by the same number of frames.
Use frames=-1 to read until the end of the file.
By default, a two-dimensional NumPy array is returned, where the
channels are stored along the first dimension, i.e. as columns.
Use channels_first=False to store the channels along the second
dimension, i.e. as rows. A two-dimensional array is returned
even if the sound file has only one channel. Use
always_2d=False to return a one-dimensional array in this case.
A two-dimensional NumPy array is returned, where the channels
are stored along the first dimension, i.e. as columns.
A two-dimensional array is returned even if the sound file has
only one channel. Use always_2d=False to return a
one-dimensional array in this case.
If out is specified, the data is written into the given NumPy
array. In this case, the arguments frames, dtype and always_2d
Expand All @@ -638,14 +632,9 @@ def read(self, frames=-1, dtype='float64', channels_first=True,
repr([dt.name for dt in _ffi_types]))

if out is not None:
frames = _check_frames_and_channels(
out, "out", channels_first, channels=self.channels)
if channels_first and not out.flags.c_contiguous:
raise ValueError(
"out must be C-contiguous for channels_first=True")
if not channels_first and not out.flags.f_contiguous:
raise ValueError(
"out must be Fortran-contiguous for channels_first=False")
frames = _check_frames_and_channels(out, "out", self.channels)
if not out.flags.c_contiguous:
raise ValueError("out must be C-contiguous")

max_frames = self.frames - self.seek(0, SEEK_CUR, 'r')
if frames < 0:
Expand All @@ -655,8 +644,7 @@ def read(self, frames=-1, dtype='float64', channels_first=True,
valid_frames = max_frames

if out is None:
out = self._create_out_array(frames, dtype,
channels_first, always_2d)
out = self._create_out_array(frames, dtype, always_2d)

assert out.dtype.itemsize == _ffi.sizeof(ffi_type)

Expand All @@ -667,22 +655,14 @@ def read(self, frames=-1, dtype='float64', channels_first=True,
assert read == valid_frames

if frames > valid_frames:
def multichannel_slice(start, stop):
"""Return a slice of frames, considering channels_first"""
if channels_first:
idx = slice(start, stop)
else:
idx = Ellipsis, slice(start, stop)
return idx

if fill_value is None:
out = out[multichannel_slice(None, valid_frames)]
out = out[:valid_frames]
else:
out[multichannel_slice(valid_frames, None)] = fill_value
out[valid_frames:] = fill_value

return out

def write(self, data, channels_first=True):
def write(self, data):
"""Write a number of frames to the file.
Writes a number of frames to the current write position in the
Expand All @@ -691,34 +671,24 @@ def write(self, data, channels_first=True):
The data must be provided as a (frames x channels) NumPy
array or as one-dimensional array for mono signals.
Use channels_first=False if you want to provide a (channels x
frames) array.
"""
self._check_if_closed()
if self.mode == 'r':
raise RuntimeError("Cannot write to file opened in read mode")

if channels_first:
# no copy is made if data has already the correct memory layout:
data = _np.ascontiguousarray(data)
else:
# this shouldn't make a copy either if already in Fortran order:
data = _np.asfortranarray(data)
if data.ndim != 2:
raise ValueError(
"data.ndim must be 2 for channels_first=False")
# no copy is made if data has already the correct memory layout:
data = _np.ascontiguousarray(data)

try:
ffi_type = _ffi_types[data.dtype]
except KeyError:
raise ValueError("data.dtype must be one of %s" %
repr([dt.name for dt in _ffi_types]))

frames = _check_frames_and_channels(
data, "data", channels_first, channels=self.channels)
frames = _check_frames_and_channels(data, "data", self.channels)

assert data.flags['C_CONTIGUOUS' if channels_first else 'F_CONTIGUOUS']
assert data.flags.c_contiguous
assert data.dtype.itemsize == _ffi.sizeof(ffi_type)

writer = getattr(_snd, 'sf_writef_' + ffi_type)
Expand All @@ -732,12 +702,12 @@ def write(self, data, channels_first=True):
self.seek(curr, SEEK_SET, 'w')


def _check_frames_and_channels(array, name, channels_first, channels=None):
def _check_frames_and_channels(array, name, channels=None):
# Return frames and channels for a given array. If channels is given (and
# if the number of channels matches), return only frames.
if array.ndim not in (1, 2):
raise ValueError("%s must be one- or two-dimensional" % repr(name))
frames = array.shape[not channels_first]
frames = array.shape[0]
if frames == 0:
raise ValueError("%s is empty" % repr(name))

Expand Down Expand Up @@ -773,8 +743,8 @@ def read(file, frames=-1, start=None, stop=None, **kwargs):
Both start and stop accept negative indices to specify positions
relative to the end of the file.
The keyword arguments out, dtype, fill_value, channels_first and
always_2d are forwarded to SoundFile.read().
The keyword arguments out, dtype, fill_value and always_2d are
forwarded to SoundFile.read().
All further arguments are forwarded to SoundFile.__init__().
"""
Expand Down Expand Up @@ -804,8 +774,7 @@ def write(data, file, sample_rate, *args, **kwargs):
If file exists, it will be overwritten!
If data is one-dimensional, a mono file is written.
For two-dimensional data, the columns are interpreted as channels by
default. Use channels_first=False to interpret the rows as channels.
For two-dimensional data, the columns are interpreted as channels.
All further arguments are forwarded to SoundFile.__init__().
Example usage:
Expand All @@ -815,10 +784,9 @@ def write(data, file, sample_rate, *args, **kwargs):
"""
data = _np.asarray(data)
channels_first = kwargs.pop('channels_first', True)
_, channels = _check_frames_and_channels(data, "data", channels_first)
_, channels = _check_frames_and_channels(data, "data")
with SoundFile(file, 'w', sample_rate, channels, *args, **kwargs) as f:
f.write(data, channels_first=channels_first)
f.write(data)


def default_subtype(format):
Expand Down

0 comments on commit 12294be

Please sign in to comment.