julius-tf 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.
- julius_tf-0.1.0/.gitignore +11 -0
- julius_tf-0.1.0/LICENSE +17 -0
- julius_tf-0.1.0/PKG-INFO +203 -0
- julius_tf-0.1.0/README.md +178 -0
- julius_tf-0.1.0/bench/__init__.py +6 -0
- julius_tf-0.1.0/bench/fftconv.py +46 -0
- julius_tf-0.1.0/bench/gen.py +70 -0
- julius_tf-0.1.0/bench/lowpass.py +45 -0
- julius_tf-0.1.0/bench/resample.py +53 -0
- julius_tf-0.1.0/julius/__init__.py +42 -0
- julius_tf-0.1.0/julius/bands.py +123 -0
- julius_tf-0.1.0/julius/core.py +161 -0
- julius_tf-0.1.0/julius/fftconv.py +154 -0
- julius_tf-0.1.0/julius/filters.py +261 -0
- julius_tf-0.1.0/julius/lowpass.py +187 -0
- julius_tf-0.1.0/julius/py.typed +0 -0
- julius_tf-0.1.0/julius/resample.py +222 -0
- julius_tf-0.1.0/julius/utils.py +99 -0
- julius_tf-0.1.0/pyproject.toml +64 -0
- julius_tf-0.1.0/tests/__init__.py +0 -0
- julius_tf-0.1.0/tests/test_bands.py +62 -0
- julius_tf-0.1.0/tests/test_doc.py +24 -0
- julius_tf-0.1.0/tests/test_fftconv.py +110 -0
- julius_tf-0.1.0/tests/test_filters.py +185 -0
- julius_tf-0.1.0/tests/test_lowpass.py +94 -0
- julius_tf-0.1.0/tests/test_resample.py +210 -0
julius_tf-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Copyright 2020 Alexandre Défossez (original PyTorch implementation)
|
|
2
|
+
Copyright 2026 Clément Laroche (TensorFlow port)
|
|
3
|
+
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
|
5
|
+
associated documentation files (the "Software"), to deal in the Software without restriction,
|
|
6
|
+
including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
|
7
|
+
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all copies or
|
|
11
|
+
substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
|
14
|
+
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
15
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
16
|
+
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
17
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
julius_tf-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: julius-tf
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Nice DSP sweets: resampling, FFT Convolutions — a TensorFlow port of Alexandre Défossez's julius, differentiable and with GPU support.
|
|
5
|
+
Project-URL: Homepage, https://github.com/LarocheC/julius-tf
|
|
6
|
+
Project-URL: Repository, https://github.com/LarocheC/julius-tf
|
|
7
|
+
Project-URL: Original project (PyTorch), https://github.com/adefossez/julius
|
|
8
|
+
Author-email: Alexandre Défossez <alexandre.defossez@gmail.com>, Clément Laroche <clement.laroche@gmail.com>
|
|
9
|
+
Maintainer-email: Clément Laroche <clement.laroche@gmail.com>
|
|
10
|
+
License: MIT License
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Topic :: Multimedia :: Sound/Audio
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering
|
|
15
|
+
Requires-Python: >=3.9.0
|
|
16
|
+
Requires-Dist: tensorflow>=2.11.0
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: coverage>=7.0; extra == 'dev'
|
|
19
|
+
Requires-Dist: flake8>=5.0; extra == 'dev'
|
|
20
|
+
Requires-Dist: mypy>=1.0; extra == 'dev'
|
|
21
|
+
Requires-Dist: numpy>=1.21; extra == 'dev'
|
|
22
|
+
Requires-Dist: pdoc3>=0.10; extra == 'dev'
|
|
23
|
+
Requires-Dist: resampy==0.4.3; extra == 'dev'
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# Julius, fast TensorFlow based DSP for audio and 1D signals
|
|
27
|
+
|
|
28
|
+

|
|
29
|
+

|
|
30
|
+

