pax_global_header00006660000000000000000000000064146643451240014523gustar00rootroot0000000000000052 comment=ebe422ceaf44c240bfc8e43a7e1dc023d9d584ba python-soxr-0.5.0.post1/000077500000000000000000000000001466434512400151045ustar00rootroot00000000000000python-soxr-0.5.0.post1/.github/000077500000000000000000000000001466434512400164445ustar00rootroot00000000000000python-soxr-0.5.0.post1/.github/workflows/000077500000000000000000000000001466434512400205015ustar00rootroot00000000000000python-soxr-0.5.0.post1/.github/workflows/build-dist.yml000066400000000000000000000060421466434512400232660ustar00rootroot00000000000000name: Build and upload to PyPI # Build on every branch push, tag push, and pull request change: on: [push, pull_request, workflow_dispatch] # Alternatively, to publish when a (published) GitHub Release is created, use the following: # on: # push: # pull_request: # release: # types: # - published jobs: build_wheels: name: Build wheels on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: # macos-13 is an intel runner, macos-14 is apple silicon os: [ubuntu-22.04, windows-2022, macos-13, macos-14] steps: - uses: actions/checkout@v4 with: fetch-depth: 0 submodules: true - name: Build wheels uses: pypa/cibuildwheel@v2.20 env: # Skip builds(save time / avoid build error) CIBW_SKIP: "pp* *-musllinux_* *_i686 *-win32" - uses: actions/upload-artifact@v4 with: name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} path: ./wheelhouse/*.whl build_archs: name: Build wheels manylinux_aarch64 runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 with: fetch-depth: 0 submodules: true - name: Set up QEMU uses: docker/setup-qemu-action@v3 with: platforms: all - name: Build wheels uses: pypa/cibuildwheel@v2.20 env: CIBW_ARCHS_LINUX: aarch64 CIBW_BUILD: cp*-manylinux_aarch64 CIBW_TEST_SKIP: "*" # Skip tests(save time) - uses: actions/upload-artifact@v4 with: name: cibw-wheels-archs path: ./wheelhouse/*.whl build_sdist: name: Build source distribution runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 with: fetch-depth: 0 submodules: true - name: Build SDist run: pipx run build --sdist - name: Check metadata run: pipx run twine check dist/* - uses: actions/upload-artifact@v4 with: name: sdist path: dist/*.tar.gz upload_pypi: needs: [build_wheels, build_archs, build_sdist] runs-on: ubuntu-latest # upload to PyPI on every tag starting with 'v' if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') # alternatively, to publish when a GitHub Release is created, use the following rule: # if: github.event_name == 'release' && github.event.action == 'published' environment: name: pypi url: https://pypi.org/p/soxr permissions: contents: write id-token: write # IMPORTANT: mandatory for trusted publishing steps: - uses: actions/download-artifact@v4 with: path: dist pattern: "*" merge-multiple: true - uses: softprops/action-gh-release@v2 with: files: | dist/*.tar.gz dist/*.whl - uses: pypa/gh-action-pypi-publish@release/v1 # with: # repository_url: https://test.pypi.org/legacy/ # To test python-soxr-0.5.0.post1/.github/workflows/run-test.yml000066400000000000000000000015621466434512400230110ustar00rootroot00000000000000name: Run tests on: [push, pull_request, workflow_dispatch] jobs: build: strategy: fail-fast: false matrix: python-version: ["3.9", "3.10", "3.11", "3.12", "3.13-dev", "pypy3.9", "pypy3.10"] os: [ubuntu-latest, windows-latest, macos-latest] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 with: submodules: true - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Set min macOS version if: runner.os == 'macOS' run: | echo "MACOS_DEPLOYMENT_TARGET=10.14" >> $GITHUB_ENV - name: Build and install run: | python -m pip install pytest pip install --verbose . - name: Test with pytest run: | cd tests python -m pytest python-soxr-0.5.0.post1/.gitignore000066400000000000000000000064051466434512400171010ustar00rootroot00000000000000src/csoxr_ver_vcs.cpp src/soxr/_version.py ### Python ### # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ cover/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 db.sqlite3-journal # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder .pybuilder/ target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: # .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. #Pipfile.lock # poetry # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. # This is especially recommended for binary packages to ensure reproducibility, and is more # commonly ignored for libraries. # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control #poetry.lock # pdm # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. #pdm.lock # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it # in version control. # https://pdm.fming.dev/#use-with-ide .pdm.toml # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm __pypackages__/ # Celery stuff celerybeat-schedule celerybeat.pid # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ # pytype static type analyzer .pytype/ # Cython debug symbols cython_debug/ # PyCharm # JetBrains specific template is maintained in a separate JetBrains.gitignore that can # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. .idea/ ### Python Patch ### # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration poetry.toml # ruff .ruff_cache/ # LSP config files pyrightconfig.json python-soxr-0.5.0.post1/.gitmodules000066400000000000000000000001201466434512400172520ustar00rootroot00000000000000[submodule "libsoxr"] path = libsoxr url = https://github.com/dofuuz/soxr.git python-soxr-0.5.0.post1/.readthedocs.yaml000066400000000000000000000012201466434512400203260ustar00rootroot00000000000000# .readthedocs.yaml # Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Set the OS, Python version and other tools you might need build: os: ubuntu-22.04 tools: python: "3.12" # Build documentation in the docs/ directory with Sphinx sphinx: configuration: docs/conf.py # Optionally build your docs in additional formats such as PDF # formats: # - pdf # Optionally set the version of Python and requirements required to build your docs python: install: - method: pip path: . extra_requirements: - docs submodules: include: all python-soxr-0.5.0.post1/BUILDING.md000066400000000000000000000013661466434512400166310ustar00rootroot00000000000000# Building Python-SoXR ## Preparation ``` # Upgrade PIP python -m pip install --upgrade pip # Clone code including submodule git clone --recurse-submodules https://github.com/dofuuz/python-soxr.git cd python-soxr ``` ## Build package(wheel) ``` pip wheel -v . ``` ### (Alternative method) Build using system libsoxr libsoxr should be installed before building. (e.g. `sudo apt install libsoxr-dev`) ``` pip wheel -v . -C cmake.define.USE_SYSTEM_LIBSOXR=ON ``` It will link libsoxr dynamically and won't bundle libsoxr in the wheel package. ## Install Install built .whl package(not .tar.gz sdist). ``` pip install ./soxr-[...].whl ``` ## Test ``` # Install dependencies pip install pytest # Test (using installed Python-SoXR) python -m pytest ``` python-soxr-0.5.0.post1/CMakeLists.txt000066400000000000000000000102201466434512400176370ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15...3.26) project(nanobind_example LANGUAGES CXX) if (NOT SKBUILD) message(WARNING "\ This CMake file is meant to be executed using 'scikit-build'. Running it directly will almost certainly not produce the desired result. If you are a user trying to install this package, please use the command below, which will install all necessary build dependencies, compile the package in an isolated environment, and then install it. ===================================================================== $ pip install . ===================================================================== If you are a software developer, and this is your own package, then it is usually much more efficient to install the build dependencies in your environment once and use the following command that avoids a costly creation of a new virtual environment at every compilation: ===================================================================== $ pip install nanobind scikit-build-core[pyproject] $ pip install --no-build-isolation -ve . ===================================================================== You may optionally add -Ceditable.rebuild=true to auto-rebuild when the package is imported. Otherwise, you need to re-run the above after editing C++ files.") endif() # Turn on to link libsoxr dynamically and not to bundle libsoxr in the wheel package option(USE_SYSTEM_LIBSOXR "Build using system libsoxr" OFF) find_package(Python 3.9 REQUIRED COMPONENTS Interpreter Development.Module OPTIONAL_COMPONENTS Development.SABIModule) find_package(nanobind CONFIG REQUIRED) if (USE_SYSTEM_LIBSOXR) set(CSOXR_VER_C src/csoxr_version.cpp) else () # libsoxr VCS versioning set(CSOXR_VER_C ${CMAKE_CURRENT_SOURCE_DIR}/src/csoxr_ver_vcs.cpp) set(SOXR_VER_COMMAND ${CMAKE_COMMAND} -DVERSION_IN=src/csoxr_ver_vcs.cpp.in -DVERSION_C=${CSOXR_VER_C} -DVCS_REPO_DIR=libsoxr -P cmake/versioning.cmake ) # run while CMake configuring (for sdist) execute_process( COMMAND ${SOXR_VER_COMMAND} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) # run at every build time add_custom_target(soxr_version_vcs COMMAND ${SOXR_VER_COMMAND} BYPRODUCTS ${CSOXR_VER_C} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) endif () nanobind_add_module(soxr_ext STABLE_ABI NB_STATIC src/soxr_ext.cpp ${CSOXR_VER_C} ) if (NOT CMAKE_CROSSCOMPILING) # nanobind's stub generation requires importing the module, so skip it when cross-compiling nanobind_add_stub(soxr_ext_stub MODULE soxr_ext OUTPUT soxr_ext.pyi PYTHON_PATH $ DEPENDS soxr_ext ) install(FILES ${CMAKE_BINARY_DIR}/soxr_ext.pyi DESTINATION soxr) endif () # Install directive for scikit-build-core install(TARGETS soxr_ext LIBRARY DESTINATION soxr) if (USE_SYSTEM_LIBSOXR) # Find system libsoxr find_library(SOXR_LIBRARY NAMES soxr) find_path(SOXR_INCLUDE_DIR soxr.h) message (STATUS "Building with external libsoxr") message(SOXR_LIBRARY="${SOXR_LIBRARY}") message(SOXR_INCLUDE_DIR="${SOXR_INCLUDE_DIR}") target_link_libraries(soxr_ext PRIVATE ${SOXR_LIBRARY}) target_include_directories(soxr_ext PRIVATE ${SOXR_INCLUDE_DIR}) else () target_link_libraries(soxr_ext PRIVATE soxr) target_include_directories(soxr_ext PRIVATE src libsoxr/src ) # Build static libsoxr option(BUILD_TESTS "" OFF) option(WITH_OPENMP "" OFF) # OpenMP seems not working (dunno why). Disable it for portability anyway. option(WITH_LSR_BINDINGS "" OFF) option(BUILD_SHARED_LIBS "" OFF) # make it shared someday? option(WITH_VR32 "" OFF) set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_INSTALL_PREFIX ../install) add_subdirectory(libsoxr libsoxr) # Copy licenses to package (scikit-build-core) install(FILES cmake/LICENSE-PFFFT.txt DESTINATION ${SKBUILD_METADATA_DIR}/licenses) install(FILES libsoxr/LICENCE DESTINATION ${SKBUILD_METADATA_DIR}/licenses RENAME LICENSE-libsoxr.txt) endif () python-soxr-0.5.0.post1/COPYING.LGPL000066400000000000000000000635001466434512400167000ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! python-soxr-0.5.0.post1/LICENSE.txt000066400000000000000000000013111466434512400167230ustar00rootroot00000000000000Python-SoXR, Resampler library for Python. Copyright (c) 2021 Myungchul Keum This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, see . python-soxr-0.5.0.post1/README.md000066400000000000000000000101521466434512400163620ustar00rootroot00000000000000# Python-SoXR [![GitHub](https://img.shields.io/badge/GitHub-python--soxr-181717?logo=github)](https://github.com/dofuuz/python-soxr) [![PyPI](https://img.shields.io/pypi/v/soxr.svg?logo=pypi)](https://pypi.org/project/soxr/) [![conda-forge](https://img.shields.io/conda/vn/conda-forge/soxr-python?logo=conda-forge)](https://anaconda.org/conda-forge/soxr-python) [![Packaging status](https://repology.org/badge/tiny-repos/python:soxr.svg)](https://repology.org/project/python:soxr/versions) [![Read the Docs](https://img.shields.io/readthedocs/python-soxr?logo=read-the-docs)](https://python-soxr.readthedocs.io) High quality, one-dimensional sample-rate conversion library for Python. - Homepage: https://github.com/dofuuz/python-soxr - Documentation: https://python-soxr.readthedocs.io - PyPI: https://pypi.org/project/soxr/ Keywords: Resampler, Audio resampling, Samplerate conversion, DSP(Digital Signal Processing) Python-SoXR is a Python wrapper of [libsoxr](https://sourceforge.net/projects/soxr/). ## Installation ``` pip install soxr ``` If installation fails, upgrade pip with `python -m pip install --upgrade pip` and try again. ### in Conda environment ``` conda install -c conda-forge soxr-python ``` Note: Conda packge name is `soxr-python`, not python-soxr. ## Basic usage ```python import soxr y = soxr.resample( x, # input array – mono(1D) or multi-channel(2D of [frame, channel]) 48000, # input samplerate 16000 # target samplerate ) ``` If input is not `numpy.ndarray`, it will be converted to `numpy.ndarray(dtype='float32')`. dtype should be one of float32, float64, int16, int32. Output is `numpy.ndarray` with same dimension and data type of input. ## Streaming usage Use `ResampleStream` for real-time processing or very long signal. ```python import soxr rs = soxr.ResampleStream( 44100, # input samplerate 16000, # target samplerate 1, # channel(s) dtype='float32' # data type (default = 'float32') ) eof = False while not eof: # Get chunk ... y_chunk = rs.resample_chunk( x, # input aray – mono(1D) or multi-channel(2D of [frame, channel]) last=eof # Set True at end of input ) ``` Output frame count may not be consistent. This is normal operation. (ex. [0, 0, 0, 186, 186, 166, 186, 186, 168, ...]) 📝 [More code examples](https://dofuuz.github.io/dsp/2024/05/26/sample-rate-conversion-in-python.html) ## Benchmark Sweep, impulse, speed compairsion with other resamplers for Python. https://colab.research.google.com/drive/1_xYUs00VWYOAXShB85W1MFWaUjGHfO4K?usp=sharing ### Speed comparison summary Downsampling 10 sec of 48000 Hz to 44100 Hz. Ran on Google Colab. Library | Time on CPU (ms) ------------------------ | ---------------- soxr (HQ) | 10.8 torchaudio | 13.8 soxr (VHQ) | 14.5 scipy.signal.resample | 21.3 lilfilter | 24.7 julius | 31 resampy (kaiser_fast) | 108 samplerate (sinc_medium) | 223 resampy (kaiser_best) | 310 samplerate (sinc_best) | 794 ## Technical detail For technical details behind resampler, see libsoxr docs. - https://sourceforge.net/p/soxr/wiki/Home/ - http://sox.sourceforge.net/SoX/Resampling ([archive](https://web.archive.org/web/20230626144127/https://sox.sourceforge.net/SoX/Resampling)) - https://sourceforge.net/p/soxr/code/ci/master/tree/src/soxr.h Python-SoXR package comes with [modified version](https://github.com/dofuuz/soxr) of libsoxr. [See changes here](https://github.com/dofuuz/soxr/compare/0.1.3...master). These changes do not apply to dynamic-linked builds (e.g. conda-forge build). To check the version of libsoxr, use `soxr.__libsoxr_version__`. ## Credit and License Python-SoXR is LGPL v2.1+ licensed, following libsoxr's license. ### OSS libraries used #### libsoxr (LGPLv2.1+) The SoX Resampler library https://sourceforge.net/projects/soxr/ Python-SoXR is a Python wrapper of libsoxr. #### PFFFT (BSD-like) PFFFT: a pretty fast FFT. https://bitbucket.org/jpommier/pffft/ libsoxr dependency. python-soxr-0.5.0.post1/cmake/000077500000000000000000000000001466434512400161645ustar00rootroot00000000000000python-soxr-0.5.0.post1/cmake/LICENSE-PFFFT.txt000066400000000000000000000041121466434512400206500ustar00rootroot00000000000000Copyright (c) 2013 Julien Pommier ( pommier@modartt.com ) Based on original fortran 77 code from FFTPACKv4 from NETLIB (http://www.netlib.org/fftpack), authored by Dr Paul Swarztrauber of NCAR, in 1985. As confirmed by the NCAR fftpack software curators, the following FFTPACKv5 license applies to FFTPACKv4 sources. My changes are released under the same terms. FFTPACK license: http://www.cisl.ucar.edu/css/software/fftpack5/ftpk.html Copyright (c) 2004 the University Corporation for Atmospheric Research ("UCAR"). All rights reserved. Developed by NCAR's Computational and Information Systems Laboratory, UCAR, www.cisl.ucar.edu. Redistribution and use of the Software in source and binary forms, with or without modification, is permitted provided that the following conditions are met: - Neither the names of NCAR's Computational and Information Systems Laboratory, the University Corporation for Atmospheric Research, nor the names of its sponsors or contributors may be used to endorse or promote products derived from this Software without specific prior written permission. - Redistributions of source code must retain the above copyright notices, this list of conditions, and the disclaimer below. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions, and the disclaimer below in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. PFFFT : a Pretty Fast FFT. This file is largerly based on the original FFTPACK implementation, modified in order to take advantage of SIMD instructions of modern CPUs. python-soxr-0.5.0.post1/cmake/versioning.cmake000066400000000000000000000022071466434512400213520ustar00rootroot00000000000000# Generate version.c using git describe # # Usage: # set(VER_C ${CMAKE_BINARY_DIR}/ver_vcs.cpp) # add_custom_target(version_vcs # ${CMAKE_COMMAND} # -DVERSION_IN=ver_vcs.cpp.in # -DVERSION_C=${VER_C} # -DVCS_REPO_DIR=submodule # optional # -P cmake/versioning.cmake # BYPRODUCTS ${VER_C} # WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} # ) if (NOT DEFINED VCS_REPO_DIR) set(VCS_REPO_DIR ${CMAKE_CURRENT_SOURCE_DIR}) endif () find_package(Git) if (GIT_EXECUTABLE) execute_process( COMMAND ${GIT_EXECUTABLE} describe --always --tags --dirty OUTPUT_VARIABLE GIT_VERSION RESULT_VARIABLE GIT_ERROR_CODE OUTPUT_STRIP_TRAILING_WHITESPACE WORKING_DIRECTORY ${VCS_REPO_DIR} ) if (NOT GIT_ERROR_CODE) set(VCS_VERSION ${GIT_VERSION}) endif () endif () get_filename_component(VCS_MODULE ${VCS_REPO_DIR} NAME) if (VCS_VERSION) message("${VCS_MODULE} VCS_VERSION: " ${VCS_VERSION}) configure_file(${VERSION_IN} ${VERSION_C}) else () message("${VCS_MODULE} VCS_VERSION unknown. ${VERSION_C} not changed.") endif () python-soxr-0.5.0.post1/docs/000077500000000000000000000000001466434512400160345ustar00rootroot00000000000000python-soxr-0.5.0.post1/docs/conf.py000066400000000000000000000042421466434512400173350ustar00rootroot00000000000000# Configuration file for the Sphinx documentation builder. # # This file only contains a selection of the most common options. For a full # list see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html # -- Path setup -------------------------------------------------------------- # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. import os import sys sys.path.insert(0, os.path.abspath('..')) # -- Project information ----------------------------------------------------- import importlib.metadata project = 'Python-SoXR' copyright = '2021-24 Myungchul Keum' author = 'Myungchul Keum' # The full version, including alpha/beta/rc tags release = importlib.metadata.version('soxr') version = '.'.join(release.split('.')[:2]) # -- General configuration --------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.napoleon', 'myst_parser', ] autodoc_mock_imports = ['soxr.soxr_ext', 'soxr.version', 'numpy'] myst_enable_extensions = ['linkify'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'sphinx_book_theme' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static']python-soxr-0.5.0.post1/docs/index.md000066400000000000000000000001141466434512400174610ustar00rootroot00000000000000```{toctree} --- maxdepth: 2 --- soxr.md ``` ```{include} ../README.md ``` python-soxr-0.5.0.post1/docs/soxr.md000066400000000000000000000001271466434512400173510ustar00rootroot00000000000000# soxr package ```{eval-rst} .. automodule:: soxr :members: :undoc-members: ``` python-soxr-0.5.0.post1/libsoxr/000077500000000000000000000000001466434512400165665ustar00rootroot00000000000000python-soxr-0.5.0.post1/pyproject.toml000066400000000000000000000044471466434512400200310ustar00rootroot00000000000000[build-system] requires = [ "scikit-build-core >=0.9.0", "nanobind >=2", "setuptools>=45", "setuptools_scm[toml]>=6.2", "typing-extensions; python_version < '3.11'", ] build-backend = "scikit_build_core.build" [project] name = "soxr" dynamic = ["version"] description = "High quality, one-dimensional sample-rate conversion library" readme = "README.md" requires-python = ">=3.9" authors = [ { name = "KEUM Myungchul" }, ] keywords = [ "audio resampling", "samplerate conversion", "SRC", "signal processing", "resampler", ] classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "Intended Audience :: Science/Research", "Intended Audience :: Telecommunications Industry", "Topic :: Multimedia :: Sound/Audio :: Analysis", "Topic :: Multimedia :: Sound/Audio :: Conversion", "Topic :: Scientific/Engineering", "License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)", "Programming Language :: C", "Programming Language :: C++", "Programming Language :: Python :: 3", ] dependencies = ["numpy"] [project.optional-dependencies] docs = ["sphinx", "sphinx-book-theme", "myst-parser", "linkify-it-py"] test = ["pytest"] [project.urls] Homepage = "https://github.com/dofuuz/python-soxr" Documentation = "https://python-soxr.readthedocs.io" Source = "https://github.com/dofuuz/python-soxr" "Bug Tracker" = "https://github.com/dofuuz/python-soxr/issues" [tool.scikit-build] # Protect the configuration against future changes in scikit-build-core minimum-version = "0.9" # Setuptools-style build caching in a local directory build-dir = "build/{wheel_tag}" # Build stable ABI wheels for CPython 3.12+ wheel.py-api = "cp312" metadata.version.provider = "scikit_build_core.metadata.setuptools_scm" sdist.include = ["src/csoxr_ver_vcs.cpp", "src/soxr/_version.py"] sdist.exclude = [".github"] sdist.cmake = true [tool.cibuildwheel] # Necessary to see build output from the actual compilation build-verbosity = 1 # Run pytest to ensure that the package was correctly built test-command = "pytest {project}/tests" test-extras = ["test"] # Needed for full C++17 support [tool.cibuildwheel.macos.environment] MACOSX_DEPLOYMENT_TARGET = "10.14" [tool.setuptools_scm] write_to = "src/soxr/_version.py" python-soxr-0.5.0.post1/src/000077500000000000000000000000001466434512400156735ustar00rootroot00000000000000python-soxr-0.5.0.post1/src/csoxr_ver_vcs.cpp.in000066400000000000000000000001761466434512400216750ustar00rootroot00000000000000// versioning for bundled libsoxr #include "csoxr_version.h" const char * libsoxr_version() { return "@VCS_VERSION@"; } python-soxr-0.5.0.post1/src/csoxr_version.cpp000066400000000000000000000002221466434512400212760ustar00rootroot00000000000000// versioning for USE_SYSTEM_LIBSOXR #include #include "csoxr_version.h" const char * libsoxr_version() { return soxr_version(); } python-soxr-0.5.0.post1/src/csoxr_version.h000066400000000000000000000002671466434512400207540ustar00rootroot00000000000000#ifndef CSOXR_VERSION_H_ #define CSOXR_VERSION_H_ #ifdef __cplusplus extern "C" { #endif const char * libsoxr_version(); #ifdef __cplusplus } #endif #endif /* CSOXR_VERSION_H_ */ python-soxr-0.5.0.post1/src/soxr/000077500000000000000000000000001466434512400166665ustar00rootroot00000000000000python-soxr-0.5.0.post1/src/soxr/__init__.py000066400000000000000000000161311466434512400210010ustar00rootroot00000000000000# Python-SoXR # https://github.com/dofuuz/python-soxr # SPDX-FileCopyrightText: (c) 2021 Myungchul Keum # SPDX-License-Identifier: LGPL-2.1-or-later # High quality, one-dimensional sample-rate conversion library for Python. # Python-SoXR is a Python wrapper of libsoxr. import numpy as np from numpy.typing import ArrayLike from . import soxr_ext from .soxr_ext import QQ, LQ, MQ, HQ, VHQ from ._version import version as __version__ __libsoxr_version__ = soxr_ext.libsoxr_version() # libsoxr locates memory per each channel. # Too much channels will cause memory error. _CH_LIMIT = 65536 _DTYPE_UNMATCH_ERR_STR = 'Input should be a `np.ndarray` with matching dtype for ResampleStream({}).' _CH_EXEED_ERR_STR = 'Channel num({}) out of limit. Should be in [1, %d]' % _CH_LIMIT _DTYPE_ERR_STR = 'Data type must be one of [float32, float64, int16, int32], not {}' _QUALITY_ERR_STR = "Quality must be one of [QQ, LQ, MQ, HQ, VHQ]" def _quality_to_enum(q): if q in (VHQ, HQ, MQ, LQ, QQ): return q if type(q) is int: raise ValueError(_QUALITY_ERR_STR) q = q.lower() if q in ('vhq', 'soxr_vhq'): return VHQ elif q in ('hq', 'soxr_hq'): return HQ elif q in ('mq', 'soxr_mq'): return MQ elif q in ('lq', 'soxr_lq'): return LQ elif q in ('qq', 'soxr_qq'): return QQ raise ValueError(_QUALITY_ERR_STR) def _to_soxr_datatype(ntype): if ntype == np.float32: return soxr_ext.SOXR_FLOAT32_I elif ntype == np.float64: return soxr_ext.SOXR_FLOAT64_I elif ntype == np.int32: return soxr_ext.SOXR_INT32_I elif ntype == np.int16: return soxr_ext.SOXR_INT16_I else: raise TypeError(_DTYPE_ERR_STR.format(ntype)) class ResampleStream: """ Streaming resampler Use `ResampleStream` for real-time processing or very long signal. Parameters ---------- in_rate : float Input sample-rate. out_rate : float Output sample-rate. num_channels : int Number of channels. dtype : type or str, optional Internal data type processed with. Should be one of float32, float64, int16, int32. quality : int or str, optional Quality setting. One of `QQ`, `LQ`, `MQ`, `HQ`, `VHQ`. """ def __init__(self, in_rate: float, out_rate: float, num_channels: int, dtype='float32', quality='HQ'): if in_rate <= 0 or out_rate <= 0: raise ValueError('Sample rate should be over 0') if num_channels < 1 or _CH_LIMIT < num_channels: raise ValueError(_CH_EXEED_ERR_STR.format(num_channels)) self._type = np.dtype(dtype) stype = _to_soxr_datatype(self._type) q = _quality_to_enum(quality) self._csoxr = soxr_ext.CSoxr(in_rate, out_rate, num_channels, stype, q) self._process = getattr(self._csoxr, f'process_{self._type}') def resample_chunk(self, x: np.ndarray, last=False) -> np.ndarray: """ Resample chunk with streaming resampler Parameters ---------- x : np.ndarray Input array. Input can be mono(1D) or multi-channel(2D of [frame, channel]). dtype should match with constructor. last : bool, optional Set True at final chunk to flush last outputs. It should be `True` only once at the end of a continuous sequence. Returns ------- np.ndarray Resampled data. Output is np.ndarray with same ndim with input. """ if type(x) != np.ndarray or x.dtype != self._type: raise TypeError(_DTYPE_UNMATCH_ERR_STR.format(self._type)) x = np.ascontiguousarray(x) # make array C-contiguous if x.ndim == 1: y = self._process(x[:, np.newaxis], last) return np.squeeze(y, axis=1) elif x.ndim == 2: return self._process(x, last) else: raise ValueError('Input must be 1-D or 2-D array') def num_clips(self) -> int: """ Clip counter. (for int I/O) Returns ------- int Count of clipped samples. """ return self._csoxr.num_clips() def delay(self) -> float: """ Get current delay. SoXR output has an algorithmic delay. This function returns the length of current pending output. Returns ------- float Current delay in output samples. """ return self._csoxr.delay() def clear(self) -> None: """ Reset resampler. Ready for fresh signal, same config. This can be used to save initialization time. """ self._csoxr.clear() def resample(x: ArrayLike, in_rate: float, out_rate: float, quality='HQ') -> np.ndarray: """ Resample signal Parameters ---------- x : array_like Input array. Input can be mono(1D) or multi-channel(2D of [frame, channel]). If input is not `np.ndarray`, it will be converted to `np.ndarray(dtype='float32')`. Its dtype should be one of float32, float64, int16, int32. in_rate : float Input sample-rate. out_rate : float Output sample-rate. quality : int or str, optional Quality setting. One of `QQ`, `LQ`, `MQ`, `HQ`, `VHQ`. Returns ------- np.ndarray Resampled data. Output is `np.ndarray` with same ndim and dtype with input. """ if in_rate <= 0 or out_rate <= 0: raise ValueError('Sample rate should be over 0') if type(x) != np.ndarray: x = np.asarray(x, dtype=np.float32) try: if x.strides[0] == x.itemsize: # split channel memory layout divide_proc = getattr(soxr_ext, f'csoxr_split_ch_{x.dtype}') else: divide_proc = getattr(soxr_ext, f'csoxr_divide_proc_{x.dtype}') except AttributeError: raise TypeError(_DTYPE_ERR_STR.format(x.dtype)) q = _quality_to_enum(quality) if x.ndim == 1: y = divide_proc(in_rate, out_rate, x[:, np.newaxis], q) return np.squeeze(y, axis=1) elif x.ndim == 2: num_channels = x.shape[1] if num_channels < 1 or _CH_LIMIT < num_channels: raise ValueError(_CH_EXEED_ERR_STR.format(num_channels)) return divide_proc(in_rate, out_rate, x, q) else: raise ValueError('Input must be 1-D or 2-D array') def _resample_oneshot(x: np.ndarray, in_rate: float, out_rate: float, quality='HQ') -> np.ndarray: """ Resample using libsoxr's `soxr_oneshot()`. Use `resample()` for general use. `soxr_oneshot()` becomes slow with long input. This function exists for test purpose. """ try: oneshot = getattr(soxr_ext, f'csoxr_oneshot_{x.dtype}') except AttributeError: raise TypeError(_DTYPE_ERR_STR.format(x.dtype)) x = np.ascontiguousarray(x) # make array C-contiguous if x.ndim == 1: y = oneshot(in_rate, out_rate, x[:, np.newaxis], _quality_to_enum(quality)) return np.squeeze(y, axis=1) return oneshot(in_rate, out_rate, x, _quality_to_enum(quality)) python-soxr-0.5.0.post1/src/soxr_ext.cpp000066400000000000000000000303611466434512400202550ustar00rootroot00000000000000/* Python-SoXR https://github.com/dofuuz/python-soxr SPDX-FileCopyrightText: (c) 2024 Myungchul Keum SPDX-License-Identifier: LGPL-2.1-or-later High quality, one-dimensional sample-rate conversion library for Python. Python-SoXR is a Python wrapper of libsoxr. */ #include #include #include #include #include #include #include #include #include "csoxr_version.h" using std::type_info; using std::make_unique; namespace nb = nanobind; using namespace nb::literals; using nb::ndarray; static soxr_datatype_t to_soxr_datatype(const type_info& ntype) { if (ntype == typeid(float)) return SOXR_FLOAT32_I; else if (ntype == typeid(double)) return SOXR_FLOAT64_I; else if (ntype == typeid(int32_t)) return SOXR_INT32_I; else if (ntype == typeid(int16_t)) return SOXR_INT16_I; else throw nb::type_error("Data type not support"); } static soxr_datatype_t to_soxr_split_dtype(const type_info& ntype) { if (ntype == typeid(float)) return SOXR_FLOAT32_S; else if (ntype == typeid(double)) return SOXR_FLOAT64_S; else if (ntype == typeid(int32_t)) return SOXR_INT32_S; else if (ntype == typeid(int16_t)) return SOXR_INT16_S; else throw nb::type_error("Data type not support"); } class CSoxr { soxr_t _soxr = nullptr; const double _oi_rate; public: const double _in_rate; const double _out_rate; const soxr_datatype_t _ntype; const unsigned _channels; const size_t _div_len; bool _ended = false; CSoxr(double in_rate, double out_rate, unsigned num_channels, soxr_datatype_t ntype, unsigned long quality) : _in_rate(in_rate), _out_rate(out_rate), _oi_rate(out_rate / in_rate), _ntype(ntype), _channels(num_channels), _div_len(std::max(1000., 48000 * _in_rate / _out_rate)) { soxr_error_t err = NULL; soxr_io_spec_t io_spec = soxr_io_spec(ntype, ntype); soxr_quality_spec_t quality_spec = soxr_quality_spec(quality, 0); _soxr = soxr_create( in_rate, out_rate, num_channels, &err, &io_spec, &quality_spec, NULL); if (err != NULL) { throw std::runtime_error(err); } } ~CSoxr() { soxr_delete(_soxr); } template auto process( ndarray, nb::c_contig, nb::device::cpu> x, bool last=false) { const unsigned channels = x.shape(1); if (_ended) throw std::runtime_error("Input after last input"); if (channels != _channels) throw std::invalid_argument("Channel num mismatch"); const soxr_datatype_t ntype = to_soxr_datatype(typeid(T)); if (ntype != _ntype) throw nb::type_error("Data type mismatch"); T *y = nullptr; soxr_error_t err = NULL; size_t out_pos = 0; { nb::gil_scoped_release release; const size_t ilen = x.shape(0); // This is slower then allocating fixed `ilen * _oi_rate`. // But it insures lowest output delay provided by libsoxr. const size_t olen = soxr_delay(_soxr) + ilen * _oi_rate + 1; // alloc y = new T[olen * channels] { 0 }; // divide long input and process size_t odone = 0; for (size_t idx = 0; idx < ilen; idx += _div_len) { err = soxr_process( _soxr, &x.data()[idx*channels], std::min(_div_len, ilen-idx), NULL, &y[out_pos*channels], olen-out_pos, &odone); out_pos += odone; } // flush if last input if (last) { _ended = true; err = soxr_process( _soxr, NULL, 0, NULL, &y[out_pos*channels], olen-out_pos, &odone); out_pos += odone; } } if (err) { delete[] y; throw std::runtime_error(err); } // Delete 'y' when the 'owner' capsule expires nb::capsule owner(y, [](void *p) noexcept { delete[] (T *) p; }); return ndarray(y, { out_pos, channels }, owner); } size_t num_clips() { return *soxr_num_clips(_soxr); } double delay() { return soxr_delay(_soxr); } char const * engine() { return soxr_engine(_soxr); } void clear() { soxr_error_t err = soxr_clear(_soxr); if (err != NULL) throw std::runtime_error(err); _ended = false; } }; // soxr_oneshot() becomes much slower when input is long. // To avoid this, divide long input and process. template auto csoxr_divide_proc( double in_rate, double out_rate, ndarray, nb::c_contig, nb::device::cpu> x, unsigned long quality) { const unsigned channels = x.shape(1); soxr_error_t err = NULL; T *y = nullptr; size_t out_pos = 0; do { nb::gil_scoped_release release; const soxr_datatype_t ntype = to_soxr_datatype(typeid(T)); // init soxr const soxr_io_spec_t io_spec = soxr_io_spec(ntype, ntype); const soxr_quality_spec_t quality_spec = soxr_quality_spec(quality, 0); soxr_t soxr = soxr_create( in_rate, out_rate, channels, &err, &io_spec, &quality_spec, NULL); if (err) break; // alloc const size_t ilen = x.shape(0); const size_t olen = ilen * out_rate / in_rate + 1; const size_t div_len = std::max(1000., 48000 * in_rate / out_rate); y = new T[olen * channels] { 0 }; // divide long input and process size_t odone = 0; for (size_t idx = 0; idx < ilen; idx += div_len) { err = soxr_process( soxr, &x.data()[idx*channels], std::min(div_len, ilen-idx), NULL, &y[out_pos*channels], olen-out_pos, &odone); out_pos += odone; } // flush err = soxr_process( soxr, NULL, 0, NULL, &y[out_pos*channels], olen-out_pos, &odone); out_pos += odone; // destruct soxr_delete(soxr); } while (false); if (err) { delete[] y; throw std::runtime_error(err); } // Delete 'y' when the 'owner' capsule expires nb::capsule owner(y, [](void *p) noexcept { delete[] (T *) p; }); return ndarray(y, { out_pos, channels }, owner); } // split channel memory I/O (e.g. Fortran order) template auto csoxr_split_ch( double in_rate, double out_rate, ndarray, nb::device::cpu> x, unsigned long quality) { if (in_rate <= 0 || out_rate <= 0) throw std::invalid_argument("Sample rate should be over 0"); const size_t ilen = x.shape(0); const size_t olen = ilen * out_rate / in_rate + 1; const unsigned channels = x.shape(1); if (ilen != 0 && x.stride(0) != 1) throw std::invalid_argument("Data not contiguous"); soxr_error_t err = NULL; T *y = nullptr; size_t out_pos = 0; do { nb::gil_scoped_release release; const soxr_datatype_t ntype = to_soxr_split_dtype(typeid(T)); // init soxr const soxr_io_spec_t io_spec = soxr_io_spec(ntype, ntype); const soxr_quality_spec_t quality_spec = soxr_quality_spec(quality, 0); soxr_t soxr = soxr_create( in_rate, out_rate, channels, &err, &io_spec, &quality_spec, NULL); if (err) break; // alloc const size_t div_len = std::max(1000., 48000 * in_rate / out_rate); y = new T[olen * channels] { 0 }; const int64_t st = x.stride(1); auto ibuf_ptrs = make_unique(channels); auto obuf_ptrs = make_unique(channels); // divide long input and process size_t odone = 0; for (size_t idx = 0; idx < ilen; idx += div_len) { // get pointers to each channel i/o for (size_t ch = 0; ch < channels; ++ch) { ibuf_ptrs[ch] = &x.data()[st * ch + idx]; obuf_ptrs[ch] = &y[olen * ch + out_pos]; } err = soxr_process( soxr, ibuf_ptrs.get(), std::min(div_len, ilen-idx), NULL, obuf_ptrs.get(), olen-out_pos, &odone); out_pos += odone; } // flush for (size_t ch = 0; ch < channels; ++ch) { obuf_ptrs[ch] = &y[olen * ch + out_pos]; } err = soxr_process( soxr, NULL, 0, NULL, obuf_ptrs.get(), olen-out_pos, &odone); out_pos += odone; // destruct soxr_delete(soxr); } while (false); if (err) { delete[] y; throw std::runtime_error(err); } // Delete 'y' when the 'owner' capsule expires nb::capsule owner(y, [](void *p) noexcept { delete[] (T *) p; }); return ndarray(y, { out_pos, channels }, owner, { (int64_t)1, (int64_t)olen }); } template auto csoxr_oneshot( double in_rate, double out_rate, ndarray, nb::c_contig, nb::device::cpu> x, unsigned long quality) { const size_t ilen = x.shape(0); const size_t olen = ilen * out_rate / in_rate + 1; unsigned channels = x.shape(1); const soxr_datatype_t ntype = to_soxr_datatype(typeid(T)); // make soxr config soxr_error_t err = NULL; const soxr_io_spec_t io_spec = soxr_io_spec(ntype, ntype); const soxr_quality_spec_t quality_spec = soxr_quality_spec(quality, 0); size_t odone = 0; T *y = nullptr; { nb::gil_scoped_release release; y = new T[olen * channels] { 0 }; err = soxr_oneshot( in_rate, out_rate, channels, x.data(), ilen, NULL, y, olen, &odone, &io_spec, &quality_spec, NULL); } if (err) { delete[] y; throw std::runtime_error(err); } // Delete 'y' when the 'owner' capsule expires nb::capsule owner(y, [](void *p) noexcept { delete[] (T *) p; }); return ndarray(y, { odone, channels }, owner); } NB_MODULE(soxr_ext, m) { m.def("libsoxr_version", libsoxr_version); nb::class_(m, "CSoxr") .def_ro("in_rate", &CSoxr::_in_rate) .def_ro("out_rate", &CSoxr::_out_rate) .def_ro("ntype", &CSoxr::_ntype) .def_ro("channels", &CSoxr::_channels) .def_ro("ended", &CSoxr::_ended) .def(nb::init()) .def("process_float32", &CSoxr::process) .def("process_float64", &CSoxr::process) .def("process_int32", &CSoxr::process) .def("process_int16", &CSoxr::process) .def("num_clips", &CSoxr::num_clips) .def("delay", &CSoxr::delay) .def("engine", &CSoxr::engine) .def("clear", &CSoxr::clear); m.def("csoxr_divide_proc_float32", csoxr_divide_proc); m.def("csoxr_divide_proc_float64", csoxr_divide_proc); m.def("csoxr_divide_proc_int32", csoxr_divide_proc); m.def("csoxr_divide_proc_int16", csoxr_divide_proc); m.def("csoxr_split_ch_float32", csoxr_split_ch); m.def("csoxr_split_ch_float64", csoxr_split_ch); m.def("csoxr_split_ch_int32", csoxr_split_ch); m.def("csoxr_split_ch_int16", csoxr_split_ch); m.def("csoxr_oneshot_float32", csoxr_oneshot); m.def("csoxr_oneshot_float64", csoxr_oneshot); m.def("csoxr_oneshot_int32", csoxr_oneshot); m.def("csoxr_oneshot_int16", csoxr_oneshot); nb::enum_(m, "soxr_datatype_t") .value("SOXR_FLOAT32_I", SOXR_FLOAT32_I) .value("SOXR_FLOAT64_I", SOXR_FLOAT64_I) .value("SOXR_INT32_I", SOXR_INT32_I) .value("SOXR_INT16_I", SOXR_INT16_I) .export_values(); m.attr("QQ") = SOXR_QQ; m.attr("LQ") = SOXR_LQ; m.attr("MQ") = SOXR_MQ; m.attr("HQ") = SOXR_HQ; m.attr("VHQ") = SOXR_VHQ; } python-soxr-0.5.0.post1/tests/000077500000000000000000000000001466434512400162465ustar00rootroot00000000000000python-soxr-0.5.0.post1/tests/bench.py000066400000000000000000000045201466434512400177000ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ Python-SoXR https://github.com/dofuuz/python-soxr SPDX-FileCopyrightText: (c) 2021 Myungchul Keum SPDX-License-Identifier: LGPL-2.1-or-later Simple speed benchmark for Python-SoXR. """ import timeit import numpy as np import soxr LEN = 96000 REPEAT = 1000 P = 48000 Q = 44100 QUALITY = 'HQ' CHUNK_SIZE = int(P * 0.01) print(f'{soxr.__version__ = }') print(f'{soxr.__libsoxr_version__ = }') print(f'{QUALITY = }') # generate signal offset = 2000 instfreq = np.exp(np.linspace(np.log(offset+100), np.log(offset+23900), LEN))-offset deltaphase = 2*np.pi*instfreq/P cphase = np.cumsum(deltaphase) sig = np.sin(cphase) sig = np.stack([sig, sig, sig, sig], axis=-1, dtype=np.float64) print(f'{sig.shape = }') # soxr oneshot (test purpose only) t = timeit.timeit(lambda: soxr._resample_oneshot(sig, P, Q, quality=QUALITY), number=REPEAT) print(f'soxr oneshot: {t:f} (sec)') # soxr resample t = timeit.timeit(lambda: soxr.resample(sig, P, Q, quality=QUALITY), number=REPEAT) print(f'soxr resample: {t:f} (sec)') # soxr split ch I/O: sig_s = np.asfortranarray(sig) t = timeit.timeit(lambda: soxr.resample(sig_s, P, Q, quality=QUALITY), number=REPEAT) print(f'soxr split ch I/O: {t:f} (sec)') # soxr with clear() # It becomes faster then soxr.resample() when input length (=LEN) is short rs = soxr.ResampleStream(P, Q, sig.shape[1], dtype=sig.dtype, quality=QUALITY) def soxr_with_reset(): rs.clear() return rs.resample_chunk(sig, last=True) t = timeit.timeit(soxr_with_reset, number=REPEAT) print(f'soxr w/ clear(): {t:f} (sec)') # soxr stream chunk processing def soxr_stream(): rs_stream = soxr.ResampleStream(P, Q, sig.shape[1], dtype=sig.dtype, quality=QUALITY) y_list = [] for idx in range(0, len(sig), CHUNK_SIZE): end = idx + CHUNK_SIZE eof = False if len(sig) <= end: eof = True end = len(sig) y_chunk = rs_stream.resample_chunk(sig[idx:end], last=eof) y_list.append(y_chunk) return np.concatenate(y_list) t = timeit.timeit(soxr_stream, number=REPEAT) print(f'{CHUNK_SIZE = }') print(f'soxr stream: {t:f} (sec)') # resampy kaiser_fast try: import resampy t = timeit.timeit(lambda: resampy.resample(sig.T, P, Q, filter='kaiser_fast'), number=REPEAT) print(f'resampy kaiser_fast: {t:f} (sec)') except ModuleNotFoundError: pass python-soxr-0.5.0.post1/tests/bench_soxr_oneshot.py000066400000000000000000000030711466434512400225120ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ Python-SoXR https://github.com/dofuuz/python-soxr SPDX-FileCopyrightText: (c) 2021 Myungchul Keum SPDX-License-Identifier: LGPL-2.1-or-later soxr_oneshot() becomes much slower when input is long. This script demonstrates it. soxr.resample() divides input automatically to retain speed. """ import timeit import matplotlib.pyplot as plt import numpy as np import soxr P = 48000 Q = 44100 # generate signal offset = 2000 instfreq = np.exp(np.linspace(np.log(offset+100), np.log(offset+23900), 96000*5))-offset deltaphase = 2*np.pi*instfreq/P cphase = np.cumsum(deltaphase) sig1 = np.sin(cphase) sig2 = np.cos(cphase) sig_i = np.stack([sig1, sig2, sig1, sig2], axis=-1, dtype=np.float64) # C memory order (interleaved) sig_s = np.asarray([sig1, sig2, sig1, sig2], dtype=np.float64).T # Fortran memory order (channel splited) in_lens = range(4800, len(sig1), 4800) time_divide = [] time_oneshot = [] time_split = [] for length in in_lens: # soxr resample time_proc = timeit.timeit(lambda: soxr.resample(sig_i[:length], P, Q), number=2) time_divide.append(time_proc) # soxr resample w/ split channel I/O time_proc = timeit.timeit(lambda: soxr.resample(sig_s[:length], P, Q), number=2) time_split.append(time_proc) # soxr oneshot time_proc = timeit.timeit(lambda: soxr._resample_oneshot(sig_i[:length], P, Q), number=2) time_oneshot.append(time_proc) plt.plot(in_lens, time_divide, label='divide') plt.plot(in_lens, time_split, label='split ch') plt.plot(in_lens, time_oneshot, label='oneshot') plt.legend() plt.show() python-soxr-0.5.0.post1/tests/gil_bench.py000066400000000000000000000021011466434512400205240ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ Created on Wed Apr 13 00:33:32 2022 @author: dof """ import asyncio from time import time import numpy as np import soxr # generate 150s 2ch data fs = 44100 ch1 = np.sin(np.linspace(0, fs, 150 * 44100)) ch2 = np.cos(np.linspace(0, fs, 150 * 44100)) data = np.stack([ch1, ch2], axis=-1) print(data.shape) def resample(): soxr.resample(data, fs, 24000) async def th_resample(): await asyncio.to_thread(resample) async def main(): t = time() resample() print(time() - t) t = time() await asyncio.gather(th_resample()) print(time() - t) t = time() await asyncio.gather(th_resample(), th_resample()) print(time() - t) t = time() await asyncio.gather(th_resample(), th_resample(), th_resample()) print(time() - t) t = time() await asyncio.gather(th_resample(), th_resample(), th_resample(), th_resample()) print(time() - t) t = time() await asyncio.gather(th_resample(), th_resample(), th_resample(), th_resample(), th_resample()) print(time() - t) asyncio.run(main()) python-soxr-0.5.0.post1/tests/resample_stream.py000066400000000000000000000014001466434512400217760ustar00rootroot00000000000000import soundfile as sf import soxr TARGET_RATE = 16000 CHUNK_SIZE = 96000 # Open input audio file in_file = sf.SoundFile('very_long.flac', 'r') source_rate = in_file.samplerate channels = in_file.channels # Config ResampleStream resampler = soxr.ResampleStream(source_rate, TARGET_RATE, channels, dtype='float32') # Open output audio file with sf.SoundFile('output.flac', 'w', TARGET_RATE, channels) as out_file: while True: # Read chunk of audio x = in_file.read(CHUNK_SIZE, dtype='float32') is_last = (in_file.tell() == in_file.frames) # Resample the chunk y = resampler.resample_chunk(x, last=is_last) # Write to output file out_file.write(y) if is_last: break in_file.close() python-soxr-0.5.0.post1/tests/test_resample.py000066400000000000000000000137761466434512400215050ustar00rootroot00000000000000""" Python-SoXR https://github.com/dofuuz/python-soxr SPDX-FileCopyrightText: (c) 2021 Myungchul Keum SPDX-License-Identifier: LGPL-2.1-or-later High quality, one-dimensional sample-rate conversion library for Python. Python-SoXR is a Python wrapper of libsoxr. """ from concurrent.futures import ThreadPoolExecutor from functools import partial import numpy as np import pytest import soxr @pytest.mark.xfail(raises=ValueError, strict=True) @pytest.mark.parametrize('in_rate, out_rate', [(100, 0), (100, -1), (0, 100), (-1, 100)]) def test_bad_sr(in_rate, out_rate): x = np.zeros(100) soxr.resample(x, in_rate, out_rate) @pytest.mark.parametrize('dtype', [np.float32, np.float64, np.int16, np.int32]) def test_dtype(dtype): x = np.random.randn(100).astype(dtype) y = soxr.resample(x, 100, 200) assert x.dtype == y.dtype @pytest.mark.xfail(raises=(TypeError, ValueError), strict=True) @pytest.mark.parametrize('dtype', [np.complex64, np.complex128, np.int8, np.int64]) def test_bad_dtype(dtype): x = np.zeros(100, dtype=dtype) soxr.resample(x, 100, 200) @pytest.mark.parametrize('in_rate, out_rate', [(44100, 32000), (32000, 44100)]) @pytest.mark.parametrize('dtype', [np.float32, np.float64]) def test_divide_match(in_rate, out_rate, dtype): x = np.random.randn(25999,2).astype(dtype) y_oneshot = soxr._resample_oneshot(x, in_rate, out_rate) y_divide = soxr.resample(x, in_rate, out_rate) y_split = soxr.resample(np.asfortranarray(x), in_rate, out_rate) assert np.allclose(y_oneshot, y_divide) assert np.allclose(y_oneshot, y_split) @pytest.mark.parametrize('in_rate, out_rate', [(44100, 32000), (32000, 44100)]) @pytest.mark.parametrize('length', [0, 1, 2, 99, 100, 101, 31999, 32000, 32001, 34828, 34829, 34830, 44099, 44100, 44101, 47999, 48000, 48001, 66149, 66150, 266151]) def test_length_match(in_rate, out_rate, length): x = np.random.randn(266151, 2).astype(np.float32) y_oneshot = soxr._resample_oneshot(x[:length], in_rate, out_rate) y_divide = soxr.resample(x[:length], in_rate, out_rate) y_split = soxr.resample(np.asfortranarray(x)[:length], in_rate, out_rate) assert np.allclose(y_oneshot, y_divide) assert np.allclose(y_oneshot, y_split) @pytest.mark.parametrize('channels', [1, 2, 3, 5, 7, 24, 49]) def test_channel_match(channels): x = np.random.randn(30011, channels).astype(np.float32) y_oneshot = soxr._resample_oneshot(x, 44100, 32000) y_divide = soxr.resample(x, 44100, 32000) y_split = soxr.resample(np.asfortranarray(x), 44100, 32000) assert np.allclose(y_oneshot, y_divide) assert np.allclose(y_oneshot, y_split) @pytest.mark.parametrize('in_rate, out_rate', [(44100, 32000), (32000, 44100)]) @pytest.mark.parametrize('dtype', [np.float32, np.float64]) @pytest.mark.parametrize('channels', [1, 2]) def test_stream_match(in_rate, out_rate, dtype, channels): CHUNK_SIZE = 509 x = np.random.randn(49999, channels).astype(dtype) y_oneshot = soxr._resample_oneshot(x, in_rate, out_rate) rs_stream = soxr.ResampleStream(in_rate, out_rate, channels, dtype=dtype) y_list = [] for idx in range(0, len(x), CHUNK_SIZE): end = idx + CHUNK_SIZE eof = False if len(x) <= end: eof = True end = len(x) y_chunk = rs_stream.resample_chunk(x[idx:end], last=eof) y_list.append(y_chunk) y_stream = np.concatenate(y_list) assert np.allclose(y_oneshot, y_stream) @pytest.mark.parametrize('in_rate, out_rate', [(44100, 32000), (32000, 44100)]) @pytest.mark.parametrize('chunk_size', [7, 50, 101, 44100]) @pytest.mark.parametrize('length', [0, 1, 100, 101, 31999, 32000, 44100, 44101, 266151]) def test_stream_length(in_rate, out_rate, chunk_size, length): x = np.random.randn(length, 1).astype(np.float32) y_oneshot = soxr._resample_oneshot(x, in_rate, out_rate) rs_stream = soxr.ResampleStream(in_rate, out_rate, 1, dtype=np.float32) y_list = [np.ndarray([0, 1], dtype=np.float32)] for idx in range(0, len(x), chunk_size): end = idx + chunk_size eof = False if len(x) <= end: eof = True end = len(x) y_chunk = rs_stream.resample_chunk(x[idx:end], last=eof) y_list.append(y_chunk) y_stream = np.concatenate(y_list) assert np.allclose(y_oneshot, y_stream) def make_tone(freq, sr, duration): length = int(sr * duration) sig = np.sin(2 * np.pi * freq / sr * np.arange(length)) sig = sig * np.hanning(length) return np.stack([sig, np.zeros_like(sig)], axis=-1) @pytest.mark.parametrize('in_rate,out_rate', [(44100, 22050), (22050, 32000)]) @pytest.mark.parametrize('quality', ['VHQ', 'HQ', 'MQ', 'LQ', 'QQ']) def test_quality_sine(in_rate, out_rate, quality): FREQ = 32.0 DURATION = 2.0 x = make_tone(FREQ, in_rate, DURATION) y = make_tone(FREQ, out_rate, DURATION) y_pred = soxr.resample(x, in_rate, out_rate, quality=quality) y_split = soxr.resample(np.asfortranarray(x), in_rate, out_rate, quality=quality) assert np.allclose(y, y_pred, atol=1e-4) assert np.allclose(y, y_split, atol=1e-4) @pytest.mark.parametrize('in_rate,out_rate', [(48000, 24000), (32000, 44100)]) @pytest.mark.parametrize('dtype', [np.int32, np.int16]) def test_int_sine(in_rate, out_rate, dtype): FREQ = 32.0 DURATION = 2.0 x = (make_tone(FREQ, in_rate, DURATION) * 16384).astype(dtype) y = (make_tone(FREQ, out_rate, DURATION) * 16384).astype(dtype) y_pred = soxr.resample(x, in_rate, out_rate) y_split = soxr.resample(np.asfortranarray(x), in_rate, out_rate) assert np.allclose(y, y_pred, atol=2) assert np.allclose(y, y_split, atol=2) @pytest.mark.parametrize('num_task', [2, 3, 4, 5, 6, 7, 8, 9, 12, 17, 32]) def test_multithread(num_task): x = np.random.randn(25999, 2).astype(np.float32) with ThreadPoolExecutor() as p: results = p.map( partial(soxr.resample, in_rate=44100, out_rate=32000), [x] * num_task ) results = list(results) assert np.allclose(results[-2], results[-1])