vndecorrelate 1.0.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.
- vndecorrelate-1.0.0/LICENSE +24 -0
- vndecorrelate-1.0.0/PKG-INFO +138 -0
- vndecorrelate-1.0.0/README.md +124 -0
- vndecorrelate-1.0.0/pyproject.toml +36 -0
- vndecorrelate-1.0.0/setup.cfg +4 -0
- vndecorrelate-1.0.0/src/vndecorrelate/__init__.py +1 -0
- vndecorrelate-1.0.0/src/vndecorrelate/decorrelation.py +721 -0
- vndecorrelate-1.0.0/src/vndecorrelate/optimization.py +292 -0
- vndecorrelate-1.0.0/src/vndecorrelate/utils/__init__.py +26 -0
- vndecorrelate-1.0.0/src/vndecorrelate/utils/dsp.py +411 -0
- vndecorrelate-1.0.0/src/vndecorrelate/utils/plot.py +106 -0
- vndecorrelate-1.0.0/src/vndecorrelate.egg-info/PKG-INFO +138 -0
- vndecorrelate-1.0.0/src/vndecorrelate.egg-info/SOURCES.txt +19 -0
- vndecorrelate-1.0.0/src/vndecorrelate.egg-info/dependency_links.txt +1 -0
- vndecorrelate-1.0.0/src/vndecorrelate.egg-info/requires.txt +4 -0
- vndecorrelate-1.0.0/src/vndecorrelate.egg-info/top_level.txt +1 -0
- vndecorrelate-1.0.0/tests/test_decorrelation.py +207 -0
- vndecorrelate-1.0.0/tests/test_dsp.py +317 -0
- vndecorrelate-1.0.0/tests/test_example.py +47 -0
- vndecorrelate-1.0.0/tests/test_optimization.py +74 -0
- vndecorrelate-1.0.0/tests/test_plot.py +260 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
This is free and unencumbered software released into the public domain.
|
|
2
|
+
|
|
3
|
+
Anyone is free to copy, modify, publish, use, compile, sell, or
|
|
4
|
+
distribute this software, either in source code form or as a compiled
|
|
5
|
+
binary, for any purpose, commercial or non-commercial, and by any
|
|
6
|
+
means.
|
|
7
|
+
|
|
8
|
+
In jurisdictions that recognize copyright laws, the author or authors
|
|
9
|
+
of this software dedicate any and all copyright interest in the
|
|
10
|
+
software to the public domain. We make this dedication for the benefit
|
|
11
|
+
of the public at large and to the detriment of our heirs and
|
|
12
|
+
successors. We intend this dedication to be an overt act of
|
|
13
|
+
relinquishment in perpetuity of all present and future rights to this
|
|
14
|
+
software under copyright law.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
19
|
+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
20
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
21
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
|
23
|
+
|
|
24
|
+
For more information, please refer to <http://unlicense.org/>
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vndecorrelate
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A Velvet-Noise Decorrelator for audio
|
|
5
|
+
Author-email: Christian Konstantinov <ckonst98@gmail.com>
|
|
6
|
+
Requires-Python: >=3.12
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: matplotlib>=3.10.3
|
|
10
|
+
Requires-Dist: numpy>=2.3.1
|
|
11
|
+
Requires-Dist: pillow==12.2.0
|
|
12
|
+
Requires-Dist: scipy>=1.16.0
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
|
|
15
|
+
# VNDecorrelate
|
|
16
|
+
[](https://pypi.org/project/vndecorrelate)
|
|
17
|
+
|
|
18
|
+
A Velvet-Noise Decorrelator for audio.
|
|
19
|
+
|
|
20
|
+
Decorrelation refers to the process of transforming an audio source signal into multiple output signals with different waveforms from each other, but with the same sound as the source signal [[1]](#1).
|
|
21
|
+
|
|
22
|
+
In music production, decorrelation is typically applied to the left and right audio channels, creating the perception of stereo width and space. This, however, may come at the cost of potential coloration or transient smearing artifacts.
|
|
23
|
+
|
|
24
|
+
Velvet-Noise Decorrelation (VND) attempts to minimize these artifacts as well as computation cost while reducing the correlation of the outputs as much as possible [[2]](#2).
|
|
25
|
+
|
|
26
|
+
## Velvet Noise
|
|
27
|
+
|
|
28
|
+
Velvet Noise is a sparse noise sequence generated from randomly time-shifted impulses with a random value of either -1 or 1 [[2]](#2):
|
|
29
|
+
|
|
30
|
+

|
|
31
|
+
|
|
32
|
+
To reduce transient smearing and frequency coloration you can apply a segmented decay envelope [[2]](#2):
|
|
33
|
+
|
|
34
|
+

|
|
35
|
+
|
|
36
|
+
As well as logarithmically distributing the impulses towards the start of the sequence [[2]](#2):
|
|
37
|
+
|
|
38
|
+

|
|
39
|
+
|
|
40
|
+
## Quick Start
|
|
41
|
+
|
|
42
|
+
First install the package into your environment:
|
|
43
|
+
```pip install vndecorrelate```
|
|
44
|
+
|
|
45
|
+
Then load an audio file.
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
import scipy.io.wavfile as wavfile
|
|
49
|
+
from vndecorrelate.decorrelation import *
|
|
50
|
+
|
|
51
|
+
fs, input_signal = wavfile.read("audio/viola.wav")
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Then you can simply use the `VelvetNoise` class:
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
velvet_noise = VelvetNoise(
|
|
58
|
+
duration_seconds=0.03,
|
|
59
|
+
num_impulses=30,
|
|
60
|
+
)
|
|
61
|
+
output_signal = velvet_noise.decorrelate(input_signal)
|
|
62
|
+
```
|
|
63
|
+
Or:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
# manually generate the velvet noise as numpy NDArrays
|
|
67
|
+
velvet_noise = generate_velvet_noise(
|
|
68
|
+
duration_seconds=0.03,
|
|
69
|
+
num_impulses=30,
|
|
70
|
+
)
|
|
71
|
+
# numerically equivalent to VelvetNoise.convolve
|
|
72
|
+
output_signal = convolve_velvet_noise(input_signal, velvet_noise)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Or you can create a chain of signal processors:
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
chain = (
|
|
79
|
+
SignalChain(sample_rate_hz=fs)
|
|
80
|
+
.velvet_noise(
|
|
81
|
+
duration_seconds=0.03,
|
|
82
|
+
num_impulses=30,
|
|
83
|
+
log_distribution_strength=1.0,
|
|
84
|
+
seed=1,
|
|
85
|
+
)
|
|
86
|
+
.haas_effect(
|
|
87
|
+
delay_time_seconds=0.02,
|
|
88
|
+
delayed_channel=1, # Right Channel
|
|
89
|
+
mode='LR',
|
|
90
|
+
)
|
|
91
|
+
)
|
|
92
|
+
# SignalChain is lazy, so instatiation of its signal processors happens here
|
|
93
|
+
output_signal = chain(input_signal)
|
|
94
|
+
```
|
|
95
|
+
To listen back to the processed audio, simply save to a wav file locally.
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
wavfile.write('audio/viola_out.wav', fs, output_signal)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Optimization
|
|
102
|
+
`optimization.py` contains functions for optimizing `VelvetNoise` or `HaasEffect` for maximizing stereo seperation while maintaining polar sample symmetry and mono compatiblilty.
|
|
103
|
+
|
|
104
|
+
`optimize_velvet_noise` optimizes the concentration of impulses towards the start of the filter: $\kappa \in [0.0, 1.0]$, referred to as `log_distribution_strength`.
|
|
105
|
+
|
|
106
|
+
`optimize_haas_delay` optimizes the `delay_time_seconds` parameter: $\tau \in [0.0, \text{max\\_delay\\_seconds}]$
|
|
107
|
+
|
|
108
|
+
`symmetry_aware_objective` takes the input signal and converts it to polar samples to compute the scalar objective function defined by:
|
|
109
|
+
|
|
110
|
+
$f(\alpha) = E_w[\theta^2] - \lambda_1(E_w[\theta])^2 - \lambda_2(E_w[\theta^3])^2 - \lambda_3r^2 - \lambda_4(max|{\theta}| - \phi)^2$
|
|
111
|
+
|
|
112
|
+
where $\alpha$ is the input scalar to optimize, each $E_w[\theta^n]$ is a moment of the polar sample distribution: $E_w[\theta^2]$ is the weighted angular variance, $(E_w[\theta])^2$ is the weighted mean (centroid), and $(E_w[\theta^3])^2$ is the skewness. $r$ is the correlation between the input left and right channels, $\phi$ is the `angle_limit` parameter, and each $\lambda_n$ is a penalty weight.
|
|
113
|
+
|
|
114
|
+
Sample runs of `VelvetNoise.decorrelate` with unoptimized and optimized filters can be compared by their polar sample plots generated from `plot_polar_sample`:
|
|
115
|
+
|
|
116
|
+

|
|
117
|
+

|
|
118
|
+
|
|
119
|
+
## Visualization
|
|
120
|
+
To provide further visualization of the effects decorrelation `plot_correlogram` is provided. Short windows of typically ~20ms are taken from two signals to calculate normalized cross-correlation values at various lag distances. `sine_sweep` can be used to generate a test signal that can be compared before and after applying a velvet noise decorrelation.
|
|
121
|
+

|
|
122
|
+
We can use the auto correlogram as a baseline:
|
|
123
|
+

|
|
124
|
+
Plot the cross correlogram after filtering each channel with velvet noise:
|
|
125
|
+

|
|
126
|
+
And compare to the behavior of filtering with white noise:
|
|
127
|
+

|
|
128
|
+
|
|
129
|
+
## References
|
|
130
|
+
<a id="1"> </a>
|
|
131
|
+
[1] “What is ‘Decorrelation’? | Sweetwater”. <a
|
|
132
|
+
href="https://www.sweetwater.com/insync/decorrelation/">
|
|
133
|
+
https://www.sweetwater.com/insync/decorrelation/</a> (accessed Aug. 10, 2020).
|
|
134
|
+
|
|
135
|
+
<a id="2"> </a>
|
|
136
|
+
[2] “Velvet-Noise Decorrelator”. <a
|
|
137
|
+
href="http://www.dafx17.eca.ed.ac.uk/papers/DAFx17_paper_96.pdf">
|
|
138
|
+
http://www.dafx17.eca.ed.ac.uk/papers/DAFx17_paper_96.pdf</a> (accessed Aug. 04, 2020).
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# VNDecorrelate
|
|
2
|
+
[](https://pypi.org/project/vndecorrelate)
|
|
3
|
+
|
|
4
|
+
A Velvet-Noise Decorrelator for audio.
|
|
5
|
+
|
|
6
|
+
Decorrelation refers to the process of transforming an audio source signal into multiple output signals with different waveforms from each other, but with the same sound as the source signal [[1]](#1).
|
|
7
|
+
|
|
8
|
+
In music production, decorrelation is typically applied to the left and right audio channels, creating the perception of stereo width and space. This, however, may come at the cost of potential coloration or transient smearing artifacts.
|
|
9
|
+
|
|
10
|
+
Velvet-Noise Decorrelation (VND) attempts to minimize these artifacts as well as computation cost while reducing the correlation of the outputs as much as possible [[2]](#2).
|
|
11
|
+
|
|
12
|
+
## Velvet Noise
|
|
13
|
+
|
|
14
|
+
Velvet Noise is a sparse noise sequence generated from randomly time-shifted impulses with a random value of either -1 or 1 [[2]](#2):
|
|
15
|
+
|
|
16
|
+

|
|
17
|
+
|
|
18
|
+
To reduce transient smearing and frequency coloration you can apply a segmented decay envelope [[2]](#2):
|
|
19
|
+
|
|
20
|
+

|
|
21
|
+
|
|
22
|
+
As well as logarithmically distributing the impulses towards the start of the sequence [[2]](#2):
|
|
23
|
+
|
|
24
|
+

|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
First install the package into your environment:
|
|
29
|
+
```pip install vndecorrelate```
|
|
30
|
+
|
|
31
|
+
Then load an audio file.
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
import scipy.io.wavfile as wavfile
|
|
35
|
+
from vndecorrelate.decorrelation import *
|
|
36
|
+
|
|
37
|
+
fs, input_signal = wavfile.read("audio/viola.wav")
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Then you can simply use the `VelvetNoise` class:
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
velvet_noise = VelvetNoise(
|
|
44
|
+
duration_seconds=0.03,
|
|
45
|
+
num_impulses=30,
|
|
46
|
+
)
|
|
47
|
+
output_signal = velvet_noise.decorrelate(input_signal)
|
|
48
|
+
```
|
|
49
|
+
Or:
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
# manually generate the velvet noise as numpy NDArrays
|
|
53
|
+
velvet_noise = generate_velvet_noise(
|
|
54
|
+
duration_seconds=0.03,
|
|
55
|
+
num_impulses=30,
|
|
56
|
+
)
|
|
57
|
+
# numerically equivalent to VelvetNoise.convolve
|
|
58
|
+
output_signal = convolve_velvet_noise(input_signal, velvet_noise)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Or you can create a chain of signal processors:
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
chain = (
|
|
65
|
+
SignalChain(sample_rate_hz=fs)
|
|
66
|
+
.velvet_noise(
|
|
67
|
+
duration_seconds=0.03,
|
|
68
|
+
num_impulses=30,
|
|
69
|
+
log_distribution_strength=1.0,
|
|
70
|
+
seed=1,
|
|
71
|
+
)
|
|
72
|
+
.haas_effect(
|
|
73
|
+
delay_time_seconds=0.02,
|
|
74
|
+
delayed_channel=1, # Right Channel
|
|
75
|
+
mode='LR',
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
# SignalChain is lazy, so instatiation of its signal processors happens here
|
|
79
|
+
output_signal = chain(input_signal)
|
|
80
|
+
```
|
|
81
|
+
To listen back to the processed audio, simply save to a wav file locally.
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
wavfile.write('audio/viola_out.wav', fs, output_signal)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Optimization
|
|
88
|
+
`optimization.py` contains functions for optimizing `VelvetNoise` or `HaasEffect` for maximizing stereo seperation while maintaining polar sample symmetry and mono compatiblilty.
|
|
89
|
+
|
|
90
|
+
`optimize_velvet_noise` optimizes the concentration of impulses towards the start of the filter: $\kappa \in [0.0, 1.0]$, referred to as `log_distribution_strength`.
|
|
91
|
+
|
|
92
|
+
`optimize_haas_delay` optimizes the `delay_time_seconds` parameter: $\tau \in [0.0, \text{max\\_delay\\_seconds}]$
|
|
93
|
+
|
|
94
|
+
`symmetry_aware_objective` takes the input signal and converts it to polar samples to compute the scalar objective function defined by:
|
|
95
|
+
|
|
96
|
+
$f(\alpha) = E_w[\theta^2] - \lambda_1(E_w[\theta])^2 - \lambda_2(E_w[\theta^3])^2 - \lambda_3r^2 - \lambda_4(max|{\theta}| - \phi)^2$
|
|
97
|
+
|
|
98
|
+
where $\alpha$ is the input scalar to optimize, each $E_w[\theta^n]$ is a moment of the polar sample distribution: $E_w[\theta^2]$ is the weighted angular variance, $(E_w[\theta])^2$ is the weighted mean (centroid), and $(E_w[\theta^3])^2$ is the skewness. $r$ is the correlation between the input left and right channels, $\phi$ is the `angle_limit` parameter, and each $\lambda_n$ is a penalty weight.
|
|
99
|
+
|
|
100
|
+
Sample runs of `VelvetNoise.decorrelate` with unoptimized and optimized filters can be compared by their polar sample plots generated from `plot_polar_sample`:
|
|
101
|
+
|
|
102
|
+

|
|
103
|
+

|
|
104
|
+
|
|
105
|
+
## Visualization
|
|
106
|
+
To provide further visualization of the effects decorrelation `plot_correlogram` is provided. Short windows of typically ~20ms are taken from two signals to calculate normalized cross-correlation values at various lag distances. `sine_sweep` can be used to generate a test signal that can be compared before and after applying a velvet noise decorrelation.
|
|
107
|
+

|
|
108
|
+
We can use the auto correlogram as a baseline:
|
|
109
|
+

|
|
110
|
+
Plot the cross correlogram after filtering each channel with velvet noise:
|
|
111
|
+

|
|
112
|
+
And compare to the behavior of filtering with white noise:
|
|
113
|
+

|
|
114
|
+
|
|
115
|
+
## References
|
|
116
|
+
<a id="1"> </a>
|
|
117
|
+
[1] “What is ‘Decorrelation’? | Sweetwater”. <a
|
|
118
|
+
href="https://www.sweetwater.com/insync/decorrelation/">
|
|
119
|
+
https://www.sweetwater.com/insync/decorrelation/</a> (accessed Aug. 10, 2020).
|
|
120
|
+
|
|
121
|
+
<a id="2"> </a>
|
|
122
|
+
[2] “Velvet-Noise Decorrelator”. <a
|
|
123
|
+
href="http://www.dafx17.eca.ed.ac.uk/papers/DAFx17_paper_96.pdf">
|
|
124
|
+
http://www.dafx17.eca.ed.ac.uk/papers/DAFx17_paper_96.pdf</a> (accessed Aug. 04, 2020).
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "vndecorrelate"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
description = "A Velvet-Noise Decorrelator for audio"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "Christian Konstantinov", email = "ckonst98@gmail.com" }
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
dependencies = [
|
|
12
|
+
"matplotlib>=3.10.3",
|
|
13
|
+
"numpy>=2.3.1",
|
|
14
|
+
"pillow==12.2.0",
|
|
15
|
+
"scipy>=1.16.0",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[dependency-groups]
|
|
19
|
+
dev = [
|
|
20
|
+
"pytest>=9.0.3",
|
|
21
|
+
"ruff>=0.12.1",
|
|
22
|
+
"snakeviz>=2.2.2",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[tool.ruff]
|
|
26
|
+
exclude = [".venv"]
|
|
27
|
+
fix = true
|
|
28
|
+
|
|
29
|
+
[tool.ruff.format]
|
|
30
|
+
quote-style = "single"
|
|
31
|
+
|
|
32
|
+
[tool.ruff.lint.flake8-quotes]
|
|
33
|
+
docstring-quotes = "single"
|
|
34
|
+
|
|
35
|
+
[tool.ruff.lint.pydocstyle]
|
|
36
|
+
convention = "numpy"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '0.0.0'
|