Skip to content

Commit

Permalink
Merge pull request #364 from bastibe/v0.12.0
Browse files Browse the repository at this point in the history
    Thank you, Barabazs, Andrew Murray, Jon Peirce, for contributions
    to this release.

    - Updated libsndfile to v1.2.0
    - Improves precompiled library location, especially with py2app or cx-freeze.
    - Now provide binary wheels for Linux x86_64
    - Now prefers packaged libsndfile over system-installed libsndfile
  • Loading branch information
bastibe committed Feb 15, 2023
2 parents 77444fe + 07ae8fd commit d8e1c00
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 39 deletions.
47 changes: 41 additions & 6 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ interface for Python calling C code. CFFI is supported for CPython 2.6+,
Breaking Changes
----------------

The ``soundfile`` module has evolved rapidly during the last few releases. Most
The ``soundfile`` module has evolved rapidly in the past. 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
Expand All @@ -52,17 +52,25 @@ In 0.9.0, we changed the ``ctype`` arguments of the ``buffer_*``
methods to ``dtype``, using the Numpy ``dtype`` notation. The old
``ctype`` arguments still work, but are now officially deprecated.

In 0.12.0, we changed the load order of the libsndfile library. Now,
the packaged libsndfile in the platform-specific wheels is tried
before falling back to any system-provided libsndfile. If you would
prefer using the system-provided libsndfile, install the source
package or source wheel instead of the platform-specific wheels.

Installation
------------

The ``soundfile`` module depends on the Python packages CFFI and NumPy, and the
system library libsndfile.
library libsndfile.

In a modern Python, you can use ``pip install soundfile`` to download
and install the latest release of the ``soundfile`` module 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``.
and install the latest release of the ``soundfile`` module and its
dependencies. On Windows (64/32) and OS X (Intel/ARM) and Linux 64,
this will also install a current version of the library libsndfile. If
you install the source module, you need to install libsndfile using
your distribution's package manager, for example ``sudo apt 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,
Expand All @@ -71,6 +79,24 @@ Binaries for Python Extension Packages <http://www.lfd.uci.edu/~gohlke/pythonlib

.. _Anaconda: https://www.continuum.io/downloads

Building
--------

``Soundfile`` itself does not contain any compiled code and can be
bundled into a wheel with the usual ``python setup.py bdist_wheel``.
However, ``soundfile`` relies on libsndfile, and optionally ships its
own copy of libsndfile in the wheel.

