huri-module-vad 0.1.0.dev0__py3-none-any.whl

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.
@@ -0,0 +1,39 @@
1
+ Metadata-Version: 2.4
2
+ Name: huri-module-vad
3
+ Version: 0.1.0.dev0
4
+ Summary: HuRI module: Voice activity detection (WebRTC VAD): microphone frames in, speech segments out
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: huri-core[server]<0.5,>=0.4.0.dev0
8
+ Requires-Dist: huri-events<0.2,>=0.1.0.dev0
9
+ Requires-Dist: numpy<2,>=1.26
10
+ Requires-Dist: webrtcvad==2.0.10
11
+ Requires-Dist: setuptools<82
12
+
13
+ # huri-module-vad
14
+
15
+ Voice activity detection (WebRTC VAD): microphone frames in, speech segments out.
16
+
17
+ | Module | Topics | What it does |
18
+ |---|---|---|
19
+ | `vad` | `huri.audio.in` → `huri.voice` | Detects speech in microphone frames (WebRTC VAD) |
20
+
21
+ ```sh
22
+ pip install -e packages/huri-module-vad # from a huri-modules checkout, next to huri-core and huri-events
23
+ HURI_MODULES=vad ... # enable it on the server
24
+ ```
25
+
26
+ A distribution of
27
+ [huri-modules](https://github.com/Lucy-Robotics/huri-modules), versioned in its
28
+ `pyproject.toml` (see RELEASING.md there). It depends on `huri-core` and
29
+ `huri-events`, which are not on PyPI: without a checkout, install them from git
30
+ in the same pip command, so pip never looks them up on an index:
31
+
32
+ ```sh
33
+ ref=0.1.0 # a huri-modules release tag
34
+ core=0.4.0 # HURI_CORE_REF in that release's versions.env
35
+ pip install \
36
+ "huri-core[server] @ git+https://github.com/Lucy-Robotics/huri-core.git@$core" \
37
+ "huri-events @ git+https://github.com/Lucy-Robotics/huri-modules.git@$ref#subdirectory=packages/huri-events" \
38
+ "huri-module-vad @ git+https://github.com/Lucy-Robotics/huri-modules.git@$ref#subdirectory=packages/huri-module-vad"
39
+ ```
@@ -0,0 +1,8 @@
1
+ huri_vad/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ huri_vad/huri_module.toml,sha256=YtYluQ3RDV7JaLZeU2oGSUria7OpTY86v2qSfPbaLyo,337
3
+ huri_vad/module.py,sha256=XMXHcSU66aqG0MOKkwlhjPAOnITsqkm9aib5w6r_W1w,2352
4
+ huri_module_vad-0.1.0.dev0.dist-info/METADATA,sha256=-CI5OfxKN9crpIUT4T5y6cTDD5yzgII43P2Uh2rYHOU,1592
5
+ huri_module_vad-0.1.0.dev0.dist-info/WHEEL,sha256=YLJXdYXQ2FQ0Uqn2J-6iEIC-3iOey8lH3xCtvFLkd8Q,91
6
+ huri_module_vad-0.1.0.dev0.dist-info/entry_points.txt,sha256=rvYkPeqJUyKUMRG7MYJptdWp7epBANLp6xgIrZC6kQo,41
7
+ huri_module_vad-0.1.0.dev0.dist-info/top_level.txt,sha256=Viv-8JWNDXL5jm86N5Q6e_boQf2Sd3Q6sife2hSxFRM,9
8
+ huri_module_vad-0.1.0.dev0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (81.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [huri.modules]
2
+ vad = huri_vad.module:VAD
@@ -0,0 +1 @@
1
+ huri_vad
huri_vad/__init__.py ADDED
File without changes
@@ -0,0 +1,7 @@
1
+ name = "vad"
2
+ summary = "Detects speech in microphone frames (WebRTC VAD)"
3
+ input = { topic = "huri.audio.in", event = "huri_core.core.events:BytesEvent" }
4
+ output = { topic = "huri.voice", event = "huri_events.speech:Voice" }
5
+ events = ["huri-events>=0.1,<0.2"]
6
+ requires = { gpu = false, actuation = false }
7
+ huri_core = ">=0.4.0.dev0,<0.5"
huri_vad/module.py ADDED
@@ -0,0 +1,69 @@
1
+ from typing import Optional
2
+
3
+ import numpy as np
4
+ import webrtcvad
5
+ from huri_events.speech import Voice
6
+
7
+ from huri_core.core.events import BytesEvent
8
+ from huri_core.core.module import Module
9
+
10
+
11
+ class VAD(Module):
12
+ """VAD Module
13
+
14
+ Detect voice and silence using WebRTC VAD.
15
+
16
+ input: huri.audio.in,
17
+ output: huri.voice
18
+
19
+ :vad_agressiveness: from 0 (low) to 3 (high, can distord audio).
20
+ :silence_duration: how many seconds will a no voice be considered a silence.
21
+ :sample_rate: size of received chunk of audio. Usually 8000, 16000 or 48000.
22
+ :block_duration: size of received chunk of audio (in s).
23
+ Can only be 0.010, 0.020 and 0.030.
24
+ """
25
+
26
+ # Inbound microphone frames travel on their own topic so the TTS-output
27
+ # "audio" topic (consumed by MOV and the client Sender) never collides
28
+ # with mic input — otherwise raw mic bytes get echoed back to the client.
29
+ input_type = "huri.audio.in"
30
+ output_type = "huri.voice"
31
+ input_event = BytesEvent
32
+ output_event = Voice
33
+
34
+ def __init__(
35
+ self,
36
+ vad_agressiveness: int = 3,
37
+ silence_duration: float = 3, # s
38
+ sample_rate: int = 16000,
39
+ block_duration: float = 0.020, # s
40
+ ):
41
+ super().__init__()
42
+
43
+ if block_duration not in [0.010, 0.020, 0.030]:
44
+ raise RuntimeError("block duration must be 0.010, 0.020 or 0.030 s")
45
+
46
+ self.sample_rate: int = sample_rate
47
+ self.block_size: int = int(block_duration * sample_rate) # ms
48
+
49
+ self.silence_frames_size: int = int(silence_duration * sample_rate)
50
+ self.silence_frames_count: int = -1
51
+
52
+ self.vad = webrtcvad.Vad(vad_agressiveness)
53
+
54
+ async def process(self, data: BytesEvent) -> Optional[Voice]:
55
+ if self.vad.is_speech(data.data, self.sample_rate) is True:
56
+ self.silence_frames_count = 0
57
+
58
+ audio_array = np.frombuffer(data.data, dtype=np.int16)
59
+ audio_array_float = audio_array.astype(np.float32) / 32768.0
60
+
61
+ return Voice(audio_array_float)
62
+ else:
63
+ if self.silence_frames_count != -1:
64
+ self.silence_frames_count += self.block_size
65
+ if self.silence_frames_count > self.silence_frames_size:
66
+ self.silence_frames_count = -1 # sent only once
67
+
68
+ return Voice(None)
69
+ return None