From a5571891d09340a498e4550ab68e9cefd57038cb Mon Sep 17 00:00:00 2001 From: Bastian Bechtold Date: Wed, 11 Mar 2015 18:53:43 +0100 Subject: [PATCH] added append, overwrite, and accumulate function --- soundfile.py | 126 ++++++++++++++++++++++++++++++++++++++ tests/test_pysoundfile.py | 42 +++++++++++++ 2 files changed, 168 insertions(+) diff --git a/soundfile.py b/soundfile.py index cddf40a..5840f9f 100644 --- a/soundfile.py +++ b/soundfile.py @@ -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, + 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`. + + file : str or int or file-like object + The file to write to. See :class:`SoundFile` for details. + where : int + 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. diff --git a/tests/test_pysoundfile.py b/tests/test_pysoundfile.py index 04101d5..6de1c09 100644 --- a/tests/test_pysoundfile.py +++ b/tests/test_pysoundfile.py @@ -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__() # -----------------------------------------------------------------------------