To build a binary wheel that contains libsndfile, make sure to
checkout and update the ``_soundfile_data`` submodule, then run
``python setup.py bdist_wheel`` as usual. If the resulting file size
of the wheel is around one megabyte, a matching libsndfile has been
bundled (without libsndfile, it's around 25 KB).

To build binary wheels for all supported platforms, run ``python
build_wheels.py``, which will ``python setup.py bdist_wheel`` for each
of the platforms we have precompiled libsndfiles for.

Error Reporting
---------------

Expand Down Expand Up @@ -309,3 +335,12 @@ News
- Improves documentation, error messages and tests
- Displays length of very short files in samples
- Supports the file system path protocol (pathlib et al)

2023-02-02 V0.12.0 Bastian Bechtold
Thank you, Barabazs, Andrew Murray, Jon Peirce, for contributions
to this release.

- Updated libsndfile to v1.2.0
- Improves precompiled library location, especially with py2app or cx-freeze.
- Now provide binary wheels for Linux x86_64
- Now prefers packaged libsndfile over system-installed libsndfile
1 change: 1 addition & 0 deletions build_wheels.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

architectures = dict(darwin=['x86_64', 'arm64'],
win32=['32bit', '64bit'],
linux=['x86_64'],
noplatform='noarch')

def cleanup():
Expand Down
32 changes: 27 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
#!/usr/bin/env python
import os
from platform import architecture
from platform import architecture, machine
from setuptools import setup
from setuptools.command.test import test as TestCommand
import sys

# environment variables for cross-platform package creation
platform = os.environ.get('PYSOUNDFILE_PLATFORM', sys.platform)
architecture0 = os.environ.get('PYSOUNDFILE_ARCHITECTURE', architecture()[0])
architecture0 = os.environ.get('PYSOUNDFILE_ARCHITECTURE')
if architecture0 is None:
# follow the same decision tree as in soundfile.py after
# _find_library('sndfile') fails:
if sys.platform == 'win32':
architecture0 = architecture()[0] # 64bit or 32bit
else:
architecture0 = machine() # x86_64 or arm64

if platform == 'darwin':
libname = 'libsndfile_' + architecture0 + '.dylib'
elif platform == 'win32':
libname = 'libsndfile_' + architecture0 + '.dll'
elif platform == 'linux':
libname = 'libsndfile_' + architecture0 + '.so'
else:
libname = None

Expand Down Expand Up @@ -60,24 +69,37 @@ def get_tag(self):
pythons = 'py2.py3'
if platform == 'darwin':
if architecture0 == 'x86_64':
oses = 'macosx_10_9_x86_64.macosx_11_0_x86_64'
oses = 'macosx_10_9_x86_64'
else:
oses = 'macosx_10_9_arm64.macosx_11_0_arm64'
oses = 'macosx_11_0_arm64'
elif platform == 'win32':
if architecture0 == '32bit':
oses = 'win32'
else:
oses = 'win_amd64'
elif platform == 'linux':
# the oldest mainline github runner available is ubuntu 20.04,
# which runs glibc 2.31:
oses = 'manylinux_2_31_x86_64'
else:
pythons = 'py2.py3'
oses = 'any'
return pythons, 'none', oses

cmdclass['bdist_wheel'] = bdist_wheel_half_pure

with open('soundfile.py') as f:
for line in f:
if line.startswith('__version__'):
_, soundfile_version = line.split('=')
soundfile_version = soundfile_version.strip(' "\'\n')
break
else:
raise RuntimeError("Could not find __version__ in soundfile.py")

setup(
name='soundfile',
version='0.11.0',
version=soundfile_version,
description='An audio library based on libsndfile, CFFI and NumPy',
author='Bastian Bechtold',
author_email='[email protected]',
Expand Down
57 changes: 30 additions & 27 deletions soundfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
For further information, see https://python-soundfile.readthedocs.io/.
"""
__version__ = "0.11.0"
__version__ = "0.12.0"

import os as _os
import sys as _sys
Expand Down Expand Up @@ -145,48 +145,51 @@
'int16': 'short'
}

try:
_libname = _find_library('sndfile')
if _libname is None:
raise OSError('sndfile library not found')
_snd = _ffi.dlopen(_libname)
except OSError:
try: # packaged lib (in _soundfile_data which should be on python path)
if _sys.platform == 'darwin':
from platform import machine as _machine
_packaged_libname = 'libsndfile_' + _machine() + '.dylib'
_libname = 'libsndfile.dylib'
elif _sys.platform == 'win32':
from platform import architecture as _architecture
_packaged_libname = 'libsndfile_' + _architecture()[0] + '.dll'
_libname = 'libsndfile.dll'
elif _sys.platform == 'linux':
_packaged_libname = 'libsndfile.so' # not provided!
_libname = 'libsndfile.so'
from platform import machine as _machine
_packaged_libname = 'libsndfile_' + _machine() + '.so'
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, '..'))
raise OSError('no packaged library for this platform')

import _soundfile_data # ImportError if this doesn't exist
_path = _os.path.dirname(_soundfile_data.__file__) # TypeError if __file__ is None
_full_path = _os.path.join(_path, _packaged_libname)
_snd = _ffi.dlopen(_full_path) # OSError if file doesn't exist or can't be loaded

except (OSError, ImportError, TypeError):
try: # system-wide libsndfile:
_libname = _find_library('sndfile')
if _libname is None:
raise OSError('sndfile library not found using ctypes.util.find_library')
_snd = _ffi.dlopen(_libname)

except OSError:
# Try explicit file name, if the general does not work (e.g. on nixos)
if _sys.platform == 'darwin':
_explicit_libname = 'libsndfile.dylib'
elif _sys.platform == 'win32':
_explicit_libname = 'libsndfile.dll'
elif _sys.plaform == 'linux':
_explicit_libname = 'libsndfile.so'
else:
raise

try: # packaged libsndfile:
_snd = _ffi.dlopen(_os.path.join(_path, '_soundfile_data', _packaged_libname))
except OSError: # try system-wide libsndfile:
# Homebrew on Apple M1 uses a `/opt/homebrew/lib` instead of
# `/usr/local/lib`. We are making sure we pick that up.
from platform import machine as _machine
if _sys.platform == 'darwin' and _machine() == 'arm64':
_hbrew_path = '/opt/homebrew/lib/' if _os.path.isdir('/opt/homebrew/lib/') \
else '/usr/local/lib/'
_snd = _ffi.dlopen(_os.path.join(_hbrew_path, _libname))
_snd = _ffi.dlopen(_os.path.join(_hbrew_path, _explicit_libname))
else:
# Try explicit file name, if the general does not work (e.g. on nixos)
_snd = _ffi.dlopen(_libname)
_snd = _ffi.dlopen(_explicit_libname)

__libsndfile_version__ = _ffi.string(_snd.sf_version_string()).decode('utf-8', 'replace')
if __libsndfile_version__.startswith('libsndfile-'):
Expand Down

0 comments on commit d8e1c00

Please sign in to comment.