pyminidsp 0.2.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.
- pyminidsp-0.2.0/LICENSE +21 -0
- pyminidsp-0.2.0/MANIFEST.in +6 -0
- pyminidsp-0.2.0/PKG-INFO +189 -0
- pyminidsp-0.2.0/README.md +150 -0
- pyminidsp-0.2.0/pyminidsp/__init__.py +100 -0
- pyminidsp-0.2.0/pyminidsp/_analysis.py +169 -0
- pyminidsp-0.2.0/pyminidsp/_build_minidsp.py +279 -0
- pyminidsp-0.2.0/pyminidsp/_core.py +62 -0
- pyminidsp-0.2.0/pyminidsp/_dtmf.py +57 -0
- pyminidsp-0.2.0/pyminidsp/_effects.py +66 -0
- pyminidsp-0.2.0/pyminidsp/_filters.py +132 -0
- pyminidsp-0.2.0/pyminidsp/_gcc.py +76 -0
- pyminidsp-0.2.0/pyminidsp/_generators.py +158 -0
- pyminidsp-0.2.0/pyminidsp/_helpers.py +58 -0
- pyminidsp-0.2.0/pyminidsp/_spectral.py +175 -0
- pyminidsp-0.2.0/pyminidsp/_steganography.py +103 -0
- pyminidsp-0.2.0/pyminidsp.egg-info/PKG-INFO +189 -0
- pyminidsp-0.2.0/pyminidsp.egg-info/SOURCES.txt +75 -0
- pyminidsp-0.2.0/pyminidsp.egg-info/dependency_links.txt +1 -0
- pyminidsp-0.2.0/pyminidsp.egg-info/requires.txt +10 -0
- pyminidsp-0.2.0/pyminidsp.egg-info/top_level.txt +1 -0
- pyminidsp-0.2.0/pyproject.toml +93 -0
- pyminidsp-0.2.0/setup.cfg +4 -0
- pyminidsp-0.2.0/setup.py +7 -0
- pyminidsp-0.2.0/tests/test_analysis.py +192 -0
- pyminidsp-0.2.0/tests/test_dtmf.py +56 -0
- pyminidsp-0.2.0/tests/test_effects.py +50 -0
- pyminidsp-0.2.0/tests/test_filters.py +126 -0
- pyminidsp-0.2.0/tests/test_gcc.py +61 -0
- pyminidsp-0.2.0/tests/test_generators.py +157 -0
- pyminidsp-0.2.0/tests/test_helpers.py +95 -0
- pyminidsp-0.2.0/tests/test_spectral.py +170 -0
- pyminidsp-0.2.0/tests/test_steganography.py +83 -0
- pyminidsp-0.2.0/vendor/miniDSP/examples/audio_steg.c +641 -0
- pyminidsp-0.2.0/vendor/miniDSP/examples/basic_operations.c +260 -0
- pyminidsp-0.2.0/vendor/miniDSP/examples/chirp_wave.c +209 -0
- pyminidsp-0.2.0/vendor/miniDSP/examples/dtmf_detector.c +237 -0
- pyminidsp-0.2.0/vendor/miniDSP/examples/fir_convolution.c +132 -0
- pyminidsp-0.2.0/vendor/miniDSP/examples/gen_audio_samples.c +328 -0
- pyminidsp-0.2.0/vendor/miniDSP/examples/gen_signal_plots.c +1685 -0
- pyminidsp-0.2.0/vendor/miniDSP/examples/impulse.c +195 -0
- pyminidsp-0.2.0/vendor/miniDSP/examples/magnitude_spectrum.c +268 -0
- pyminidsp-0.2.0/vendor/miniDSP/examples/mel_mfcc.c +177 -0
- pyminidsp-0.2.0/vendor/miniDSP/examples/phase_spectrum.c +255 -0
- pyminidsp-0.2.0/vendor/miniDSP/examples/pitch_detection.c +237 -0
- pyminidsp-0.2.0/vendor/miniDSP/examples/plot_html.h +91 -0
- pyminidsp-0.2.0/vendor/miniDSP/examples/power_spectral_density.c +279 -0
- pyminidsp-0.2.0/vendor/miniDSP/examples/shepard_tone.c +218 -0
- pyminidsp-0.2.0/vendor/miniDSP/examples/simple_effects.c +151 -0
- pyminidsp-0.2.0/vendor/miniDSP/examples/sine_wave.c +181 -0
- pyminidsp-0.2.0/vendor/miniDSP/examples/spectrogram.c +222 -0
- pyminidsp-0.2.0/vendor/miniDSP/examples/spectrogram_text.c +327 -0
- pyminidsp-0.2.0/vendor/miniDSP/examples/square_sawtooth.c +188 -0
- pyminidsp-0.2.0/vendor/miniDSP/examples/white_noise.c +159 -0
- pyminidsp-0.2.0/vendor/miniDSP/examples/window_functions.c +55 -0
- pyminidsp-0.2.0/vendor/miniDSP/include/biquad.h +97 -0
- pyminidsp-0.2.0/vendor/miniDSP/include/fileio.h +109 -0
- pyminidsp-0.2.0/vendor/miniDSP/include/liveio.h +115 -0
- pyminidsp-0.2.0/vendor/miniDSP/include/minidsp.h +1506 -0
- pyminidsp-0.2.0/vendor/miniDSP/src/biquad.c +226 -0
- pyminidsp-0.2.0/vendor/miniDSP/src/fileio.c +369 -0
- pyminidsp-0.2.0/vendor/miniDSP/src/liveio.c +458 -0
- pyminidsp-0.2.0/vendor/miniDSP/src/minidsp_core.c +601 -0
- pyminidsp-0.2.0/vendor/miniDSP/src/minidsp_dtmf.c +371 -0
- pyminidsp-0.2.0/vendor/miniDSP/src/minidsp_fir.c +166 -0
- pyminidsp-0.2.0/vendor/miniDSP/src/minidsp_gcc.c +390 -0
- pyminidsp-0.2.0/vendor/miniDSP/src/minidsp_generators.c +235 -0
- pyminidsp-0.2.0/vendor/miniDSP/src/minidsp_internal.h +21 -0
- pyminidsp-0.2.0/vendor/miniDSP/src/minidsp_spectext.c +235 -0
- pyminidsp-0.2.0/vendor/miniDSP/src/minidsp_spectrum.c +609 -0
- pyminidsp-0.2.0/vendor/miniDSP/src/minidsp_steg.c +519 -0
- pyminidsp-0.2.0/vendor/miniDSP/tests/gcc_phat_multi_test.c +67 -0
- pyminidsp-0.2.0/vendor/miniDSP/tests/gcc_phat_test.c +59 -0
- pyminidsp-0.2.0/vendor/miniDSP/tests/test_minidsp.c +4813 -0
- pyminidsp-0.2.0/vendor/miniDSP/tests/testliveio.c +73 -0
- pyminidsp-0.2.0/vendor/miniDSP/tests/testliverec.c +35 -0
- pyminidsp-0.2.0/vendor/miniDSP/third_party/stb_image_write.h +1724 -0
pyminidsp-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Chuck Wooters
|
|
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.
|
pyminidsp-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyminidsp
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Python bindings to the miniDSP C library
|
|
5
|
+
Author: Chuck Wooters
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/wooters/pyminidsp
|
|
8
|
+
Project-URL: Repository, https://github.com/wooters/pyminidsp
|
|
9
|
+
Project-URL: Documentation, https://wooters.github.io/pyminidsp/
|
|
10
|
+
Project-URL: Issues, https://github.com/wooters/pyminidsp/issues
|
|
11
|
+
Keywords: dsp,audio,signal-processing,fft,biquad,cffi
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Operating System :: MacOS
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Programming Language :: C
|
|
25
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: Analysis
|
|
26
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
27
|
+
Requires-Python: >=3.9
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Requires-Dist: cffi>=1.0
|
|
31
|
+
Requires-Dist: numpy
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: pytest; extra == "dev"
|
|
34
|
+
Provides-Extra: docs
|
|
35
|
+
Requires-Dist: sphinx>=7.0; extra == "docs"
|
|
36
|
+
Requires-Dist: furo; extra == "docs"
|
|
37
|
+
Requires-Dist: sphinx-copybutton; extra == "docs"
|
|
38
|
+
Dynamic: license-file
|
|
39
|
+
|
|
40
|
+
# pyminidsp
|
|
41
|
+
|
|
42
|
+
**[Documentation](https://wooters.github.io/pyminidsp/)**
|
|
43
|
+
|
|
44
|
+
Python bindings to the [miniDSP](https://github.com/wooters/miniDSP) C library.
|
|
45
|
+
|
|
46
|
+
A comprehensive DSP toolkit providing signal generation, spectral analysis,
|
|
47
|
+
filtering, effects, and more. All functions accept and return NumPy arrays.
|
|
48
|
+
|
|
49
|
+
## Prerequisites
|
|
50
|
+
|
|
51
|
+
- Python >= 3.9
|
|
52
|
+
- [FFTW3](http://www.fftw.org/) development headers
|
|
53
|
+
- Ubuntu/Debian: `sudo apt install libfftw3-dev`
|
|
54
|
+
- macOS: `brew install fftw`
|
|
55
|
+
- A C compiler (gcc or clang)
|
|
56
|
+
|
|
57
|
+
## Installation
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# Clone the miniDSP C library (if not already present)
|
|
61
|
+
git clone https://github.com/wooters/miniDSP.git
|
|
62
|
+
|
|
63
|
+
# Create virtual environment (uses .python-version for Python version)
|
|
64
|
+
uv venv
|
|
65
|
+
|
|
66
|
+
# Install pyminidsp (set MINIDSP_SRC to point to the C library)
|
|
67
|
+
MINIDSP_SRC=./miniDSP uv sync
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Or for development:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
MINIDSP_SRC=./miniDSP uv sync --extra dev
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Quick Start
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
import pyminidsp as md
|
|
80
|
+
|
|
81
|
+
# Generate a 440 Hz sine wave (1 second at 44.1 kHz)
|
|
82
|
+
signal = md.sine_wave(44100, amplitude=1.0, freq=440.0, sample_rate=44100.0)
|
|
83
|
+
|
|
84
|
+
# Compute the magnitude spectrum
|
|
85
|
+
mag = md.magnitude_spectrum(signal)
|
|
86
|
+
|
|
87
|
+
# Compute MFCCs from a frame
|
|
88
|
+
coeffs = md.mfcc(signal[:512], sample_rate=44100.0, num_mels=26, num_coeffs=13)
|
|
89
|
+
|
|
90
|
+
# Apply a low-pass biquad filter
|
|
91
|
+
lpf = md.BiquadFilter(md.LPF, freq=1000.0, sample_rate=44100.0)
|
|
92
|
+
filtered = lpf.process_array(signal)
|
|
93
|
+
|
|
94
|
+
# Clean up FFT caches when done
|
|
95
|
+
md.shutdown()
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## API Overview
|
|
99
|
+
|
|
100
|
+
### Signal Generators
|
|
101
|
+
|
|
102
|
+
| Function | Description |
|
|
103
|
+
|----------|-------------|
|
|
104
|
+
| `sine_wave(n, amplitude, freq, sample_rate)` | Pure sine tone |
|
|
105
|
+
| `white_noise(n, amplitude, seed)` | Gaussian white noise |
|
|
106
|
+
| `impulse(n, amplitude, position)` | Kronecker delta |
|
|
107
|
+
| `chirp_linear(n, amplitude, f_start, f_end, sample_rate)` | Linear frequency sweep |
|
|
108
|
+
| `chirp_log(n, amplitude, f_start, f_end, sample_rate)` | Logarithmic frequency sweep |
|
|
109
|
+
| `square_wave(n, amplitude, freq, sample_rate)` | Square wave |
|
|
110
|
+
| `sawtooth_wave(n, amplitude, freq, sample_rate)` | Sawtooth wave |
|
|
111
|
+
| `shepard_tone(n, amplitude, base_freq, sample_rate, rate, num_octaves)` | Shepard tone illusion |
|
|
112
|
+
|
|
113
|
+
### Spectral Analysis
|
|
114
|
+
|
|
115
|
+
| Function | Description |
|
|
116
|
+
|----------|-------------|
|
|
117
|
+
| `magnitude_spectrum(signal)` | One-sided magnitude spectrum via FFT |
|
|
118
|
+
| `power_spectral_density(signal)` | Periodogram PSD estimate |
|
|
119
|
+
| `phase_spectrum(signal)` | Phase spectrum in radians |
|
|
120
|
+
| `stft(signal, n, hop)` | Short-Time Fourier Transform |
|
|
121
|
+
| `mel_filterbank(n, sample_rate, num_mels)` | Mel-spaced triangular filterbank |
|
|
122
|
+
| `mel_energies(signal, sample_rate, num_mels)` | Mel-band energies |
|
|
123
|
+
| `mfcc(signal, sample_rate, num_mels, num_coeffs)` | Mel-frequency cepstral coefficients |
|
|
124
|
+
|
|
125
|
+
### Signal Analysis
|
|
126
|
+
|
|
127
|
+
| Function | Description |
|
|
128
|
+
|----------|-------------|
|
|
129
|
+
| `rms(signal)` | Root mean square |
|
|
130
|
+
| `energy(signal)` | Signal energy |
|
|
131
|
+
| `power(signal)` / `power_db(signal)` | Signal power (linear / dB) |
|
|
132
|
+
| `zero_crossing_rate(signal)` | Zero-crossing rate |
|
|
133
|
+
| `autocorrelation(signal, max_lag)` | Normalised autocorrelation |
|
|
134
|
+
| `peak_detect(signal, threshold, min_distance)` | Local maxima detection |
|
|
135
|
+
| `f0_autocorrelation(signal, sample_rate, min_freq, max_freq)` | F0 via autocorrelation |
|
|
136
|
+
| `f0_fft(signal, sample_rate, min_freq, max_freq)` | F0 via FFT peak picking |
|
|
137
|
+
|
|
138
|
+
### Effects & Filters
|
|
139
|
+
|
|
140
|
+
| Function | Description |
|
|
141
|
+
|----------|-------------|
|
|
142
|
+
| `delay_echo(signal, delay_samples, feedback, dry, wet)` | Echo effect |
|
|
143
|
+
| `tremolo(signal, rate_hz, depth, sample_rate)` | Amplitude modulation |
|
|
144
|
+
| `comb_reverb(signal, delay_samples, feedback, dry, wet)` | Comb-filter reverb |
|
|
145
|
+
| `convolution_time(signal, kernel)` | Time-domain convolution |
|
|
146
|
+
| `convolution_fft_ola(signal, kernel)` | FFT overlap-add convolution |
|
|
147
|
+
| `moving_average(signal, window_len)` | Moving average filter |
|
|
148
|
+
| `fir_filter(signal, coeffs)` | FIR filter |
|
|
149
|
+
| `BiquadFilter(type, freq, sample_rate)` | IIR biquad filter (LPF/HPF/BPF/etc.) |
|
|
150
|
+
|
|
151
|
+
### DTMF
|
|
152
|
+
|
|
153
|
+
| Function | Description |
|
|
154
|
+
|----------|-------------|
|
|
155
|
+
| `dtmf_generate(digits, sample_rate, tone_ms, pause_ms)` | Generate DTMF tones |
|
|
156
|
+
| `dtmf_detect(signal, sample_rate)` | Detect DTMF digits |
|
|
157
|
+
|
|
158
|
+
### Delay Estimation (GCC-PHAT)
|
|
159
|
+
|
|
160
|
+
| Function | Description |
|
|
161
|
+
|----------|-------------|
|
|
162
|
+
| `get_delay(sig_a, sig_b, margin, weighting)` | Delay between two signals |
|
|
163
|
+
| `gcc(sig_a, sig_b, weighting)` | Full cross-correlation |
|
|
164
|
+
|
|
165
|
+
### Audio Steganography
|
|
166
|
+
|
|
167
|
+
| Function | Description |
|
|
168
|
+
|----------|-------------|
|
|
169
|
+
| `steg_encode(host, message, sample_rate, method)` | Hide text in audio |
|
|
170
|
+
| `steg_decode(stego, sample_rate, method)` | Recover hidden text |
|
|
171
|
+
| `steg_encode_bytes(host, data, sample_rate, method)` | Hide binary data |
|
|
172
|
+
| `steg_decode_bytes(stego, sample_rate, method)` | Recover binary data |
|
|
173
|
+
| `steg_detect(signal, sample_rate)` | Detect steganography method |
|
|
174
|
+
|
|
175
|
+
### Spectrogram Art
|
|
176
|
+
|
|
177
|
+
| Function | Description |
|
|
178
|
+
|----------|-------------|
|
|
179
|
+
| `spectrogram_text(text, freq_lo, freq_hi, duration, sample_rate)` | Text visible in spectrogram |
|
|
180
|
+
|
|
181
|
+
## Running Tests
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
MINIDSP_SRC=./miniDSP uv run pytest tests/ -v
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## License
|
|
188
|
+
|
|
189
|
+
MIT
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# pyminidsp
|
|
2
|
+
|
|
3
|
+
**[Documentation](https://wooters.github.io/pyminidsp/)**
|
|
4
|
+
|
|
5
|
+
Python bindings to the [miniDSP](https://github.com/wooters/miniDSP) C library.
|
|
6
|
+
|
|
7
|
+
A comprehensive DSP toolkit providing signal generation, spectral analysis,
|
|
8
|
+
filtering, effects, and more. All functions accept and return NumPy arrays.
|
|
9
|
+
|
|
10
|
+
## Prerequisites
|
|
11
|
+
|
|
12
|
+
- Python >= 3.9
|
|
13
|
+
- [FFTW3](http://www.fftw.org/) development headers
|
|
14
|
+
- Ubuntu/Debian: `sudo apt install libfftw3-dev`
|
|
15
|
+
- macOS: `brew install fftw`
|
|
16
|
+
- A C compiler (gcc or clang)
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# Clone the miniDSP C library (if not already present)
|
|
22
|
+
git clone https://github.com/wooters/miniDSP.git
|
|
23
|
+
|
|
24
|
+
# Create virtual environment (uses .python-version for Python version)
|
|
25
|
+
uv venv
|
|
26
|
+
|
|
27
|
+
# Install pyminidsp (set MINIDSP_SRC to point to the C library)
|
|
28
|
+
MINIDSP_SRC=./miniDSP uv sync
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Or for development:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
MINIDSP_SRC=./miniDSP uv sync --extra dev
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Quick Start
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
import pyminidsp as md
|
|
41
|
+
|
|
42
|
+
# Generate a 440 Hz sine wave (1 second at 44.1 kHz)
|
|
43
|
+
signal = md.sine_wave(44100, amplitude=1.0, freq=440.0, sample_rate=44100.0)
|
|
44
|
+
|
|
45
|
+
# Compute the magnitude spectrum
|
|
46
|
+
mag = md.magnitude_spectrum(signal)
|
|
47
|
+
|
|
48
|
+
# Compute MFCCs from a frame
|
|
49
|
+
coeffs = md.mfcc(signal[:512], sample_rate=44100.0, num_mels=26, num_coeffs=13)
|
|
50
|
+
|
|
51
|
+
# Apply a low-pass biquad filter
|
|
52
|
+
lpf = md.BiquadFilter(md.LPF, freq=1000.0, sample_rate=44100.0)
|
|
53
|
+
filtered = lpf.process_array(signal)
|
|
54
|
+
|
|
55
|
+
# Clean up FFT caches when done
|
|
56
|
+
md.shutdown()
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## API Overview
|
|
60
|
+
|
|
61
|
+
### Signal Generators
|
|
62
|
+
|
|
63
|
+
| Function | Description |
|
|
64
|
+
|----------|-------------|
|
|
65
|
+
| `sine_wave(n, amplitude, freq, sample_rate)` | Pure sine tone |
|
|
66
|
+
| `white_noise(n, amplitude, seed)` | Gaussian white noise |
|
|
67
|
+
| `impulse(n, amplitude, position)` | Kronecker delta |
|
|
68
|
+
| `chirp_linear(n, amplitude, f_start, f_end, sample_rate)` | Linear frequency sweep |
|
|
69
|
+
| `chirp_log(n, amplitude, f_start, f_end, sample_rate)` | Logarithmic frequency sweep |
|
|
70
|
+
| `square_wave(n, amplitude, freq, sample_rate)` | Square wave |
|
|
71
|
+
| `sawtooth_wave(n, amplitude, freq, sample_rate)` | Sawtooth wave |
|
|
72
|
+
| `shepard_tone(n, amplitude, base_freq, sample_rate, rate, num_octaves)` | Shepard tone illusion |
|
|
73
|
+
|
|
74
|
+
### Spectral Analysis
|
|
75
|
+
|
|
76
|
+
| Function | Description |
|
|
77
|
+
|----------|-------------|
|
|
78
|
+
| `magnitude_spectrum(signal)` | One-sided magnitude spectrum via FFT |
|
|
79
|
+
| `power_spectral_density(signal)` | Periodogram PSD estimate |
|
|
80
|
+
| `phase_spectrum(signal)` | Phase spectrum in radians |
|
|
81
|
+
| `stft(signal, n, hop)` | Short-Time Fourier Transform |
|
|
82
|
+
| `mel_filterbank(n, sample_rate, num_mels)` | Mel-spaced triangular filterbank |
|
|
83
|
+
| `mel_energies(signal, sample_rate, num_mels)` | Mel-band energies |
|
|
84
|
+
| `mfcc(signal, sample_rate, num_mels, num_coeffs)` | Mel-frequency cepstral coefficients |
|
|
85
|
+
|
|
86
|
+
### Signal Analysis
|
|
87
|
+
|
|
88
|
+
| Function | Description |
|
|
89
|
+
|----------|-------------|
|
|
90
|
+
| `rms(signal)` | Root mean square |
|
|
91
|
+
| `energy(signal)` | Signal energy |
|
|
92
|
+
| `power(signal)` / `power_db(signal)` | Signal power (linear / dB) |
|
|
93
|
+
| `zero_crossing_rate(signal)` | Zero-crossing rate |
|
|
94
|
+
| `autocorrelation(signal, max_lag)` | Normalised autocorrelation |
|
|
95
|
+
| `peak_detect(signal, threshold, min_distance)` | Local maxima detection |
|
|
96
|
+
| `f0_autocorrelation(signal, sample_rate, min_freq, max_freq)` | F0 via autocorrelation |
|
|
97
|
+
| `f0_fft(signal, sample_rate, min_freq, max_freq)` | F0 via FFT peak picking |
|
|
98
|
+
|
|
99
|
+
### Effects & Filters
|
|
100
|
+
|
|
101
|
+
| Function | Description |
|
|
102
|
+
|----------|-------------|
|
|
103
|
+
| `delay_echo(signal, delay_samples, feedback, dry, wet)` | Echo effect |
|
|
104
|
+
| `tremolo(signal, rate_hz, depth, sample_rate)` | Amplitude modulation |
|
|
105
|
+
| `comb_reverb(signal, delay_samples, feedback, dry, wet)` | Comb-filter reverb |
|
|
106
|
+
| `convolution_time(signal, kernel)` | Time-domain convolution |
|
|
107
|
+
| `convolution_fft_ola(signal, kernel)` | FFT overlap-add convolution |
|
|
108
|
+
| `moving_average(signal, window_len)` | Moving average filter |
|
|
109
|
+
| `fir_filter(signal, coeffs)` | FIR filter |
|
|
110
|
+
| `BiquadFilter(type, freq, sample_rate)` | IIR biquad filter (LPF/HPF/BPF/etc.) |
|
|
111
|
+
|
|
112
|
+
### DTMF
|
|
113
|
+
|
|
114
|
+
| Function | Description |
|
|
115
|
+
|----------|-------------|
|
|
116
|
+
| `dtmf_generate(digits, sample_rate, tone_ms, pause_ms)` | Generate DTMF tones |
|
|
117
|
+
| `dtmf_detect(signal, sample_rate)` | Detect DTMF digits |
|
|
118
|
+
|
|
119
|
+
### Delay Estimation (GCC-PHAT)
|
|
120
|
+
|
|
121
|
+
| Function | Description |
|
|
122
|
+
|----------|-------------|
|
|
123
|
+
| `get_delay(sig_a, sig_b, margin, weighting)` | Delay between two signals |
|
|
124
|
+
| `gcc(sig_a, sig_b, weighting)` | Full cross-correlation |
|
|
125
|
+
|
|
126
|
+
### Audio Steganography
|
|
127
|
+
|
|
128
|
+
| Function | Description |
|
|
129
|
+
|----------|-------------|
|
|
130
|
+
| `steg_encode(host, message, sample_rate, method)` | Hide text in audio |
|
|
131
|
+
| `steg_decode(stego, sample_rate, method)` | Recover hidden text |
|
|
132
|
+
| `steg_encode_bytes(host, data, sample_rate, method)` | Hide binary data |
|
|
133
|
+
| `steg_decode_bytes(stego, sample_rate, method)` | Recover binary data |
|
|
134
|
+
| `steg_detect(signal, sample_rate)` | Detect steganography method |
|
|
135
|
+
|
|
136
|
+
### Spectrogram Art
|
|
137
|
+
|
|
138
|
+
| Function | Description |
|
|
139
|
+
|----------|-------------|
|
|
140
|
+
| `spectrogram_text(text, freq_lo, freq_hi, duration, sample_rate)` | Text visible in spectrogram |
|
|
141
|
+
|
|
142
|
+
## Running Tests
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
MINIDSP_SRC=./miniDSP uv run pytest tests/ -v
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## License
|
|
149
|
+
|
|
150
|
+
MIT
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pyminidsp - Python bindings to the miniDSP C library.
|
|
3
|
+
|
|
4
|
+
A comprehensive DSP toolkit providing signal generation, spectral analysis,
|
|
5
|
+
filtering, effects, and more. All functions accept and return NumPy arrays.
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
>>> import pyminidsp as md
|
|
9
|
+
>>> signal = md.sine_wave(1024, amplitude=1.0, freq=440.0, sample_rate=44100.0)
|
|
10
|
+
>>> mag = md.magnitude_spectrum(signal)
|
|
11
|
+
>>> md.shutdown()
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from pyminidsp._core import * # noqa: F401, F403
|
|
15
|
+
from pyminidsp._core import (
|
|
16
|
+
# Re-export everything explicitly for documentation
|
|
17
|
+
# Signal measurement
|
|
18
|
+
dot, entropy, energy, power, power_db,
|
|
19
|
+
# Signal analysis
|
|
20
|
+
rms, zero_crossing_rate, autocorrelation, peak_detect,
|
|
21
|
+
f0_autocorrelation, f0_fft, mix,
|
|
22
|
+
# Effects
|
|
23
|
+
delay_echo, tremolo, comb_reverb,
|
|
24
|
+
# FIR / Convolution
|
|
25
|
+
convolution_num_samples, convolution_time, moving_average,
|
|
26
|
+
fir_filter, convolution_fft_ola,
|
|
27
|
+
# Scaling
|
|
28
|
+
scale, scale_vec, fit_within_range, adjust_dblevel,
|
|
29
|
+
# Spectrum
|
|
30
|
+
magnitude_spectrum, power_spectral_density, phase_spectrum,
|
|
31
|
+
stft_num_frames, stft, mel_filterbank, mel_energies, mfcc,
|
|
32
|
+
# Windows
|
|
33
|
+
hann_window, hamming_window, blackman_window, rect_window,
|
|
34
|
+
# Generators
|
|
35
|
+
sine_wave, white_noise, impulse, chirp_linear, chirp_log,
|
|
36
|
+
square_wave, sawtooth_wave, shepard_tone,
|
|
37
|
+
# DTMF
|
|
38
|
+
dtmf_detect, dtmf_generate, dtmf_signal_length,
|
|
39
|
+
# Cleanup
|
|
40
|
+
shutdown,
|
|
41
|
+
# GCC
|
|
42
|
+
get_delay, get_multiple_delays, gcc,
|
|
43
|
+
# Spectrogram text
|
|
44
|
+
spectrogram_text,
|
|
45
|
+
# Steganography
|
|
46
|
+
steg_capacity, steg_encode, steg_decode,
|
|
47
|
+
steg_encode_bytes, steg_decode_bytes, steg_detect,
|
|
48
|
+
# Biquad
|
|
49
|
+
BiquadFilter,
|
|
50
|
+
# Constants
|
|
51
|
+
LPF, HPF, BPF, NOTCH, PEQ, LSH, HSH,
|
|
52
|
+
STEG_LSB, STEG_FREQ_BAND, STEG_TYPE_TEXT, STEG_TYPE_BINARY,
|
|
53
|
+
GCC_SIMP, GCC_PHAT,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
from importlib.metadata import version as _version, PackageNotFoundError as _PNF
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
__version__ = _version("pyminidsp")
|
|
60
|
+
except _PNF:
|
|
61
|
+
__version__ = "0.0.0" # not installed via pip (e.g. editable dev checkout)
|
|
62
|
+
__all__ = [
|
|
63
|
+
# Signal measurement
|
|
64
|
+
"dot", "entropy", "energy", "power", "power_db",
|
|
65
|
+
# Signal analysis
|
|
66
|
+
"rms", "zero_crossing_rate", "autocorrelation", "peak_detect",
|
|
67
|
+
"f0_autocorrelation", "f0_fft", "mix",
|
|
68
|
+
# Effects
|
|
69
|
+
"delay_echo", "tremolo", "comb_reverb",
|
|
70
|
+
# FIR / Convolution
|
|
71
|
+
"convolution_num_samples", "convolution_time", "moving_average",
|
|
72
|
+
"fir_filter", "convolution_fft_ola",
|
|
73
|
+
# Scaling
|
|
74
|
+
"scale", "scale_vec", "fit_within_range", "adjust_dblevel",
|
|
75
|
+
# Spectrum
|
|
76
|
+
"magnitude_spectrum", "power_spectral_density", "phase_spectrum",
|
|
77
|
+
"stft_num_frames", "stft", "mel_filterbank", "mel_energies", "mfcc",
|
|
78
|
+
# Windows
|
|
79
|
+
"hann_window", "hamming_window", "blackman_window", "rect_window",
|
|
80
|
+
# Generators
|
|
81
|
+
"sine_wave", "white_noise", "impulse", "chirp_linear", "chirp_log",
|
|
82
|
+
"square_wave", "sawtooth_wave", "shepard_tone",
|
|
83
|
+
# DTMF
|
|
84
|
+
"dtmf_detect", "dtmf_generate", "dtmf_signal_length",
|
|
85
|
+
# Cleanup
|
|
86
|
+
"shutdown",
|
|
87
|
+
# GCC
|
|
88
|
+
"get_delay", "get_multiple_delays", "gcc",
|
|
89
|
+
# Spectrogram text
|
|
90
|
+
"spectrogram_text",
|
|
91
|
+
# Steganography
|
|
92
|
+
"steg_capacity", "steg_encode", "steg_decode",
|
|
93
|
+
"steg_encode_bytes", "steg_decode_bytes", "steg_detect",
|
|
94
|
+
# Biquad
|
|
95
|
+
"BiquadFilter",
|
|
96
|
+
# Constants
|
|
97
|
+
"LPF", "HPF", "BPF", "NOTCH", "PEQ", "LSH", "HSH",
|
|
98
|
+
"STEG_LSB", "STEG_FREQ_BAND", "STEG_TYPE_TEXT", "STEG_TYPE_BINARY",
|
|
99
|
+
"GCC_SIMP", "GCC_PHAT",
|
|
100
|
+
]
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"""Signal measurement, analysis, and scaling functions."""
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from pyminidsp._minidsp_cffi import ffi, lib
|
|
6
|
+
from pyminidsp._helpers import _as_double_ptr, _new_double_array
|
|
7
|
+
|
|
8
|
+
# ---------------------------------------------------------------------------
|
|
9
|
+
# Signal measurement
|
|
10
|
+
# ---------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
def dot(a, b):
|
|
13
|
+
"""Compute the dot product of two vectors."""
|
|
14
|
+
a_ptr, a = _as_double_ptr(a)
|
|
15
|
+
b_ptr, b = _as_double_ptr(b)
|
|
16
|
+
n = min(len(a), len(b))
|
|
17
|
+
return lib.MD_dot(a_ptr, b_ptr, n)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def entropy(a, clip=False):
|
|
21
|
+
"""Compute the normalized entropy of a distribution."""
|
|
22
|
+
a_ptr, a = _as_double_ptr(a)
|
|
23
|
+
return lib.MD_entropy(a_ptr, len(a), clip)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def energy(a):
|
|
27
|
+
"""Compute signal energy: sum of squared samples."""
|
|
28
|
+
a_ptr, a = _as_double_ptr(a)
|
|
29
|
+
return lib.MD_energy(a_ptr, len(a))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def power(a):
|
|
33
|
+
"""Compute signal power: energy / N."""
|
|
34
|
+
a_ptr, a = _as_double_ptr(a)
|
|
35
|
+
return lib.MD_power(a_ptr, len(a))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def power_db(a):
|
|
39
|
+
"""Compute signal power in decibels."""
|
|
40
|
+
a_ptr, a = _as_double_ptr(a)
|
|
41
|
+
return lib.MD_power_db(a_ptr, len(a))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# ---------------------------------------------------------------------------
|
|
45
|
+
# Signal analysis
|
|
46
|
+
# ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
def rms(a):
|
|
49
|
+
"""Compute the root mean square (RMS) of a signal."""
|
|
50
|
+
a_ptr, a = _as_double_ptr(a)
|
|
51
|
+
return lib.MD_rms(a_ptr, len(a))
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def zero_crossing_rate(a):
|
|
55
|
+
"""Compute the zero-crossing rate of a signal."""
|
|
56
|
+
a_ptr, a = _as_double_ptr(a)
|
|
57
|
+
return lib.MD_zero_crossing_rate(a_ptr, len(a))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def autocorrelation(a, max_lag):
|
|
61
|
+
"""
|
|
62
|
+
Compute the normalised autocorrelation of a signal.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
a: Input signal.
|
|
66
|
+
max_lag: Number of lag values to compute.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
numpy array of autocorrelation values, length max_lag.
|
|
70
|
+
"""
|
|
71
|
+
a_ptr, a = _as_double_ptr(a)
|
|
72
|
+
out, out_ptr = _new_double_array(max_lag)
|
|
73
|
+
lib.MD_autocorrelation(a_ptr, len(a), out_ptr, max_lag)
|
|
74
|
+
return out
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def peak_detect(a, threshold=0.0, min_distance=1):
|
|
78
|
+
"""
|
|
79
|
+
Detect peaks (local maxima) in a signal.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
a: Input signal.
|
|
83
|
+
threshold: Minimum value for a peak.
|
|
84
|
+
min_distance: Minimum index gap between peaks.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
numpy array of peak indices.
|
|
88
|
+
"""
|
|
89
|
+
a_ptr, a = _as_double_ptr(a)
|
|
90
|
+
n = len(a)
|
|
91
|
+
peaks = np.zeros(n, dtype=np.uint32)
|
|
92
|
+
num_peaks = ffi.new("unsigned *")
|
|
93
|
+
lib.MD_peak_detect(
|
|
94
|
+
a_ptr, n, threshold, min_distance,
|
|
95
|
+
ffi.cast("unsigned *", peaks.ctypes.data), num_peaks,
|
|
96
|
+
)
|
|
97
|
+
return peaks[: num_peaks[0]].copy()
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def f0_autocorrelation(signal, sample_rate, min_freq_hz=80.0, max_freq_hz=400.0):
|
|
101
|
+
"""Estimate F0 using autocorrelation."""
|
|
102
|
+
s_ptr, signal = _as_double_ptr(signal)
|
|
103
|
+
return lib.MD_f0_autocorrelation(s_ptr, len(signal), sample_rate, min_freq_hz, max_freq_hz)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def f0_fft(signal, sample_rate, min_freq_hz=80.0, max_freq_hz=400.0):
|
|
107
|
+
"""Estimate F0 using FFT peak picking."""
|
|
108
|
+
s_ptr, signal = _as_double_ptr(signal)
|
|
109
|
+
return lib.MD_f0_fft(s_ptr, len(signal), sample_rate, min_freq_hz, max_freq_hz)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def mix(a, b, w_a=0.5, w_b=0.5):
|
|
113
|
+
"""
|
|
114
|
+
Mix (weighted sum) two signals.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
a, b: Input signals of the same length.
|
|
118
|
+
w_a, w_b: Weights for signals a and b.
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
numpy array of the mixed signal.
|
|
122
|
+
"""
|
|
123
|
+
a_ptr, a = _as_double_ptr(a)
|
|
124
|
+
b_ptr, b = _as_double_ptr(b)
|
|
125
|
+
n = min(len(a), len(b))
|
|
126
|
+
out, out_ptr = _new_double_array(n)
|
|
127
|
+
lib.MD_mix(a_ptr, b_ptr, out_ptr, n, w_a, w_b)
|
|
128
|
+
return out
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
# ---------------------------------------------------------------------------
|
|
132
|
+
# Signal scaling
|
|
133
|
+
# ---------------------------------------------------------------------------
|
|
134
|
+
|
|
135
|
+
def scale(value, oldmin, oldmax, newmin, newmax):
|
|
136
|
+
"""Map a single value from one range to another."""
|
|
137
|
+
return lib.MD_scale(value, oldmin, oldmax, newmin, newmax)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def scale_vec(a, oldmin, oldmax, newmin, newmax):
|
|
141
|
+
"""Map every element of a vector from one range to another."""
|
|
142
|
+
a_ptr, a_arr = _as_double_ptr(a)
|
|
143
|
+
n = len(a_arr)
|
|
144
|
+
out, out_ptr = _new_double_array(n)
|
|
145
|
+
# MD_scale_vec takes non-const input pointer
|
|
146
|
+
in_copy = np.array(a_arr, dtype=np.float64, copy=True)
|
|
147
|
+
in_ptr = ffi.cast("double *", in_copy.ctypes.data)
|
|
148
|
+
lib.MD_scale_vec(in_ptr, out_ptr, n, oldmin, oldmax, newmin, newmax)
|
|
149
|
+
return out
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def fit_within_range(a, newmin, newmax):
|
|
153
|
+
"""Fit values within [newmin, newmax]."""
|
|
154
|
+
a_arr = np.ascontiguousarray(a, dtype=np.float64)
|
|
155
|
+
n = len(a_arr)
|
|
156
|
+
in_copy = np.array(a_arr, copy=True)
|
|
157
|
+
in_ptr = ffi.cast("double *", in_copy.ctypes.data)
|
|
158
|
+
out, out_ptr = _new_double_array(n)
|
|
159
|
+
lib.MD_fit_within_range(in_ptr, out_ptr, n, newmin, newmax)
|
|
160
|
+
return out
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def adjust_dblevel(signal, dblevel):
|
|
164
|
+
"""Automatic Gain Control: scale signal to target dB level, clip to [-1, 1]."""
|
|
165
|
+
s_ptr, signal = _as_double_ptr(signal)
|
|
166
|
+
n = len(signal)
|
|
167
|
+
out, out_ptr = _new_double_array(n)
|
|
168
|
+
lib.MD_adjust_dblevel(s_ptr, out_ptr, n, dblevel)
|
|
169
|
+
return out
|