diff --git a/.github/workflows/list_win_paths.yml b/.github/workflows/list_win_paths.yml new file mode 100644 index 0000000..c071b81 --- /dev/null +++ b/.github/workflows/list_win_paths.yml @@ -0,0 +1,59 @@ +# This is a basic workflow to help you get started with Actions +name: Windows Paths +on: + workflow_dispatch: + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [windows-2019] + python-version: + - "3.6" + - "3.7" + - "3.8" + - "3.9" + - "3.10" + - "pypy-3.7" + - "pypy-3.8" + architecture: ["x86", "x64"] + + steps: + - name: Build on Windows + if: runner.os == 'Windows' +# run: | +# choco install libsndfile +# New-Item -ItemType Junction -Path "C:\libsndfile" -Target "C:\Program Files\libsndfile" +# Set-Item -Path Env:LIB -Value "-LC:\libsndfile\bin" +# Set-Item -Path Env:INCLUDE -Value "-IC:\libsndfile\include" +# $Env:windir +# refreshenv +# vswhere.exe -latest -property installationPath +# Get-ChildItem C:\libsndfile\lib +# Get-ChildItem C:\libsndfile\bin +# Get-ChildItem C:\libsndfile\include +# - name: vcpkg build +# uses: johnwason/vcpkg-action@v2 +# with: +# pkgs: libsndfile +# triplet: x64-windows-static + + run: | + bootstrap-vcpkg + vcpkg install libsndfile + vcpkg integrate install + - uses: actions/checkout@v2 + with: + submodules: true + - uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + architecture: ${{ matrix.architecture }} + - name: Install requirements + run: pip install numpy pytest cffi + - name: Install editable package + run: pip install --editable . --verbose + - name: Run tests + run: python -m pytest diff --git a/.github/workflows/python-package.yml b/.github/workflows/main.yml similarity index 51% rename from .github/workflows/python-package.yml rename to .github/workflows/main.yml index edc399a..61deb36 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/main.yml @@ -1,6 +1,19 @@ -name: Python Package +# This is a basic workflow to help you get started with Actions +name: CI +# Controls when the workflow will run +on: + # Triggers the workflow on push or pull request events but only for the "master" branch + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] -on: [push, pull_request] + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# name: Python Package + +# on: [push, pull_request] jobs: test: @@ -29,7 +42,21 @@ jobs: steps: - name: Install APT dependencies if: runner.os == 'Linux' - run: sudo apt-get install libsndfile1 + run: sudo apt-get install libsndfile1 libsndfile1-dev +# - name: Install choco for Windows + - name: Build on Windows + if: runner.os == 'Windows' + run: | + choco install libsndfile + New-Item -ItemType Junction -Path "C:\libsndfile" -Target "C:\Program Files\libsndfile" + $Env:windir + $Env:INCLUDE + $Env:LIB + $Env:PATH + $Env:PATH += ';C:\libsnfile\bin' + $Env:INCLUDE += ';C:\libsnfile\include' +#Set-Item -Path Env:CGO_LDFLAGS -Value "-LC:\libsndfile\bin" + - uses: actions/checkout@v2 with: submodules: true @@ -38,8 +65,9 @@ jobs: python-version: ${{ matrix.python-version }} architecture: ${{ matrix.architecture }} - name: Install requirements - run: pip install numpy pytest + run: pip install numpy pytest cffi - name: Install editable package run: pip install --editable . --verbose - name: Run tests run: python -m pytest + diff --git a/.github/workflows/windows.yaml b/.github/workflows/windows.yaml new file mode 100644 index 0000000..4950c4d --- /dev/null +++ b/.github/workflows/windows.yaml @@ -0,0 +1,70 @@ +# This is a basic workflow to help you get started with Actions +name: Windows Build +# Controls when the workflow will run +on: + # Triggers the workflow on push or pull request events but only for the "master" branch + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# name: Python Package + +# on: [push, pull_request] + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [windows-2019] + python-version: + - "3.6" + - "3.7" + - "3.8" + - "3.9" + - "3.10" + - "pypy-3.7" + - "pypy-3.8" + architecture: ["x86", "x64"] + + steps: + - name: Build on Windows + if: runner.os == 'Windows' + run: | + choco install libsndfile + New-Item -ItemType Junction -Path "C:\libsndfile" -Target "C:\Program Files\libsndfile" + Set-Item -Path Env:LIB -Value "-LC:\libsndfile\bin" + Set-Item -Path Env:INCLUDE -Value "-IC:\libsndfile\include" + $Env:windir + refreshenv + vswhere.exe -latest -property installationPath + Get-ChildItem C:\libsndfile\lib + +# $Env:PATH +#Set-Item -Path Env:CFLAGS -Value "C:\libsndfile\include" +# $Env:INCLUDE +# $Env:LIB +# $Env:PATH +# $Env:PATH += ';C:\libsnfile\bin +# $Env:INCLUDE += ';C:\libsnfile\include' +#Set-Item -Path Env:CGO_LDFLAGS -Value "-LC:\libsndfile\bin" + + - uses: actions/checkout@v2 + with: + submodules: true + - uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + architecture: ${{ matrix.architecture }} + - name: Install requirements + run: pip install numpy pytest cffi + - name: Install editable package + run: pip install --editable . --verbose + - name: Run tests + run: python -m pytest + diff --git a/.travis.yml b/.travis.yml index e952beb..e5444e8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ python: addons: apt: packages: - - libsndfile1 + - libsndfile1-dev install: - "if [[ $TRAVIS_PYTHON_VERSION == pypy ]]; then pip install git+https://bitbucket.org/pypy/numpy.git ; fi" script: diff --git a/README.rst b/README.rst index f7a7edb..28ac60b 100644 --- a/README.rst +++ b/README.rst @@ -52,6 +52,7 @@ 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.11.0 switched from cffi ABI mode to API mode Installation ------------ diff --git a/setup.py b/setup.py index bed16f1..093973b 100644 --- a/setup.py +++ b/setup.py @@ -5,6 +5,8 @@ from setuptools.command.test import test as TestCommand import sys +from setuptools.extension import Extension + PYTHON_INTERPRETERS = '.'.join([ 'cp26', 'cp27', 'cp32', 'cp33', 'cp34', 'cp35', 'cp36', @@ -87,7 +89,7 @@ def get_tag(self): setup( name='soundfile', - version='0.10.3post1', + version='0.11.1', description='An audio library based on libsndfile, CFFI and NumPy', author='Bastian Bechtold', author_email='basti@bastibe.de', diff --git a/soundfile.py b/soundfile.py index b15a706..496c74b 100644 --- a/soundfile.py +++ b/soundfile.py @@ -8,7 +8,7 @@ For further information, see https://python-soundfile.readthedocs.io/. """ -__version__ = "0.10.3" +__version__ = "0.11.1" import os as _os import sys as _sys @@ -16,6 +16,7 @@ from os import SEEK_SET, SEEK_CUR, SEEK_END from ctypes.util import find_library as _find_library from _soundfile import ffi as _ffi +from _soundfile import lib try: _unicode = unicode # doesn't exist in Python 3.x @@ -23,6 +24,7 @@ _unicode = str + _str_types = { 'title': 0x01, 'copyright': 0x02, @@ -383,7 +385,6 @@ def blocks(file, blocksize=None, overlap=0, frames=-1, start=0, stop=None, dtype, always_2d, fill_value, out): yield block - class _SoundFileInfo(object): """Information about a SoundFile""" @@ -438,7 +439,6 @@ def __repr__(self): def info(file, verbose=False): """Returns an object with information about a `SoundFile`. - Parameters ---------- verbose : bool @@ -449,7 +449,6 @@ def info(file, verbose=False): def available_formats(): """Return a dictionary of available major formats. - Examples -------- >>> import soundfile as sf @@ -462,7 +461,6 @@ def available_formats(): 'WAVEX': 'WAVEX (Microsoft)', 'RAW': 'RAW (header-less)', 'MAT5': 'MAT5 (GNU Octave 2.1 / Matlab 5.0)'} - """ return dict(_available_formats_helper(_snd.SFC_GET_FORMAT_MAJOR_COUNT, _snd.SFC_GET_FORMAT_MAJOR)) @@ -470,12 +468,10 @@ def available_formats(): def available_subtypes(format=None): """Return a dictionary of available subtypes. - Parameters ---------- format : str If given, only compatible subtypes are returned. - Examples -------- >>> import soundfile as sf @@ -483,7 +479,6 @@ def available_subtypes(format=None): {'PCM_24': 'Signed 24 bit PCM', 'PCM_16': 'Signed 16 bit PCM', 'PCM_S8': 'Signed 8 bit PCM'} - """ subtypes = _available_formats_helper(_snd.SFC_GET_FORMAT_SUBTYPE_COUNT, _snd.SFC_GET_FORMAT_SUBTYPE) @@ -493,7 +488,6 @@ def available_subtypes(format=None): def check_format(format, subtype=None, endian=None): """Check if the combination of format/subtype/endian is valid. - Examples -------- >>> import soundfile as sf @@ -501,7 +495,6 @@ def check_format(format, subtype=None, endian=None): True >>> sf.check_format('FLAC', 'VORBIS') False - """ try: return bool(_format_int(format, subtype, endian)) @@ -511,7 +504,6 @@ def check_format(format, subtype=None, endian=None): def default_subtype(format): """Return the default subtype for a given format. - Examples -------- >>> import soundfile as sf @@ -519,12 +511,13 @@ def default_subtype(format): 'PCM_16' >>> sf.default_subtype('MAT5') 'DOUBLE' - """ _check_format(format) return _default_subtypes.get(format.upper()) + + class SoundFile(object): """A sound file. @@ -1205,7 +1198,7 @@ def _open(self, file, mode_int, closefd): def _init_virtual_io(self, file): """Initialize callback functions for sf_open_virtual().""" - @_ffi.callback("sf_vio_get_filelen") + @_ffi.def_extern() def vio_get_filelen(user_data): curr = file.tell() file.seek(0, SEEK_END) @@ -1213,12 +1206,13 @@ def vio_get_filelen(user_data): file.seek(curr, SEEK_SET) return size - @_ffi.callback("sf_vio_seek") + @_ffi.def_extern() def vio_seek(offset, whence, user_data): file.seek(offset, whence) return file.tell() - @_ffi.callback("sf_vio_read") + # @_ffi.callback("sf_vio_read") + @_ffi.def_extern() def vio_read(ptr, count, user_data): # first try readinto(), if not available fall back to read() try: @@ -1231,7 +1225,7 @@ def vio_read(ptr, count, user_data): buf[0:data_read] = data return data_read - @_ffi.callback("sf_vio_write") + @_ffi.def_extern() def vio_write(ptr, count, user_data): buf = _ffi.buffer(ptr, count) data = buf[:] @@ -1241,16 +1235,16 @@ def vio_write(ptr, count, user_data): written = count return written - @_ffi.callback("sf_vio_tell") + @_ffi.def_extern() def vio_tell(user_data): return file.tell() # Note: the callback functions must be kept alive! - self._virtual_io = {'get_filelen': vio_get_filelen, - 'seek': vio_seek, - 'read': vio_read, - 'write': vio_write, - 'tell': vio_tell} + self._virtual_io = {'get_filelen': lib.vio_get_filelen, + 'seek': lib.vio_seek, + 'read': lib.vio_read, + 'write': lib.vio_write, + 'tell': lib.vio_tell} return _ffi.new("SF_VIRTUAL_IO*", self._virtual_io) diff --git a/soundfile_build.py b/soundfile_build.py index 774fd9b..4814333 100644 --- a/soundfile_build.py +++ b/soundfile_build.py @@ -1,9 +1,27 @@ import os import sys from cffi import FFI +platform = os.environ.get('PYSOUNDFILE_PLATFORM', sys.platform) +print("Platform:", platform) ffibuilder = FFI() -ffibuilder.set_source("_soundfile", None) +import sysconfig +try: + extra_libs = sysconfig.get_config_var('LIB').split() + extra_includes = sysconfig.get_config_var('INCLUDE').split() +except: + extra_includes = [r"C:vcpkg\packages\libsndfile_x86-windows\include"] + extra_libs = [r"C:vcpkg\packages\libsndfile_x86-windows\lib"] +print("$INCLUDE: ", extra_includes) +print("$LIB: ", extra_libs) + +ffibuilder.set_source('_soundfile', + ''' #include + ''', + libraries=['sndfile'], + include_dirs = extra_includes, + library_dirs=extra_libs) + ffibuilder.cdef(""" enum { @@ -44,6 +62,8 @@ typedef struct SNDFILE_tag SNDFILE ; + + typedef struct SF_INFO { sf_count_t frames ; /* Used to be called samples. Changed to avoid confusion. */ @@ -52,8 +72,15 @@ int format ; int sections ; int seekable ; -} SF_INFO ; +} SF_INFO ;""") +if platform == 'win32': + ffibuilder.cdef(""" + SNDFILE* sf_wchar_open (const wchar_t *wpath, int mode, SF_INFO *sfinfo); + """) + +ffibuilder.cdef( +""" SNDFILE* sf_open (const char *path, int mode, SF_INFO *sfinfo) ; int sf_format_check (const SF_INFO *info) ; @@ -108,6 +135,7 @@ 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) ; @@ -116,6 +144,7 @@ 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) ; @@ -125,13 +154,14 @@ const char* name ; const char* extension ; } SF_FORMAT_INFO ; -""") -platform = os.environ.get('PYSOUNDFILE_PLATFORM', sys.platform) -if platform == 'win32': - ffibuilder.cdef(""" - SNDFILE* sf_wchar_open (const wchar_t *wpath, int mode, SF_INFO *sfinfo) ; - """) +extern "Python" sf_count_t vio_get_filelen (void *user_data) ; +extern "Python" sf_count_t vio_seek (sf_count_t offset, int whence, void *user_data) ; +extern "Python" sf_count_t vio_read (void *ptr, sf_count_t count, void *user_data) ; +extern "Python" sf_count_t vio_write (const void *ptr, sf_count_t count, void *user_data) ; +extern "Python" sf_count_t vio_tell (void *user_data) ; + +""") if __name__ == "__main__": ffibuilder.compile(verbose=True)