[SCM] pysoundfile/master: New upstream version 0.10.0
umlaeute at users.alioth.debian.org
umlaeute at users.alioth.debian.org
Thu Nov 23 12:52:25 UTC 2017
The following commit has been merged in the master branch:
commit cffd49fec17fe3e105c08e193ed41cdfb110706a
Author: IOhannes m zmölnig <zmoelnig at umlautS.umlaeute.mur.at>
Date: Thu Nov 23 12:11:58 2017 +0100
New upstream version 0.10.0
diff --git a/.gitignore b/.gitignore
index e054ea1..76bda8d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,6 @@ dist/
build/
PySoundFile.egg-info/
.eggs/
+*.egg-info/
+.cache/
+.vscode/
diff --git a/MANIFEST.in b/MANIFEST.in
index 90a504a..dd0e55e 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,3 +1,4 @@
include LICENSE
+include soundfile_build.py
recursive-include doc *.py *.rst *.txt Makefile
recursive-include tests *.py *.wav *.raw
diff --git a/README.rst b/README.rst
index 2c1312e..3ba7634 100644
--- a/README.rst
+++ b/README.rst
@@ -1,27 +1,27 @@
-PySoundFile
-===========
+SoundFile
+=========
-`PySoundFile <https://github.com/bastibe/PySoundFile>`__ is an audio
+`SoundFile <https://github.com/bastibe/SoundFile>`__ is an audio
library based on libsndfile, CFFI and NumPy. Full documentation is
available on http://pysoundfile.readthedocs.org/.
-PySoundFile can read and write sound files. File reading/writing is
+SoundFile can read and write sound files. File reading/writing is
supported through `libsndfile <http://www.mega-nerd.com/libsndfile/>`__,
which is a free, cross-platform, open-source (LGPL) library for reading
and writing many different sampled sound file formats that runs on many
platforms including Windows, OS X, and Unix. It is accessed through
`CFFI <http://cffi.readthedocs.org/>`__, which is a foreign function
interface for Python calling C code. CFFI is supported for CPython 2.6+,
-3.x and PyPy 2.0+. PySoundFile represents audio data as NumPy arrays.
+3.x and PyPy 2.0+. SoundFile represents audio data as NumPy arrays.
-| PySoundFile is BSD licensed (BSD 3-Clause License).
+| SoundFile is BSD licensed (BSD 3-Clause License).
| (c) 2013, Bastian Bechtold
Breaking Changes
----------------
-PySoundFile has evolved rapidly during the last few releases. Most
+SoundFile has evolved rapidly during the last few releases. Most
notably, we changed the import name from ``import pysoundfile`` to
``import soundfile`` in 0.7. In 0.6, we cleaned up many small
inconsistencies, particularly in the the ordering and naming of
@@ -38,41 +38,40 @@ methods to ``dtype``, using the Numpy ``dtype`` notation. The old
Installation
------------
-PySoundFile depends on the Python packages CFFI and NumPy, and the
+SoundFile depends on the Python packages CFFI and NumPy, and the
system library libsndfile.
-To install the Python dependencies, I recommend using the `Anaconda
-<http://continuum.io/downloads>`__ distribution of Python 3. This will
-come with all dependencies pre-installed. To install the dependencies
-manually, you can use the ``conda`` package manager, which will
-install all dependencies using ``conda install cffi numpy`` (conda is
-also available independently of Anaconda with ``pip install conda;
-conda init``).
+In a modern Python, you can use ``pip install soundfile`` to download
+and install the latest release of SoundFile and its dependencies.
+On Windows and OS X, this will also install the library libsndfile.
+On Linux, you need to install libsndfile using your distribution's
+package manager, for example ``sudo apt-get install libsndfile1``.
-With CFFI and NumPy installed, you can use ``pip install pysoundfile``
-to download and install the latest release of PySoundFile. On Windows
-and OS X, this will also install the library libsndfile. On Linux, you
-need to install libsndfile using your distribution's package manager,
-for example ``sudo apt-get install libsndfile1``.
+If you are running on an unusual platform or if you are using an older
+version of Python, you might need to install NumPy and CFFI separately,
+for example using the Anaconda_ package manager or the `Unofficial Windows
+Binaries for Python Extension Packages <http://www.lfd.uci.edu/~gohlke/pythonlibs/>`_.
+
+.. _Anaconda: https://www.continuum.io/downloads
Read/Write Functions
--------------------
Data can be written to the file using `soundfile.write()`, or read from
-the file using `soundfile.read()`. PySoundFile can open all file formats
+the file using `soundfile.read()`. SoundFile can open all file formats
that `libsndfile supports
<http://www.mega-nerd.com/libsndfile/#Features>`__, for example WAV,
-FLAC, OGG and MAT files.
+FLAC, OGG and MAT files (see `Known Issues <https://github.com/bastibe/SoundFile#known-issues>`__ below about writing OGG files).
Here is an example for a program that reads a wave file and copies it
-into an ogg-vorbis file:
+into an FLAC file:
.. code:: python
import soundfile as sf
data, samplerate = sf.read('existing_file.wav')
- sf.write('new_file.ogg', data, samplerate)
+ sf.write('new_file.flac', data, samplerate)
Block Processing
----------------
@@ -106,8 +105,8 @@ context manager to close the file explicitly:
import soundfile as sf
- with sf.SoundFile('myfile.wav', 'rw') as f:
- while f.tell() < len(f):
+ with sf.SoundFile('myfile.wav', 'r+') as f:
+ while f.tell() < f.frames:
pos = f.tell()
data = f.read(1024)
f.seek(pos)
@@ -167,6 +166,11 @@ For Python 2.x support, replace the third line with:
from urllib2 import urlopen
+Known Issues
+------------
+
+Writing to OGG files can result in empty files with certain versions of libsndfile. See `#130 <https://github.com/bastibe/SoundFile/issues/130>`__ for news on this issue.
+
News
----
@@ -246,3 +250,14 @@ News
- Adds official support for Python 3.6
And some minor bug fixes.
+
+2017-11-12 V0.10.0 Bastian Bechtold:
+ Thank you, Matthias Geier, Toni Barth, Jon Peirce, Till Hoffmann,
+ and Tomas Garcia, for contributions to this release.
+
+ - Should now work with cx_freeze.
+ - Several documentation fixes in the README.
+ - Removes deprecated ``ctype`` argument in favor of ``dtype`` in ``buffer_*()``.
+ - Adds ``SoundFile.frames`` in favor of now-deprecated ``__len__()``.
+ - Improves performance of ``blocks`` and ``SoundFile.blocks()``.
+ - Improves import time by using CFFI's out of line mode.
diff --git a/doc/conf.py b/doc/conf.py
index 0db0bc9..20c7053 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -277,5 +277,5 @@ napoleon_include_special_with_doc = False
# Fake imports to avoid actually loading NumPy and libsndfile
import fake_numpy
sys.modules['numpy'] = sys.modules['fake_numpy']
-import fake_cffi
-sys.modules['cffi'] = sys.modules['fake_cffi']
+import fake__soundfile
+sys.modules['_soundfile'] = sys.modules['fake__soundfile']
diff --git a/doc/fake_cffi.py b/doc/fake__soundfile.py
similarity index 82%
rename from doc/fake_cffi.py
rename to doc/fake__soundfile.py
index ea1cc30..4887706 100644
--- a/doc/fake_cffi.py
+++ b/doc/fake__soundfile.py
@@ -1,10 +1,7 @@
"""Mock module for Sphinx autodoc."""
-class FFI(object):
-
- def cdef(self, _):
- pass
+class ffi(object):
def dlopen(self, _):
return self
@@ -16,3 +13,6 @@ class FFI(object):
return NotImplemented
SFC_GET_FORMAT_INFO = NotImplemented
+
+
+ffi = ffi()
diff --git a/setup.py b/setup.py
index cfd57f1..6bd8632 100644
--- a/setup.py
+++ b/setup.py
@@ -87,7 +87,7 @@ else:
setup(
name='SoundFile',
- version='0.9.0',
+ version='0.10.0',
description='An audio library based on libsndfile, CFFI and NumPy',
author='Bastian Bechtold',
author_email='basti at bastibe.de',
@@ -98,11 +98,13 @@ setup(
package_data=package_data,
zip_safe=zip_safe,
license='BSD 3-Clause License',
- install_requires=['cffi>=0.6'],
+ setup_requires=["cffi>=1.0"],
+ install_requires=['cffi>=1.0'],
+ cffi_modules=["soundfile_build.py:ffibuilder"],
extras_require={'numpy': ['numpy']},
platforms='any',
classifiers=[
- 'Development Status :: 3 - Alpha',
+ 'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'Intended Audience :: Science/Research',
'License :: OSI Approved :: BSD License',
diff --git a/soundfile.py b/soundfile.py
index 4e62296..5b4bb85 100644
--- a/soundfile.py
+++ b/soundfile.py
@@ -1,4 +1,4 @@
-"""PySoundFile is an audio library based on libsndfile, CFFI and NumPy.
+"""SoundFile is an audio library based on libsndfile, CFFI and NumPy.
Sound files can be read or written directly using the functions
:func:`read` and :func:`write`.
@@ -8,146 +8,19 @@ Alternatively, sound files can be opened as :class:`SoundFile` objects.
For further information, see http://pysoundfile.readthedocs.org/.
"""
-__version__ = "0.9.0"
+__version__ = "0.10.0"
import os as _os
import sys as _sys
-from cffi import FFI as _FFI
from os import SEEK_SET, SEEK_CUR, SEEK_END
+from ctypes.util import find_library as _find_library
+from _soundfile import ffi as _ffi
try:
_unicode = unicode # doesn't exist in Python 3.x
except NameError:
_unicode = str
-_ffi = _FFI()
-_ffi.cdef("""
-enum
-{
- SF_FORMAT_SUBMASK = 0x0000FFFF,
- SF_FORMAT_TYPEMASK = 0x0FFF0000,
- SF_FORMAT_ENDMASK = 0x30000000
-} ;
-
-enum
-{
- SFC_GET_LIB_VERSION = 0x1000,
- SFC_GET_LOG_INFO = 0x1001,
- SFC_GET_FORMAT_INFO = 0x1028,
-
- SFC_GET_FORMAT_MAJOR_COUNT = 0x1030,
- SFC_GET_FORMAT_MAJOR = 0x1031,
- SFC_GET_FORMAT_SUBTYPE_COUNT = 0x1032,
- SFC_GET_FORMAT_SUBTYPE = 0x1033,
- SFC_FILE_TRUNCATE = 0x1080,
- SFC_SET_CLIPPING = 0x10C0,
-
- SFC_SET_SCALE_FLOAT_INT_READ = 0x1014,
- SFC_SET_SCALE_INT_FLOAT_WRITE = 0x1015,
-} ;
-
-enum
-{
- SF_FALSE = 0,
- SF_TRUE = 1,
-
- /* Modes for opening files. */
- SFM_READ = 0x10,
- SFM_WRITE = 0x20,
- SFM_RDWR = 0x30,
-} ;
-
-typedef int64_t sf_count_t ;
-
-typedef struct SNDFILE_tag SNDFILE ;
-
-typedef struct SF_INFO
-{
- sf_count_t frames ; /* Used to be called samples. Changed to avoid confusion. */
- int samplerate ;
- int channels ;
- int format ;
- int sections ;
- int seekable ;
-} SF_INFO ;
-
-SNDFILE* sf_open (const char *path, int mode, SF_INFO *sfinfo) ;
-int sf_format_check (const SF_INFO *info) ;
-
-sf_count_t sf_seek (SNDFILE *sndfile, sf_count_t frames, int whence) ;
-
-int sf_command (SNDFILE *sndfile, int cmd, void *data, int datasize) ;
-
-int sf_error (SNDFILE *sndfile) ;
-const char* sf_strerror (SNDFILE *sndfile) ;
-const char* sf_error_number (int errnum) ;
-
-int sf_perror (SNDFILE *sndfile) ;
-int sf_error_str (SNDFILE *sndfile, char* str, size_t len) ;
-
-int sf_close (SNDFILE *sndfile) ;
-void sf_write_sync (SNDFILE *sndfile) ;
-
-sf_count_t sf_read_short (SNDFILE *sndfile, short *ptr, sf_count_t items) ;
-sf_count_t sf_read_int (SNDFILE *sndfile, int *ptr, sf_count_t items) ;
-sf_count_t sf_read_float (SNDFILE *sndfile, float *ptr, sf_count_t items) ;
-sf_count_t sf_read_double (SNDFILE *sndfile, double *ptr, sf_count_t items) ;
-
-/* Note: Data ptr argument types are declared as void* here in order to
- avoid an implicit cast warning. (gh183). */
-sf_count_t sf_readf_short (SNDFILE *sndfile, void *ptr, sf_count_t frames) ;
-sf_count_t sf_readf_int (SNDFILE *sndfile, void *ptr, sf_count_t frames) ;
-sf_count_t sf_readf_float (SNDFILE *sndfile, void *ptr, sf_count_t frames) ;
-sf_count_t sf_readf_double (SNDFILE *sndfile, void *ptr, sf_count_t frames) ;
-
-sf_count_t sf_write_short (SNDFILE *sndfile, short *ptr, sf_count_t items) ;
-sf_count_t sf_write_int (SNDFILE *sndfile, int *ptr, sf_count_t items) ;
-sf_count_t sf_write_float (SNDFILE *sndfile, float *ptr, sf_count_t items) ;
-sf_count_t sf_write_double (SNDFILE *sndfile, double *ptr, sf_count_t items) ;
-
-/* Note: The argument types were changed to void* in order to allow
- writing bytes in SoundFile.buffer_write() */
-sf_count_t sf_writef_short (SNDFILE *sndfile, void *ptr, sf_count_t frames) ;
-sf_count_t sf_writef_int (SNDFILE *sndfile, void *ptr, sf_count_t frames) ;
-sf_count_t sf_writef_float (SNDFILE *sndfile, void *ptr, sf_count_t frames) ;
-sf_count_t sf_writef_double (SNDFILE *sndfile, void *ptr, sf_count_t frames) ;
-
-sf_count_t sf_read_raw (SNDFILE *sndfile, void *ptr, sf_count_t bytes) ;
-sf_count_t sf_write_raw (SNDFILE *sndfile, void *ptr, sf_count_t bytes) ;
-
-const char* sf_get_string (SNDFILE *sndfile, int str_type) ;
-int sf_set_string (SNDFILE *sndfile, int str_type, const char* str) ;
-const char * sf_version_string (void) ;
-
-typedef sf_count_t (*sf_vio_get_filelen) (void *user_data) ;
-typedef sf_count_t (*sf_vio_seek) (sf_count_t offset, int whence, void *user_data) ;
-typedef sf_count_t (*sf_vio_read) (void *ptr, sf_count_t count, void *user_data) ;
-typedef sf_count_t (*sf_vio_write) (const void *ptr, sf_count_t count, void *user_data) ;
-typedef sf_count_t (*sf_vio_tell) (void *user_data) ;
-
-typedef struct SF_VIRTUAL_IO
-{ sf_count_t (*get_filelen) (void *user_data) ;
- sf_count_t (*seek) (sf_count_t offset, int whence, void *user_data) ;
- sf_count_t (*read) (void *ptr, sf_count_t count, void *user_data) ;
- sf_count_t (*write) (const void *ptr, sf_count_t count, void *user_data) ;
- sf_count_t (*tell) (void *user_data) ;
-} SF_VIRTUAL_IO ;
-
-SNDFILE* sf_open_virtual (SF_VIRTUAL_IO *sfvirtual, int mode, SF_INFO *sfinfo, void *user_data) ;
-SNDFILE* sf_open_fd (int fd, int mode, SF_INFO *sfinfo, int close_desc) ;
-
-typedef struct SF_FORMAT_INFO
-{
- int format ;
- const char* name ;
- const char* extension ;
-} SF_FORMAT_INFO ;
-""")
-
-if _sys.platform == 'win32':
- _ffi.cdef("""
- SNDFILE* sf_wchar_open (LPCWSTR wpath, int mode, SF_INFO *sfinfo) ;
- """)
_str_types = {
'title': 0x01,
@@ -264,7 +137,7 @@ _ffi_types = {
}
try:
- _snd = _ffi.dlopen('sndfile')
+ _snd = _ffi.dlopen(_find_library('sndfile'))
except OSError:
if _sys.platform == 'darwin':
_libname = 'libsndfile.dylib'
@@ -273,9 +146,18 @@ except OSError:
_libname = 'libsndfile' + _architecture()[0] + '.dll'
else:
raise
+
+ # hack for packaging tools like cx_Freeze, which
+ # compress all scripts into a zip file
+ # which causes __file__ to be inside this zip file
+
+ _path = _os.path.dirname(_os.path.abspath(__file__))
+
+ while not _os.path.isdir(_path):
+ _path = _os.path.abspath(_os.path.join(_path, '..'))
+
_snd = _ffi.dlopen(_os.path.join(
- _os.path.dirname(_os.path.abspath(__file__)),
- '_soundfile_data', _libname))
+ _path, '_soundfile_data', _libname))
__libsndfile_version__ = _ffi.string(_snd.sf_version_string()).decode('utf-8', 'replace')
if __libsndfile_version__.startswith('libsndfile-'):
@@ -323,8 +205,7 @@ def read(file, frames=-1, start=0, stop=None, dtype='float64', always_2d=False,
Returns
-------
audiodata : numpy.ndarray or type(out)
- A two-dimensional NumPy array is returned, where the channels
- are stored along the first dimension, i.e. as columns.
+ A two-dimensional (frames x channels) NumPy array is returned.
If the sound file has only one channel, a one-dimensional array
is returned. Use ``always_2d=True`` to return a two-dimensional
array anyway.
@@ -387,7 +268,7 @@ def write(file, data, samplerate, subtype=None, endian=None, format=None,
file : str or int or file-like object
The file to write to. See :class:`SoundFile` for details.
data : array_like
- The data to write. Usually two-dimensional (channels x frames),
+ The data to write. Usually two-dimensional (frames x channels),
but one-dimensional `data` can be used for mono files.
Only the data types ``'float64'``, ``'float32'``, ``'int32'``
and ``'int16'`` are supported.
@@ -500,8 +381,8 @@ class _SoundFileInfo(object):
self.name = f.name
self.samplerate = f.samplerate
self.channels = f.channels
- self.frames = len(f)
- self.duration = self.frames/f.samplerate
+ self.frames = f.frames
+ self.duration = float(self.frames)/f.samplerate
self.format = f.format
self.subtype = f.subtype
self.endian = f.endian
@@ -750,6 +631,8 @@ class SoundFile(object):
"""The open mode the sound file was opened with."""
samplerate = property(lambda self: self._info.samplerate)
"""The sample rate of the sound file."""
+ frames = property(lambda self: self._info.frames)
+ """The number of frames in the sound file."""
channels = property(lambda self: self._info.channels)
"""The number of channels in the sound file."""
format = property(
@@ -823,8 +706,20 @@ class SoundFile(object):
"'SoundFile' object has no attribute {0!r}".format(name))
def __len__(self):
+ # Note: This is deprecated and will be removed at some point,
+ # see https://github.com/bastibe/SoundFile/issues/199
return self._info.frames
+ def __bool__(self):
+ # Note: This is temporary until __len__ is removed, afterwards it
+ # can (and should) be removed without change of behavior
+ return True
+
+ def __nonzero__(self):
+ # Note: This is only for compatibility with Python 2 and it shall be
+ # removed at the same time as __bool__().
+ return self.__bool__()
+
def seekable(self):
"""Return True if the file supports seeking."""
return self._info.seekable == _snd.SF_TRUE
@@ -906,9 +801,8 @@ class SoundFile(object):
Returns
-------
audiodata : numpy.ndarray or type(out)
- A two-dimensional NumPy array is returned, where the
- channels are stored along the first dimension, i.e. as
- columns. If the sound file has only one channel, a
+ A two-dimensional NumPy (frames x channels) array is
+ returned. If the sound file has only one channel, a
one-dimensional array is returned. Use ``always_2d=True``
to return a two-dimensional array anyway.
@@ -970,7 +864,7 @@ class SoundFile(object):
out[frames:] = fill_value
return out
- def buffer_read(self, frames=-1, ctype=None, dtype=None):
+ def buffer_read(self, frames=-1, dtype=None):
"""Read from the file and return data as buffer object.
Reads the given number of `frames` in the given data format
@@ -999,14 +893,13 @@ class SoundFile(object):
"""
frames = self._check_frames(frames, fill_value=None)
- dtype = self._ctype_is_deprecated(ctype, dtype)
ctype = self._check_dtype(dtype)
cdata = _ffi.new(ctype + '[]', frames * self.channels)
read_frames = self._cdata_io('read', cdata, ctype, frames)
assert read_frames == frames
return _ffi.buffer(cdata)
- def buffer_read_into(self, buffer, ctype=None, dtype=None):
+ def buffer_read_into(self, buffer, dtype):
"""Read from the file into a given buffer object.
Fills the given `buffer` with frames in the given data format
@@ -1034,7 +927,6 @@ class SoundFile(object):
buffer_read, .read
"""
- dtype = self._ctype_is_deprecated(ctype, dtype)
ctype = self._check_dtype(dtype)
cdata, frames = self._check_buffer(buffer, ctype)
frames = self._cdata_io('read', cdata, ctype, frames)
@@ -1056,8 +948,8 @@ class SoundFile(object):
Parameters
----------
data : array_like
- The data to write. Usually two-dimensional (channels x
- frames), but one-dimensional `data` can be used for mono
+ The data to write. Usually two-dimensional (frames x
+ channels), but one-dimensional `data` can be used for mono
files. Only the data types ``'float64'``, ``'float32'``,
``'int32'`` and ``'int16'`` are supported.
@@ -1091,9 +983,9 @@ class SoundFile(object):
data = np.ascontiguousarray(data)
written = self._array_io('write', data, len(data))
assert written == len(data)
- self._update_len(written)
+ self._update_frames(written)
- def buffer_write(self, data, ctype=None, dtype=None):
+ def buffer_write(self, data, dtype):
"""Write audio data from a buffer/bytes object to the file.
Writes the contents of `data` to the file at the current
@@ -1114,12 +1006,11 @@ class SoundFile(object):
.write, buffer_read
"""
- dtype = self._ctype_is_deprecated(ctype, dtype)
ctype = self._check_dtype(dtype)
cdata, frames = self._check_buffer(data, ctype)
written = self._cdata_io('write', cdata, ctype, frames)
assert written == frames
- self._update_len(written)
+ self._update_frames(written)
def blocks(self, blocksize=None, overlap=0, frames=-1, dtype='float64',
always_2d=False, fill_value=None, out=None):
@@ -1138,7 +1029,7 @@ class SoundFile(object):
The number of frames to rewind between each block.
frames : int, optional
The number of frames to read.
- If ``frames < 1``, the file is read until the end.
+ If ``frames < 0``, the file is read until the end.
dtype : {'float64', 'float32', 'int32', 'int16'}, optional
See :meth:`.read`.
@@ -1156,6 +1047,12 @@ class SoundFile(object):
----------------
always_2d, fill_value, out
See :meth:`.read`.
+ fill_value : float, optional
+ 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
--------
@@ -1165,33 +1062,47 @@ class SoundFile(object):
>>> 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 overlap != 0 and not self.seekable():
- raise ValueError("overlap is only allowed for seekable files")
-
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)
while frames > 0:
- if frames < blocksize:
- if fill_value is not None and out is None:
- out = self._create_empty_array(blocksize, always_2d, dtype)
- blocksize = frames
- block = self.read(blocksize, dtype, always_2d, fill_value, out)
- frames -= blocksize
- if frames > 0 and self.seekable():
- self.seek(-overlap, SEEK_CUR)
- frames += overlap
- yield block
+ 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:])
+
+ 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
def truncate(self, frames=None):
"""Truncate the file to a given number of frames.
@@ -1343,7 +1254,7 @@ class SoundFile(object):
def _check_frames(self, frames, fill_value):
"""Reduce frames to no more than are available in the file."""
if self.seekable():
- remaining_frames = len(self) - self.tell()
+ remaining_frames = self.frames - self.tell()
if frames < 0 or (frames > remaining_frames and
fill_value is None):
frames = remaining_frames
@@ -1379,23 +1290,6 @@ class SoundFile(object):
raise ValueError("dtype must be one of {0!r}".format(
sorted(_ffi_types.keys())))
- def _ctype_is_deprecated(self, ctype, dtype):
- """Show warning if ctype is used instead of dtype.
-
- At some point, ctype arguments shall be removed and the
- corresponding dtype arguments shall lose their default value.
-
- """
- if ctype is not None:
- from warnings import warn
- warn('ctype is deprecated; use dtype instead', Warning)
- if dtype is not None:
- raise TypeError('Use dtype instead of ctype')
- for k, v in _ffi_types.items():
- if v == ctype:
- return k
- return dtype
-
def _array_io(self, action, array, frames):
"""Check array and call low-level IO function."""
if (array.ndim not in (1, 2) or
@@ -1422,8 +1316,8 @@ class SoundFile(object):
self.seek(curr + frames, SEEK_SET) # Update read & write position
return frames
- def _update_len(self, written):
- """Update len(self) after writing."""
+ def _update_frames(self, written):
+ """Update self.frames after writing."""
if self.seekable():
curr = self.tell()
self._info.frames = self.seek(0, SEEK_END)
@@ -1438,7 +1332,7 @@ class SoundFile(object):
if frames >= 0 and stop is not None:
raise TypeError("Only one of {frames, stop} may be used")
- start, stop, _ = slice(start, stop).indices(len(self))
+ start, stop, _ = slice(start, stop).indices(self.frames)
if stop < start:
stop = start
if frames < 0:
diff --git a/soundfile_build.py b/soundfile_build.py
new file mode 100644
index 0000000..5fdcc17
--- /dev/null
+++ b/soundfile_build.py
@@ -0,0 +1,135 @@
+import sys
+from cffi import FFI
+
+ffibuilder = FFI()
+ffibuilder.set_source("_soundfile", None)
+ffibuilder.cdef("""
+enum
+{
+ SF_FORMAT_SUBMASK = 0x0000FFFF,
+ SF_FORMAT_TYPEMASK = 0x0FFF0000,
+ SF_FORMAT_ENDMASK = 0x30000000
+} ;
+
+enum
+{
+ SFC_GET_LIB_VERSION = 0x1000,
+ SFC_GET_LOG_INFO = 0x1001,
+ SFC_GET_FORMAT_INFO = 0x1028,
+
+ SFC_GET_FORMAT_MAJOR_COUNT = 0x1030,
+ SFC_GET_FORMAT_MAJOR = 0x1031,
+ SFC_GET_FORMAT_SUBTYPE_COUNT = 0x1032,
+ SFC_GET_FORMAT_SUBTYPE = 0x1033,
+ SFC_FILE_TRUNCATE = 0x1080,
+ SFC_SET_CLIPPING = 0x10C0,
+
+ SFC_SET_SCALE_FLOAT_INT_READ = 0x1014,
+ SFC_SET_SCALE_INT_FLOAT_WRITE = 0x1015,
+} ;
+
+enum
+{
+ SF_FALSE = 0,
+ SF_TRUE = 1,
+
+ /* Modes for opening files. */
+ SFM_READ = 0x10,
+ SFM_WRITE = 0x20,
+ SFM_RDWR = 0x30,
+} ;
+
+typedef int64_t sf_count_t ;
+
+typedef struct SNDFILE_tag SNDFILE ;
+
+typedef struct SF_INFO
+{
+ sf_count_t frames ; /* Used to be called samples. Changed to avoid confusion. */
+ int samplerate ;
+ int channels ;
+ int format ;
+ int sections ;
+ int seekable ;
+} SF_INFO ;
+
+SNDFILE* sf_open (const char *path, int mode, SF_INFO *sfinfo) ;
+int sf_format_check (const SF_INFO *info) ;
+
+sf_count_t sf_seek (SNDFILE *sndfile, sf_count_t frames, int whence) ;
+
+int sf_command (SNDFILE *sndfile, int cmd, void *data, int datasize) ;
+
+int sf_error (SNDFILE *sndfile) ;
+const char* sf_strerror (SNDFILE *sndfile) ;
+const char* sf_error_number (int errnum) ;
+
+int sf_perror (SNDFILE *sndfile) ;
+int sf_error_str (SNDFILE *sndfile, char* str, size_t len) ;
+
+int sf_close (SNDFILE *sndfile) ;
+void sf_write_sync (SNDFILE *sndfile) ;
+
+sf_count_t sf_read_short (SNDFILE *sndfile, short *ptr, sf_count_t items) ;
+sf_count_t sf_read_int (SNDFILE *sndfile, int *ptr, sf_count_t items) ;
+sf_count_t sf_read_float (SNDFILE *sndfile, float *ptr, sf_count_t items) ;
+sf_count_t sf_read_double (SNDFILE *sndfile, double *ptr, sf_count_t items) ;
+
+/* Note: Data ptr argument types are declared as void* here in order to
+ avoid an implicit cast warning. (gh183). */
+sf_count_t sf_readf_short (SNDFILE *sndfile, void *ptr, sf_count_t frames) ;
+sf_count_t sf_readf_int (SNDFILE *sndfile, void *ptr, sf_count_t frames) ;
+sf_count_t sf_readf_float (SNDFILE *sndfile, void *ptr, sf_count_t frames) ;
+sf_count_t sf_readf_double (SNDFILE *sndfile, void *ptr, sf_count_t frames) ;
+
+sf_count_t sf_write_short (SNDFILE *sndfile, short *ptr, sf_count_t items) ;
+sf_count_t sf_write_int (SNDFILE *sndfile, int *ptr, sf_count_t items) ;
+sf_count_t sf_write_float (SNDFILE *sndfile, float *ptr, sf_count_t items) ;
+sf_count_t sf_write_double (SNDFILE *sndfile, double *ptr, sf_count_t items) ;
+
+/* Note: The argument types were changed to void* in order to allow
+ writing bytes in SoundFile.buffer_write() */
+sf_count_t sf_writef_short (SNDFILE *sndfile, void *ptr, sf_count_t frames) ;
+sf_count_t sf_writef_int (SNDFILE *sndfile, void *ptr, sf_count_t frames) ;
+sf_count_t sf_writef_float (SNDFILE *sndfile, void *ptr, sf_count_t frames) ;
+sf_count_t sf_writef_double (SNDFILE *sndfile, void *ptr, sf_count_t frames) ;
+
+sf_count_t sf_read_raw (SNDFILE *sndfile, void *ptr, sf_count_t bytes) ;
+sf_count_t sf_write_raw (SNDFILE *sndfile, void *ptr, sf_count_t bytes) ;
+
+const char* sf_get_string (SNDFILE *sndfile, int str_type) ;
+int sf_set_string (SNDFILE *sndfile, int str_type, const char* str) ;
+const char * sf_version_string (void) ;
+
+typedef sf_count_t (*sf_vio_get_filelen) (void *user_data) ;
+typedef sf_count_t (*sf_vio_seek) (sf_count_t offset, int whence, void *user_data) ;
+typedef sf_count_t (*sf_vio_read) (void *ptr, sf_count_t count, void *user_data) ;
+typedef sf_count_t (*sf_vio_write) (const void *ptr, sf_count_t count, void *user_data) ;
+typedef sf_count_t (*sf_vio_tell) (void *user_data) ;
+
+typedef struct SF_VIRTUAL_IO
+{ sf_count_t (*get_filelen) (void *user_data) ;
+ sf_count_t (*seek) (sf_count_t offset, int whence, void *user_data) ;
+ sf_count_t (*read) (void *ptr, sf_count_t count, void *user_data) ;
+ sf_count_t (*write) (const void *ptr, sf_count_t count, void *user_data) ;
+ sf_count_t (*tell) (void *user_data) ;
+} SF_VIRTUAL_IO ;
+
+SNDFILE* sf_open_virtual (SF_VIRTUAL_IO *sfvirtual, int mode, SF_INFO *sfinfo, void *user_data) ;
+SNDFILE* sf_open_fd (int fd, int mode, SF_INFO *sfinfo, int close_desc) ;
+
+typedef struct SF_FORMAT_INFO
+{
+ int format ;
+ const char* name ;
+ const char* extension ;
+} SF_FORMAT_INFO ;
+""")
+
+if sys.platform == 'win32':
+ ffibuilder.cdef("""
+ SNDFILE* sf_wchar_open (LPCWSTR wpath, int mode, SF_INFO *sfinfo) ;
+ """)
+
+if __name__ == "__main__":
+ ffibuilder.compile(verbose=True)
diff --git a/tests/test_pysoundfile.py b/tests/test_pysoundfile.py
index ca10325..7c1ed6e 100644
--- a/tests/test_pysoundfile.py
+++ b/tests/test_pysoundfile.py
@@ -382,6 +382,17 @@ def test_blocks_with_out(file_stereo_r):
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_mono():
blocks = list(sf.blocks(filename_mono, blocksize=3, dtype='int16',
fill_value=0))
@@ -515,7 +526,7 @@ def test_if_open_with_mode_w_truncates(file_stereo_rplus, mode):
assert f.samplerate == 48000
assert f.channels == 6
assert f.format == 'AIFF'
- assert len(f) == 0
+ assert f.frames == 0
else:
# This doesn't really work for file descriptors and file objects
pass
@@ -586,7 +597,7 @@ def test_file_attributes_in_read_mode(sf_stereo_r):
assert sf_stereo_r.sections == 1
assert sf_stereo_r.closed is False
assert sf_stereo_r.seekable() is True
- assert len(sf_stereo_r) == len(data_stereo)
+ assert sf_stereo_r.frames == len(data_stereo)
def test__repr__(sf_stereo_r):
@@ -602,13 +613,18 @@ def test_extra_info(sf_stereo_r):
def test_mode_should_be_in_write_mode(sf_stereo_w):
assert sf_stereo_w.mode == 'w'
- assert len(sf_stereo_w) == 0
+ assert sf_stereo_w.frames == 0
def test_mode_should_be_in_readwrite_mode(sf_stereo_rplus):
assert sf_stereo_rplus.mode == 'r+'
+def test_file_truthiness(file_w):
+ with sf.SoundFile(file_w, 'w', 44100, 2, format='WAV') as f:
+ assert f
+
+
# -----------------------------------------------------------------------------
# Test seek/tell
# -----------------------------------------------------------------------------
@@ -652,7 +668,7 @@ def test_truncate(file_stereo_rplus, use_default):
else:
f.truncate(2)
assert f.tell() == 2
- assert len(f) == 2
+ assert f.frames == 2
if isinstance(file_stereo_rplus, int):
os.lseek(file_stereo_rplus, 0, os.SEEK_SET)
data, fs = sf.read(file_stereo_rplus)
@@ -748,6 +764,9 @@ def test_buffer_read(sf_stereo_r):
with pytest.raises(ValueError) as excinfo:
sf_stereo_r.buffer_read(dtype='int8')
assert "dtype must be one of" in str(excinfo.value)
+ with pytest.raises(ValueError) as excinfo:
+ sf_stereo_r.buffer_read()
+ assert "dtype must be one of" in str(excinfo.value)
@xfail_from_buffer
@@ -975,9 +994,9 @@ def test_default_subtype():
def test_write_non_seekable_file(file_w):
with sf.SoundFile(file_w, 'w', 44100, 1, format='XI') as f:
assert not f.seekable()
- assert len(f) == 0
+ assert f.frames == 0
f.write(data_mono)
- assert len(f) == len(data_mono)
+ assert f.frames == len(data_mono)
with pytest.raises(RuntimeError) as excinfo:
f.seek(2)
@@ -985,7 +1004,7 @@ def test_write_non_seekable_file(file_w):
with sf.SoundFile(filename_new) as f:
assert not f.seekable()
- assert len(f) == len(data_mono)
+ assert f.frames == len(data_mono)
data = f.read(3, dtype='int16')
assert np.all(data == data_mono[:3])
data = f.read(666, dtype='int16')
@@ -999,10 +1018,6 @@ def test_write_non_seekable_file(file_w):
f.read()
assert "frames" in str(excinfo.value)
- with pytest.raises(ValueError) as excinfo:
- list(f.blocks(blocksize=3, overlap=1))
- assert "overlap" in str(excinfo.value)
-
data, fs = sf.read(filename_new, dtype='int16')
assert np.all(data == data_mono)
assert fs == 44100
--
pysoundfile packaging
More information about the pkg-multimedia-commits
mailing list