pysilentmic 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.
- pysilentmic-0.1.0/PKG-INFO +65 -0
- pysilentmic-0.1.0/README.md +43 -0
- pysilentmic-0.1.0/pyproject.toml +33 -0
- pysilentmic-0.1.0/pysilentmic/__init__.py +10 -0
- pysilentmic-0.1.0/pysilentmic/engine.py +211 -0
- pysilentmic-0.1.0/pysilentmic.egg-info/PKG-INFO +65 -0
- pysilentmic-0.1.0/pysilentmic.egg-info/SOURCES.txt +10 -0
- pysilentmic-0.1.0/pysilentmic.egg-info/dependency_links.txt +1 -0
- pysilentmic-0.1.0/pysilentmic.egg-info/requires.txt +1 -0
- pysilentmic-0.1.0/pysilentmic.egg-info/top_level.txt +1 -0
- pysilentmic-0.1.0/setup.cfg +4 -0
- pysilentmic-0.1.0/tests/test_pysilentmic.py +41 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pysilentmic
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python bindings for SilentMic-RT: Deterministic, Zero-Allocation Sub-3ms Hard Real-Time Neural Voice Isolator
|
|
5
|
+
Author-email: "Shah Md. Mahi" <shahmdmahi15@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://shahmdmahi.dpdns.org
|
|
8
|
+
Project-URL: Repository, https://github.com/shahmdmahi15/silentmic-rt
|
|
9
|
+
Project-URL: Documentation, https://github.com/shahmdmahi15/silentmic-rt#python-quickstart
|
|
10
|
+
Keywords: audio,dsp,noise-suppression,real-time,voice-isolation,pipewire
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Rust
|
|
17
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Requires-Python: >=3.8
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
Requires-Dist: numpy>=1.20.0
|
|
22
|
+
|
|
23
|
+
# pysilentmic: Python Real-Time Voice Isolator
|
|
24
|
+
|
|
25
|
+
Python C-FFI bindings for **SilentMic-RT** — the deterministic, zero-allocation sub-3ms hard real-time neural voice isolator and virtual microphone driver.
|
|
26
|
+
|
|
27
|
+
## Features
|
|
28
|
+
- **Ultra-Low Latency**: 1.33 ms frame hop (64 samples at 48,000 Hz).
|
|
29
|
+
- **Sub-3ms Total Latency**: Complete input-to-output algorithmic delay under 2.67 ms.
|
|
30
|
+
- **Zero Allocation**: Lock-free, zero heap memory allocations on audio worker threads.
|
|
31
|
+
- **Pure Offline SIMD**: Operates entirely on CPU SIMD instructions with zero network or cloud latency.
|
|
32
|
+
- **NumPy Integration**: Seamlessly denoises NumPy arrays and streaming audio generators.
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install pysilentmic
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Quickstart
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
import numpy as np
|
|
44
|
+
from pysilentmic import SilentMic
|
|
45
|
+
|
|
46
|
+
# Initialize SilentMic engine at 48kHz
|
|
47
|
+
mic = SilentMic(sample_rate=48000, attenuation_db=-48.0)
|
|
48
|
+
|
|
49
|
+
# Process 64-sample audio frame in real time (< 30 us)
|
|
50
|
+
raw_frame = np.random.randn(64).astype(np.float32)
|
|
51
|
+
clean_frame = mic.process_frame(raw_frame)
|
|
52
|
+
|
|
53
|
+
# Denoise complete audio file / NumPy array
|
|
54
|
+
noisy_audio = np.random.randn(48000 * 5).astype(np.float32)
|
|
55
|
+
isolated_voice = mic.process(noisy_audio)
|
|
56
|
+
|
|
57
|
+
# Access hardware telemetry
|
|
58
|
+
print(mic.get_stats())
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Author & Maintainer
|
|
62
|
+
- **Author**: Shah Md. Mahi
|
|
63
|
+
- **Email**: shahmdmahi15@gmail.com
|
|
64
|
+
- **Website**: [https://shahmdmahi.dpdns.org](https://shahmdmahi.dpdns.org)
|
|
65
|
+
- **License**: MIT
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# pysilentmic: Python Real-Time Voice Isolator
|
|
2
|
+
|
|
3
|
+
Python C-FFI bindings for **SilentMic-RT** — the deterministic, zero-allocation sub-3ms hard real-time neural voice isolator and virtual microphone driver.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
- **Ultra-Low Latency**: 1.33 ms frame hop (64 samples at 48,000 Hz).
|
|
7
|
+
- **Sub-3ms Total Latency**: Complete input-to-output algorithmic delay under 2.67 ms.
|
|
8
|
+
- **Zero Allocation**: Lock-free, zero heap memory allocations on audio worker threads.
|
|
9
|
+
- **Pure Offline SIMD**: Operates entirely on CPU SIMD instructions with zero network or cloud latency.
|
|
10
|
+
- **NumPy Integration**: Seamlessly denoises NumPy arrays and streaming audio generators.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pip install pysilentmic
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Quickstart
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
import numpy as np
|
|
22
|
+
from pysilentmic import SilentMic
|
|
23
|
+
|
|
24
|
+
# Initialize SilentMic engine at 48kHz
|
|
25
|
+
mic = SilentMic(sample_rate=48000, attenuation_db=-48.0)
|
|
26
|
+
|
|
27
|
+
# Process 64-sample audio frame in real time (< 30 us)
|
|
28
|
+
raw_frame = np.random.randn(64).astype(np.float32)
|
|
29
|
+
clean_frame = mic.process_frame(raw_frame)
|
|
30
|
+
|
|
31
|
+
# Denoise complete audio file / NumPy array
|
|
32
|
+
noisy_audio = np.random.randn(48000 * 5).astype(np.float32)
|
|
33
|
+
isolated_voice = mic.process(noisy_audio)
|
|
34
|
+
|
|
35
|
+
# Access hardware telemetry
|
|
36
|
+
print(mic.get_stats())
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Author & Maintainer
|
|
40
|
+
- **Author**: Shah Md. Mahi
|
|
41
|
+
- **Email**: shahmdmahi15@gmail.com
|
|
42
|
+
- **Website**: [https://shahmdmahi.dpdns.org](https://shahmdmahi.dpdns.org)
|
|
43
|
+
- **License**: MIT
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pysilentmic"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "Shah Md. Mahi", email = "shahmdmahi15@gmail.com" }
|
|
10
|
+
]
|
|
11
|
+
description = "Python bindings for SilentMic-RT: Deterministic, Zero-Allocation Sub-3ms Hard Real-Time Neural Voice Isolator"
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
license = { text = "MIT" }
|
|
14
|
+
requires-python = ">=3.8"
|
|
15
|
+
keywords = ["audio", "dsp", "noise-suppression", "real-time", "voice-isolation", "pipewire"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Intended Audience :: Science/Research",
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Rust",
|
|
23
|
+
"Topic :: Multimedia :: Sound/Audio :: Speech",
|
|
24
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence"
|
|
25
|
+
]
|
|
26
|
+
dependencies = [
|
|
27
|
+
"numpy>=1.20.0"
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.urls]
|
|
31
|
+
Homepage = "https://shahmdmahi.dpdns.org"
|
|
32
|
+
Repository = "https://github.com/shahmdmahi15/silentmic-rt"
|
|
33
|
+
Documentation = "https://github.com/shahmdmahi15/silentmic-rt#python-quickstart"
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pysilentmic: High-performance Python bindings for SilentMic-RT.
|
|
3
|
+
Deterministic, Zero-Allocation Sub-3ms Hard Real-Time Neural Voice Isolator.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from .engine import SilentMic
|
|
7
|
+
|
|
8
|
+
__version__ = "0.1.0"
|
|
9
|
+
__author__ = "Shah Md. Mahi <shahmdmahi15@gmail.com>"
|
|
10
|
+
__all__ = ["SilentMic"]
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SilentMic-RT Python ctypes binding.
|
|
3
|
+
Ultra-low latency sub-3ms neural voice isolator & noise suppressor.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import ctypes
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
from typing import Optional, Union, Dict, Any, List
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
import numpy as np
|
|
13
|
+
HAS_NUMPY = True
|
|
14
|
+
except ImportError:
|
|
15
|
+
HAS_NUMPY = False
|
|
16
|
+
|
|
17
|
+
class SilentMicStatsC(ctypes.Structure):
|
|
18
|
+
_fields_ = [
|
|
19
|
+
("frames_processed", ctypes.c_uint64),
|
|
20
|
+
("samples_processed", ctypes.c_uint64),
|
|
21
|
+
("last_frame_micros", ctypes.c_double),
|
|
22
|
+
("max_frame_micros", ctypes.c_double),
|
|
23
|
+
("avg_frame_micros", ctypes.c_double),
|
|
24
|
+
("cpu_budget_pct", ctypes.c_double),
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
def _find_library() -> str:
|
|
28
|
+
"""Locates the compiled SilentMic dynamic library."""
|
|
29
|
+
lib_names = []
|
|
30
|
+
if sys.platform.startswith("linux"):
|
|
31
|
+
lib_names = ["libsilentmic.so"]
|
|
32
|
+
elif sys.platform == "darwin":
|
|
33
|
+
lib_names = ["libsilentmic.dylib"]
|
|
34
|
+
elif sys.platform == "win32":
|
|
35
|
+
lib_names = ["silentmic.dll", "libsilentmic.dll"]
|
|
36
|
+
else:
|
|
37
|
+
lib_names = ["libsilentmic.so"]
|
|
38
|
+
|
|
39
|
+
search_dirs = [
|
|
40
|
+
os.path.dirname(os.path.abspath(__file__)),
|
|
41
|
+
os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "..", "target", "release"),
|
|
42
|
+
os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "..", "target", "debug"),
|
|
43
|
+
"/usr/local/lib",
|
|
44
|
+
"/usr/lib",
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
for d in search_dirs:
|
|
48
|
+
for name in lib_names:
|
|
49
|
+
path = os.path.join(d, name)
|
|
50
|
+
if os.path.isfile(path):
|
|
51
|
+
return os.path.abspath(path)
|
|
52
|
+
|
|
53
|
+
return lib_names[0]
|
|
54
|
+
|
|
55
|
+
class SilentMic:
|
|
56
|
+
"""
|
|
57
|
+
SilentMic Real-Time Neural Voice Isolator Engine.
|
|
58
|
+
|
|
59
|
+
Processes 48kHz audio in 64-sample frames with deterministic sub-3ms algorithmic latency.
|
|
60
|
+
"""
|
|
61
|
+
FRAME_SIZE = 64
|
|
62
|
+
SAMPLE_RATE = 48000
|
|
63
|
+
|
|
64
|
+
def __init__(self, sample_rate: int = 48000, attenuation_db: float = -48.0, lib_path: Optional[str] = None):
|
|
65
|
+
if sample_rate != self.SAMPLE_RATE:
|
|
66
|
+
raise ValueError(f"SilentMic requires sample_rate={self.SAMPLE_RATE} Hz, received {sample_rate} Hz")
|
|
67
|
+
|
|
68
|
+
dll_path = lib_path or _find_library()
|
|
69
|
+
self._lib = ctypes.CDLL(dll_path)
|
|
70
|
+
|
|
71
|
+
# Declare function signatures
|
|
72
|
+
self._lib.silentmic_create.argtypes = [ctypes.c_uint32]
|
|
73
|
+
self._lib.silentmic_create.restype = ctypes.c_void_p
|
|
74
|
+
|
|
75
|
+
self._lib.silentmic_process_frame.argtypes = [
|
|
76
|
+
ctypes.c_void_p,
|
|
77
|
+
ctypes.POINTER(ctypes.c_float),
|
|
78
|
+
ctypes.POINTER(ctypes.c_float),
|
|
79
|
+
]
|
|
80
|
+
self._lib.silentmic_process_frame.restype = ctypes.c_int
|
|
81
|
+
|
|
82
|
+
self._lib.silentmic_set_attenuation_db.argtypes = [ctypes.c_void_p, ctypes.c_float]
|
|
83
|
+
self._lib.silentmic_set_attenuation_db.restype = ctypes.c_int
|
|
84
|
+
|
|
85
|
+
self._lib.silentmic_reset.argtypes = [ctypes.c_void_p]
|
|
86
|
+
self._lib.silentmic_reset.restype = ctypes.c_int
|
|
87
|
+
|
|
88
|
+
self._lib.silentmic_get_stats.argtypes = [ctypes.c_void_p, ctypes.POINTER(SilentMicStatsC)]
|
|
89
|
+
self._lib.silentmic_get_stats.restype = ctypes.c_int
|
|
90
|
+
|
|
91
|
+
self._lib.silentmic_destroy.argtypes = [ctypes.c_void_p]
|
|
92
|
+
self._lib.silentmic_destroy.restype = None
|
|
93
|
+
|
|
94
|
+
self._lib.silentmic_version.argtypes = []
|
|
95
|
+
self._lib.silentmic_version.restype = ctypes.c_char_p
|
|
96
|
+
|
|
97
|
+
self._handle = self._lib.silentmic_create(sample_rate)
|
|
98
|
+
if not self._handle:
|
|
99
|
+
raise RuntimeError("Failed to create native SilentMic-RT engine instance")
|
|
100
|
+
|
|
101
|
+
self.set_attenuation_db(attenuation_db)
|
|
102
|
+
|
|
103
|
+
def set_attenuation_db(self, db: float) -> None:
|
|
104
|
+
"""Sets the maximum noise floor attenuation in decibels (e.g. -48.0 dB)."""
|
|
105
|
+
ret = self._lib.silentmic_set_attenuation_db(self._handle, ctypes.c_float(db))
|
|
106
|
+
if ret != 0:
|
|
107
|
+
raise RuntimeError("Failed to set attenuation level")
|
|
108
|
+
|
|
109
|
+
def reset(self) -> None:
|
|
110
|
+
"""Resets all internal delay lines and recurrent states."""
|
|
111
|
+
self._lib.silentmic_reset(self._handle)
|
|
112
|
+
|
|
113
|
+
def process_frame(self, in_frame: Union[Any, List[float]]) -> Union[Any, List[float]]:
|
|
114
|
+
"""
|
|
115
|
+
Processes an exact 64-sample float32 array or list.
|
|
116
|
+
"""
|
|
117
|
+
if HAS_NUMPY and isinstance(in_frame, np.ndarray):
|
|
118
|
+
if in_frame.shape != (self.FRAME_SIZE,) or in_frame.dtype != np.float32:
|
|
119
|
+
in_frame = np.ascontiguousarray(in_frame, dtype=np.float32)
|
|
120
|
+
if in_frame.shape != (self.FRAME_SIZE,):
|
|
121
|
+
raise ValueError(f"in_frame must have shape ({self.FRAME_SIZE},), got {in_frame.shape}")
|
|
122
|
+
|
|
123
|
+
out_frame = np.empty(self.FRAME_SIZE, dtype=np.float32)
|
|
124
|
+
in_ptr = in_frame.ctypes.data_as(ctypes.POINTER(ctypes.c_float))
|
|
125
|
+
out_ptr = out_frame.ctypes.data_as(ctypes.POINTER(ctypes.c_float))
|
|
126
|
+
|
|
127
|
+
ret = self._lib.silentmic_process_frame(self._handle, in_ptr, out_ptr)
|
|
128
|
+
if ret != 0:
|
|
129
|
+
raise RuntimeError(f"silentmic_process_frame returned error code {ret}")
|
|
130
|
+
return out_frame
|
|
131
|
+
else:
|
|
132
|
+
in_list = list(in_frame)
|
|
133
|
+
if len(in_list) != self.FRAME_SIZE:
|
|
134
|
+
raise ValueError(f"in_frame must have length {self.FRAME_SIZE}, got {len(in_list)}")
|
|
135
|
+
|
|
136
|
+
c_in = (ctypes.c_float * self.FRAME_SIZE)(*in_list)
|
|
137
|
+
c_out = (ctypes.c_float * self.FRAME_SIZE)()
|
|
138
|
+
|
|
139
|
+
ret = self._lib.silentmic_process_frame(self._handle, c_in, c_out)
|
|
140
|
+
if ret != 0:
|
|
141
|
+
raise RuntimeError(f"silentmic_process_frame returned error code {ret}")
|
|
142
|
+
return [float(x) for x in c_out]
|
|
143
|
+
|
|
144
|
+
def process(self, audio: Union[Any, List[float]]) -> Union[Any, List[float]]:
|
|
145
|
+
"""
|
|
146
|
+
Denoises an arbitrary length audio array in streaming 64-sample hops.
|
|
147
|
+
"""
|
|
148
|
+
if HAS_NUMPY and isinstance(audio, np.ndarray):
|
|
149
|
+
arr = np.ascontiguousarray(audio, dtype=np.float32).flatten()
|
|
150
|
+
total_len = len(arr)
|
|
151
|
+
output = np.empty(total_len, dtype=np.float32)
|
|
152
|
+
|
|
153
|
+
full_hops = total_len // self.FRAME_SIZE
|
|
154
|
+
for i in range(full_hops):
|
|
155
|
+
start = i * self.FRAME_SIZE
|
|
156
|
+
end = start + self.FRAME_SIZE
|
|
157
|
+
output[start:end] = self.process_frame(arr[start:end])
|
|
158
|
+
|
|
159
|
+
rem = total_len % self.FRAME_SIZE
|
|
160
|
+
if rem > 0:
|
|
161
|
+
pad_frame = np.zeros(self.FRAME_SIZE, dtype=np.float32)
|
|
162
|
+
pad_frame[:rem] = arr[full_hops * self.FRAME_SIZE :]
|
|
163
|
+
cleaned_pad = self.process_frame(pad_frame)
|
|
164
|
+
output[full_hops * self.FRAME_SIZE :] = cleaned_pad[:rem]
|
|
165
|
+
|
|
166
|
+
return output
|
|
167
|
+
else:
|
|
168
|
+
arr = list(audio)
|
|
169
|
+
total_len = len(arr)
|
|
170
|
+
output = [0.0] * total_len
|
|
171
|
+
|
|
172
|
+
full_hops = total_len // self.FRAME_SIZE
|
|
173
|
+
for i in range(full_hops):
|
|
174
|
+
start = i * self.FRAME_SIZE
|
|
175
|
+
end = start + self.FRAME_SIZE
|
|
176
|
+
cleaned = self.process_frame(arr[start:end])
|
|
177
|
+
output[start:end] = cleaned
|
|
178
|
+
|
|
179
|
+
rem = total_len % self.FRAME_SIZE
|
|
180
|
+
if rem > 0:
|
|
181
|
+
pad = arr[full_hops * self.FRAME_SIZE :] + [0.0] * (self.FRAME_SIZE - rem)
|
|
182
|
+
cleaned = self.process_frame(pad)
|
|
183
|
+
output[full_hops * self.FRAME_SIZE :] = cleaned[:rem]
|
|
184
|
+
|
|
185
|
+
return output
|
|
186
|
+
|
|
187
|
+
def get_stats(self) -> Dict[str, Any]:
|
|
188
|
+
"""Returns real-time execution telemetry and latency profiling metrics."""
|
|
189
|
+
stats = SilentMicStatsC()
|
|
190
|
+
ret = self._lib.silentmic_get_stats(self._handle, ctypes.byref(stats))
|
|
191
|
+
if ret != 0:
|
|
192
|
+
return {}
|
|
193
|
+
return {
|
|
194
|
+
"frames_processed": stats.frames_processed,
|
|
195
|
+
"samples_processed": stats.samples_processed,
|
|
196
|
+
"last_frame_micros": stats.last_frame_micros,
|
|
197
|
+
"max_frame_micros": stats.max_frame_micros,
|
|
198
|
+
"avg_frame_micros": stats.avg_frame_micros,
|
|
199
|
+
"cpu_budget_pct": stats.cpu_budget_pct,
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
@property
|
|
203
|
+
def version(self) -> str:
|
|
204
|
+
"""Returns the native library version string."""
|
|
205
|
+
ver = self._lib.silentmic_version()
|
|
206
|
+
return ver.decode("utf-8") if ver else "unknown"
|
|
207
|
+
|
|
208
|
+
def __del__(self):
|
|
209
|
+
if hasattr(self, "_handle") and self._handle:
|
|
210
|
+
self._lib.silentmic_destroy(self._handle)
|
|
211
|
+
self._handle = None
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pysilentmic
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python bindings for SilentMic-RT: Deterministic, Zero-Allocation Sub-3ms Hard Real-Time Neural Voice Isolator
|
|
5
|
+
Author-email: "Shah Md. Mahi" <shahmdmahi15@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://shahmdmahi.dpdns.org
|
|
8
|
+
Project-URL: Repository, https://github.com/shahmdmahi15/silentmic-rt
|
|
9
|
+
Project-URL: Documentation, https://github.com/shahmdmahi15/silentmic-rt#python-quickstart
|
|
10
|
+
Keywords: audio,dsp,noise-suppression,real-time,voice-isolation,pipewire
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Rust
|
|
17
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Requires-Python: >=3.8
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
Requires-Dist: numpy>=1.20.0
|
|
22
|
+
|
|
23
|
+
# pysilentmic: Python Real-Time Voice Isolator
|
|
24
|
+
|
|
25
|
+
Python C-FFI bindings for **SilentMic-RT** — the deterministic, zero-allocation sub-3ms hard real-time neural voice isolator and virtual microphone driver.
|
|
26
|
+
|
|
27
|
+
## Features
|
|
28
|
+
- **Ultra-Low Latency**: 1.33 ms frame hop (64 samples at 48,000 Hz).
|
|
29
|
+
- **Sub-3ms Total Latency**: Complete input-to-output algorithmic delay under 2.67 ms.
|
|
30
|
+
- **Zero Allocation**: Lock-free, zero heap memory allocations on audio worker threads.
|
|
31
|
+
- **Pure Offline SIMD**: Operates entirely on CPU SIMD instructions with zero network or cloud latency.
|
|
32
|
+
- **NumPy Integration**: Seamlessly denoises NumPy arrays and streaming audio generators.
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install pysilentmic
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Quickstart
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
import numpy as np
|
|
44
|
+
from pysilentmic import SilentMic
|
|
45
|
+
|
|
46
|
+
# Initialize SilentMic engine at 48kHz
|
|
47
|
+
mic = SilentMic(sample_rate=48000, attenuation_db=-48.0)
|
|
48
|
+
|
|
49
|
+
# Process 64-sample audio frame in real time (< 30 us)
|
|
50
|
+
raw_frame = np.random.randn(64).astype(np.float32)
|
|
51
|
+
clean_frame = mic.process_frame(raw_frame)
|
|
52
|
+
|
|
53
|
+
# Denoise complete audio file / NumPy array
|
|
54
|
+
noisy_audio = np.random.randn(48000 * 5).astype(np.float32)
|
|
55
|
+
isolated_voice = mic.process(noisy_audio)
|
|
56
|
+
|
|
57
|
+
# Access hardware telemetry
|
|
58
|
+
print(mic.get_stats())
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Author & Maintainer
|
|
62
|
+
- **Author**: Shah Md. Mahi
|
|
63
|
+
- **Email**: shahmdmahi15@gmail.com
|
|
64
|
+
- **Website**: [https://shahmdmahi.dpdns.org](https://shahmdmahi.dpdns.org)
|
|
65
|
+
- **License**: MIT
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
pysilentmic/__init__.py
|
|
4
|
+
pysilentmic/engine.py
|
|
5
|
+
pysilentmic.egg-info/PKG-INFO
|
|
6
|
+
pysilentmic.egg-info/SOURCES.txt
|
|
7
|
+
pysilentmic.egg-info/dependency_links.txt
|
|
8
|
+
pysilentmic.egg-info/requires.txt
|
|
9
|
+
pysilentmic.egg-info/top_level.txt
|
|
10
|
+
tests/test_pysilentmic.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
numpy>=1.20.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pysilentmic
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unit and integration tests for pysilentmic.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import unittest
|
|
6
|
+
import math
|
|
7
|
+
from pysilentmic import SilentMic
|
|
8
|
+
|
|
9
|
+
class TestPySilentMic(unittest.TestCase):
|
|
10
|
+
def setUp(self):
|
|
11
|
+
self.mic = SilentMic(sample_rate=48000, attenuation_db=-48.0)
|
|
12
|
+
|
|
13
|
+
def test_version(self):
|
|
14
|
+
self.assertEqual(self.mic.version, "0.1.0")
|
|
15
|
+
|
|
16
|
+
def test_process_frame(self):
|
|
17
|
+
in_frame = [0.1] * 64
|
|
18
|
+
out_frame = self.mic.process_frame(in_frame)
|
|
19
|
+
self.assertEqual(len(out_frame), 64)
|
|
20
|
+
for val in out_frame:
|
|
21
|
+
self.assertTrue(math.isfinite(val))
|
|
22
|
+
|
|
23
|
+
def test_process_stream(self):
|
|
24
|
+
# 1000 samples of synthetic audio at 48kHz
|
|
25
|
+
audio = [
|
|
26
|
+
0.5 * math.sin(2 * math.pi * 440 * (i / 48000.0)) + 0.05 * ((i % 13) / 13.0)
|
|
27
|
+
for i in range(1280)
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
cleaned = self.mic.process(audio)
|
|
31
|
+
self.assertEqual(len(cleaned), len(audio))
|
|
32
|
+
for val in cleaned:
|
|
33
|
+
self.assertTrue(math.isfinite(val))
|
|
34
|
+
|
|
35
|
+
stats = self.mic.get_stats()
|
|
36
|
+
self.assertGreater(stats["frames_processed"], 15)
|
|
37
|
+
self.assertGreater(stats["avg_frame_micros"], 0.0)
|
|
38
|
+
print(f"\n[PySilentMic Verified] Telemetry: {stats}")
|
|
39
|
+
|
|
40
|
+
if __name__ == "__main__":
|
|
41
|
+
unittest.main()
|