videosdk-plugins-rnnoise 0.0.47__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.
- videosdk_plugins_rnnoise-0.0.47/.gitignore +19 -0
- videosdk_plugins_rnnoise-0.0.47/PKG-INFO +35 -0
- videosdk_plugins_rnnoise-0.0.47/README.md +16 -0
- videosdk_plugins_rnnoise-0.0.47/pyproject.toml +32 -0
- videosdk_plugins_rnnoise-0.0.47/videosdk/plugins/rnnoise/__init__.py +3 -0
- videosdk_plugins_rnnoise-0.0.47/videosdk/plugins/rnnoise/build_rnnoise.py +76 -0
- videosdk_plugins_rnnoise-0.0.47/videosdk/plugins/rnnoise/denoise.py +72 -0
- videosdk_plugins_rnnoise-0.0.47/videosdk/plugins/rnnoise/files/librnnoise.0.dylib +0 -0
- videosdk_plugins_rnnoise-0.0.47/videosdk/plugins/rnnoise/files/librnnoise.dylib +1 -0
- videosdk_plugins_rnnoise-0.0.47/videosdk/plugins/rnnoise/rnnoise.py +43 -0
- videosdk_plugins_rnnoise-0.0.47/videosdk/plugins/rnnoise/version.py +1 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: videosdk-plugins-rnnoise
|
|
3
|
+
Version: 0.0.47
|
|
4
|
+
Summary: VideoSDK Agent Framework plugin for RNNoise.
|
|
5
|
+
Author: videosdk
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Keywords: ai,audio,lmnt,rime,tts,video,videosdk
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Topic :: Communications :: Conferencing
|
|
11
|
+
Classifier: Topic :: Multimedia :: Sound/Audio
|
|
12
|
+
Classifier: Topic :: Multimedia :: Video
|
|
13
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
14
|
+
Requires-Python: >=3.11
|
|
15
|
+
Requires-Dist: numpy>=1.27
|
|
16
|
+
Requires-Dist: resampy
|
|
17
|
+
Requires-Dist: videosdk-agents>=0.0.47
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# VideoSDK RNNoise Plugin
|
|
21
|
+
|
|
22
|
+
Agent Framework plugin for de-noising with RNNoise.
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install videosdk-plugins-rnnoise
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Building RNNoise for your OS
|
|
31
|
+
|
|
32
|
+
To avoid OS security issues with prebuilt libraries, build RNNoise locally:
|
|
33
|
+
1. Ensure you have git and build tools (autoconf/make on Mac/Linux, Visual Studio with nmake on Windows).
|
|
34
|
+
2. Run `python build_rnnoise.py` in the project root.
|
|
35
|
+
3. This clones RNNoise, builds the library for your OS, and places it in videosdk/plugins/rnnoise/files/.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# VideoSDK RNNoise Plugin
|
|
2
|
+
|
|
3
|
+
Agent Framework plugin for de-noising with RNNoise.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install videosdk-plugins-rnnoise
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Building RNNoise for your OS
|
|
12
|
+
|
|
13
|
+
To avoid OS security issues with prebuilt libraries, build RNNoise locally:
|
|
14
|
+
1. Ensure you have git and build tools (autoconf/make on Mac/Linux, Visual Studio with nmake on Windows).
|
|
15
|
+
2. Run `python build_rnnoise.py` in the project root.
|
|
16
|
+
3. This clones RNNoise, builds the library for your OS, and places it in videosdk/plugins/rnnoise/files/.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "videosdk-plugins-rnnoise"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "VideoSDK Agent Framework plugin for RNNoise."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "Apache-2.0"
|
|
11
|
+
requires-python = ">=3.11"
|
|
12
|
+
authors = [{ name = "videosdk" }]
|
|
13
|
+
keywords = ["video", "audio", "ai", "lmnt", "tts", "videosdk", "rime"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Topic :: Communications :: Conferencing",
|
|
19
|
+
"Topic :: Multimedia :: Sound/Audio",
|
|
20
|
+
"Topic :: Multimedia :: Video",
|
|
21
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
22
|
+
]
|
|
23
|
+
dependencies = ["videosdk-agents>=0.0.47", "resampy", "numpy>=1.27"]
|
|
24
|
+
|
|
25
|
+
[tool.hatch.version]
|
|
26
|
+
path = "videosdk/plugins/rnnoise/version.py"
|
|
27
|
+
|
|
28
|
+
[tool.hatch.build.targets.wheel]
|
|
29
|
+
packages = ["videosdk"]
|
|
30
|
+
|
|
31
|
+
[tool.hatch.build.targets.sdist]
|
|
32
|
+
include = ["/videosdk"]
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import platform
|
|
3
|
+
import subprocess
|
|
4
|
+
import shutil
|
|
5
|
+
import tempfile
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
from shutil import which
|
|
9
|
+
|
|
10
|
+
def check_command(cmd, install_tip):
|
|
11
|
+
if not which(cmd):
|
|
12
|
+
raise EnvironmentError(f"{cmd} not found. Install it: {install_tip}")
|
|
13
|
+
|
|
14
|
+
def run_subprocess(cmd, cwd=None):
|
|
15
|
+
print(f"Running: {' '.join(cmd)}")
|
|
16
|
+
try:
|
|
17
|
+
result = subprocess.run(cmd, cwd=cwd, check=True, capture_output=True, text=True)
|
|
18
|
+
print(result.stdout)
|
|
19
|
+
if result.stderr:
|
|
20
|
+
print(f"Warnings/Errors: {result.stderr}")
|
|
21
|
+
return result
|
|
22
|
+
except subprocess.CalledProcessError as e:
|
|
23
|
+
print(f"Command failed with code {e.returncode}: {e.stderr}")
|
|
24
|
+
raise
|
|
25
|
+
|
|
26
|
+
def build_rnnoise():
|
|
27
|
+
os_sys = platform.system()
|
|
28
|
+
repo_url = "https://github.com/xiph/rnnoise.git"
|
|
29
|
+
|
|
30
|
+
git_tip = "Install git (e.g., apt install git on Debian/Ubuntu, brew install git on macOS, or Chocolatey on Windows)."
|
|
31
|
+
check_command("git", git_tip)
|
|
32
|
+
|
|
33
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
34
|
+
run_subprocess(["git", "clone", repo_url, tmpdir])
|
|
35
|
+
os.chdir(tmpdir)
|
|
36
|
+
if os_sys == "Darwin" or os_sys == "Linux":
|
|
37
|
+
# Check required Unix build tools
|
|
38
|
+
unix_tip = "Install build tools (e.g., apt install build-essential autoconf automake libtool on Debian/Ubuntu; brew install autoconf automake libtool on macOS)."
|
|
39
|
+
for tool in ["autoconf", "automake", "libtool", "make"]:
|
|
40
|
+
check_command(tool, unix_tip)
|
|
41
|
+
|
|
42
|
+
# Generate build files and compile
|
|
43
|
+
run_subprocess(["./autogen.sh"])
|
|
44
|
+
run_subprocess(["./configure"])
|
|
45
|
+
run_subprocess(["make"])
|
|
46
|
+
lib_dir = ".libs"
|
|
47
|
+
lib_file = "librnnoise.dylib" if os_sys == "Darwin" else "librnnoise.so"
|
|
48
|
+
built_lib = os.path.join(lib_dir, lib_file)
|
|
49
|
+
elif os_sys == "Windows":
|
|
50
|
+
# Check nmake for Visual Studio-based build
|
|
51
|
+
nmake_tip = "Install Visual Studio (community edition) and run this script from Developer Command Prompt. Alternatively, use MSYS2 (install via https://www.msys2.org/) and run make in the repo."
|
|
52
|
+
check_command("nmake", nmake_tip)
|
|
53
|
+
|
|
54
|
+
os.chdir("msvc")
|
|
55
|
+
# Assumes Visual Studio is installed; run from Developer Command Prompt if needed
|
|
56
|
+
run_subprocess(["nmake", "/f", "Makefile.ms"])
|
|
57
|
+
built_lib = "rnnoise.dll" # Adjust if name differs after build
|
|
58
|
+
else:
|
|
59
|
+
raise ValueError(f"Unsupported OS: {os_sys}. Manual build required (clone repo and follow README).")
|
|
60
|
+
|
|
61
|
+
if not os.path.exists(built_lib):
|
|
62
|
+
raise FileNotFoundError(f"Build failed: {built_lib} not found. Ensure build tools are installed and environment is set up (e.g., in containers, add tools via Dockerfile/package manager).")
|
|
63
|
+
|
|
64
|
+
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
65
|
+
target_dir = os.path.join(script_dir, "files")
|
|
66
|
+
os.makedirs(target_dir, exist_ok=True)
|
|
67
|
+
shutil.copy(built_lib, os.path.join(target_dir, os.path.basename(built_lib)))
|
|
68
|
+
|
|
69
|
+
if __name__ == "__main__":
|
|
70
|
+
try:
|
|
71
|
+
build_rnnoise()
|
|
72
|
+
print("RNNoise library built successfully for your OS.")
|
|
73
|
+
except Exception as e:
|
|
74
|
+
print(f"Build failed: {e}")
|
|
75
|
+
print("If in a container/cloud/terminal, ensure tools are installed (e.g., via apt/brew/Chocolatey) or build manually per RNNoise README: https://github.com/xiph/rnnoise")
|
|
76
|
+
sys.exit(1)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
from videosdk.agents.denoise import Denoise
|
|
3
|
+
from .rnnoise import RNN
|
|
4
|
+
import numpy as np
|
|
5
|
+
import resampy
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class RNNoise(Denoise):
|
|
9
|
+
def __init__(self):
|
|
10
|
+
"""Initialize the RNNoise denoise plugin.
|
|
11
|
+
"""
|
|
12
|
+
super().__init__()
|
|
13
|
+
self.rnnoise = RNN()
|
|
14
|
+
self._target_sample_rate = 48000
|
|
15
|
+
self._frame_duration_ms = 20
|
|
16
|
+
self._rnnoise_frame_size = 480
|
|
17
|
+
|
|
18
|
+
async def denoise(self, audio_frames: bytes, **kwargs: Any) -> bytes:
|
|
19
|
+
if not audio_frames:
|
|
20
|
+
return b""
|
|
21
|
+
|
|
22
|
+
audio_np = np.frombuffer(audio_frames, dtype=np.int16)
|
|
23
|
+
num_samples = len(audio_np)
|
|
24
|
+
original_sample_rate = int(
|
|
25
|
+
num_samples * 1000 / self._frame_duration_ms)
|
|
26
|
+
|
|
27
|
+
if original_sample_rate != self._target_sample_rate:
|
|
28
|
+
audio_float = audio_np.astype(np.float32) / 32767.0
|
|
29
|
+
resampled_audio_float = resampy.resample(
|
|
30
|
+
audio_float, sr_orig=original_sample_rate, sr_new=self._target_sample_rate)
|
|
31
|
+
resampled_audio_np = (resampled_audio_float *
|
|
32
|
+
32767.0).astype(np.int16)
|
|
33
|
+
else:
|
|
34
|
+
resampled_audio_np = audio_np
|
|
35
|
+
|
|
36
|
+
num_rnnoise_frames = len(
|
|
37
|
+
resampled_audio_np) // self._rnnoise_frame_size
|
|
38
|
+
denoised_chunks = []
|
|
39
|
+
|
|
40
|
+
for i in range(num_rnnoise_frames):
|
|
41
|
+
start = i * self._rnnoise_frame_size
|
|
42
|
+
end = start + self._rnnoise_frame_size
|
|
43
|
+
chunk = resampled_audio_np[start:end]
|
|
44
|
+
|
|
45
|
+
if len(chunk) != self._rnnoise_frame_size:
|
|
46
|
+
continue
|
|
47
|
+
|
|
48
|
+
chunk_bytes = chunk.tobytes()
|
|
49
|
+
_vod_prob, denoised_chunk_bytes = self.rnnoise.process_frame(
|
|
50
|
+
chunk_bytes)
|
|
51
|
+
denoised_chunk_np = np.frombuffer(
|
|
52
|
+
denoised_chunk_bytes, dtype=np.int16)
|
|
53
|
+
denoised_chunks.append(denoised_chunk_np)
|
|
54
|
+
|
|
55
|
+
if not denoised_chunks:
|
|
56
|
+
return b""
|
|
57
|
+
|
|
58
|
+
denoised_audio_np = np.concatenate(denoised_chunks)
|
|
59
|
+
|
|
60
|
+
if original_sample_rate != self._target_sample_rate:
|
|
61
|
+
denoised_float = denoised_audio_np.astype(np.float32) / 32767.0
|
|
62
|
+
original_format_float = resampy.resample(
|
|
63
|
+
denoised_float, sr_orig=self._target_sample_rate, sr_new=original_sample_rate)
|
|
64
|
+
final_audio_np = (original_format_float * 32767.0).astype(np.int16)
|
|
65
|
+
else:
|
|
66
|
+
final_audio_np = denoised_audio_np
|
|
67
|
+
|
|
68
|
+
return final_audio_np.tobytes()
|
|
69
|
+
|
|
70
|
+
async def aclose(self) -> None:
|
|
71
|
+
self.rnnoise.destroy()
|
|
72
|
+
await super().aclose()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
librnnoise.0.dylib
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import ctypes,numpy,os
|
|
2
|
+
import platform
|
|
3
|
+
|
|
4
|
+
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
sys_name = platform.system()
|
|
8
|
+
if sys_name == "Darwin":
|
|
9
|
+
lib_name = "librnnoise.dylib"
|
|
10
|
+
elif sys_name == "Linux":
|
|
11
|
+
lib_name = "librnnoise.so"
|
|
12
|
+
elif sys_name == "Windows":
|
|
13
|
+
lib_name = "rnnoise.dll"
|
|
14
|
+
else:
|
|
15
|
+
raise OSError(f"Unsupported OS: {sys_name}")
|
|
16
|
+
|
|
17
|
+
lib_path = os.path.join(script_dir, "files", lib_name)
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
lib = ctypes.cdll.LoadLibrary(lib_path)
|
|
21
|
+
except OSError as e:
|
|
22
|
+
raise OSError(
|
|
23
|
+
f"Error loading rnnoise library at {lib_path}. "
|
|
24
|
+
f"It may be corrupted or incompatible with your platform. "
|
|
25
|
+
f"Original error: {e}"
|
|
26
|
+
) from e
|
|
27
|
+
|
|
28
|
+
lib.rnnoise_process_frame.argtypes = [ctypes.c_void_p,ctypes.POINTER(ctypes.c_float),ctypes.POINTER(ctypes.c_float)]
|
|
29
|
+
lib.rnnoise_process_frame.restype = ctypes.c_float
|
|
30
|
+
lib.rnnoise_create.restype = ctypes.c_void_p
|
|
31
|
+
lib.rnnoise_destroy.argtypes = [ctypes.c_void_p]
|
|
32
|
+
|
|
33
|
+
class RNN(object):
|
|
34
|
+
def __init__(self):
|
|
35
|
+
self.obj = lib.rnnoise_create()
|
|
36
|
+
def process_frame(self,inbuf):
|
|
37
|
+
outbuf = numpy.ndarray((480,), 'h', inbuf).astype(ctypes.c_float)
|
|
38
|
+
outbuf_ptr = outbuf.ctypes.data_as(ctypes.POINTER(ctypes.c_float))
|
|
39
|
+
VodProb = lib.rnnoise_process_frame(self.obj,outbuf_ptr,outbuf_ptr)
|
|
40
|
+
return (VodProb,outbuf.astype(ctypes.c_short).tobytes())
|
|
41
|
+
|
|
42
|
+
def destroy(self):
|
|
43
|
+
lib.rnnoise_destroy(self.obj)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.0.47"
|