Skip to content
Closed
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
126 changes: 126 additions & 0 deletions soundfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,132 @@ def blocks(file, blocksize=None, overlap=0, frames=-1, start=0, stop=None,
dtype, always_2d, fill_value, out):
yield block

def append(data, file, samplerate=None, channels=None, format=None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think channels has to be removed here.
It should be obtained from data, like in the write() function.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for overwrite() and accumulate(), of course.

subtype=None, endian=None, closefd=True):
"""Append data to a sound file.

Parameters
----------
data : array_like
The data to append. Usually two-dimensional (channels x frames),
but one-dimensional `data` can be used for mono files.
Only the data types ``'float64'``, ``'float32'``, ``'int32'``
and ``'int16'`` are supported.

.. note:: The data type of `data` does **not** select the data
type of the written file.
Audio data will be converted to the given `subtype`.

file : str or int or file-like object
The file to write to. See :class:`SoundFile` for details.

Other Parameters
----------------
format, endian, closefd
See :class:`SoundFile`.

Examples
--------

Append 10 frames of random data to a file:

>>> import numpy as np
>>> import soundfile as sf
>>> sf.append(np.random.randn(10, 2), 'stereo_file.wav', 44100, 'PCM_24')

"""
with SoundFile(file, 'r+', samplerate, channels,
subtype, endian, format, closefd) as f:
f.seek(0, SEEK_END)
f.write(data)


def overwrite(data, file, start, samplerate=None, channels=None, format=None,
subtype=None, endian=None, closefd=True):
"""Overwrite data in a sound file.

Parameters
----------
data : array_like
The data to write. Usually two-dimensional (channels x frames),
but one-dimensional `data` can be used for mono files.
Only the data types ``'float64'``, ``'float32'``, ``'int32'``
and ``'int16'`` are supported.

.. note:: The data type of `data` does **not** select the data
type of the written file.
Audio data will be converted to the given `subtype`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like the repetition of those details. I would just refer to the function :func:write``.


file : str or int or file-like object
The file to write to. See :class:`SoundFile` for details.
where : int
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is called start in the signature.

The frame index where overwriting should start.

Other Parameters
----------------
format, endian, closefd
See :class:`SoundFile`.

Examples
--------

Write 10 frames of random data to a file at frame 20:

>>> import numpy as np
>>> import soundfile as sf
>>> sf.overwrite(np.random.randn(10, 2), 'stereo_file.wav', 20, 44100, 'PCM_24')

"""
with SoundFile(file, 'r+', samplerate, channels,
subtype, endian, format, closefd) as f:
f.seek(start)
f.write(data)


def accumulate(data, file, start, samplerate=None, channels=None, format=None,
subtype=None, endian=None, closefd=True):
"""Accumulate data into a sound file.

Parameters
----------
data : array_like
The data to added to the existing data. Usually
two-dimensional (channels x frames), but one-dimensional
`data` can be used for mono files. Only the data types
``'float64'``, ``'float32'``, ``'int32'`` and ``'int16'`` are
supported.

.. note:: The data type of `data` does **not** select the data
type of the written file.
Audio data will be converted to the given `subtype`.

file : str or int or file-like object
The file to write to. See :class:`SoundFile` for details.
where : int
The frame index where accumulation should start.

Other Parameters
----------------
format, endian, closefd
See :class:`SoundFile`.

Examples
--------

Append 10 frames of random data to a file:

>>> import numpy as np
>>> import soundfile as sf
>>> sf.overwrite(np.random.randn(10, 2), 'stereo_file.wav', 10, 44100, 'PCM_24')

"""
with SoundFile(file, 'r+', samplerate, channels,
subtype, endian, format, closefd) as f:
f.seek(start)
existing = f.read(len(data), fill_value=0)
f.seek(start)
f.write(_np.asarray(data)+existing)


def available_formats():
"""Return a dictionary of available major formats.
Expand Down
42 changes: 42 additions & 0 deletions tests/test_pysoundfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,48 @@ def test_blocks_write(sf_stereo_w):
list(sf_stereo_w.blocks(blocksize=2))


# -----------------------------------------------------------------------------
# Test append() function
# -----------------------------------------------------------------------------

# The read() and write() function is tested above, we assume here that
# it is working.

def test_append_function(file_stereo_rplus):
sf.append(data_stereo, file_stereo_rplus)
data, fs = sf.read(filename_new)
assert np.all(data[-len(data_stereo):] == data_stereo)
assert len(data) == 2*len(data_stereo)


# -----------------------------------------------------------------------------
# Test overwrite() function
# -----------------------------------------------------------------------------

# The read() and write() function is tested above, we assume here that
# it is working.

def test_overwrite_function(file_stereo_rplus):
sf.overwrite(data_stereo, file_stereo_rplus, 2)
data, fs = sf.read(filename_new)
assert np.all(data[:2] == data_stereo[:2])
assert np.all(data[-len(data_stereo):] == data_stereo)
assert len(data) == 2+len(data_stereo)


# -----------------------------------------------------------------------------
# Test accumulate() function
# -----------------------------------------------------------------------------

# The read() and write() function is tested above, we assume here that
# it is working.

def test_accumulate_function(file_stereo_rplus):
sf.accumulate(data_stereo, file_stereo_rplus, 0)
data, fs = sf.read(filename_new)
assert np.all(data == data_stereo*2)


# -----------------------------------------------------------------------------
# Test SoundFile.__init__()
# -----------------------------------------------------------------------------
Expand Down