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.
Files changed (77) hide show
  1. pyminidsp-0.2.0/LICENSE +21 -0
  2. pyminidsp-0.2.0/MANIFEST.in +6 -0
  3. pyminidsp-0.2.0/PKG-INFO +189 -0
  4. pyminidsp-0.2.0/README.md +150 -0
  5. pyminidsp-0.2.0/pyminidsp/__init__.py +100 -0
  6. pyminidsp-0.2.0/pyminidsp/_analysis.py +169 -0
  7. pyminidsp-0.2.0/pyminidsp/_build_minidsp.py +279 -0
  8. pyminidsp-0.2.0/pyminidsp/_core.py +62 -0
  9. pyminidsp-0.2.0/pyminidsp/_dtmf.py +57 -0
  10. pyminidsp-0.2.0/pyminidsp/_effects.py +66 -0
  11. pyminidsp-0.2.0/pyminidsp/_filters.py +132 -0
  12. pyminidsp-0.2.0/pyminidsp/_gcc.py +76 -0
  13. pyminidsp-0.2.0/pyminidsp/_generators.py +158 -0
  14. pyminidsp-0.2.0/pyminidsp/_helpers.py +58 -0
  15. pyminidsp-0.2.0/pyminidsp/_spectral.py +175 -0
  16. pyminidsp-0.2.0/pyminidsp/_steganography.py +103 -0
  17. pyminidsp-0.2.0/pyminidsp.egg-info/PKG-INFO +189 -0
  18. pyminidsp-0.2.0/pyminidsp.egg-info/SOURCES.txt +75 -0
  19. pyminidsp-0.2.0/pyminidsp.egg-info/dependency_links.txt +1 -0
  20. pyminidsp-0.2.0/pyminidsp.egg-info/requires.txt +10 -0
  21. pyminidsp-0.2.0/pyminidsp.egg-info/top_level.txt +1 -0
  22. pyminidsp-0.2.0/pyproject.toml +93 -0
  23. pyminidsp-0.2.0/setup.cfg +4 -0
  24. pyminidsp-0.2.0/setup.py +7 -0
  25. pyminidsp-0.2.0/tests/test_analysis.py +192 -0
  26. pyminidsp-0.2.0/tests/test_dtmf.py +56 -0
  27. pyminidsp-0.2.0/tests/test_effects.py +50 -0
  28. pyminidsp-0.2.0/tests/test_filters.py +126 -0
  29. pyminidsp-0.2.0/tests/test_gcc.py +61 -0
  30. pyminidsp-0.2.0/tests/test_generators.py +157 -0
  31. pyminidsp-0.2.0/tests/test_helpers.py +95 -0
  32. pyminidsp-0.2.0/tests/test_spectral.py +170 -0
  33. pyminidsp-0.2.0/tests/test_steganography.py +83 -0
  34. pyminidsp-0.2.0/vendor/miniDSP/examples/audio_steg.c +641 -0
  35. pyminidsp-0.2.0/vendor/miniDSP/examples/basic_operations.c +260 -0
  36. pyminidsp-0.2.0/vendor/miniDSP/examples/chirp_wave.c +209 -0
  37. pyminidsp-0.2.0/vendor/miniDSP/examples/dtmf_detector.c +237 -0
  38. pyminidsp-0.2.0/vendor/miniDSP/examples/fir_convolution.c +132 -0
  39. pyminidsp-0.2.0/vendor/miniDSP/examples/gen_audio_samples.c +328 -0
  40. pyminidsp-0.2.0/vendor/miniDSP/examples/gen_signal_plots.c +1685 -0
  41. pyminidsp-0.2.0/vendor/miniDSP/examples/impulse.c +195 -0
  42. pyminidsp-0.2.0/vendor/miniDSP/examples/magnitude_spectrum.c +268 -0
  43. pyminidsp-0.2.0/vendor/miniDSP/examples/mel_mfcc.c +177 -0
  44. pyminidsp-0.2.0/vendor/miniDSP/examples/phase_spectrum.c +255 -0
  45. pyminidsp-0.2.0/vendor/miniDSP/examples/pitch_detection.c +237 -0
  46. pyminidsp-0.2.0/vendor/miniDSP/examples/plot_html.h +91 -0
  47. pyminidsp-0.2.0/vendor/miniDSP/examples/power_spectral_density.c +279 -0
  48. pyminidsp-0.2.0/vendor/miniDSP/examples/shepard_tone.c +218 -0
  49. pyminidsp-0.2.0/vendor/miniDSP/examples/simple_effects.c +151 -0
  50. pyminidsp-0.2.0/vendor/miniDSP/examples/sine_wave.c +181 -0
  51. pyminidsp-0.2.0/vendor/miniDSP/examples/spectrogram.c +222 -0
  52. pyminidsp-0.2.0/vendor/miniDSP/examples/spectrogram_text.c +327 -0
  53. pyminidsp-0.2.0/vendor/miniDSP/examples/square_sawtooth.c +188 -0
  54. pyminidsp-0.2.0/vendor/miniDSP/examples/white_noise.c +159 -0
  55. pyminidsp-0.2.0/vendor/miniDSP/examples/window_functions.c +55 -0
  56. pyminidsp-0.2.0/vendor/miniDSP/include/biquad.h +97 -0
  57. pyminidsp-0.2.0/vendor/miniDSP/include/fileio.h +109 -0
  58. pyminidsp-0.2.0/vendor/miniDSP/include/liveio.h +115 -0
  59. pyminidsp-0.2.0/vendor/miniDSP/include/minidsp.h +1506 -0
  60. pyminidsp-0.2.0/vendor/miniDSP/src/biquad.c +226 -0
  61. pyminidsp-0.2.0/vendor/miniDSP/src/fileio.c +369 -0
  62. pyminidsp-0.2.0/vendor/miniDSP/src/liveio.c +458 -0
  63. pyminidsp-0.2.0/vendor/miniDSP/src/minidsp_core.c +601 -0
  64. pyminidsp-0.2.0/vendor/miniDSP/src/minidsp_dtmf.c +371 -0
  65. pyminidsp-0.2.0/vendor/miniDSP/src/minidsp_fir.c +166 -0
  66. pyminidsp-0.2.0/vendor/miniDSP/src/minidsp_gcc.c +390 -0
  67. pyminidsp-0.2.0/vendor/miniDSP/src/minidsp_generators.c +235 -0
  68. pyminidsp-0.2.0/vendor/miniDSP/src/minidsp_internal.h +21 -0
  69. pyminidsp-0.2.0/vendor/miniDSP/src/minidsp_spectext.c +235 -0
  70. pyminidsp-0.2.0/vendor/miniDSP/src/minidsp_spectrum.c +609 -0
  71. pyminidsp-0.2.0/vendor/miniDSP/src/minidsp_steg.c +519 -0
  72. pyminidsp-0.2.0/vendor/miniDSP/tests/gcc_phat_multi_test.c +67 -0
  73. pyminidsp-0.2.0/vendor/miniDSP/tests/gcc_phat_test.c +59 -0
  74. pyminidsp-0.2.0/vendor/miniDSP/tests/test_minidsp.c +4813 -0
  75. pyminidsp-0.2.0/vendor/miniDSP/tests/testliveio.c +73 -0
  76. pyminidsp-0.2.0/vendor/miniDSP/tests/testliverec.c +35 -0
  77. pyminidsp-0.2.0/vendor/miniDSP/third_party/stb_image_write.h +1724 -0
@@ -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.
@@ -0,0 +1,6 @@
1
+ include LICENSE
2
+ include README.md
3
+ include pyproject.toml
4
+ include setup.py
5
+ recursive-include pyminidsp *.py
6
+ recursive-include vendor/miniDSP *.c *.h
@@ -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