sox-effects 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sox_effects-0.1.0/.github/dependabot.yml +14 -0
- sox_effects-0.1.0/.github/workflows/ci.yml +186 -0
- sox_effects-0.1.0/.github/workflows/release.yml +75 -0
- sox_effects-0.1.0/.gitignore +12 -0
- sox_effects-0.1.0/CMakeLists.txt +31 -0
- sox_effects-0.1.0/LICENSE +21 -0
- sox_effects-0.1.0/PKG-INFO +108 -0
- sox_effects-0.1.0/README.md +99 -0
- sox_effects-0.1.0/prek.toml +48 -0
- sox_effects-0.1.0/pyproject.toml +57 -0
- sox_effects-0.1.0/src/sox_effects/__init__.py +77 -0
- sox_effects-0.1.0/src/sox_effects/py.typed +0 -0
- sox_effects-0.1.0/src/sox_effects/sox_effects.cpp +290 -0
- sox_effects-0.1.0/tests/test_sox_effects.py +293 -0
- sox_effects-0.1.0/uv.lock +817 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
permissions: {}
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
pull_request:
|
|
9
|
+
branches:
|
|
10
|
+
- main
|
|
11
|
+
workflow_call: # Called by the release workflow, which publishes the dists built here.
|
|
12
|
+
|
|
13
|
+
concurrency:
|
|
14
|
+
group: ci-${{ github.ref }}
|
|
15
|
+
cancel-in-progress: true
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
lint:
|
|
19
|
+
name: Linters
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
permissions:
|
|
22
|
+
security-events: write # Required for upload-sarif (used by zizmor-action) to upload SARIF files.
|
|
23
|
+
steps:
|
|
24
|
+
- name: Checkout code
|
|
25
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
26
|
+
with:
|
|
27
|
+
persist-credentials: false
|
|
28
|
+
|
|
29
|
+
- name: zizmor
|
|
30
|
+
uses: zizmorcore/zizmor-action@45d7276c9ca790aabd705e31ca99c6185c3e76dd # v0.8.1
|
|
31
|
+
|
|
32
|
+
- name: Install SoX
|
|
33
|
+
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends libsox-dev # ty builds the package
|
|
34
|
+
|
|
35
|
+
- name: prek
|
|
36
|
+
uses: j178/prek-action@1382ad72dea72f9d53832f2bcb65b3e87a947204 # v3.0.1
|
|
37
|
+
|
|
38
|
+
test:
|
|
39
|
+
name: Test source code (${{ matrix.os }}, Python ${{ matrix.python-version }})
|
|
40
|
+
runs-on: ${{ matrix.os }}
|
|
41
|
+
needs: lint
|
|
42
|
+
strategy:
|
|
43
|
+
fail-fast: false
|
|
44
|
+
matrix:
|
|
45
|
+
os:
|
|
46
|
+
- ubuntu-latest
|
|
47
|
+
- macos-latest
|
|
48
|
+
python-version:
|
|
49
|
+
- "3.12"
|
|
50
|
+
- "3.13"
|
|
51
|
+
- "3.14"
|
|
52
|
+
steps:
|
|
53
|
+
- name: Checkout code
|
|
54
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
55
|
+
with:
|
|
56
|
+
persist-credentials: false
|
|
57
|
+
|
|
58
|
+
- name: Install SoX (Linux)
|
|
59
|
+
if: runner.os == 'Linux'
|
|
60
|
+
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends libsox-dev sox
|
|
61
|
+
|
|
62
|
+
- name: Install SoX (macOS)
|
|
63
|
+
if: runner.os == 'macOS'
|
|
64
|
+
run: brew install sox
|
|
65
|
+
|
|
66
|
+
- name: Install uv
|
|
67
|
+
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
|
|
68
|
+
with:
|
|
69
|
+
version: "latest"
|
|
70
|
+
enable-cache: false
|
|
71
|
+
python-version: ${{ matrix.python-version }}
|
|
72
|
+
|
|
73
|
+
- name: Run tests
|
|
74
|
+
run: uv run --locked --group test pytest
|
|
75
|
+
|
|
76
|
+
test-lowest:
|
|
77
|
+
name: Test with the lowest supported dependencies (${{ matrix.os }})
|
|
78
|
+
runs-on: ${{ matrix.os }}
|
|
79
|
+
needs: lint
|
|
80
|
+
strategy:
|
|
81
|
+
fail-fast: false
|
|
82
|
+
matrix:
|
|
83
|
+
os:
|
|
84
|
+
- ubuntu-latest
|
|
85
|
+
- macos-latest
|
|
86
|
+
steps:
|
|
87
|
+
- name: Checkout code
|
|
88
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
89
|
+
with:
|
|
90
|
+
persist-credentials: false
|
|
91
|
+
|
|
92
|
+
- name: Install SoX (Linux)
|
|
93
|
+
if: runner.os == 'Linux'
|
|
94
|
+
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends libsox-dev sox
|
|
95
|
+
|
|
96
|
+
- name: Install SoX (macOS)
|
|
97
|
+
if: runner.os == 'macOS'
|
|
98
|
+
run: brew install sox
|
|
99
|
+
|
|
100
|
+
- name: Install uv
|
|
101
|
+
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
|
|
102
|
+
with:
|
|
103
|
+
version: "latest"
|
|
104
|
+
enable-cache: false
|
|
105
|
+
python-version: "3.12"
|
|
106
|
+
|
|
107
|
+
- name: Lock the lowest dependencies
|
|
108
|
+
run: uv lock --resolution lowest-direct
|
|
109
|
+
|
|
110
|
+
- name: Run tests
|
|
111
|
+
run: uv run --locked --resolution lowest-direct --group test pytest
|
|
112
|
+
|
|
113
|
+
build-wheels:
|
|
114
|
+
name: Build wheels (${{ matrix.os }})
|
|
115
|
+
runs-on: ${{ matrix.os }}
|
|
116
|
+
needs: [test, test-lowest]
|
|
117
|
+
strategy:
|
|
118
|
+
fail-fast: false
|
|
119
|
+
matrix:
|
|
120
|
+
os:
|
|
121
|
+
- ubuntu-22.04
|
|
122
|
+
- ubuntu-22.04-arm
|
|
123
|
+
- macos-15
|
|
124
|
+
permissions:
|
|
125
|
+
artifact-metadata: write # Create the artifact storage record
|
|
126
|
+
attestations: write # Write the build provenance to the repository attestation store
|
|
127
|
+
id-token: write # Sigstore signing of the provenance
|
|
128
|
+
steps:
|
|
129
|
+
- name: Checkout code
|
|
130
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
131
|
+
with:
|
|
132
|
+
persist-credentials: false
|
|
133
|
+
fetch-depth: 0 # setuptools-scm needs the tags
|
|
134
|
+
|
|
135
|
+
- name: Build wheels
|
|
136
|
+
uses: pypa/cibuildwheel@f9b966cfadf4109556cadc5bb39ddb72d0ce664e # v4.3.5
|
|
137
|
+
with:
|
|
138
|
+
output-dir: dist
|
|
139
|
+
|
|
140
|
+
- name: Attest build provenance
|
|
141
|
+
if: startsWith(github.ref, 'refs/tags/')
|
|
142
|
+
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
|
|
143
|
+
with:
|
|
144
|
+
subject-path: dist/*.whl
|
|
145
|
+
|
|
146
|
+
- name: Upload wheels
|
|
147
|
+
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
148
|
+
with:
|
|
149
|
+
name: dist-wheels-${{ matrix.os }}
|
|
150
|
+
path: dist/*.whl
|
|
151
|
+
|
|
152
|
+
build-sdist:
|
|
153
|
+
name: Build sdist
|
|
154
|
+
runs-on: ubuntu-latest
|
|
155
|
+
needs: [test, test-lowest]
|
|
156
|
+
permissions:
|
|
157
|
+
artifact-metadata: write # Create the artifact storage record
|
|
158
|
+
attestations: write # Write the build provenance to the repository attestation store
|
|
159
|
+
id-token: write # Sigstore signing of the provenance
|
|
160
|
+
steps:
|
|
161
|
+
- name: Checkout code
|
|
162
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
163
|
+
with:
|
|
164
|
+
persist-credentials: false
|
|
165
|
+
fetch-depth: 0 # setuptools-scm needs the tags to compute the version
|
|
166
|
+
|
|
167
|
+
- name: Install uv
|
|
168
|
+
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
|
|
169
|
+
with:
|
|
170
|
+
version: "latest"
|
|
171
|
+
enable-cache: false
|
|
172
|
+
|
|
173
|
+
- name: Build sdist
|
|
174
|
+
run: uv build --sdist
|
|
175
|
+
|
|
176
|
+
- name: Attest build provenance
|
|
177
|
+
if: startsWith(github.ref, 'refs/tags/')
|
|
178
|
+
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
|
|
179
|
+
with:
|
|
180
|
+
subject-path: dist/*.tar.gz
|
|
181
|
+
|
|
182
|
+
- name: Upload sdist
|
|
183
|
+
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
184
|
+
with:
|
|
185
|
+
name: dist-sdist
|
|
186
|
+
path: dist/*.tar.gz
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
name: Release workflow
|
|
2
|
+
permissions: {}
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
tags:
|
|
7
|
+
- "*"
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: release-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: false
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
ci:
|
|
15
|
+
name: CI
|
|
16
|
+
uses: $/.github/workflows/ci.yml
|
|
17
|
+
# The called jobs can't get more than what is granted here: this is the union of what they request.
|
|
18
|
+
permissions:
|
|
19
|
+
security-events: write # Upload zizmor's SARIF report
|
|
20
|
+
artifact-metadata: write # Create the artifact storage record
|
|
21
|
+
attestations: write # Write the build provenance to the repository attestation store
|
|
22
|
+
id-token: write # Sigstore signing of the provenance
|
|
23
|
+
|
|
24
|
+
publish-to-pypi:
|
|
25
|
+
name: Publish to PyPI
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
needs: ci
|
|
28
|
+
environment:
|
|
29
|
+
name: publish-to-pypi
|
|
30
|
+
permissions:
|
|
31
|
+
id-token: write # Needed for trusted publishing, and to sign the attestations
|
|
32
|
+
steps:
|
|
33
|
+
- name: Download all dists
|
|
34
|
+
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
|
35
|
+
with:
|
|
36
|
+
pattern: dist-*
|
|
37
|
+
merge-multiple: true
|
|
38
|
+
path: dist/
|
|
39
|
+
|
|
40
|
+
- name: Install uv
|
|
41
|
+
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
|
|
42
|
+
with:
|
|
43
|
+
version: "latest"
|
|
44
|
+
enable-cache: false
|
|
45
|
+
|
|
46
|
+
- name: Generate PEP 740 attestations
|
|
47
|
+
uses: astral-sh/attest-action@f589a42a7efb6fe400b4f400de60b4bc90390027 # v0.0.6
|
|
48
|
+
|
|
49
|
+
- name: Upload to PyPI
|
|
50
|
+
run: uv publish --trusted-publishing always
|
|
51
|
+
|
|
52
|
+
github-release:
|
|
53
|
+
name: GitHub release
|
|
54
|
+
runs-on: ubuntu-latest
|
|
55
|
+
needs: publish-to-pypi # Only tag a release once it is actually on PyPI.
|
|
56
|
+
permissions:
|
|
57
|
+
contents: write # Create GitHub release
|
|
58
|
+
steps:
|
|
59
|
+
- name: Checkout code
|
|
60
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
61
|
+
with:
|
|
62
|
+
persist-credentials: false
|
|
63
|
+
|
|
64
|
+
- name: Download all dists
|
|
65
|
+
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
|
66
|
+
with:
|
|
67
|
+
pattern: dist-*
|
|
68
|
+
merge-multiple: true
|
|
69
|
+
path: ./dist/
|
|
70
|
+
|
|
71
|
+
- name: Create release
|
|
72
|
+
env:
|
|
73
|
+
GITHUB_TOKEN: ${{ github.token }}
|
|
74
|
+
TAG: ${{ github.ref_name }}
|
|
75
|
+
run: gh release create "$TAG" --verify-tag ./dist/*
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.20)
|
|
2
|
+
project(${SKBUILD_PROJECT_NAME} LANGUAGES CXX)
|
|
3
|
+
|
|
4
|
+
find_package(Python 3.12 REQUIRED COMPONENTS Interpreter Development.Module ${SKBUILD_SABI_COMPONENT})
|
|
5
|
+
find_package(nanobind CONFIG REQUIRED)
|
|
6
|
+
|
|
7
|
+
if(NOT "$ENV{SOX_ROOT}" STREQUAL "")
|
|
8
|
+
find_path(SOX_INCLUDE_DIR sox.h PATHS "$ENV{SOX_ROOT}/include" PATH_SUFFIXES sox NO_DEFAULT_PATH)
|
|
9
|
+
else()
|
|
10
|
+
find_program(SOX_EXECUTABLE sox)
|
|
11
|
+
if(SOX_EXECUTABLE)
|
|
12
|
+
file(REAL_PATH "${SOX_EXECUTABLE}" _sox)
|
|
13
|
+
cmake_path(GET _sox PARENT_PATH _sox_bin)
|
|
14
|
+
cmake_path(GET _sox_bin PARENT_PATH _sox_root)
|
|
15
|
+
endif()
|
|
16
|
+
find_path(
|
|
17
|
+
SOX_INCLUDE_DIR sox.h
|
|
18
|
+
HINTS "${_sox_root}/include"
|
|
19
|
+
PATHS /usr/include /usr/local/include /opt/homebrew/include
|
|
20
|
+
PATH_SUFFIXES sox # RHEL/Fedora sox-devel installs include/sox/sox.h
|
|
21
|
+
)
|
|
22
|
+
endif()
|
|
23
|
+
if(NOT SOX_INCLUDE_DIR)
|
|
24
|
+
message(FATAL_ERROR "sox.h not found: install the libsox headers (libsox-dev, sox-devel, brew install sox) or set SOX_ROOT")
|
|
25
|
+
endif()
|
|
26
|
+
|
|
27
|
+
nanobind_add_module(_C STABLE_ABI NB_STATIC src/sox_effects/sox_effects.cpp)
|
|
28
|
+
target_compile_features(_C PRIVATE cxx_std_17)
|
|
29
|
+
target_include_directories(_C PRIVATE "${SOX_INCLUDE_DIR}")
|
|
30
|
+
target_link_libraries(_C PRIVATE ${CMAKE_DL_LIBS})
|
|
31
|
+
install(TARGETS _C LIBRARY DESTINATION sox_effects)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Maxime Poli.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sox-effects
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Minimal libsox effects bindings for any array
|
|
5
|
+
Author: Maxime Poli
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Requires-Python: >=3.12
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
|
|
10
|
+
# sox-effects
|
|
11
|
+
|
|
12
|
+
Apply [SoX](https://sox.sourceforge.net/) audio effects (pitch, reverb, tempo, gain, …) directly to arrays from
|
|
13
|
+
NumPy, PyTorch, JAX, or any library that supports DLPack or the buffer protocol. It has no Python dependencies,
|
|
14
|
+
and replaces `torchaudio.sox_effects.apply_effects_tensor`, which was removed from torchaudio.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
pip install sox-effects
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
You'll also need the SoX library on your system:
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
apt install libsox3 # Debian / Ubuntu
|
|
26
|
+
dnf install sox # Fedora / RHEL
|
|
27
|
+
brew install sox # macOS
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
It works with Python >= 3.12, on Linux and macOS.
|
|
31
|
+
|
|
32
|
+
## Quick start
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
import numpy as np, sox_effects
|
|
36
|
+
|
|
37
|
+
wav = np.random.randn(2, 16000).astype(np.float32) * 0.1 # (channels, time), float32, CPU
|
|
38
|
+
out, sr = sox_effects.apply_effects(
|
|
39
|
+
wav, 16000, [["gain", "-n"], ["pitch", "200"], ["reverb", "50"], ["rate", "16000"]]
|
|
40
|
+
)
|
|
41
|
+
sox_effects.effect_names() # list the available effects
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Effects and their arguments are the same as on the `sox` command line. The output comes back in the same
|
|
45
|
+
library as the input (a torch tensor in, a torch tensor out), without copies at the boundaries.
|
|
46
|
+
For `(time, channels)` arrays, pass `channels_first=False`.
|
|
47
|
+
|
|
48
|
+
### Random augmentation
|
|
49
|
+
|
|
50
|
+
`EffectChain` lets you build a chain of effects step by step. Every SoX effect is available as a
|
|
51
|
+
method, and any argument given as a function is re-drawn each time you call `apply`:
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
import random
|
|
55
|
+
from sox_effects import EffectChain
|
|
56
|
+
|
|
57
|
+
chain = EffectChain().pitch("-q", lambda: random.randint(-300, 300)).rate(16000).reverb(50).channels(1)
|
|
58
|
+
out, sr = chain.apply(wav, 16000)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Good to know
|
|
62
|
+
|
|
63
|
+
- **Inputs are strict.** Arrays must be 2D, float32 and on the CPU: anything else raises an error.
|
|
64
|
+
SoX effects aren't differentiable, so torch tensors that require grad are rejected too: call
|
|
65
|
+
`.detach()` first (no copy) to cut the gradient explicitly. Dataset tensors used for augmentation
|
|
66
|
+
don't require grad, so this only concerns outputs of a network.
|
|
67
|
+
- **Keep values in [-1, 1].** SoX silently clips anything outside this range, including what an
|
|
68
|
+
effect pushes past full scale (e.g. `gain +10`). Use `["gain", "-n"]` or `vol` to stay safe.
|
|
69
|
+
- **The output may change shape.** `speed`, `pitch` and `tempo` change the length, and `speed` and
|
|
70
|
+
`pitch` also change the sample rate: add `["rate", "<sr>"]` to get back to your rate. `reverb`
|
|
71
|
+
turns mono into stereo: add `["channels", "1"]`. Nothing is added for you, so with a
|
|
72
|
+
`DataLoader` you may need to crop/pad or use a custom `collate_fn`.
|
|
73
|
+
- **Always pass arguments.** SoX's defaults aren't always sensible: `["tempo"]` on its own never
|
|
74
|
+
returns.
|
|
75
|
+
- **Go faster with `-q`.** For augmentation, quick mode trades a little quality for speed:
|
|
76
|
+
`["pitch", "-q", "200"]`, `["tempo", "-q", "1.1"]`, `["rate", "-q", "16000"]` (the latter can
|
|
77
|
+
alias). Put `-q` before the effect's other arguments.
|
|
78
|
+
- **Parallelism.** Threads run in parallel, but `DataLoader` worker processes are the safer choice.
|
|
79
|
+
With `spawn`/`forkserver` workers, use regular functions instead of lambdas in an `EffectChain`
|
|
80
|
+
(lambdas can't be pickled).
|
|
81
|
+
- Effects that read or write files (`spectrogram`, `noiseprof`, `noisered`, `splice`) aren't supported.
|
|
82
|
+
|
|
83
|
+
## Troubleshooting
|
|
84
|
+
|
|
85
|
+
`import sox_effects` always works; the SoX library is only loaded on the first call. If it can't be
|
|
86
|
+
found, install it as shown above, or point `LIBSOX_PATH` to the library file (e.g.
|
|
87
|
+
`libsox.so.3`).
|
|
88
|
+
|
|
89
|
+
## Development
|
|
90
|
+
|
|
91
|
+
The extension is built with [nanobind](https://github.com/wjakob/nanobind) and
|
|
92
|
+
[scikit-build-core](https://github.com/scikit-build/scikit-build-core). Building from source only needs the
|
|
93
|
+
`sox.h` header, which is looked up in `$SOX_ROOT/include`, next to the `sox` executable on your `PATH`, or in
|
|
94
|
+
`/usr`, `/usr/local` and `/opt/homebrew` (as `include/sox.h` or `include/sox/sox.h`).
|
|
95
|
+
|
|
96
|
+
```sh
|
|
97
|
+
uv run --group test pytest # or: pip install . --group test && pytest
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The tests run against NumPy, PyTorch and JAX, which are test dependencies only.
|
|
101
|
+
|
|
102
|
+
The extension uses the Python stable ABI, so a single build works across all Python versions >= 3.12.
|
|
103
|
+
|
|
104
|
+
## License
|
|
105
|
+
|
|
106
|
+
sox-effects is released under the MIT License. It builds on the approach from
|
|
107
|
+
[torchaudio](https://github.com/pytorch/audio) (BSD 2-Clause License) and
|
|
108
|
+
[WavAugment](https://github.com/facebookresearch/WavAugment) (MIT License).
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# sox-effects
|
|
2
|
+
|
|
3
|
+
Apply [SoX](https://sox.sourceforge.net/) audio effects (pitch, reverb, tempo, gain, …) directly to arrays from
|
|
4
|
+
NumPy, PyTorch, JAX, or any library that supports DLPack or the buffer protocol. It has no Python dependencies,
|
|
5
|
+
and replaces `torchaudio.sox_effects.apply_effects_tensor`, which was removed from torchaudio.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
pip install sox-effects
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
You'll also need the SoX library on your system:
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
apt install libsox3 # Debian / Ubuntu
|
|
17
|
+
dnf install sox # Fedora / RHEL
|
|
18
|
+
brew install sox # macOS
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
It works with Python >= 3.12, on Linux and macOS.
|
|
22
|
+
|
|
23
|
+
## Quick start
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
import numpy as np, sox_effects
|
|
27
|
+
|
|
28
|
+
wav = np.random.randn(2, 16000).astype(np.float32) * 0.1 # (channels, time), float32, CPU
|
|
29
|
+
out, sr = sox_effects.apply_effects(
|
|
30
|
+
wav, 16000, [["gain", "-n"], ["pitch", "200"], ["reverb", "50"], ["rate", "16000"]]
|
|
31
|
+
)
|
|
32
|
+
sox_effects.effect_names() # list the available effects
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Effects and their arguments are the same as on the `sox` command line. The output comes back in the same
|
|
36
|
+
library as the input (a torch tensor in, a torch tensor out), without copies at the boundaries.
|
|
37
|
+
For `(time, channels)` arrays, pass `channels_first=False`.
|
|
38
|
+
|
|
39
|
+
### Random augmentation
|
|
40
|
+
|
|
41
|
+
`EffectChain` lets you build a chain of effects step by step. Every SoX effect is available as a
|
|
42
|
+
method, and any argument given as a function is re-drawn each time you call `apply`:
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
import random
|
|
46
|
+
from sox_effects import EffectChain
|
|
47
|
+
|
|
48
|
+
chain = EffectChain().pitch("-q", lambda: random.randint(-300, 300)).rate(16000).reverb(50).channels(1)
|
|
49
|
+
out, sr = chain.apply(wav, 16000)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Good to know
|
|
53
|
+
|
|
54
|
+
- **Inputs are strict.** Arrays must be 2D, float32 and on the CPU: anything else raises an error.
|
|
55
|
+
SoX effects aren't differentiable, so torch tensors that require grad are rejected too: call
|
|
56
|
+
`.detach()` first (no copy) to cut the gradient explicitly. Dataset tensors used for augmentation
|
|
57
|
+
don't require grad, so this only concerns outputs of a network.
|
|
58
|
+
- **Keep values in [-1, 1].** SoX silently clips anything outside this range, including what an
|
|
59
|
+
effect pushes past full scale (e.g. `gain +10`). Use `["gain", "-n"]` or `vol` to stay safe.
|
|
60
|
+
- **The output may change shape.** `speed`, `pitch` and `tempo` change the length, and `speed` and
|
|
61
|
+
`pitch` also change the sample rate: add `["rate", "<sr>"]` to get back to your rate. `reverb`
|
|
62
|
+
turns mono into stereo: add `["channels", "1"]`. Nothing is added for you, so with a
|
|
63
|
+
`DataLoader` you may need to crop/pad or use a custom `collate_fn`.
|
|
64
|
+
- **Always pass arguments.** SoX's defaults aren't always sensible: `["tempo"]` on its own never
|
|
65
|
+
returns.
|
|
66
|
+
- **Go faster with `-q`.** For augmentation, quick mode trades a little quality for speed:
|
|
67
|
+
`["pitch", "-q", "200"]`, `["tempo", "-q", "1.1"]`, `["rate", "-q", "16000"]` (the latter can
|
|
68
|
+
alias). Put `-q` before the effect's other arguments.
|
|
69
|
+
- **Parallelism.** Threads run in parallel, but `DataLoader` worker processes are the safer choice.
|
|
70
|
+
With `spawn`/`forkserver` workers, use regular functions instead of lambdas in an `EffectChain`
|
|
71
|
+
(lambdas can't be pickled).
|
|
72
|
+
- Effects that read or write files (`spectrogram`, `noiseprof`, `noisered`, `splice`) aren't supported.
|
|
73
|
+
|
|
74
|
+
## Troubleshooting
|
|
75
|
+
|
|
76
|
+
`import sox_effects` always works; the SoX library is only loaded on the first call. If it can't be
|
|
77
|
+
found, install it as shown above, or point `LIBSOX_PATH` to the library file (e.g.
|
|
78
|
+
`libsox.so.3`).
|
|
79
|
+
|
|
80
|
+
## Development
|
|
81
|
+
|
|
82
|
+
The extension is built with [nanobind](https://github.com/wjakob/nanobind) and
|
|
83
|
+
[scikit-build-core](https://github.com/scikit-build/scikit-build-core). Building from source only needs the
|
|
84
|
+
`sox.h` header, which is looked up in `$SOX_ROOT/include`, next to the `sox` executable on your `PATH`, or in
|
|
85
|
+
`/usr`, `/usr/local` and `/opt/homebrew` (as `include/sox.h` or `include/sox/sox.h`).
|
|
86
|
+
|
|
87
|
+
```sh
|
|
88
|
+
uv run --group test pytest # or: pip install . --group test && pytest
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The tests run against NumPy, PyTorch and JAX, which are test dependencies only.
|
|
92
|
+
|
|
93
|
+
The extension uses the Python stable ABI, so a single build works across all Python versions >= 3.12.
|
|
94
|
+
|
|
95
|
+
## License
|
|
96
|
+
|
|
97
|
+
sox-effects is released under the MIT License. It builds on the approach from
|
|
98
|
+
[torchaudio](https://github.com/pytorch/audio) (BSD 2-Clause License) and
|
|
99
|
+
[WavAugment](https://github.com/facebookresearch/WavAugment) (MIT License).
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
exclude = "uv.lock"
|
|
2
|
+
|
|
3
|
+
[[repos]]
|
|
4
|
+
repo = "builtin"
|
|
5
|
+
hooks = [
|
|
6
|
+
{ id = "trailing-whitespace" },
|
|
7
|
+
{ id = "end-of-file-fixer" },
|
|
8
|
+
{ id = "check-added-large-files" },
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
[[repos]]
|
|
12
|
+
repo = "https://github.com/zizmorcore/zizmor-pre-commit"
|
|
13
|
+
rev = "v1.30.1"
|
|
14
|
+
hooks = [{ id = "zizmor" }]
|
|
15
|
+
|
|
16
|
+
[[repos]]
|
|
17
|
+
repo = "https://github.com/crate-ci/typos"
|
|
18
|
+
rev = "v1.51.1"
|
|
19
|
+
hooks = [{ id = "typos" }]
|
|
20
|
+
|
|
21
|
+
[[repos]]
|
|
22
|
+
repo = "https://github.com/tombi-toml/tombi-pre-commit"
|
|
23
|
+
rev = "v1.7.3"
|
|
24
|
+
hooks = [
|
|
25
|
+
# { id = "tombi-format" },
|
|
26
|
+
# { id = "tombi-lint" }, # Fail for [[tool.dynamic-metadata]]
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[[repos]]
|
|
30
|
+
repo = "https://github.com/astral-sh/ruff-pre-commit"
|
|
31
|
+
rev = "v0.17.0"
|
|
32
|
+
hooks = [
|
|
33
|
+
{ id = "ruff-check", args = ["--fix"] },
|
|
34
|
+
{ id = "ruff-format" },
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[[repos]]
|
|
38
|
+
repo = "https://github.com/astral-sh/uv-pre-commit"
|
|
39
|
+
rev = "0.13.0"
|
|
40
|
+
hooks = [
|
|
41
|
+
{ id = "uv-lock" },
|
|
42
|
+
{ id = "uv-audit" },
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
[[repos]]
|
|
46
|
+
repo = "https://github.com/astral-sh/ty-pre-commit"
|
|
47
|
+
rev = "v0.0.99"
|
|
48
|
+
hooks = [{ id = "ty", args = ["--group", "test"] }]
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "sox-effects"
|
|
3
|
+
description = "Minimal libsox effects bindings for any array"
|
|
4
|
+
readme = "README.md"
|
|
5
|
+
requires-python = ">=3.12"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
authors = [{ name = "Maxime Poli" }]
|
|
8
|
+
dependencies = []
|
|
9
|
+
dynamic = ["version"]
|
|
10
|
+
|
|
11
|
+
[dependency-groups]
|
|
12
|
+
test = [
|
|
13
|
+
"jax>=0.12.0",
|
|
14
|
+
"numpy>=2.2.6",
|
|
15
|
+
"pytest>=9.2.0",
|
|
16
|
+
"torch>=2.10",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[build-system]
|
|
20
|
+
requires = ["nanobind>=3.1.0", "scikit-build-core>=1.1.1", "setuptools-scm>=10.3.4"]
|
|
21
|
+
build-backend = "scikit_build_core.build"
|
|
22
|
+
|
|
23
|
+
[tool.cibuildwheel]
|
|
24
|
+
build = "cp312-*" # abi3: a single wheel per platform covers Python >= 3.12
|
|
25
|
+
skip = "*-musllinux_*"
|
|
26
|
+
test-command = "python -c \"import sox_effects; assert sox_effects.effect_names()\""
|
|
27
|
+
|
|
28
|
+
[tool.cibuildwheel.linux]
|
|
29
|
+
# sox.h for the build (sox-devel is in PowerTools on manylinux_2_28, CRB on manylinux_2_34), libsox for the test.
|
|
30
|
+
before-all = "dnf install -y 'dnf-command(config-manager)' && (dnf config-manager --set-enabled powertools || dnf config-manager --set-enabled crb) && dnf install -y sox-devel"
|
|
31
|
+
|
|
32
|
+
[tool.cibuildwheel.macos]
|
|
33
|
+
before-all = "brew install sox"
|
|
34
|
+
|
|
35
|
+
[[tool.dynamic-metadata]]
|
|
36
|
+
provider = "scikit_build_core.metadata.setuptools_scm"
|
|
37
|
+
|
|
38
|
+
[tool.ruff]
|
|
39
|
+
line-length = 119
|
|
40
|
+
|
|
41
|
+
[tool.scikit-build]
|
|
42
|
+
minimum-version = "build-system.requires"
|
|
43
|
+
build-dir = "build/{wheel_tag}"
|
|
44
|
+
wheel.py-api = "cp312"
|
|
45
|
+
|
|
46
|
+
[tool.setuptools_scm]
|
|
47
|
+
|
|
48
|
+
[tool.typos.default.extend-words]
|
|
49
|
+
writeable = "writeable"
|
|
50
|
+
|
|
51
|
+
[tool.uv]
|
|
52
|
+
cache-keys = [
|
|
53
|
+
{ file = "pyproject.toml" },
|
|
54
|
+
{ file = "CMakeLists.txt" },
|
|
55
|
+
{ file = "src/sox_effects/sox_effects.cpp" },
|
|
56
|
+
{ git = { commit = true, tags = true } },
|
|
57
|
+
]
|