|
|
31
|
+
|
|
32
|
+
Julius contains different Digital Signal Processing algorithms implemented
|
|
33
|
+
with TensorFlow, so that they are differentiable and available on GPU.
|
|
34
|
+
Note that all the modules implemented here can be used inside a `tf.function`.
|
|
35
|
+
|
|
36
|
+
> **`julius-tf` is a TensorFlow port of [`julius`](https://github.com/adefossez/julius),
|
|
37
|
+
> the PyTorch DSP library by [Alexandre Défossez](https://github.com/adefossez).** The DSP
|
|
38
|
+
> algorithms and the public API are his work; this project re-implements them on top of
|
|
39
|
+
> TensorFlow. See [Credits](#credits) for full attribution.
|
|
40
|
+
|
|
41
|
+
For now, I have implemented:
|
|
42
|
+
|
|
43
|
+
- [julius.resample](https://LarocheC.github.io/julius-tf/julius/resample.html): fast sinc resampling.
|
|
44
|
+
- [julius.fftconv](https://LarocheC.github.io/julius-tf/julius/fftconv.html): FFT based convolutions.
|
|
45
|
+
- [julius.lowpass](https://LarocheC.github.io/julius-tf/julius/lowpass.html): FIR low pass filter banks.
|
|
46
|
+
- [julius.filters](https://LarocheC.github.io/julius-tf/julius/filters.html): FIR high pass and band pass filters.
|
|
47
|
+
- [julius.bands](https://LarocheC.github.io/julius-tf/julius/bands.html): Decomposition of a waveform signal over mel-scale frequency bands.
|
|
48
|
+
|
|
49
|
+
Along that, you might found useful utilities in:
|
|
50
|
+
|
|
51
|
+
- [julius.core](https://LarocheC.github.io/julius-tf/julius/core.html): DSP related functions.
|
|
52
|
+
- [julius.utils](https://LarocheC.github.io/julius-tf/julius/utils.html): Generic utilities.
|
|
53
|
+
|
|
54
|
+
<p align="center">
|
|
55
|
+
<img src="./logo.png" alt="Representation of the convolutions filters used for the efficient resampling."
|
|
56
|
+
width="500px"></p>
|
|
57
|
+
|
|
58
|
+
## News
|
|
59
|
+
|
|
60
|
+
- `julius-tf` ports the whole library from PyTorch to __TensorFlow__. The public API of the
|
|
61
|
+
original `julius` is preserved: modules are `tf.Module`s, callable just like before, and
|
|
62
|
+
usable inside a `tf.function`. The dated entries below are the upstream `julius` releases
|
|
63
|
+
whose behavior this port reproduces.
|
|
64
|
+
- 23/06/2026: __`julius-tf` 0.1.0:__ first release on PyPI — TensorFlow port reproducing
|
|
65
|
+
upstream `julius` 0.2.8. Install with `pip install julius-tf`.
|
|
66
|
+
- 03/06/2026: __`julius` 0.2.8 released:__: Switching to pyproject.toml, now requires python >= 3.9. Bug fix with -O flag (thanks @aiknownc)
|
|
67
|
+
- 19/09/2022: __`julius` 0.2.7 released:__: fixed ONNX compat (thanks @iver56). I know I missed the 0.2.6 one...
|
|
68
|
+
- 28/07/2021: __`julius` 0.2.5 released:__: support for setting a custom output length when resampling.
|
|
69
|
+
- 22/06/2021: __`julius` 0.2.4 released:__: adding highpass and band passfilters.
|
|
70
|
+
Extra linting and type checking of the code. New `unfold` implemention, up to
|
|
71
|
+
x6 faster FFT convolutions and more efficient memory usage.
|
|
72
|
+
- 26/01/2021: __`julius` 0.2.2 released:__ fixing normalization of filters in lowpass and resample to avoid very low frequencies to be leaked.
|
|
73
|
+
Switch from zero padding to replicate padding (uses first/last value instead of 0) to avoid discontinuities with strong artifacts.
|
|
74
|
+
- 20/01/2021: `julius` implementation of resampling is now officially <a href="https://github.com/pytorch/audio/pull/1087">part of Torchaudio.</a>
|
|
75
|
+
|
|
76
|
+
## Installation
|
|
77
|
+
|
|
78
|
+
`julius-tf` requires python >= 3.9 and TensorFlow >= 2.11. To install:
|
|
79
|
+
```bash
|
|
80
|
+
pip3 install -U julius-tf
|
|
81
|
+
```
|
|
82
|
+
The import name stays `julius` (i.e. `pip install julius-tf` then `import julius`).
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
## Usage
|
|
86
|
+
|
|
87
|
+
See the [Julius documentation][docs] for the usage of Julius. Hereafter you will find a few examples
|
|
88
|
+
to get you quickly started:
|
|
89
|
+
|
|
90
|
+
```python3
|
|
91
|
+
import julius
|
|
92
|
+
import tensorflow as tf
|
|
93
|
+
|
|
94
|
+
signal = tf.random.normal((6, 4, 1024))
|
|
95
|
+
# Resample from a sample rate of 100 to 70. The old and new sample rate must be integers,
|
|
96
|
+
# and resampling will be fast if they form an irreductible fraction with small numerator
|
|
97
|
+
# and denominator (here 10 and 7). Any shape is supported, last dim is time.
|
|
98
|
+
resampled_signal = julius.resample_frac(signal, 100, 70)
|
|
99
|
+
|
|
100
|
+
# Low pass filter with a `0.1 * sample_rate` cutoff frequency.
|
|
101
|
+
low_freqs = julius.lowpass_filter(signal, 0.1)
|
|
102
|
+
|
|
103
|
+
# Fast convolutions with FFT, useful for large kernels
|
|
104
|
+
conv = julius.FFTConv1d(4, 10, 512)
|
|
105
|
+
convolved = conv(signal)
|
|
106
|
+
|
|
107
|
+
# Decomposition over frequency bands in the Waveform domain
|
|
108
|
+
bands = julius.split_bands(signal, n_bands=10, sample_rate=100)
|
|
109
|
+
# Decomposition with n_bands frequency bands evenly spaced in mel space.
|
|
110
|
+
# Input shape can be `[*, T]`, output will be `[n_bands, *, T]`.
|
|
111
|
+
random_eq = tf.reduce_sum(tf.random.uniform((10, 1, 1, 1)) * bands, axis=0)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Algorithms
|
|
115
|
+
|
|
116
|
+
### Resample
|
|
117
|
+
|
|
118
|
+
This is an implementation of the [sinc resample algorithm][resample] by Julius O. Smith.
|
|
119
|
+
It is the same algorithm than the one used in [resampy][resampy] but to run efficiently on GPU it
|
|
120
|
+
is limited to fractional changes of the sample rate. It will be fast if the old and new sample rate
|
|
121
|
+
are small after dividing them by their GCD. For instance going from a sample rate of 2000 to 3000 (2, 3 after removing the GCD)
|
|
122
|
+
will be extremely fast, while going from 20001 to 30001 will not.
|
|
123
|
+
Julius resampling is faster than resampy even on CPU, and when running on GPU it makes resampling a completely negligible part of your pipeline
|
|
124
|
+
(except of course for weird cases like going from a sample rate of 20001 to 30001).
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
### FFTConv1d
|
|
128
|
+
|
|
129
|
+
Computing convolutions with very large kernels (>= 128) and a stride of 1 can be much faster
|
|
130
|
+
using FFT. This implements the same API as `tf.keras.layers.Conv1D` / `tf.nn.conv1d`
|
|
131
|
+
(using the channels-first `[B, C, T]` convention) but with a FFT backend. Dilation and groups
|
|
132
|
+
are not supported.
|
|
133
|
+
FFTConv will be faster on CPU even for relatively small tensors (a few dozen channels, kernel size
|
|
134
|
+
of 128). On CUDA, due to the higher parallelism, regular convolution can be faster in many cases,
|
|
135
|
+
but for kernel sizes above 128, for a large number of channels or batch size, FFTConv1d
|
|
136
|
+
will eventually be faster (basically when you no longer have idle cores that can hide
|
|
137
|
+
the true complexity of the operation).
|
|
138
|
+
|
|
139
|
+
### LowPass
|
|
140
|
+
|
|
141
|
+
Classical Finite Impulse Reponse windowed sinc lowpass filter. It will use FFT convolutions automatically
|
|
142
|
+
if the filter size is large enough. This is the basic block from which you can build
|
|
143
|
+
high pass and band pass filters (see `julius.filters`).
|
|
144
|
+
|
|
145
|
+
### Bands
|
|
146
|
+
|
|
147
|
+
Decomposition of a signal over frequency bands in the waveform domain. This can be useful for
|
|
148
|
+
instance to perform parametric EQ (see [Usage](#usage) above).
|
|
149
|
+
|
|
150
|
+
## Benchmarks
|
|
151
|
+
|
|
152
|
+
You can find speed tests (and comparisons to reference implementations) on the
|
|
153
|
+
[benchmark][bench]. The CPU benchmarks are run on a Mac Book Pro 2020, with a 2.4 GHz
|
|
154
|
+
8-core intel CPU i9. The GPUs benchmark are run on Nvidia V100 with 16GB of memory.
|
|
155
|
+
We also compare the validity of our implementations, as compared to reference ones like `resampy`
|
|
156
|
+
or `tf.nn.conv1d`.
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
## Running tests
|
|
161
|
+
|
|
162
|
+
Clone this repository, then
|
|
163
|
+
```bash
|
|
164
|
+
pip3 install '.[dev]'
|
|
165
|
+
python3 -m unittest discover -s tests
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
To run the benchmarks:
|
|
169
|
+
```
|
|
170
|
+
pip3 install .[dev]'
|
|
171
|
+
python3 -m bench.gen
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
## License
|
|
176
|
+
|
|
177
|
+
`julius-tf` is released under the MIT license, the same license as the original `julius`.
|
|
178
|
+
The license retains the original copyright of Alexandre Défossez (2020) alongside the
|
|
179
|
+
copyright for the TensorFlow port (2026). See [LICENSE](./LICENSE).
|
|
180
|
+
|
|
181
|
+
## Credits
|
|
182
|
+
|
|
183
|
+
This project is a TensorFlow port of [`julius`](https://github.com/adefossez/julius) by
|
|
184
|
+
[**Alexandre Défossez**](https://github.com/adefossez). All of the DSP algorithms, the
|
|
185
|
+
overall design, and the public API originate from his original PyTorch implementation —
|
|
186
|
+
full credit for the underlying work goes to him. This repository only re-implements those
|
|
187
|
+
algorithms on top of TensorFlow.
|
|
188
|
+
|
|
189
|
+
- Original project: https://github.com/adefossez/julius (MIT, © 2020 Alexandre Défossez)
|
|
190
|
+
- TensorFlow port: Clément Laroche ([@LarocheC](https://github.com/LarocheC))
|
|
191
|
+
|
|
192
|
+
## Thanks
|
|
193
|
+
|
|
194
|
+
This package is named in the honor of
|
|
195
|
+
[Julius O. Smith](https://ccrma.stanford.edu/~jos/),
|
|
196
|
+
whose books and website were a gold mine of information for learning about DSP. Go checkout his website if you want
|
|
197
|
+
to learn more about DSP.
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
[resample]: https://ccrma.stanford.edu/~jos/resample/resample.html
|
|
201
|
+
[resampy]: https://resampy.readthedocs.io/
|
|
202
|
+
[docs]: https://LarocheC.github.io/julius-tf/julius/index.html
|
|
203
|
+
[bench]: ./bench.md
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Julius, fast TensorFlow based DSP for audio and 1D signals
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
Julius contains different Digital Signal Processing algorithms implemented
|
|
8
|
+
with TensorFlow, so that they are differentiable and available on GPU.
|
|
9
|
+
Note that all the modules implemented here can be used inside a `tf.function`.
|
|
10
|
+
|
|
11
|
+
> **`julius-tf` is a TensorFlow port of [`julius`](https://github.com/adefossez/julius),
|
|
12
|
+
> the PyTorch DSP library by [Alexandre Défossez](https://github.com/adefossez).** The DSP
|
|
13
|
+
> algorithms and the public API are his work; this project re-implements them on top of
|
|
14
|
+
> TensorFlow. See [Credits](#credits) for full attribution.
|
|
15
|
+
|
|
16
|
+
For now, I have implemented:
|
|
17
|
+
|
|
18
|
+
- [julius.resample](https://LarocheC.github.io/julius-tf/julius/resample.html): fast sinc resampling.
|
|
19
|
+
- [julius.fftconv](https://LarocheC.github.io/julius-tf/julius/fftconv.html): FFT based convolutions.
|
|
20
|
+
- [julius.lowpass](https://LarocheC.github.io/julius-tf/julius/lowpass.html): FIR low pass filter banks.
|
|
21
|
+
- [julius.filters](https://LarocheC.github.io/julius-tf/julius/filters.html): FIR high pass and band pass filters.
|
|
22
|
+
- [julius.bands](https://LarocheC.github.io/julius-tf/julius/bands.html): Decomposition of a waveform signal over mel-scale frequency bands.
|
|
23
|
+
|
|
24
|
+
Along that, you might found useful utilities in:
|
|
25
|
+
|
|
26
|
+
- [julius.core](https://LarocheC.github.io/julius-tf/julius/core.html): DSP related functions.
|
|
27
|
+
- [julius.utils](https://LarocheC.github.io/julius-tf/julius/utils.html): Generic utilities.
|
|
28
|
+
|
|
29
|
+
<p align="center">
|
|
30
|
+
<img src="./logo.png" alt="Representation of the convolutions filters used for the efficient resampling."
|
|
31
|
+
width="500px"></p>
|
|
32
|
+
|
|
33
|
+
## News
|
|
34
|
+
|
|
35
|
+
- `julius-tf` ports the whole library from PyTorch to __TensorFlow__. The public API of the
|
|
36
|
+
original `julius` is preserved: modules are `tf.Module`s, callable just like before, and
|
|
37
|
+
usable inside a `tf.function`. The dated entries below are the upstream `julius` releases
|
|
38
|
+
whose behavior this port reproduces.
|
|
39
|
+
- 23/06/2026: __`julius-tf` 0.1.0:__ first release on PyPI — TensorFlow port reproducing
|
|
40
|
+
upstream `julius` 0.2.8. Install with `pip install julius-tf`.
|
|
41
|
+
- 03/06/2026: __`julius` 0.2.8 released:__: Switching to pyproject.toml, now requires python >= 3.9. Bug fix with -O flag (thanks @aiknownc)
|
|
42
|
+
- 19/09/2022: __`julius` 0.2.7 released:__: fixed ONNX compat (thanks @iver56). I know I missed the 0.2.6 one...
|
|
43
|
+
- 28/07/2021: __`julius` 0.2.5 released:__: support for setting a custom output length when resampling.
|
|
44
|
+
- 22/06/2021: __`julius` 0.2.4 released:__: adding highpass and band passfilters.
|
|
45
|
+
Extra linting and type checking of the code. New `unfold` implemention, up to
|
|
46
|
+
x6 faster FFT convolutions and more efficient memory usage.
|
|
47
|
+
- 26/01/2021: __`julius` 0.2.2 released:__ fixing normalization of filters in lowpass and resample to avoid very low frequencies to be leaked.
|
|
48
|
+
Switch from zero padding to replicate padding (uses first/last value instead of 0) to avoid discontinuities with strong artifacts.
|
|
49
|
+
- 20/01/2021: `julius` implementation of resampling is now officially <a href="https://github.com/pytorch/audio/pull/1087">part of Torchaudio.</a>
|
|
50
|
+
|
|
51
|
+
## Installation
|
|
52
|
+
|
|
53
|
+
`julius-tf` requires python >= 3.9 and TensorFlow >= 2.11. To install:
|
|
54
|
+
```bash
|
|
55
|
+
pip3 install -U julius-tf
|
|
56
|
+
```
|
|
57
|
+
The import name stays `julius` (i.e. `pip install julius-tf` then `import julius`).
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
## Usage
|
|
61
|
+
|
|
62
|
+
See the [Julius documentation][docs] for the usage of Julius. Hereafter you will find a few examples
|
|
63
|
+
to get you quickly started:
|
|
64
|
+
|
|
65
|
+
```python3
|
|
66
|
+
import julius
|
|
67
|
+
import tensorflow as tf
|
|
68
|
+
|
|
69
|
+
signal = tf.random.normal((6, 4, 1024))
|
|
70
|
+
# Resample from a sample rate of 100 to 70. The old and new sample rate must be integers,
|
|
71
|
+
# and resampling will be fast if they form an irreductible fraction with small numerator
|
|
72
|
+
# and denominator (here 10 and 7). Any shape is supported, last dim is time.
|
|
73
|
+
resampled_signal = julius.resample_frac(signal, 100, 70)
|
|
74
|
+
|
|
75
|
+
# Low pass filter with a `0.1 * sample_rate` cutoff frequency.
|
|
76
|
+
low_freqs = julius.lowpass_filter(signal, 0.1)
|
|
77
|
+
|
|
78
|
+
# Fast convolutions with FFT, useful for large kernels
|
|
79
|
+
conv = julius.FFTConv1d(4, 10, 512)
|
|
80
|
+
convolved = conv(signal)
|
|
81
|
+
|
|
82
|
+
# Decomposition over frequency bands in the Waveform domain
|
|
83
|
+
bands = julius.split_bands(signal, n_bands=10, sample_rate=100)
|
|
84
|
+
# Decomposition with n_bands frequency bands evenly spaced in mel space.
|
|
85
|
+
# Input shape can be `[*, T]`, output will be `[n_bands, *, T]`.
|
|
86
|
+
random_eq = tf.reduce_sum(tf.random.uniform((10, 1, 1, 1)) * bands, axis=0)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Algorithms
|
|
90
|
+
|
|
91
|
+
### Resample
|
|
92
|
+
|
|
93
|
+
This is an implementation of the [sinc resample algorithm][resample] by Julius O. Smith.
|
|
94
|
+
It is the same algorithm than the one used in [resampy][resampy] but to run efficiently on GPU it
|
|
95
|
+
is limited to fractional changes of the sample rate. It will be fast if the old and new sample rate
|
|
96
|
+
are small after dividing them by their GCD. For instance going from a sample rate of 2000 to 3000 (2, 3 after removing the GCD)
|
|
97
|
+
will be extremely fast, while going from 20001 to 30001 will not.
|
|
98
|
+
Julius resampling is faster than resampy even on CPU, and when running on GPU it makes resampling a completely negligible part of your pipeline
|
|
99
|
+
(except of course for weird cases like going from a sample rate of 20001 to 30001).
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
### FFTConv1d
|
|
103
|
+
|
|
104
|
+
Computing convolutions with very large kernels (>= 128) and a stride of 1 can be much faster
|
|
105
|
+
using FFT. This implements the same API as `tf.keras.layers.Conv1D` / `tf.nn.conv1d`
|
|
106
|
+
(using the channels-first `[B, C, T]` convention) but with a FFT backend. Dilation and groups
|
|
107
|
+
are not supported.
|
|
108
|
+
FFTConv will be faster on CPU even for relatively small tensors (a few dozen channels, kernel size
|
|
109
|
+
of 128). On CUDA, due to the higher parallelism, regular convolution can be faster in many cases,
|
|
110
|
+
but for kernel sizes above 128, for a large number of channels or batch size, FFTConv1d
|
|
111
|
+
will eventually be faster (basically when you no longer have idle cores that can hide
|
|
112
|
+
the true complexity of the operation).
|
|
113
|
+
|
|
114
|
+
### LowPass
|
|
115
|
+
|
|
116
|
+
Classical Finite Impulse Reponse windowed sinc lowpass filter. It will use FFT convolutions automatically
|
|
117
|
+
if the filter size is large enough. This is the basic block from which you can build
|
|
118
|
+
high pass and band pass filters (see `julius.filters`).
|
|
119
|
+
|
|
120
|
+
### Bands
|
|
121
|
+
|
|
122
|
+
Decomposition of a signal over frequency bands in the waveform domain. This can be useful for
|
|
123
|
+
instance to perform parametric EQ (see [Usage](#usage) above).
|
|
124
|
+
|
|
125
|
+
## Benchmarks
|
|
126
|
+
|
|
127
|
+
You can find speed tests (and comparisons to reference implementations) on the
|
|
128
|
+
[benchmark][bench]. The CPU benchmarks are run on a Mac Book Pro 2020, with a 2.4 GHz
|
|
129
|
+
8-core intel CPU i9. The GPUs benchmark are run on Nvidia V100 with 16GB of memory.
|
|
130
|
+
We also compare the validity of our implementations, as compared to reference ones like `resampy`
|
|
131
|
+
or `tf.nn.conv1d`.
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
## Running tests
|
|
136
|
+
|
|
137
|
+
Clone this repository, then
|
|
138
|
+
```bash
|
|
139
|
+
pip3 install '.[dev]'
|
|
140
|
+
python3 -m unittest discover -s tests
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
To run the benchmarks:
|
|
144
|
+
```
|
|
145
|
+
pip3 install .[dev]'
|
|
146
|
+
python3 -m bench.gen
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
## License
|
|
151
|
+
|
|
152
|
+
`julius-tf` is released under the MIT license, the same license as the original `julius`.
|
|
153
|
+
The license retains the original copyright of Alexandre Défossez (2020) alongside the
|
|
154
|
+
copyright for the TensorFlow port (2026). See [LICENSE](./LICENSE).
|
|
155
|
+
|
|
156
|
+
## Credits
|
|
157
|
+
|
|
158
|
+
This project is a TensorFlow port of [`julius`](https://github.com/adefossez/julius) by
|
|
159
|
+
[**Alexandre Défossez**](https://github.com/adefossez). All of the DSP algorithms, the
|
|
160
|
+
overall design, and the public API originate from his original PyTorch implementation —
|
|
161
|
+
full credit for the underlying work goes to him. This repository only re-implements those
|
|
162
|
+
algorithms on top of TensorFlow.
|
|
163
|
+
|
|
164
|
+
- Original project: https://github.com/adefossez/julius (MIT, © 2020 Alexandre Défossez)
|
|
165
|
+
- TensorFlow port: Clément Laroche ([@LarocheC](https://github.com/LarocheC))
|
|
166
|
+
|
|
167
|
+
## Thanks
|
|
168
|
+
|
|
169
|
+
This package is named in the honor of
|
|
170
|
+
[Julius O. Smith](https://ccrma.stanford.edu/~jos/),
|
|
171
|
+
whose books and website were a gold mine of information for learning about DSP. Go checkout his website if you want
|
|
172
|
+
to learn more about DSP.
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
[resample]: https://ccrma.stanford.edu/~jos/resample/resample.html
|
|
176
|
+
[resampy]: https://resampy.readthedocs.io/
|
|
177
|
+
[docs]: https://LarocheC.github.io/julius-tf/julius/index.html
|
|
178
|
+
[bench]: ./bench.md
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# File under the MIT license, see the LICENSE file for details.
|
|
2
|
+
# Original author: Alexandre Défossez (adefossez), 2020
|
|
3
|
+
# TensorFlow port: Clément Laroche (LarocheC), 2026
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
|
|
7
|
+
import tensorflow as tf
|
|
8
|
+
|
|
9
|
+
from julius import fft_conv1d
|
|
10
|
+
from julius.core import conv1d
|
|
11
|
+
from julius.utils import Chrono, MarkdownTable
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test(table, kernel_size, block_ratio=5, device="cpu"):
|
|
15
|
+
tf_device = "/GPU:0" if device == "cuda" else "/CPU:0"
|
|
16
|
+
with tf.device(tf_device):
|
|
17
|
+
x = tf.random.normal((32, 32, 1024 * 10))
|
|
18
|
+
w = tf.random.normal((64, 32, kernel_size))
|
|
19
|
+
|
|
20
|
+
with Chrono() as chrono_ref:
|
|
21
|
+
y_ref = conv1d(x, w)
|
|
22
|
+
|
|
23
|
+
with Chrono() as chrono_fft:
|
|
24
|
+
y_fft = fft_conv1d(x, w, block_ratio=block_ratio)
|
|
25
|
+
|
|
26
|
+
delta = format(float(tf.reduce_mean(tf.abs(y_ref - y_fft))), ".1e")
|
|
27
|
+
table.line(
|
|
28
|
+
[kernel_size, int(1000 * chrono_fft.duration), int(1000 * chrono_ref.duration), delta])
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def main():
|
|
32
|
+
parser = argparse.ArgumentParser("fftconv.py")
|
|
33
|
+
parser.add_argument("-d", "--device", default="cpu")
|
|
34
|
+
parser.add_argument("-b", "--block_ratio", default=5, type=float)
|
|
35
|
+
args = parser.parse_args()
|
|
36
|
+
|
|
37
|
+
table = MarkdownTable(
|
|
38
|
+
["Kernel size", "FFT (ms)", "No FFT (ms)", " Delta"])
|
|
39
|
+
table.header()
|
|
40
|
+
|
|
41
|
+
for kernel_size in [8, 32, 64, 128, 256, 1024, 2048]:
|
|
42
|
+
test(table, kernel_size, block_ratio=args.block_ratio, device=args.device)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
if __name__ == "__main__":
|
|
46
|
+
main()
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import subprocess as sp
|
|
2
|
+
import tensorflow as tf
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def run_bench(name, *args, device="cpu"):
|
|
6
|
+
args = list(args)
|
|
7
|
+
args += ["-d", device]
|
|
8
|
+
if device == "cuda" and not tf.config.list_physical_devices('GPU'):
|
|
9
|
+
return "Not available /!\\"
|
|
10
|
+
return sp.check_output(["python3", "-m", f"bench.{name}"] + args).decode('utf8')
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def main():
|
|
14
|
+
template = f"""\
|
|
15
|
+
## Benchmarking and verification of Julius
|
|
16
|
+
|
|
17
|
+
In order to verify the correctness and speed of the implementations in Julius,
|
|
18
|
+
we compare ourselves to different reference implementations, comparing speed and
|
|
19
|
+
checking how far we are.
|
|
20
|
+
|
|
21
|
+
### ResampleFrac
|
|
22
|
+
|
|
23
|
+
We compare `julius.resample` to `resampy`, on an input of size (32, 8 * 44100),
|
|
24
|
+
i.e. a batch of size 16 of 8 second of audio at 44.1kHz.
|
|
25
|
+
We use the same number of zero crossing as `resampy` for this benchmark.
|
|
26
|
+
The small delta is probably
|
|
27
|
+
due to the different window function used.
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
On CPU we have:
|
|
31
|
+
|
|
32
|
+
{run_bench('resample')}
|
|
33
|
+
|
|
34
|
+
On GPU we have:
|
|
35
|
+
|
|
36
|
+
{run_bench('resample', device='cuda')}
|
|
37
|
+
|
|
38
|
+
### FFTConv1d
|
|
39
|
+
|
|
40
|
+
We compare to `tf.nn.conv1d`, on a input of size [32, 32, 10240],
|
|
41
|
+
for a convolution with 32 input channels, 64 output channels and various kernel sizes.
|
|
42
|
+
|
|
43
|
+
On CPU we have:
|
|
44
|
+
|
|
45
|
+
{run_bench('fftconv')}
|
|
46
|
+
|
|
47
|
+
On GPU we have:
|
|
48
|
+
|
|
49
|
+
{run_bench('fftconv', device='cuda')}
|
|
50
|
+
|
|
51
|
+
### LowPassFilter
|
|
52
|
+
|
|
53
|
+
We do not compare to anything, but measure the attenuation in dB of a pure tone
|
|
54
|
+
at `0.9 * cutoff`, at the `cutoff`, and at `1.1 * cutoff`.
|
|
55
|
+
Note that our implementation automatically choses to use FFTConv1d or not when appropriate.
|
|
56
|
+
|
|
57
|
+
On CPU we have:
|
|
58
|
+
|
|
59
|
+
{run_bench('lowpass')}
|
|
60
|
+
|
|
61
|
+
On GPU we have:
|
|
62
|
+
|
|
63
|
+
{run_bench('lowpass', device='cuda')}
|
|
64
|
+
|
|
65
|
+
"""
|
|
66
|
+
print(template)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
if __name__ == "__main__":
|
|
70
|
+
main()
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# File under the MIT license, see the LICENSE file for details.
|
|
2
|
+
# Original author: Alexandre Défossez (adefossez), 2020
|
|
3
|
+
# TensorFlow port: Clément Laroche (LarocheC), 2026
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
|
|
7
|
+
import tensorflow as tf
|
|
8
|
+
|
|
9
|
+
from julius import lowpass_filter
|
|
10
|
+
from julius.core import pure_tone, volume
|
|
11
|
+
from julius.utils import Chrono, MarkdownTable
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test(table, freq, zeros, fft=None, device="cpu"):
|
|
15
|
+
sr = 44_100
|
|
16
|
+
tf_device = "/GPU:0" if device == "cuda" else "/CPU:0"
|
|
17
|
+
|
|
18
|
+
attns = []
|
|
19
|
+
for ratio in [0.9, 1, 1.1]:
|
|
20
|
+
with tf.device(tf_device):
|
|
21
|
+
x = pure_tone(ratio * freq * sr, sr, 4)
|
|
22
|
+
with Chrono() as chrono:
|
|
23
|
+
y = lowpass_filter(x, freq, fft=fft, zeros=zeros)
|
|
24
|
+
attns.append(format(float(volume(y) - volume(x)), ".2f"))
|
|
25
|
+
|
|
26
|
+
table.line([freq] + attns + [int(1000 * chrono.duration)])
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def main():
|
|
30
|
+
parser = argparse.ArgumentParser("lowpass.py")
|
|
31
|
+
parser.add_argument("-d", "--device", default="cpu")
|
|
32
|
+
parser.add_argument("--fft", action="store_true", default=None)
|
|
33
|
+
parser.add_argument("--no_fft", action="store_false", dest="fft")
|
|
34
|
+
parser.add_argument("--zeros", default=8, type=float)
|
|
35
|
+
args = parser.parse_args()
|
|
36
|
+
|
|
37
|
+
table = MarkdownTable(
|
|
38
|
+
["Freq.", "Attn. 0.9 (dB)", "Attn 1.0 (dB)", "Attn 1.1 (dB)", "Time (ms)"])
|
|
39
|
+
table.header()
|
|
40
|
+
for freq in [0.005, 0.01, 0.1, 0.2, 0.4]:
|
|
41
|
+
test(table, freq, zeros=args.zeros, fft=args.fft, device=args.device)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
if __name__ == "__main__":
|
|
45
|
+
main()
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# File under the MIT license, see the LICENSE file for details.
|
|
2
|
+
# Original author: Alexandre Défossez (adefossez), 2020
|
|
3
|
+
# TensorFlow port: Clément Laroche (LarocheC), 2026
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import math
|
|
7
|
+
|
|
8
|
+
import resampy
|
|
9
|
+
import tensorflow as tf
|
|
10
|
+
|
|
11
|
+
from julius import resample_frac
|
|
12
|
+
from julius.utils import Chrono, MarkdownTable
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test(table, old_sr, new_sr, device="cpu"):
|
|
16
|
+
tf_device = "/GPU:0" if device == "cuda" else "/CPU:0"
|
|
17
|
+
with tf.device(tf_device):
|
|
18
|
+
x = tf.random.normal((16, 8 * old_sr * int(math.ceil(44_100 / old_sr))))
|
|
19
|
+
|
|
20
|
+
with Chrono() as chrono:
|
|
21
|
+
y = resample_frac(x, old_sr, new_sr, zeros=56)
|
|
22
|
+
dur_julius = int(1000 * chrono.duration)
|
|
23
|
+
|
|
24
|
+
if device == "cpu":
|
|
25
|
+
with Chrono() as chrono:
|
|
26
|
+
y_resampy = tf.constant(
|
|
27
|
+
resampy.resample(x.numpy(), old_sr, new_sr), dtype=tf.float32)
|
|
28
|
+
dur_resampy = int(1000 * chrono.duration)
|
|
29
|
+
|
|
30
|
+
delta = float(tf.reduce_mean(tf.abs(y_resampy - y)))
|
|
31
|
+
table.line([old_sr, new_sr, dur_julius, dur_resampy, format(delta, ".1%")])
|
|
32
|
+
else:
|
|
33
|
+
table.line([old_sr, new_sr, dur_julius])
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def main():
|
|
37
|
+
parser = argparse.ArgumentParser("resample.py")
|
|
38
|
+
parser.add_argument("-d", "--device", default="cpu")
|
|
39
|
+
args = parser.parse_args()
|
|
40
|
+
|
|
41
|
+
if args.device == "cpu":
|
|
42
|
+
table = MarkdownTable(["Old sr", "New sr", "Julius (ms)", "Resampy (ms)", "Delta (%)"])
|
|
43
|
+
else:
|
|
44
|
+
table = MarkdownTable(["Old sr", "New sr", "Julius (ms)"])
|
|
45
|
+
table.header()
|
|
46
|
+
|
|
47
|
+
rates = [(2, 1), (1, 2), (4, 5), (10, 11), (44100, 16000), (20001, 30001)]
|
|
48
|
+
for old_sr, new_sr in rates:
|
|
49
|
+
test(table, old_sr, new_sr, device=args.device)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
if __name__ == "__main__":
|
|
53
|
+
main()
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# File under the MIT license, see the LICENSE file for details.
|
|
2
|
+
# Original author: Alexandre Défossez (adefossez), 2020
|
|
3
|
+
# TensorFlow port: Clément Laroche (LarocheC), 2026
|
|
4
|
+
|
|
5
|
+
# flake8: noqa
|
|
6
|
+
"""
|
|
7
|
+
.. image:: ../logo.png
|
|
8
|
+
|
|
9
|
+
Julius contains different Digital Signal Processing algorithms implemented
|
|
10
|
+
with TensorFlow, so that they are differentiable and available on GPU.
|
|
11
|
+
Note that all the modules implemented here can be used inside a `tf.function`.
|
|
12
|
+
|
|
13
|
+
For now, I have implemented:
|
|
14
|
+
|
|
15
|
+
- `julius.resample`: fast sinc resampling.
|
|
16
|
+
- `julius.fftconv`: FFT based convolutions.
|
|
17
|
+
- `julius.lowpass`: FIR low pass filter banks.
|
|
18
|
+
- `julius.filters`: FIR high pass and band pass filters.
|
|
19
|
+
- `julius.bands`: Decomposition of a waveform signal over mel-scale frequency bands.
|
|
20
|
+
|
|
21
|
+
Along that, you might found useful utilities in:
|
|
22
|
+
|
|
23
|
+
- `julius.core`: DSP related functions.
|
|
24
|
+
- `julius.utils`: Generic utilities.
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
Please checkout [the Github repository](https://github.com/LarocheC/julius-tf) for other informations.
|
|
28
|
+
For a verification of the speed and correctness of Julius, check the benchmark module `bench`.
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
This package is named in this honor of
|
|
32
|
+
[Julius O. Smith](https://ccrma.stanford.edu/~jos/),
|
|
33
|
+
whose books and website were a gold mine of information for me to learn about DSP. Go checkout his website if you want
|
|
34
|
+
to learn more about DSP.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
from .bands import SplitBands, split_bands
|
|
38
|
+
from .fftconv import fft_conv1d, FFTConv1d
|
|
39
|
+
from .filters import bandpass_filter, BandPassFilter
|
|
40
|
+
from .filters import highpass_filter, highpass_filters, HighPassFilter, HighPassFilters
|
|
41
|
+
from .lowpass import lowpass_filter, lowpass_filters, LowPassFilters, LowPassFilter
|
|
42
|
+
from .resample import resample_frac, ResampleFrac
|