Spaces:
Runtime error
Runtime error
Upload 19 files
Browse files- hub/snakers4_silero-vad_master/.github/ISSUE_TEMPLATE/bug_report.md +52 -0
- hub/snakers4_silero-vad_master/.github/ISSUE_TEMPLATE/feature_request.md +27 -0
- hub/snakers4_silero-vad_master/.github/ISSUE_TEMPLATE/questions---help---support.md +12 -0
- hub/snakers4_silero-vad_master/CODE_OF_CONDUCT.md +76 -0
- hub/snakers4_silero-vad_master/LICENSE +21 -0
- hub/snakers4_silero-vad_master/README.md +113 -0
- hub/snakers4_silero-vad_master/examples/colab_record_example.ipynb +241 -0
- hub/snakers4_silero-vad_master/examples/microphone_and_webRTC_integration/README.md +28 -0
- hub/snakers4_silero-vad_master/examples/microphone_and_webRTC_integration/microphone_and_webRTC_integration.py +201 -0
- hub/snakers4_silero-vad_master/examples/pyaudio-streaming/README.md +20 -0
- hub/snakers4_silero-vad_master/examples/pyaudio-streaming/pyaudio-streaming-examples.ipynb +331 -0
- hub/snakers4_silero-vad_master/files/lang_dict_95.json +1 -0
- hub/snakers4_silero-vad_master/files/lang_group_dict_95.json +1 -0
- hub/snakers4_silero-vad_master/files/silero_logo.jpg +0 -0
- hub/snakers4_silero-vad_master/files/silero_vad.jit +3 -0
- hub/snakers4_silero-vad_master/files/silero_vad.onnx +3 -0
- hub/snakers4_silero-vad_master/hubconf.py +105 -0
- hub/snakers4_silero-vad_master/silero-vad.ipynb +445 -0
- hub/snakers4_silero-vad_master/utils_vad.py +488 -0
hub/snakers4_silero-vad_master/.github/ISSUE_TEMPLATE/bug_report.md
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
name: Bug report
|
3 |
+
about: Create a report to help us improve
|
4 |
+
title: Bug report - [X]
|
5 |
+
labels: bug
|
6 |
+
assignees: snakers4
|
7 |
+
|
8 |
+
---
|
9 |
+
|
10 |
+
## 🐛 Bug
|
11 |
+
|
12 |
+
<!-- A clear and concise description of what the bug is. -->
|
13 |
+
|
14 |
+
## To Reproduce
|
15 |
+
|
16 |
+
Steps to reproduce the behavior:
|
17 |
+
|
18 |
+
1.
|
19 |
+
2.
|
20 |
+
3.
|
21 |
+
|
22 |
+
<!-- If you have a code sample, error messages, stack traces, please provide it here as well -->
|
23 |
+
|
24 |
+
## Expected behavior
|
25 |
+
|
26 |
+
<!-- A clear and concise description of what you expected to happen. -->
|
27 |
+
|
28 |
+
## Environment
|
29 |
+
|
30 |
+
Please copy and paste the output from this
|
31 |
+
[environment collection script](https://raw.githubusercontent.com/pytorch/pytorch/master/torch/utils/collect_env.py)
|
32 |
+
(or fill out the checklist below manually).
|
33 |
+
|
34 |
+
You can get the script and run it with:
|
35 |
+
```
|
36 |
+
wget https://raw.githubusercontent.com/pytorch/pytorch/master/torch/utils/collect_env.py
|
37 |
+
# For security purposes, please check the contents of collect_env.py before running it.
|
38 |
+
python collect_env.py
|
39 |
+
```
|
40 |
+
|
41 |
+
- PyTorch Version (e.g., 1.0):
|
42 |
+
- OS (e.g., Linux):
|
43 |
+
- How you installed PyTorch (`conda`, `pip`, source):
|
44 |
+
- Build command you used (if compiling from source):
|
45 |
+
- Python version:
|
46 |
+
- CUDA/cuDNN version:
|
47 |
+
- GPU models and configuration:
|
48 |
+
- Any other relevant information:
|
49 |
+
|
50 |
+
## Additional context
|
51 |
+
|
52 |
+
<!-- Add any other context about the problem here. -->
|
hub/snakers4_silero-vad_master/.github/ISSUE_TEMPLATE/feature_request.md
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
name: Feature request
|
3 |
+
about: Suggest an idea for this project
|
4 |
+
title: Feature request - [X]
|
5 |
+
labels: enhancement
|
6 |
+
assignees: snakers4
|
7 |
+
|
8 |
+
---
|
9 |
+
|
10 |
+
## 🚀 Feature
|
11 |
+
<!-- A clear and concise description of the feature proposal -->
|
12 |
+
|
13 |
+
## Motivation
|
14 |
+
|
15 |
+
<!-- Please outline the motivation for the proposal. Is your feature request related to a problem? e.g., I'm always frustrated when [...]. If this is related to another GitHub issue, please link here too -->
|
16 |
+
|
17 |
+
## Pitch
|
18 |
+
|
19 |
+
<!-- A clear and concise description of what you want to happen. -->
|
20 |
+
|
21 |
+
## Alternatives
|
22 |
+
|
23 |
+
<!-- A clear and concise description of any alternative solutions or features you've considered, if any. -->
|
24 |
+
|
25 |
+
## Additional context
|
26 |
+
|
27 |
+
<!-- Add any other context or screenshots about the feature request here. -->
|
hub/snakers4_silero-vad_master/.github/ISSUE_TEMPLATE/questions---help---support.md
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
name: Questions / Help / Support
|
3 |
+
about: Ask for help, support or ask a question
|
4 |
+
title: "❓ Questions / Help / Support"
|
5 |
+
labels: help wanted
|
6 |
+
assignees: snakers4
|
7 |
+
|
8 |
+
---
|
9 |
+
|
10 |
+
## ❓ Questions and Help
|
11 |
+
|
12 |
+
We have a [wiki](https://github.com/snakers4/silero-models/wiki) available for our users. Please make sure you have checked it out first.
|
hub/snakers4_silero-vad_master/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Contributor Covenant Code of Conduct
|
2 |
+
|
3 |
+
## Our Pledge
|
4 |
+
|
5 |
+
In the interest of fostering an open and welcoming environment, we as
|
6 |
+
contributors and maintainers pledge to making participation in our project and
|
7 |
+
our community a harassment-free experience for everyone, regardless of age, body
|
8 |
+
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
9 |
+
level of experience, education, socio-economic status, nationality, personal
|
10 |
+
appearance, race, religion, or sexual identity and orientation.
|
11 |
+
|
12 |
+
## Our Standards
|
13 |
+
|
14 |
+
Examples of behavior that contributes to creating a positive environment
|
15 |
+
include:
|
16 |
+
|
17 |
+
* Using welcoming and inclusive language
|
18 |
+
* Being respectful of differing viewpoints and experiences
|
19 |
+
* Gracefully accepting constructive criticism
|
20 |
+
* Focusing on what is best for the community
|
21 |
+
* Showing empathy towards other community members
|
22 |
+
|
23 |
+
Examples of unacceptable behavior by participants include:
|
24 |
+
|
25 |
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26 |
+
advances
|
27 |
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28 |
+
* Public or private harassment
|
29 |
+
* Publishing others' private information, such as a physical or electronic
|
30 |
+
address, without explicit permission
|
31 |
+
* Other conduct which could reasonably be considered inappropriate in a
|
32 |
+
professional setting
|
33 |
+
|
34 |
+
## Our Responsibilities
|
35 |
+
|
36 |
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37 |
+
behavior and are expected to take appropriate and fair corrective action in
|
38 |
+
response to any instances of unacceptable behavior.
|
39 |
+
|
40 |
+
Project maintainers have the right and responsibility to remove, edit, or
|
41 |
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42 |
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43 |
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44 |
+
threatening, offensive, or harmful.
|
45 |
+
|
46 |
+
## Scope
|
47 |
+
|
48 |
+
This Code of Conduct applies both within project spaces and in public spaces
|
49 |
+
when an individual is representing the project or its community. Examples of
|
50 |
+
representing a project or community include using an official project e-mail
|
51 |
+
address, posting via an official social media account, or acting as an appointed
|
52 |
+
representative at an online or offline event. Representation of a project may be
|
53 |
+
further defined and clarified by project maintainers.
|
54 |
+
|
55 |
+
## Enforcement
|
56 |
+
|
57 |
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58 |
+
reported by contacting the project team at aveysov@gmail.com. All
|
59 |
+
complaints will be reviewed and investigated and will result in a response that
|
60 |
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61 |
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62 |
+
Further details of specific enforcement policies may be posted separately.
|
63 |
+
|
64 |
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65 |
+
faith may face temporary or permanent repercussions as determined by other
|
66 |
+
members of the project's leadership.
|
67 |
+
|
68 |
+
## Attribution
|
69 |
+
|
70 |
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71 |
+
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
72 |
+
|
73 |
+
[homepage]: https://www.contributor-covenant.org
|
74 |
+
|
75 |
+
For answers to common questions about this code of conduct, see
|
76 |
+
https://www.contributor-covenant.org/faq
|
hub/snakers4_silero-vad_master/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
MIT License
|
2 |
+
|
3 |
+
Copyright (c) 2020-present Silero Team
|
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.
|
hub/snakers4_silero-vad_master/README.md
ADDED
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[![Mailing list : test](http://img.shields.io/badge/Email-gray.svg?style=for-the-badge&logo=gmail)](mailto:hello@silero.ai) [![Mailing list : test](http://img.shields.io/badge/Telegram-blue.svg?style=for-the-badge&logo=telegram)](https://t.me/silero_speech) [![License: CC BY-NC 4.0](https://img.shields.io/badge/License-MIT-lightgrey.svg?style=for-the-badge)](https://github.com/snakers4/silero-vad/blob/master/LICENSE)
|
2 |
+
|
3 |
+
[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/snakers4/silero-vad/blob/master/silero-vad.ipynb)
|
4 |
+
|
5 |
+
![header](https://user-images.githubusercontent.com/12515440/89997349-b3523080-dc94-11ea-9906-ca2e8bc50535.png)
|
6 |
+
|
7 |
+
<br/>
|
8 |
+
<h1 align="center">Silero VAD</h1>
|
9 |
+
<br/>
|
10 |
+
|
11 |
+
**Silero VAD** - pre-trained enterprise-grade [Voice Activity Detector](https://en.wikipedia.org/wiki/Voice_activity_detection) (also see our [STT models](https://github.com/snakers4/silero-models)).
|
12 |
+
|
13 |
+
This repository also includes Number Detector and Language classifier [models](https://github.com/snakers4/silero-vad/wiki/Other-Models)
|
14 |
+
|
15 |
+
<br/>
|
16 |
+
|
17 |
+
<p align="center">
|
18 |
+
<img src="https://user-images.githubusercontent.com/36505480/198026365-8da383e0-5398-4a12-b7f8-22c2c0059512.png" />
|
19 |
+
</p>
|
20 |
+
|
21 |
+
<details>
|
22 |
+
<summary>Real Time Example</summary>
|
23 |
+
|
24 |
+
https://user-images.githubusercontent.com/36505480/144874384-95f80f6d-a4f1-42cc-9be7-004c891dd481.mp4
|
25 |
+
|
26 |
+
</details>
|
27 |
+
|
28 |
+
<br/>
|
29 |
+
<h2 align="center">Key Features</h2>
|
30 |
+
<br/>
|
31 |
+
|
32 |
+
- **Stellar accuracy**
|
33 |
+
|
34 |
+
Silero VAD has [excellent results](https://github.com/snakers4/silero-vad/wiki/Quality-Metrics#vs-other-available-solutions) on speech detection tasks.
|
35 |
+
|
36 |
+
- **Fast**
|
37 |
+
|
38 |
+
One audio chunk (30+ ms) [takes](https://github.com/snakers4/silero-vad/wiki/Performance-Metrics#silero-vad-performance-metrics) less than **1ms** to be processed on a single CPU thread. Using batching or GPU can also improve performance considerably. Under certain conditions ONNX may even run up to 4-5x faster.
|
39 |
+
|
40 |
+
- **Lightweight**
|
41 |
+
|
42 |
+
JIT model is around one megabyte in size.
|
43 |
+
|
44 |
+
- **General**
|
45 |
+
|
46 |
+
Silero VAD was trained on huge corpora that include over **100** languages and it performs well on audios from different domains with various background noise and quality levels.
|
47 |
+
|
48 |
+
- **Flexible sampling rate**
|
49 |
+
|
50 |
+
Silero VAD [supports](https://github.com/snakers4/silero-vad/wiki/Quality-Metrics#sample-rate-comparison) **8000 Hz** and **16000 Hz** [sampling rates](https://en.wikipedia.org/wiki/Sampling_(signal_processing)#Sampling_rate).
|
51 |
+
|
52 |
+
- **Flexible chunk size**
|
53 |
+
|
54 |
+
Model was trained on **30 ms**. Longer chunks are supported directly, others may work as well.
|
55 |
+
|
56 |
+
- **Highly Portable**
|
57 |
+
|
58 |
+
Silero VAD reaps benefits from the rich ecosystems built around **PyTorch** and **ONNX** running everywhere where these runtimes are available.
|
59 |
+
|
60 |
+
- **No Strings Attached**
|
61 |
+
|
62 |
+
Published under permissive license (MIT) Silero VAD has zero strings attached - no telemetry, no keys, no registration, no built-in expiration, no keys or vendor lock.
|
63 |
+
|
64 |
+
<br/>
|
65 |
+
<h2 align="center">Typical Use Cases</h2>
|
66 |
+
<br/>
|
67 |
+
|
68 |
+
- Voice activity detection for IOT / edge / mobile use cases
|
69 |
+
- Data cleaning and preparation, voice detection in general
|
70 |
+
- Telephony and call-center automation, voice bots
|
71 |
+
- Voice interfaces
|
72 |
+
|
73 |
+
<br/>
|
74 |
+
<h2 align="center">Links</h2>
|
75 |
+
<br/>
|
76 |
+
|
77 |
+
|
78 |
+
- [Examples and Dependencies](https://github.com/snakers4/silero-vad/wiki/Examples-and-Dependencies#dependencies)
|
79 |
+
- [Quality Metrics](https://github.com/snakers4/silero-vad/wiki/Quality-Metrics)
|
80 |
+
- [Performance Metrics](https://github.com/snakers4/silero-vad/wiki/Performance-Metrics)
|
81 |
+
- [Number Detector and Language classifier models](https://github.com/snakers4/silero-vad/wiki/Other-Models)
|
82 |
+
- [Versions and Available Models](https://github.com/snakers4/silero-vad/wiki/Version-history-and-Available-Models)
|
83 |
+
- [Further reading](https://github.com/snakers4/silero-models#further-reading)
|
84 |
+
- [FAQ](https://github.com/snakers4/silero-vad/wiki/FAQ)
|
85 |
+
|
86 |
+
<br/>
|
87 |
+
<h2 align="center">Get In Touch</h2>
|
88 |
+
<br/>
|
89 |
+
|
90 |
+
Try our models, create an [issue](https://github.com/snakers4/silero-vad/issues/new), start a [discussion](https://github.com/snakers4/silero-vad/discussions/new), join our telegram [chat](https://t.me/silero_speech), [email](mailto:hello@silero.ai) us, read our [news](https://t.me/silero_news).
|
91 |
+
|
92 |
+
Please see our [wiki](https://github.com/snakers4/silero-models/wiki) and [tiers](https://github.com/snakers4/silero-models/wiki/Licensing-and-Tiers) for relevant information and [email](mailto:hello@silero.ai) us directly.
|
93 |
+
|
94 |
+
**Citations**
|
95 |
+
|
96 |
+
```
|
97 |
+
@misc{Silero VAD,
|
98 |
+
author = {Silero Team},
|
99 |
+
title = {Silero VAD: pre-trained enterprise-grade Voice Activity Detector (VAD), Number Detector and Language Classifier},
|
100 |
+
year = {2021},
|
101 |
+
publisher = {GitHub},
|
102 |
+
journal = {GitHub repository},
|
103 |
+
howpublished = {\url{https://github.com/snakers4/silero-vad}},
|
104 |
+
commit = {insert_some_commit_here},
|
105 |
+
email = {hello@silero.ai}
|
106 |
+
}
|
107 |
+
```
|
108 |
+
|
109 |
+
<br/>
|
110 |
+
<h2 align="center">VAD-based Community Apps</h2>
|
111 |
+
<br/>
|
112 |
+
|
113 |
+
- Voice activity detection for the [browser](https://github.com/ricky0123/vad) using ONNX Runtime Web
|
hub/snakers4_silero-vad_master/examples/colab_record_example.ipynb
ADDED
@@ -0,0 +1,241 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"cells": [
|
3 |
+
{
|
4 |
+
"cell_type": "markdown",
|
5 |
+
"metadata": {
|
6 |
+
"id": "bccAucKjnPHm"
|
7 |
+
},
|
8 |
+
"source": [
|
9 |
+
"### Dependencies and inputs"
|
10 |
+
]
|
11 |
+
},
|
12 |
+
{
|
13 |
+
"cell_type": "code",
|
14 |
+
"execution_count": null,
|
15 |
+
"metadata": {
|
16 |
+
"id": "cSih95WFmwgi"
|
17 |
+
},
|
18 |
+
"outputs": [],
|
19 |
+
"source": [
|
20 |
+
"!pip -q install pydub\n",
|
21 |
+
"from google.colab import output\n",
|
22 |
+
"from base64 import b64decode, b64encode\n",
|
23 |
+
"from io import BytesIO\n",
|
24 |
+
"import numpy as np\n",
|
25 |
+
"from pydub import AudioSegment\n",
|
26 |
+
"from IPython.display import HTML, display\n",
|
27 |
+
"import torch\n",
|
28 |
+
"import matplotlib.pyplot as plt\n",
|
29 |
+
"import moviepy.editor as mpe\n",
|
30 |
+
"from matplotlib.animation import FuncAnimation, FFMpegWriter\n",
|
31 |
+
"import matplotlib\n",
|
32 |
+
"matplotlib.use('Agg')\n",
|
33 |
+
"\n",
|
34 |
+
"torch.set_num_threads(1)\n",
|
35 |
+
"\n",
|
36 |
+
"model, _ = torch.hub.load(repo_or_dir='snakers4/silero-vad',\n",
|
37 |
+
" model='silero_vad',\n",
|
38 |
+
" force_reload=True)\n",
|
39 |
+
"\n",
|
40 |
+
"def int2float(sound):\n",
|
41 |
+
" abs_max = np.abs(sound).max()\n",
|
42 |
+
" sound = sound.astype('float32')\n",
|
43 |
+
" if abs_max > 0:\n",
|
44 |
+
" sound *= 1/abs_max\n",
|
45 |
+
" sound = sound.squeeze()\n",
|
46 |
+
" return sound\n",
|
47 |
+
"\n",
|
48 |
+
"AUDIO_HTML = \"\"\"\n",
|
49 |
+
"<script>\n",
|
50 |
+
"var my_div = document.createElement(\"DIV\");\n",
|
51 |
+
"var my_p = document.createElement(\"P\");\n",
|
52 |
+
"var my_btn = document.createElement(\"BUTTON\");\n",
|
53 |
+
"var t = document.createTextNode(\"Press to start recording\");\n",
|
54 |
+
"\n",
|
55 |
+
"my_btn.appendChild(t);\n",
|
56 |
+
"//my_p.appendChild(my_btn);\n",
|
57 |
+
"my_div.appendChild(my_btn);\n",
|
58 |
+
"document.body.appendChild(my_div);\n",
|
59 |
+
"\n",
|
60 |
+
"var base64data = 0;\n",
|
61 |
+
"var reader;\n",
|
62 |
+
"var recorder, gumStream;\n",
|
63 |
+
"var recordButton = my_btn;\n",
|
64 |
+
"\n",
|
65 |
+
"var handleSuccess = function(stream) {\n",
|
66 |
+
" gumStream = stream;\n",
|
67 |
+
" var options = {\n",
|
68 |
+
" //bitsPerSecond: 8000, //chrome seems to ignore, always 48k\n",
|
69 |
+
" mimeType : 'audio/webm;codecs=opus'\n",
|
70 |
+
" //mimeType : 'audio/webm;codecs=pcm'\n",
|
71 |
+
" }; \n",
|
72 |
+
" //recorder = new MediaRecorder(stream, options);\n",
|
73 |
+
" recorder = new MediaRecorder(stream);\n",
|
74 |
+
" recorder.ondataavailable = function(e) { \n",
|
75 |
+
" var url = URL.createObjectURL(e.data);\n",
|
76 |
+
" // var preview = document.createElement('audio');\n",
|
77 |
+
" // preview.controls = true;\n",
|
78 |
+
" // preview.src = url;\n",
|
79 |
+
" // document.body.appendChild(preview);\n",
|
80 |
+
"\n",
|
81 |
+
" reader = new FileReader();\n",
|
82 |
+
" reader.readAsDataURL(e.data); \n",
|
83 |
+
" reader.onloadend = function() {\n",
|
84 |
+
" base64data = reader.result;\n",
|
85 |
+
" //console.log(\"Inside FileReader:\" + base64data);\n",
|
86 |
+
" }\n",
|
87 |
+
" };\n",
|
88 |
+
" recorder.start();\n",
|
89 |
+
" };\n",
|
90 |
+
"\n",
|
91 |
+
"recordButton.innerText = \"Recording... press to stop\";\n",
|
92 |
+
"\n",
|
93 |
+
"navigator.mediaDevices.getUserMedia({audio: true}).then(handleSuccess);\n",
|
94 |
+
"\n",
|
95 |
+
"\n",
|
96 |
+
"function toggleRecording() {\n",
|
97 |
+
" if (recorder && recorder.state == \"recording\") {\n",
|
98 |
+
" recorder.stop();\n",
|
99 |
+
" gumStream.getAudioTracks()[0].stop();\n",
|
100 |
+
" recordButton.innerText = \"Saving recording...\"\n",
|
101 |
+
" }\n",
|
102 |
+
"}\n",
|
103 |
+
"\n",
|
104 |
+
"// https://stackoverflow.com/a/951057\n",
|
105 |
+
"function sleep(ms) {\n",
|
106 |
+
" return new Promise(resolve => setTimeout(resolve, ms));\n",
|
107 |
+
"}\n",
|
108 |
+
"\n",
|
109 |
+
"var data = new Promise(resolve=>{\n",
|
110 |
+
"//recordButton.addEventListener(\"click\", toggleRecording);\n",
|
111 |
+
"recordButton.onclick = ()=>{\n",
|
112 |
+
"toggleRecording()\n",
|
113 |
+
"\n",
|
114 |
+
"sleep(2000).then(() => {\n",
|
115 |
+
" // wait 2000ms for the data to be available...\n",
|
116 |
+
" // ideally this should use something like await...\n",
|
117 |
+
" //console.log(\"Inside data:\" + base64data)\n",
|
118 |
+
" resolve(base64data.toString())\n",
|
119 |
+
"\n",
|
120 |
+
"});\n",
|
121 |
+
"\n",
|
122 |
+
"}\n",
|
123 |
+
"});\n",
|
124 |
+
" \n",
|
125 |
+
"</script>\n",
|
126 |
+
"\"\"\"\n",
|
127 |
+
"\n",
|
128 |
+
"def record(sec=10):\n",
|
129 |
+
" display(HTML(AUDIO_HTML))\n",
|
130 |
+
" s = output.eval_js(\"data\")\n",
|
131 |
+
" b = b64decode(s.split(',')[1])\n",
|
132 |
+
" audio = AudioSegment.from_file(BytesIO(b))\n",
|
133 |
+
" audio.export('test.mp3', format='mp3')\n",
|
134 |
+
" audio = audio.set_channels(1)\n",
|
135 |
+
" audio = audio.set_frame_rate(16000)\n",
|
136 |
+
" audio_float = int2float(np.array(audio.get_array_of_samples()))\n",
|
137 |
+
" audio_tens = torch.tensor(audio_float )\n",
|
138 |
+
" return audio_tens\n",
|
139 |
+
"\n",
|
140 |
+
"def make_animation(probs, audio_duration, interval=40):\n",
|
141 |
+
" fig = plt.figure(figsize=(16, 9))\n",
|
142 |
+
" ax = plt.axes(xlim=(0, audio_duration), ylim=(0, 1.02))\n",
|
143 |
+
" line, = ax.plot([], [], lw=2)\n",
|
144 |
+
" x = [i / 16000 * 512 for i in range(len(probs))]\n",
|
145 |
+
" plt.xlabel('Time, seconds', fontsize=16)\n",
|
146 |
+
" plt.ylabel('Speech Probability', fontsize=16)\n",
|
147 |
+
"\n",
|
148 |
+
" def init():\n",
|
149 |
+
" plt.fill_between(x, probs, color='#064273')\n",
|
150 |
+
" line.set_data([], [])\n",
|
151 |
+
" line.set_color('#990000')\n",
|
152 |
+
" return line,\n",
|
153 |
+
"\n",
|
154 |
+
" def animate(i):\n",
|
155 |
+
" x = i * interval / 1000 - 0.04\n",
|
156 |
+
" y = np.linspace(0, 1.02, 2)\n",
|
157 |
+
" \n",
|
158 |
+
" line.set_data(x, y)\n",
|
159 |
+
" line.set_color('#990000')\n",
|
160 |
+
" return line,\n",
|
161 |
+
"\n",
|
162 |
+
" anim = FuncAnimation(fig, animate, init_func=init, interval=interval, save_count=audio_duration / (interval / 1000))\n",
|
163 |
+
"\n",
|
164 |
+
" f = r\"animation.mp4\" \n",
|
165 |
+
" writervideo = FFMpegWriter(fps=1000/interval) \n",
|
166 |
+
" anim.save(f, writer=writervideo)\n",
|
167 |
+
" plt.close('all')\n",
|
168 |
+
"\n",
|
169 |
+
"def combine_audio(vidname, audname, outname, fps=25): \n",
|
170 |
+
" my_clip = mpe.VideoFileClip(vidname, verbose=False)\n",
|
171 |
+
" audio_background = mpe.AudioFileClip(audname)\n",
|
172 |
+
" final_clip = my_clip.set_audio(audio_background)\n",
|
173 |
+
" final_clip.write_videofile(outname,fps=fps,verbose=False)\n",
|
174 |
+
"\n",
|
175 |
+
"def record_make_animation():\n",
|
176 |
+
" tensor = record()\n",
|
177 |
+
"\n",
|
178 |
+
" print('Calculating probabilities...')\n",
|
179 |
+
" speech_probs = []\n",
|
180 |
+
" window_size_samples = 512\n",
|
181 |
+
" for i in range(0, len(tensor), window_size_samples):\n",
|
182 |
+
" if len(tensor[i: i+ window_size_samples]) < window_size_samples:\n",
|
183 |
+
" break\n",
|
184 |
+
" speech_prob = model(tensor[i: i+ window_size_samples], 16000).item()\n",
|
185 |
+
" speech_probs.append(speech_prob)\n",
|
186 |
+
" model.reset_states()\n",
|
187 |
+
" print('Making animation...')\n",
|
188 |
+
" make_animation(speech_probs, len(tensor) / 16000)\n",
|
189 |
+
"\n",
|
190 |
+
" print('Merging your voice with animation...')\n",
|
191 |
+
" combine_audio('animation.mp4', 'test.mp3', 'merged.mp4')\n",
|
192 |
+
" print('Done!')\n",
|
193 |
+
" mp4 = open('merged.mp4','rb').read()\n",
|
194 |
+
" data_url = \"data:video/mp4;base64,\" + b64encode(mp4).decode()\n",
|
195 |
+
" display(HTML(\"\"\"\n",
|
196 |
+
" <video width=800 controls>\n",
|
197 |
+
" <source src=\"%s\" type=\"video/mp4\">\n",
|
198 |
+
" </video>\n",
|
199 |
+
" \"\"\" % data_url))"
|
200 |
+
]
|
201 |
+
},
|
202 |
+
{
|
203 |
+
"cell_type": "markdown",
|
204 |
+
"metadata": {
|
205 |
+
"id": "IFVs3GvTnpB1"
|
206 |
+
},
|
207 |
+
"source": [
|
208 |
+
"## Record example"
|
209 |
+
]
|
210 |
+
},
|
211 |
+
{
|
212 |
+
"cell_type": "code",
|
213 |
+
"execution_count": null,
|
214 |
+
"metadata": {
|
215 |
+
"id": "5EBjrTwiqAaQ"
|
216 |
+
},
|
217 |
+
"outputs": [],
|
218 |
+
"source": [
|
219 |
+
"record_make_animation()"
|
220 |
+
]
|
221 |
+
}
|
222 |
+
],
|
223 |
+
"metadata": {
|
224 |
+
"colab": {
|
225 |
+
"collapsed_sections": [
|
226 |
+
"bccAucKjnPHm"
|
227 |
+
],
|
228 |
+
"name": "Untitled2.ipynb",
|
229 |
+
"provenance": []
|
230 |
+
},
|
231 |
+
"kernelspec": {
|
232 |
+
"display_name": "Python 3",
|
233 |
+
"name": "python3"
|
234 |
+
},
|
235 |
+
"language_info": {
|
236 |
+
"name": "python"
|
237 |
+
}
|
238 |
+
},
|
239 |
+
"nbformat": 4,
|
240 |
+
"nbformat_minor": 0
|
241 |
+
}
|
hub/snakers4_silero-vad_master/examples/microphone_and_webRTC_integration/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
In this example, an integration with the microphone and the webRTC VAD has been done. I used [this](https://github.com/mozilla/DeepSpeech-examples/tree/r0.8/mic_vad_streaming) as a draft.
|
3 |
+
Here a short video to present the results:
|
4 |
+
|
5 |
+
https://user-images.githubusercontent.com/28188499/116685087-182ff100-a9b2-11eb-927d-ed9f621226ee.mp4
|
6 |
+
|
7 |
+
# Requirements:
|
8 |
+
The libraries used for the following example are:
|
9 |
+
```
|
10 |
+
Python == 3.6.9
|
11 |
+
webrtcvad >= 2.0.10
|
12 |
+
torchaudio >= 0.8.1
|
13 |
+
torch >= 1.8.1
|
14 |
+
halo >= 0.0.31
|
15 |
+
Soundfile >= 0.13.3
|
16 |
+
```
|
17 |
+
Using pip3:
|
18 |
+
```
|
19 |
+
pip3 install webrtcvad
|
20 |
+
pip3 install torchaudio
|
21 |
+
pip3 install torch
|
22 |
+
pip3 install halo
|
23 |
+
pip3 install soundfile
|
24 |
+
```
|
25 |
+
Moreover, to make the code easier, the default sample_rate is 16KHz without resampling.
|
26 |
+
|
27 |
+
This example has been tested on ``` ubuntu 18.04.3 LTS```
|
28 |
+
|
hub/snakers4_silero-vad_master/examples/microphone_and_webRTC_integration/microphone_and_webRTC_integration.py
ADDED
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import collections, queue
|
2 |
+
import numpy as np
|
3 |
+
import pyaudio
|
4 |
+
import webrtcvad
|
5 |
+
from halo import Halo
|
6 |
+
import torch
|
7 |
+
import torchaudio
|
8 |
+
|
9 |
+
class Audio(object):
|
10 |
+
"""Streams raw audio from microphone. Data is received in a separate thread, and stored in a buffer, to be read from."""
|
11 |
+
|
12 |
+
FORMAT = pyaudio.paInt16
|
13 |
+
# Network/VAD rate-space
|
14 |
+
RATE_PROCESS = 16000
|
15 |
+
CHANNELS = 1
|
16 |
+
BLOCKS_PER_SECOND = 50
|
17 |
+
|
18 |
+
def __init__(self, callback=None, device=None, input_rate=RATE_PROCESS):
|
19 |
+
def proxy_callback(in_data, frame_count, time_info, status):
|
20 |
+
#pylint: disable=unused-argument
|
21 |
+
callback(in_data)
|
22 |
+
return (None, pyaudio.paContinue)
|
23 |
+
if callback is None: callback = lambda in_data: self.buffer_queue.put(in_data)
|
24 |
+
self.buffer_queue = queue.Queue()
|
25 |
+
self.device = device
|
26 |
+
self.input_rate = input_rate
|
27 |
+
self.sample_rate = self.RATE_PROCESS
|
28 |
+
self.block_size = int(self.RATE_PROCESS / float(self.BLOCKS_PER_SECOND))
|
29 |
+
self.block_size_input = int(self.input_rate / float(self.BLOCKS_PER_SECOND))
|
30 |
+
self.pa = pyaudio.PyAudio()
|
31 |
+
|
32 |
+
kwargs = {
|
33 |
+
'format': self.FORMAT,
|
34 |
+
'channels': self.CHANNELS,
|
35 |
+
'rate': self.input_rate,
|
36 |
+
'input': True,
|
37 |
+
'frames_per_buffer': self.block_size_input,
|
38 |
+
'stream_callback': proxy_callback,
|
39 |
+
}
|
40 |
+
|
41 |
+
self.chunk = None
|
42 |
+
# if not default device
|
43 |
+
if self.device:
|
44 |
+
kwargs['input_device_index'] = self.device
|
45 |
+
|
46 |
+
self.stream = self.pa.open(**kwargs)
|
47 |
+
self.stream.start_stream()
|
48 |
+
|
49 |
+
def read(self):
|
50 |
+
"""Return a block of audio data, blocking if necessary."""
|
51 |
+
return self.buffer_queue.get()
|
52 |
+
|
53 |
+
def destroy(self):
|
54 |
+
self.stream.stop_stream()
|
55 |
+
self.stream.close()
|
56 |
+
self.pa.terminate()
|
57 |
+
|
58 |
+
frame_duration_ms = property(lambda self: 1000 * self.block_size // self.sample_rate)
|
59 |
+
|
60 |
+
|
61 |
+
class VADAudio(Audio):
|
62 |
+
"""Filter & segment audio with voice activity detection."""
|
63 |
+
|
64 |
+
def __init__(self, aggressiveness=3, device=None, input_rate=None):
|
65 |
+
super().__init__(device=device, input_rate=input_rate)
|
66 |
+
self.vad = webrtcvad.Vad(aggressiveness)
|
67 |
+
|
68 |
+
def frame_generator(self):
|
69 |
+
"""Generator that yields all audio frames from microphone."""
|
70 |
+
if self.input_rate == self.RATE_PROCESS:
|
71 |
+
while True:
|
72 |
+
yield self.read()
|
73 |
+
else:
|
74 |
+
raise Exception("Resampling required")
|
75 |
+
|
76 |
+
def vad_collector(self, padding_ms=300, ratio=0.75, frames=None):
|
77 |
+
"""Generator that yields series of consecutive audio frames comprising each utterence, separated by yielding a single None.
|
78 |
+
Determines voice activity by ratio of frames in padding_ms. Uses a buffer to include padding_ms prior to being triggered.
|
79 |
+
Example: (frame, ..., frame, None, frame, ..., frame, None, ...)
|
80 |
+
|---utterence---| |---utterence---|
|
81 |
+
"""
|
82 |
+
if frames is None: frames = self.frame_generator()
|
83 |
+
num_padding_frames = padding_ms // self.frame_duration_ms
|
84 |
+
ring_buffer = collections.deque(maxlen=num_padding_frames)
|
85 |
+
triggered = False
|
86 |
+
|
87 |
+
for frame in frames:
|
88 |
+
if len(frame) < 640:
|
89 |
+
return
|
90 |
+
|
91 |
+
is_speech = self.vad.is_speech(frame, self.sample_rate)
|
92 |
+
|
93 |
+
if not triggered:
|
94 |
+
ring_buffer.append((frame, is_speech))
|
95 |
+
num_voiced = len([f for f, speech in ring_buffer if speech])
|
96 |
+
if num_voiced > ratio * ring_buffer.maxlen:
|
97 |
+
triggered = True
|
98 |
+
for f, s in ring_buffer:
|
99 |
+
yield f
|
100 |
+
ring_buffer.clear()
|
101 |
+
|
102 |
+
else:
|
103 |
+
yield frame
|
104 |
+
ring_buffer.append((frame, is_speech))
|
105 |
+
num_unvoiced = len([f for f, speech in ring_buffer if not speech])
|
106 |
+
if num_unvoiced > ratio * ring_buffer.maxlen:
|
107 |
+
triggered = False
|
108 |
+
yield None
|
109 |
+
ring_buffer.clear()
|
110 |
+
|
111 |
+
def main(ARGS):
|
112 |
+
# Start audio with VAD
|
113 |
+
vad_audio = VADAudio(aggressiveness=ARGS.webRTC_aggressiveness,
|
114 |
+
device=ARGS.device,
|
115 |
+
input_rate=ARGS.rate)
|
116 |
+
|
117 |
+
print("Listening (ctrl-C to exit)...")
|
118 |
+
frames = vad_audio.vad_collector()
|
119 |
+
|
120 |
+
# load silero VAD
|
121 |
+
torchaudio.set_audio_backend("soundfile")
|
122 |
+
model, utils = torch.hub.load(repo_or_dir='snakers4/silero-vad',
|
123 |
+
model=ARGS.silaro_model_name,
|
124 |
+
force_reload= ARGS.reload)
|
125 |
+
(get_speech_ts,_,_, _,_, _, _) = utils
|
126 |
+
|
127 |
+
|
128 |
+
# Stream from microphone to DeepSpeech using VAD
|
129 |
+
spinner = None
|
130 |
+
if not ARGS.nospinner:
|
131 |
+
spinner = Halo(spinner='line')
|
132 |
+
wav_data = bytearray()
|
133 |
+
for frame in frames:
|
134 |
+
if frame is not None:
|
135 |
+
if spinner: spinner.start()
|
136 |
+
|
137 |
+
wav_data.extend(frame)
|
138 |
+
else:
|
139 |
+
if spinner: spinner.stop()
|
140 |
+
print("webRTC has detected a possible speech")
|
141 |
+
|
142 |
+
newsound= np.frombuffer(wav_data,np.int16)
|
143 |
+
audio_float32=Int2Float(newsound)
|
144 |
+
time_stamps =get_speech_ts(audio_float32, model,num_steps=ARGS.num_steps,trig_sum=ARGS.trig_sum,neg_trig_sum=ARGS.neg_trig_sum,
|
145 |
+
num_samples_per_window=ARGS.num_samples_per_window,min_speech_samples=ARGS.min_speech_samples,
|
146 |
+
min_silence_samples=ARGS.min_silence_samples)
|
147 |
+
|
148 |
+
if(len(time_stamps)>0):
|
149 |
+
print("silero VAD has detected a possible speech")
|
150 |
+
else:
|
151 |
+
print("silero VAD has detected a noise")
|
152 |
+
print()
|
153 |
+
wav_data = bytearray()
|
154 |
+
|
155 |
+
|
156 |
+
def Int2Float(sound):
|
157 |
+
_sound = np.copy(sound) #
|
158 |
+
abs_max = np.abs(_sound).max()
|
159 |
+
_sound = _sound.astype('float32')
|
160 |
+
if abs_max > 0:
|
161 |
+
_sound *= 1/abs_max
|
162 |
+
audio_float32 = torch.from_numpy(_sound.squeeze())
|
163 |
+
return audio_float32
|
164 |
+
|
165 |
+
if __name__ == '__main__':
|
166 |
+
DEFAULT_SAMPLE_RATE = 16000
|
167 |
+
|
168 |
+
import argparse
|
169 |
+
parser = argparse.ArgumentParser(description="Stream from microphone to webRTC and silero VAD")
|
170 |
+
|
171 |
+
parser.add_argument('-v', '--webRTC_aggressiveness', type=int, default=3,
|
172 |
+
help="Set aggressiveness of webRTC: an integer between 0 and 3, 0 being the least aggressive about filtering out non-speech, 3 the most aggressive. Default: 3")
|
173 |
+
parser.add_argument('--nospinner', action='store_true',
|
174 |
+
help="Disable spinner")
|
175 |
+
parser.add_argument('-d', '--device', type=int, default=None,
|
176 |
+
help="Device input index (Int) as listed by pyaudio.PyAudio.get_device_info_by_index(). If not provided, falls back to PyAudio.get_default_device().")
|
177 |
+
|
178 |
+
parser.add_argument('-name', '--silaro_model_name', type=str, default="silero_vad",
|
179 |
+
help="select the name of the model. You can select between 'silero_vad',''silero_vad_micro','silero_vad_micro_8k','silero_vad_mini','silero_vad_mini_8k'")
|
180 |
+
parser.add_argument('--reload', action='store_true',help="download the last version of the silero vad")
|
181 |
+
|
182 |
+
parser.add_argument('-ts', '--trig_sum', type=float, default=0.25,
|
183 |
+
help="overlapping windows are used for each audio chunk, trig sum defines average probability among those windows for switching into triggered state (speech state)")
|
184 |
+
|
185 |
+
parser.add_argument('-nts', '--neg_trig_sum', type=float, default=0.07,
|
186 |
+
help="same as trig_sum, but for switching from triggered to non-triggered state (non-speech)")
|
187 |
+
|
188 |
+
parser.add_argument('-N', '--num_steps', type=int, default=8,
|
189 |
+
help="nubmer of overlapping windows to split audio chunk into (we recommend 4 or 8)")
|
190 |
+
|
191 |
+
parser.add_argument('-nspw', '--num_samples_per_window', type=int, default=4000,
|
192 |
+
help="number of samples in each window, our models were trained using 4000 samples (250 ms) per window, so this is preferable value (lesser values reduce quality)")
|
193 |
+
|
194 |
+
parser.add_argument('-msps', '--min_speech_samples', type=int, default=10000,
|
195 |
+
help="minimum speech chunk duration in samples")
|
196 |
+
|
197 |
+
parser.add_argument('-msis', '--min_silence_samples', type=int, default=500,
|
198 |
+
help=" minimum silence duration in samples between to separate speech chunks")
|
199 |
+
ARGS = parser.parse_args()
|
200 |
+
ARGS.rate=DEFAULT_SAMPLE_RATE
|
201 |
+
main(ARGS)
|
hub/snakers4_silero-vad_master/examples/pyaudio-streaming/README.md
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Pyaudio Streaming Example
|
2 |
+
|
3 |
+
This example notebook shows how micophone audio fetched by pyaudio can be processed with Silero-VAD.
|
4 |
+
|
5 |
+
It has been designed as a low-level example for binary real-time streaming using only the prediction of the model, processing the binary data and plotting the speech probabilities at the end to visualize it.
|
6 |
+
|
7 |
+
Currently, the notebook consits of two examples:
|
8 |
+
- One that records audio of a predefined length from the microphone, process it with Silero-VAD, and plots it afterwards.
|
9 |
+
- The other one plots the speech probabilities in real-time (using jupyterplot) and records the audio until you press enter.
|
10 |
+
|
11 |
+
## Example Video for the Real-Time Visualization
|
12 |
+
|
13 |
+
|
14 |
+
https://user-images.githubusercontent.com/8079748/117580455-4622dd00-b0f8-11eb-858d-e6368ed4eada.mp4
|
15 |
+
|
16 |
+
|
17 |
+
|
18 |
+
|
19 |
+
|
20 |
+
|
hub/snakers4_silero-vad_master/examples/pyaudio-streaming/pyaudio-streaming-examples.ipynb
ADDED
@@ -0,0 +1,331 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"cells": [
|
3 |
+
{
|
4 |
+
"cell_type": "markdown",
|
5 |
+
"id": "62a0cccb",
|
6 |
+
"metadata": {},
|
7 |
+
"source": [
|
8 |
+
"# Pyaudio Microphone Streaming Examples\n",
|
9 |
+
"\n",
|
10 |
+
"A simple notebook that uses pyaudio to get the microphone audio and feeds this audio then to Silero VAD.\n",
|
11 |
+
"\n",
|
12 |
+
"I created it as an example on how binary data from a stream could be feed into Silero VAD.\n",
|
13 |
+
"\n",
|
14 |
+
"\n",
|
15 |
+
"Has been tested on Ubuntu 21.04 (x86). After you installed the dependencies below, no additional setup is required."
|
16 |
+
]
|
17 |
+
},
|
18 |
+
{
|
19 |
+
"cell_type": "markdown",
|
20 |
+
"id": "64cbe1eb",
|
21 |
+
"metadata": {},
|
22 |
+
"source": [
|
23 |
+
"## Dependencies\n",
|
24 |
+
"The cell below lists all used dependencies and the used versions. Uncomment to install them from within the notebook."
|
25 |
+
]
|
26 |
+
},
|
27 |
+
{
|
28 |
+
"cell_type": "code",
|
29 |
+
"execution_count": null,
|
30 |
+
"id": "57bc2aac",
|
31 |
+
"metadata": {},
|
32 |
+
"outputs": [],
|
33 |
+
"source": [
|
34 |
+
"#!pip install numpy==1.20.2\n",
|
35 |
+
"#!pip install torch==1.9.0\n",
|
36 |
+
"#!pip install matplotlib==3.4.2\n",
|
37 |
+
"#!pip install torchaudio==0.9.0\n",
|
38 |
+
"#!pip install soundfile==0.10.3.post1\n",
|
39 |
+
"#!pip install pyaudio==0.2.11"
|
40 |
+
]
|
41 |
+
},
|
42 |
+
{
|
43 |
+
"cell_type": "markdown",
|
44 |
+
"id": "110de761",
|
45 |
+
"metadata": {},
|
46 |
+
"source": [
|
47 |
+
"## Imports"
|
48 |
+
]
|
49 |
+
},
|
50 |
+
{
|
51 |
+
"cell_type": "code",
|
52 |
+
"execution_count": null,
|
53 |
+
"id": "5a647d8d",
|
54 |
+
"metadata": {},
|
55 |
+
"outputs": [],
|
56 |
+
"source": [
|
57 |
+
"import io\n",
|
58 |
+
"import numpy as np\n",
|
59 |
+
"import torch\n",
|
60 |
+
"torch.set_num_threads(1)\n",
|
61 |
+
"import torchaudio\n",
|
62 |
+
"import matplotlib\n",
|
63 |
+
"import matplotlib.pylab as plt\n",
|
64 |
+
"torchaudio.set_audio_backend(\"soundfile\")\n",
|
65 |
+
"import pyaudio"
|
66 |
+
]
|
67 |
+
},
|
68 |
+
{
|
69 |
+
"cell_type": "code",
|
70 |
+
"execution_count": null,
|
71 |
+
"id": "725d7066",
|
72 |
+
"metadata": {},
|
73 |
+
"outputs": [],
|
74 |
+
"source": [
|
75 |
+
"model, utils = torch.hub.load(repo_or_dir='snakers4/silero-vad',\n",
|
76 |
+
" model='silero_vad',\n",
|
77 |
+
" force_reload=True)"
|
78 |
+
]
|
79 |
+
},
|
80 |
+
{
|
81 |
+
"cell_type": "code",
|
82 |
+
"execution_count": null,
|
83 |
+
"id": "1c0b2ea7",
|
84 |
+
"metadata": {},
|
85 |
+
"outputs": [],
|
86 |
+
"source": [
|
87 |
+
"(get_speech_timestamps,\n",
|
88 |
+
" save_audio,\n",
|
89 |
+
" read_audio,\n",
|
90 |
+
" VADIterator,\n",
|
91 |
+
" collect_chunks) = utils"
|
92 |
+
]
|
93 |
+
},
|
94 |
+
{
|
95 |
+
"cell_type": "markdown",
|
96 |
+
"id": "f9112603",
|
97 |
+
"metadata": {},
|
98 |
+
"source": [
|
99 |
+
"### Helper Methods"
|
100 |
+
]
|
101 |
+
},
|
102 |
+
{
|
103 |
+
"cell_type": "code",
|
104 |
+
"execution_count": null,
|
105 |
+
"id": "5abc6330",
|
106 |
+
"metadata": {},
|
107 |
+
"outputs": [],
|
108 |
+
"source": [
|
109 |
+
"# Taken from utils_vad.py\n",
|
110 |
+
"def validate(model,\n",
|
111 |
+
" inputs: torch.Tensor):\n",
|
112 |
+
" with torch.no_grad():\n",
|
113 |
+
" outs = model(inputs)\n",
|
114 |
+
" return outs\n",
|
115 |
+
"\n",
|
116 |
+
"# Provided by Alexander Veysov\n",
|
117 |
+
"def int2float(sound):\n",
|
118 |
+
" abs_max = np.abs(sound).max()\n",
|
119 |
+
" sound = sound.astype('float32')\n",
|
120 |
+
" if abs_max > 0:\n",
|
121 |
+
" sound *= 1/abs_max\n",
|
122 |
+
" sound = sound.squeeze() # depends on the use case\n",
|
123 |
+
" return sound"
|
124 |
+
]
|
125 |
+
},
|
126 |
+
{
|
127 |
+
"cell_type": "markdown",
|
128 |
+
"id": "5124095e",
|
129 |
+
"metadata": {},
|
130 |
+
"source": [
|
131 |
+
"## Pyaudio Set-up"
|
132 |
+
]
|
133 |
+
},
|
134 |
+
{
|
135 |
+
"cell_type": "code",
|
136 |
+
"execution_count": null,
|
137 |
+
"id": "a845356e",
|
138 |
+
"metadata": {},
|
139 |
+
"outputs": [],
|
140 |
+
"source": [
|
141 |
+
"FORMAT = pyaudio.paInt16\n",
|
142 |
+
"CHANNELS = 1\n",
|
143 |
+
"SAMPLE_RATE = 16000\n",
|
144 |
+
"CHUNK = int(SAMPLE_RATE / 10)\n",
|
145 |
+
"\n",
|
146 |
+
"audio = pyaudio.PyAudio()"
|
147 |
+
]
|
148 |
+
},
|
149 |
+
{
|
150 |
+
"cell_type": "markdown",
|
151 |
+
"id": "0b910c99",
|
152 |
+
"metadata": {},
|
153 |
+
"source": [
|
154 |
+
"## Simple Example\n",
|
155 |
+
"The following example reads the audio as 250ms chunks from the microphone, converts them to a Pytorch Tensor, and gets the probabilities/confidences if the model thinks the frame is voiced."
|
156 |
+
]
|
157 |
+
},
|
158 |
+
{
|
159 |
+
"cell_type": "code",
|
160 |
+
"execution_count": null,
|
161 |
+
"id": "9d3d2c10",
|
162 |
+
"metadata": {},
|
163 |
+
"outputs": [],
|
164 |
+
"source": [
|
165 |
+
"num_samples = 1536"
|
166 |
+
]
|
167 |
+
},
|
168 |
+
{
|
169 |
+
"cell_type": "code",
|
170 |
+
"execution_count": null,
|
171 |
+
"id": "3cb44a4a",
|
172 |
+
"metadata": {},
|
173 |
+
"outputs": [],
|
174 |
+
"source": [
|
175 |
+
"stream = audio.open(format=FORMAT,\n",
|
176 |
+
" channels=CHANNELS,\n",
|
177 |
+
" rate=SAMPLE_RATE,\n",
|
178 |
+
" input=True,\n",
|
179 |
+
" frames_per_buffer=CHUNK)\n",
|
180 |
+
"data = []\n",
|
181 |
+
"voiced_confidences = []\n",
|
182 |
+
"\n",
|
183 |
+
"print(\"Started Recording\")\n",
|
184 |
+
"for i in range(0, frames_to_record):\n",
|
185 |
+
" \n",
|
186 |
+
" audio_chunk = stream.read(num_samples)\n",
|
187 |
+
" \n",
|
188 |
+
" # in case you want to save the audio later\n",
|
189 |
+
" data.append(audio_chunk)\n",
|
190 |
+
" \n",
|
191 |
+
" audio_int16 = np.frombuffer(audio_chunk, np.int16);\n",
|
192 |
+
"\n",
|
193 |
+
" audio_float32 = int2float(audio_int16)\n",
|
194 |
+
" \n",
|
195 |
+
" # get the confidences and add them to the list to plot them later\n",
|
196 |
+
" new_confidence = model(torch.from_numpy(audio_float32), 16000).item()\n",
|
197 |
+
" voiced_confidences.append(new_confidence)\n",
|
198 |
+
" \n",
|
199 |
+
"print(\"Stopped the recording\")\n",
|
200 |
+
"\n",
|
201 |
+
"# plot the confidences for the speech\n",
|
202 |
+
"plt.figure(figsize=(20,6))\n",
|
203 |
+
"plt.plot(voiced_confidences)\n",
|
204 |
+
"plt.show()"
|
205 |
+
]
|
206 |
+
},
|
207 |
+
{
|
208 |
+
"cell_type": "markdown",
|
209 |
+
"id": "a3dda982",
|
210 |
+
"metadata": {},
|
211 |
+
"source": [
|
212 |
+
"## Real Time Visualization\n",
|
213 |
+
"\n",
|
214 |
+
"As an enhancement to plot the speech probabilities in real time I added the implementation below.\n",
|
215 |
+
"In contrast to the simeple one, it records the audio until to stop the recording by pressing enter.\n",
|
216 |
+
"While looking into good ways to update matplotlib plots in real-time, I found a simple libarary that does the job. https://github.com/lvwerra/jupyterplot It has some limitations, but works for this use case really well.\n"
|
217 |
+
]
|
218 |
+
},
|
219 |
+
{
|
220 |
+
"cell_type": "code",
|
221 |
+
"execution_count": null,
|
222 |
+
"id": "05ef4100",
|
223 |
+
"metadata": {},
|
224 |
+
"outputs": [],
|
225 |
+
"source": [
|
226 |
+
"#!pip install jupyterplot==0.0.3"
|
227 |
+
]
|
228 |
+
},
|
229 |
+
{
|
230 |
+
"cell_type": "code",
|
231 |
+
"execution_count": null,
|
232 |
+
"id": "d1d4cdd6",
|
233 |
+
"metadata": {},
|
234 |
+
"outputs": [],
|
235 |
+
"source": [
|
236 |
+
"from jupyterplot import ProgressPlot\n",
|
237 |
+
"import threading\n",
|
238 |
+
"\n",
|
239 |
+
"continue_recording = True\n",
|
240 |
+
"\n",
|
241 |
+
"def stop():\n",
|
242 |
+
" input(\"Press Enter to stop the recording:\")\n",
|
243 |
+
" global continue_recording\n",
|
244 |
+
" continue_recording = False\n",
|
245 |
+
"\n",
|
246 |
+
"def start_recording():\n",
|
247 |
+
" \n",
|
248 |
+
" stream = audio.open(format=FORMAT,\n",
|
249 |
+
" channels=CHANNELS,\n",
|
250 |
+
" rate=SAMPLE_RATE,\n",
|
251 |
+
" input=True,\n",
|
252 |
+
" frames_per_buffer=CHUNK)\n",
|
253 |
+
"\n",
|
254 |
+
" data = []\n",
|
255 |
+
" voiced_confidences = []\n",
|
256 |
+
" \n",
|
257 |
+
" global continue_recording\n",
|
258 |
+
" continue_recording = True\n",
|
259 |
+
" \n",
|
260 |
+
" pp = ProgressPlot(plot_names=[\"Silero VAD\"],line_names=[\"speech probabilities\"], x_label=\"audio chunks\")\n",
|
261 |
+
" \n",
|
262 |
+
" stop_listener = threading.Thread(target=stop)\n",
|
263 |
+
" stop_listener.start()\n",
|
264 |
+
"\n",
|
265 |
+
" while continue_recording:\n",
|
266 |
+
" \n",
|
267 |
+
" audio_chunk = stream.read(num_samples)\n",
|
268 |
+
" \n",
|
269 |
+
" # in case you want to save the audio later\n",
|
270 |
+
" data.append(audio_chunk)\n",
|
271 |
+
" \n",
|
272 |
+
" audio_int16 = np.frombuffer(audio_chunk, np.int16);\n",
|
273 |
+
"\n",
|
274 |
+
" audio_float32 = int2float(audio_int16)\n",
|
275 |
+
" \n",
|
276 |
+
" # get the confidences and add them to the list to plot them later\n",
|
277 |
+
" new_confidence = model(torch.from_numpy(audio_float32), 16000).item()\n",
|
278 |
+
" voiced_confidences.append(new_confidence)\n",
|
279 |
+
" \n",
|
280 |
+
" pp.update(new_confidence)\n",
|
281 |
+
"\n",
|
282 |
+
"\n",
|
283 |
+
" pp.finalize()"
|
284 |
+
]
|
285 |
+
},
|
286 |
+
{
|
287 |
+
"cell_type": "code",
|
288 |
+
"execution_count": null,
|
289 |
+
"id": "1e398009",
|
290 |
+
"metadata": {},
|
291 |
+
"outputs": [],
|
292 |
+
"source": [
|
293 |
+
"start_recording()"
|
294 |
+
]
|
295 |
+
}
|
296 |
+
],
|
297 |
+
"metadata": {
|
298 |
+
"kernelspec": {
|
299 |
+
"display_name": "Python 3",
|
300 |
+
"language": "python",
|
301 |
+
"name": "python3"
|
302 |
+
},
|
303 |
+
"language_info": {
|
304 |
+
"codemirror_mode": {
|
305 |
+
"name": "ipython",
|
306 |
+
"version": 3
|
307 |
+
},
|
308 |
+
"file_extension": ".py",
|
309 |
+
"mimetype": "text/x-python",
|
310 |
+
"name": "python",
|
311 |
+
"nbconvert_exporter": "python",
|
312 |
+
"pygments_lexer": "ipython3",
|
313 |
+
"version": "3.7.10"
|
314 |
+
},
|
315 |
+
"toc": {
|
316 |
+
"base_numbering": 1,
|
317 |
+
"nav_menu": {},
|
318 |
+
"number_sections": true,
|
319 |
+
"sideBar": true,
|
320 |
+
"skip_h1_title": false,
|
321 |
+
"title_cell": "Table of Contents",
|
322 |
+
"title_sidebar": "Contents",
|
323 |
+
"toc_cell": false,
|
324 |
+
"toc_position": {},
|
325 |
+
"toc_section_display": true,
|
326 |
+
"toc_window_display": false
|
327 |
+
}
|
328 |
+
},
|
329 |
+
"nbformat": 4,
|
330 |
+
"nbformat_minor": 5
|
331 |
+
}
|
hub/snakers4_silero-vad_master/files/lang_dict_95.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"59": "mg, Malagasy", "76": "tk, Turkmen", "20": "lb, Luxembourgish, Letzeburgesch", "62": "or, Oriya", "30": "en, English", "26": "oc, Occitan", "69": "no, Norwegian", "77": "sr, Serbian", "90": "bs, Bosnian", "71": "el, Greek, Modern (1453\u2013)", "15": "az, Azerbaijani", "12": "lo, Lao", "85": "zh-HK, Chinese", "79": "cs, Czech", "43": "sv, Swedish", "37": "mn, Mongolian", "32": "fi, Finnish", "51": "tg, Tajik", "46": "am, Amharic", "17": "nn, Norwegian Nynorsk", "40": "ja, Japanese", "8": "it, Italian", "21": "ha, Hausa", "11": "as, Assamese", "29": "fa, Persian", "82": "bn, Bengali", "54": "mk, Macedonian", "31": "sw, Swahili", "45": "vi, Vietnamese", "41": "ur, Urdu", "74": "bo, Tibetan", "4": "hi, Hindi", "86": "mr, Marathi", "3": "fy-NL, Western Frisian", "65": "sk, Slovak", "2": "ln, Lingala", "92": "gl, Galician", "53": "sn, Shona", "87": "su, Sundanese", "35": "tt, Tatar", "93": "kn, Kannada", "6": "yo, Yoruba", "27": "ps, Pashto, Pushto", "34": "hy, Armenian", "25": "pa-IN, Punjabi, Panjabi", "23": "nl, Dutch, Flemish", "48": "th, Thai", "73": "mt, Maltese", "55": "ar, Arabic", "89": "ba, Bashkir", "78": "bg, Bulgarian", "42": "yi, Yiddish", "5": "ru, Russian", "84": "sv-SE, Swedish", "80": "tr, Turkish", "33": "sq, Albanian", "38": "kk, Kazakh", "50": "pl, Polish", "9": "hr, Croatian", "66": "ky, Kirghiz, Kyrgyz", "49": "hu, Hungarian", "10": "si, Sinhala, Sinhalese", "56": "la, Latin", "75": "de, German", "14": "ko, Korean", "22": "id, Indonesian", "47": "sl, Slovenian", "57": "be, Belarusian", "36": "ta, Tamil", "7": "da, Danish", "91": "sd, Sindhi", "28": "et, Estonian", "63": "pt, Portuguese", "60": "ne, Nepali", "94": "zh-TW, Chinese", "18": "zh-CN, Chinese", "88": "rw, Kinyarwanda", "19": "es, Spanish, Castilian", "39": "ht, Haitian, Haitian Creole", "64": "tl, Tagalog", "83": "ms, Malay", "70": "ro, Romanian, Moldavian, Moldovan", "68": "pa, Punjabi, Panjabi", "52": "uz, Uzbek", "58": "km, Central Khmer", "67": "my, Burmese", "0": "fr, French", "24": "af, Afrikaans", "16": "gu, Gujarati", "81": "so, Somali", "13": "uk, Ukrainian", "44": "ca, Catalan, Valencian", "72": "ml, Malayalam", "61": "te, Telugu", "1": "zh, Chinese"}
|
hub/snakers4_silero-vad_master/files/lang_group_dict_95.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"0": ["Afrikaans", "Dutch, Flemish", "Western Frisian"], "1": ["Turkish", "Azerbaijani"], "2": ["Russian", "Slovak", "Ukrainian", "Czech", "Polish", "Belarusian"], "3": ["Bulgarian", "Macedonian", "Serbian", "Croatian", "Bosnian", "Slovenian"], "4": ["Norwegian Nynorsk", "Swedish", "Danish", "Norwegian"], "5": ["English"], "6": ["Finnish", "Estonian"], "7": ["Yiddish", "Luxembourgish, Letzeburgesch", "German"], "8": ["Spanish", "Occitan", "Portuguese", "Catalan, Valencian", "Galician", "Spanish, Castilian", "Italian"], "9": ["Maltese", "Arabic"], "10": ["Marathi"], "11": ["Hindi", "Urdu"], "12": ["Lao", "Thai"], "13": ["Malay", "Indonesian"], "14": ["Romanian, Moldavian, Moldovan"], "15": ["Tagalog"], "16": ["Tajik", "Persian"], "17": ["Kazakh", "Uzbek", "Kirghiz, Kyrgyz"], "18": ["Kinyarwanda"], "19": ["Tatar", "Bashkir"], "20": ["French"], "21": ["Chinese"], "22": ["Lingala"], "23": ["Yoruba"], "24": ["Sinhala, Sinhalese"], "25": ["Assamese"], "26": ["Korean"], "27": ["Gujarati"], "28": ["Hausa"], "29": ["Punjabi, Panjabi"], "30": ["Pashto, Pushto"], "31": ["Swahili"], "32": ["Albanian"], "33": ["Armenian"], "34": ["Mongolian"], "35": ["Tamil"], "36": ["Haitian, Haitian Creole"], "37": ["Japanese"], "38": ["Vietnamese"], "39": ["Amharic"], "40": ["Hungarian"], "41": ["Shona"], "42": ["Latin"], "43": ["Central Khmer"], "44": ["Malagasy"], "45": ["Nepali"], "46": ["Telugu"], "47": ["Oriya"], "48": ["Burmese"], "49": ["Greek, Modern (1453\u2013)"], "50": ["Malayalam"], "51": ["Tibetan"], "52": ["Turkmen"], "53": ["Somali"], "54": ["Bengali"], "55": ["Sundanese"], "56": ["Sindhi"], "57": ["Kannada"]}
|
hub/snakers4_silero-vad_master/files/silero_logo.jpg
ADDED
hub/snakers4_silero-vad_master/files/silero_vad.jit
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:082e21870cf7722b0c7fa5228eaed579efb6870df81192b79bed3f7bac2f738a
|
3 |
+
size 1439299
|
hub/snakers4_silero-vad_master/files/silero_vad.onnx
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:a35ebf52fd3ce5f1469b2a36158dba761bc47b973ea3382b3186ca15b1f5af28
|
3 |
+
size 1807522
|
hub/snakers4_silero-vad_master/hubconf.py
ADDED
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
dependencies = ['torch', 'torchaudio']
|
2 |
+
import torch
|
3 |
+
import os
|
4 |
+
import json
|
5 |
+
from utils_vad import (init_jit_model,
|
6 |
+
get_speech_timestamps,
|
7 |
+
get_number_ts,
|
8 |
+
get_language,
|
9 |
+
get_language_and_group,
|
10 |
+
save_audio,
|
11 |
+
read_audio,
|
12 |
+
VADIterator,
|
13 |
+
collect_chunks,
|
14 |
+
drop_chunks,
|
15 |
+
Validator,
|
16 |
+
OnnxWrapper)
|
17 |
+
|
18 |
+
|
19 |
+
def versiontuple(v):
|
20 |
+
return tuple(map(int, (v.split('+')[0].split("."))))
|
21 |
+
|
22 |
+
|
23 |
+
def silero_vad(onnx=False, force_onnx_cpu=False):
|
24 |
+
"""Silero Voice Activity Detector
|
25 |
+
Returns a model with a set of utils
|
26 |
+
Please see https://github.com/snakers4/silero-vad for usage examples
|
27 |
+
"""
|
28 |
+
|
29 |
+
if not onnx:
|
30 |
+
installed_version = torch.__version__
|
31 |
+
supported_version = '1.12.0'
|
32 |
+
if versiontuple(installed_version) < versiontuple(supported_version):
|
33 |
+
raise Exception(f'Please install torch {supported_version} or greater ({installed_version} installed)')
|
34 |
+
|
35 |
+
model_dir = os.path.join(os.path.dirname(__file__), 'files')
|
36 |
+
if onnx:
|
37 |
+
model = OnnxWrapper(os.path.join(model_dir, 'silero_vad.onnx'))
|
38 |
+
else:
|
39 |
+
model = init_jit_model(os.path.join(model_dir, 'silero_vad.jit'))
|
40 |
+
utils = (get_speech_timestamps,
|
41 |
+
save_audio,
|
42 |
+
read_audio,
|
43 |
+
VADIterator,
|
44 |
+
collect_chunks)
|
45 |
+
|
46 |
+
return model, utils
|
47 |
+
|
48 |
+
|
49 |
+
def silero_number_detector(onnx=False, force_onnx_cpu=False):
|
50 |
+
"""Silero Number Detector
|
51 |
+
Returns a model with a set of utils
|
52 |
+
Please see https://github.com/snakers4/silero-vad for usage examples
|
53 |
+
"""
|
54 |
+
if onnx:
|
55 |
+
url = 'https://models.silero.ai/vad_models/number_detector.onnx'
|
56 |
+
else:
|
57 |
+
url = 'https://models.silero.ai/vad_models/number_detector.jit'
|
58 |
+
model = Validator(url, force_onnx_cpu)
|
59 |
+
utils = (get_number_ts,
|
60 |
+
save_audio,
|
61 |
+
read_audio,
|
62 |
+
collect_chunks,
|
63 |
+
drop_chunks)
|
64 |
+
|
65 |
+
return model, utils
|
66 |
+
|
67 |
+
|
68 |
+
def silero_lang_detector(onnx=False, force_onnx_cpu=False):
|
69 |
+
"""Silero Language Classifier
|
70 |
+
Returns a model with a set of utils
|
71 |
+
Please see https://github.com/snakers4/silero-vad for usage examples
|
72 |
+
"""
|
73 |
+
if onnx:
|
74 |
+
url = 'https://models.silero.ai/vad_models/number_detector.onnx'
|
75 |
+
else:
|
76 |
+
url = 'https://models.silero.ai/vad_models/number_detector.jit'
|
77 |
+
model = Validator(url, force_onnx_cpu)
|
78 |
+
utils = (get_language,
|
79 |
+
read_audio)
|
80 |
+
|
81 |
+
return model, utils
|
82 |
+
|
83 |
+
|
84 |
+
def silero_lang_detector_95(onnx=False, force_onnx_cpu=False):
|
85 |
+
"""Silero Language Classifier (95 languages)
|
86 |
+
Returns a model with a set of utils
|
87 |
+
Please see https://github.com/snakers4/silero-vad for usage examples
|
88 |
+
"""
|
89 |
+
|
90 |
+
if onnx:
|
91 |
+
url = 'https://models.silero.ai/vad_models/lang_classifier_95.onnx'
|
92 |
+
else:
|
93 |
+
url = 'https://models.silero.ai/vad_models/lang_classifier_95.jit'
|
94 |
+
model = Validator(url, force_onnx_cpu)
|
95 |
+
|
96 |
+
model_dir = os.path.join(os.path.dirname(__file__), 'files')
|
97 |
+
with open(os.path.join(model_dir, 'lang_dict_95.json'), 'r') as f:
|
98 |
+
lang_dict = json.load(f)
|
99 |
+
|
100 |
+
with open(os.path.join(model_dir, 'lang_group_dict_95.json'), 'r') as f:
|
101 |
+
lang_group_dict = json.load(f)
|
102 |
+
|
103 |
+
utils = (get_language_and_group, read_audio)
|
104 |
+
|
105 |
+
return model, lang_dict, lang_group_dict, utils
|
hub/snakers4_silero-vad_master/silero-vad.ipynb
ADDED
@@ -0,0 +1,445 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"cells": [
|
3 |
+
{
|
4 |
+
"cell_type": "markdown",
|
5 |
+
"metadata": {
|
6 |
+
"id": "FpMplOCA2Fwp"
|
7 |
+
},
|
8 |
+
"source": [
|
9 |
+
"#VAD"
|
10 |
+
]
|
11 |
+
},
|
12 |
+
{
|
13 |
+
"cell_type": "markdown",
|
14 |
+
"metadata": {
|
15 |
+
"heading_collapsed": true,
|
16 |
+
"id": "62A6F_072Fwq"
|
17 |
+
},
|
18 |
+
"source": [
|
19 |
+
"## Install Dependencies"
|
20 |
+
]
|
21 |
+
},
|
22 |
+
{
|
23 |
+
"cell_type": "code",
|
24 |
+
"execution_count": null,
|
25 |
+
"metadata": {
|
26 |
+
"hidden": true,
|
27 |
+
"id": "5w5AkskZ2Fwr"
|
28 |
+
},
|
29 |
+
"outputs": [],
|
30 |
+
"source": [
|
31 |
+
"#@title Install and Import Dependencies\n",
|
32 |
+
"\n",
|
33 |
+
"# this assumes that you have a relevant version of PyTorch installed\n",
|
34 |
+
"!pip install -q torchaudio\n",
|
35 |
+
"\n",
|
36 |
+
"SAMPLING_RATE = 16000\n",
|
37 |
+
"\n",
|
38 |
+
"import torch\n",
|
39 |
+
"torch.set_num_threads(1)\n",
|
40 |
+
"\n",
|
41 |
+
"from IPython.display import Audio\n",
|
42 |
+
"from pprint import pprint\n",
|
43 |
+
"# download example\n",
|
44 |
+
"torch.hub.download_url_to_file('https://models.silero.ai/vad_models/en.wav', 'en_example.wav')"
|
45 |
+
]
|
46 |
+
},
|
47 |
+
{
|
48 |
+
"cell_type": "code",
|
49 |
+
"execution_count": null,
|
50 |
+
"metadata": {
|
51 |
+
"id": "pSifus5IilRp"
|
52 |
+
},
|
53 |
+
"outputs": [],
|
54 |
+
"source": [
|
55 |
+
"USE_ONNX = False # change this to True if you want to test onnx model\n",
|
56 |
+
"if USE_ONNX:\n",
|
57 |
+
" !pip install -q onnxruntime\n",
|
58 |
+
" \n",
|
59 |
+
"model, utils = torch.hub.load(repo_or_dir='snakers4/silero-vad',\n",
|
60 |
+
" model='silero_vad',\n",
|
61 |
+
" force_reload=True,\n",
|
62 |
+
" onnx=USE_ONNX)\n",
|
63 |
+
"\n",
|
64 |
+
"(get_speech_timestamps,\n",
|
65 |
+
" save_audio,\n",
|
66 |
+
" read_audio,\n",
|
67 |
+
" VADIterator,\n",
|
68 |
+
" collect_chunks) = utils"
|
69 |
+
]
|
70 |
+
},
|
71 |
+
{
|
72 |
+
"cell_type": "markdown",
|
73 |
+
"metadata": {
|
74 |
+
"id": "fXbbaUO3jsrw"
|
75 |
+
},
|
76 |
+
"source": [
|
77 |
+
"## Full Audio"
|
78 |
+
]
|
79 |
+
},
|
80 |
+
{
|
81 |
+
"cell_type": "markdown",
|
82 |
+
"metadata": {
|
83 |
+
"id": "RAfJPb_a-Auj"
|
84 |
+
},
|
85 |
+
"source": [
|
86 |
+
"**Speech timestapms from full audio**"
|
87 |
+
]
|
88 |
+
},
|
89 |
+
{
|
90 |
+
"cell_type": "code",
|
91 |
+
"execution_count": null,
|
92 |
+
"metadata": {
|
93 |
+
"id": "aI_eydBPjsrx"
|
94 |
+
},
|
95 |
+
"outputs": [],
|
96 |
+
"source": [
|
97 |
+
"wav = read_audio('en_example.wav', sampling_rate=SAMPLING_RATE)\n",
|
98 |
+
"# get speech timestamps from full audio file\n",
|
99 |
+
"speech_timestamps = get_speech_timestamps(wav, model, sampling_rate=SAMPLING_RATE)\n",
|
100 |
+
"pprint(speech_timestamps)"
|
101 |
+
]
|
102 |
+
},
|
103 |
+
{
|
104 |
+
"cell_type": "code",
|
105 |
+
"execution_count": null,
|
106 |
+
"metadata": {
|
107 |
+
"id": "OuEobLchjsry"
|
108 |
+
},
|
109 |
+
"outputs": [],
|
110 |
+
"source": [
|
111 |
+
"# merge all speech chunks to one audio\n",
|
112 |
+
"save_audio('only_speech.wav',\n",
|
113 |
+
" collect_chunks(speech_timestamps, wav), sampling_rate=SAMPLING_RATE) \n",
|
114 |
+
"Audio('only_speech.wav')"
|
115 |
+
]
|
116 |
+
},
|
117 |
+
{
|
118 |
+
"cell_type": "markdown",
|
119 |
+
"metadata": {
|
120 |
+
"id": "iDKQbVr8jsry"
|
121 |
+
},
|
122 |
+
"source": [
|
123 |
+
"## Stream imitation example"
|
124 |
+
]
|
125 |
+
},
|
126 |
+
{
|
127 |
+
"cell_type": "code",
|
128 |
+
"execution_count": null,
|
129 |
+
"metadata": {
|
130 |
+
"id": "q-lql_2Wjsry"
|
131 |
+
},
|
132 |
+
"outputs": [],
|
133 |
+
"source": [
|
134 |
+
"## using VADIterator class\n",
|
135 |
+
"\n",
|
136 |
+
"vad_iterator = VADIterator(model)\n",
|
137 |
+
"wav = read_audio(f'en_example.wav', sampling_rate=SAMPLING_RATE)\n",
|
138 |
+
"\n",
|
139 |
+
"window_size_samples = 1536 # number of samples in a single audio chunk\n",
|
140 |
+
"for i in range(0, len(wav), window_size_samples):\n",
|
141 |
+
" chunk = wav[i: i+ window_size_samples]\n",
|
142 |
+
" if len(chunk) < window_size_samples:\n",
|
143 |
+
" break\n",
|
144 |
+
" speech_dict = vad_iterator(chunk, return_seconds=True)\n",
|
145 |
+
" if speech_dict:\n",
|
146 |
+
" print(speech_dict, end=' ')\n",
|
147 |
+
"vad_iterator.reset_states() # reset model states after each audio"
|
148 |
+
]
|
149 |
+
},
|
150 |
+
{
|
151 |
+
"cell_type": "code",
|
152 |
+
"execution_count": null,
|
153 |
+
"metadata": {
|
154 |
+
"id": "BX3UgwwB2Fwv"
|
155 |
+
},
|
156 |
+
"outputs": [],
|
157 |
+
"source": [
|
158 |
+
"## just probabilities\n",
|
159 |
+
"\n",
|
160 |
+
"wav = read_audio('en_example.wav', sampling_rate=SAMPLING_RATE)\n",
|
161 |
+
"speech_probs = []\n",
|
162 |
+
"window_size_samples = 1536\n",
|
163 |
+
"for i in range(0, len(wav), window_size_samples):\n",
|
164 |
+
" chunk = wav[i: i+ window_size_samples]\n",
|
165 |
+
" if len(chunk) < window_size_samples:\n",
|
166 |
+
" break\n",
|
167 |
+
" speech_prob = model(chunk, SAMPLING_RATE).item()\n",
|
168 |
+
" speech_probs.append(speech_prob)\n",
|
169 |
+
"vad_iterator.reset_states() # reset model states after each audio\n",
|
170 |
+
"\n",
|
171 |
+
"print(speech_probs[:10]) # first 10 chunks predicts"
|
172 |
+
]
|
173 |
+
},
|
174 |
+
{
|
175 |
+
"cell_type": "markdown",
|
176 |
+
"metadata": {
|
177 |
+
"heading_collapsed": true,
|
178 |
+
"id": "36jY0niD2Fww"
|
179 |
+
},
|
180 |
+
"source": [
|
181 |
+
"# Number detector"
|
182 |
+
]
|
183 |
+
},
|
184 |
+
{
|
185 |
+
"cell_type": "markdown",
|
186 |
+
"metadata": {
|
187 |
+
"heading_collapsed": true,
|
188 |
+
"hidden": true,
|
189 |
+
"id": "scd1DlS42Fwx"
|
190 |
+
},
|
191 |
+
"source": [
|
192 |
+
"## Install Dependencies"
|
193 |
+
]
|
194 |
+
},
|
195 |
+
{
|
196 |
+
"cell_type": "code",
|
197 |
+
"execution_count": null,
|
198 |
+
"metadata": {
|
199 |
+
"hidden": true,
|
200 |
+
"id": "Kq5gQuYq2Fwx"
|
201 |
+
},
|
202 |
+
"outputs": [],
|
203 |
+
"source": [
|
204 |
+
"#@title Install and Import Dependencies\n",
|
205 |
+
"\n",
|
206 |
+
"# this assumes that you have a relevant version of PyTorch installed\n",
|
207 |
+
"!pip install -q torchaudio\n",
|
208 |
+
"\n",
|
209 |
+
"SAMPLING_RATE = 16000\n",
|
210 |
+
"\n",
|
211 |
+
"import torch\n",
|
212 |
+
"torch.set_num_threads(1)\n",
|
213 |
+
"\n",
|
214 |
+
"from IPython.display import Audio\n",
|
215 |
+
"from pprint import pprint\n",
|
216 |
+
"# download example\n",
|
217 |
+
"torch.hub.download_url_to_file('https://models.silero.ai/vad_models/en_num.wav', 'en_number_example.wav')"
|
218 |
+
]
|
219 |
+
},
|
220 |
+
{
|
221 |
+
"cell_type": "code",
|
222 |
+
"execution_count": null,
|
223 |
+
"metadata": {
|
224 |
+
"id": "dPwCFHmFycUF"
|
225 |
+
},
|
226 |
+
"outputs": [],
|
227 |
+
"source": [
|
228 |
+
"USE_ONNX = False # change this to True if you want to test onnx model\n",
|
229 |
+
"if USE_ONNX:\n",
|
230 |
+
" !pip install -q onnxruntime\n",
|
231 |
+
" \n",
|
232 |
+
"model, utils = torch.hub.load(repo_or_dir='snakers4/silero-vad',\n",
|
233 |
+
" model='silero_number_detector',\n",
|
234 |
+
" force_reload=True,\n",
|
235 |
+
" onnx=USE_ONNX)\n",
|
236 |
+
"\n",
|
237 |
+
"(get_number_ts,\n",
|
238 |
+
" save_audio,\n",
|
239 |
+
" read_audio,\n",
|
240 |
+
" collect_chunks,\n",
|
241 |
+
" drop_chunks) = utils\n"
|
242 |
+
]
|
243 |
+
},
|
244 |
+
{
|
245 |
+
"cell_type": "markdown",
|
246 |
+
"metadata": {
|
247 |
+
"heading_collapsed": true,
|
248 |
+
"hidden": true,
|
249 |
+
"id": "qhPa30ij2Fwy"
|
250 |
+
},
|
251 |
+
"source": [
|
252 |
+
"## Full audio"
|
253 |
+
]
|
254 |
+
},
|
255 |
+
{
|
256 |
+
"cell_type": "code",
|
257 |
+
"execution_count": null,
|
258 |
+
"metadata": {
|
259 |
+
"hidden": true,
|
260 |
+
"id": "EXpau6xq2Fwy"
|
261 |
+
},
|
262 |
+
"outputs": [],
|
263 |
+
"source": [
|
264 |
+
"wav = read_audio('en_number_example.wav', sampling_rate=SAMPLING_RATE)\n",
|
265 |
+
"# get number timestamps from full audio file\n",
|
266 |
+
"number_timestamps = get_number_ts(wav, model)\n",
|
267 |
+
"pprint(number_timestamps)"
|
268 |
+
]
|
269 |
+
},
|
270 |
+
{
|
271 |
+
"cell_type": "code",
|
272 |
+
"execution_count": null,
|
273 |
+
"metadata": {
|
274 |
+
"hidden": true,
|
275 |
+
"id": "u-KfXRhZ2Fwy"
|
276 |
+
},
|
277 |
+
"outputs": [],
|
278 |
+
"source": [
|
279 |
+
"# convert ms in timestamps to samples\n",
|
280 |
+
"for timestamp in number_timestamps:\n",
|
281 |
+
" timestamp['start'] = int(timestamp['start'] * SAMPLING_RATE / 1000)\n",
|
282 |
+
" timestamp['end'] = int(timestamp['end'] * SAMPLING_RATE / 1000)"
|
283 |
+
]
|
284 |
+
},
|
285 |
+
{
|
286 |
+
"cell_type": "code",
|
287 |
+
"execution_count": null,
|
288 |
+
"metadata": {
|
289 |
+
"hidden": true,
|
290 |
+
"id": "iwYEC4aZ2Fwy"
|
291 |
+
},
|
292 |
+
"outputs": [],
|
293 |
+
"source": [
|
294 |
+
"# merge all number chunks to one audio\n",
|
295 |
+
"save_audio('only_numbers.wav',\n",
|
296 |
+
" collect_chunks(number_timestamps, wav), SAMPLING_RATE) \n",
|
297 |
+
"Audio('only_numbers.wav')"
|
298 |
+
]
|
299 |
+
},
|
300 |
+
{
|
301 |
+
"cell_type": "code",
|
302 |
+
"execution_count": null,
|
303 |
+
"metadata": {
|
304 |
+
"hidden": true,
|
305 |
+
"id": "fHaYejX12Fwy"
|
306 |
+
},
|
307 |
+
"outputs": [],
|
308 |
+
"source": [
|
309 |
+
"# drop all number chunks from audio\n",
|
310 |
+
"save_audio('no_numbers.wav',\n",
|
311 |
+
" drop_chunks(number_timestamps, wav), SAMPLING_RATE) \n",
|
312 |
+
"Audio('no_numbers.wav')"
|
313 |
+
]
|
314 |
+
},
|
315 |
+
{
|
316 |
+
"cell_type": "markdown",
|
317 |
+
"metadata": {
|
318 |
+
"heading_collapsed": true,
|
319 |
+
"id": "PnKtJKbq2Fwz"
|
320 |
+
},
|
321 |
+
"source": [
|
322 |
+
"# Language detector"
|
323 |
+
]
|
324 |
+
},
|
325 |
+
{
|
326 |
+
"cell_type": "markdown",
|
327 |
+
"metadata": {
|
328 |
+
"heading_collapsed": true,
|
329 |
+
"hidden": true,
|
330 |
+
"id": "F5cAmMbP2Fwz"
|
331 |
+
},
|
332 |
+
"source": [
|
333 |
+
"## Install Dependencies"
|
334 |
+
]
|
335 |
+
},
|
336 |
+
{
|
337 |
+
"cell_type": "code",
|
338 |
+
"execution_count": null,
|
339 |
+
"metadata": {
|
340 |
+
"hidden": true,
|
341 |
+
"id": "Zu9D0t6n2Fwz"
|
342 |
+
},
|
343 |
+
"outputs": [],
|
344 |
+
"source": [
|
345 |
+
"#@title Install and Import Dependencies\n",
|
346 |
+
"\n",
|
347 |
+
"# this assumes that you have a relevant version of PyTorch installed\n",
|
348 |
+
"!pip install -q torchaudio\n",
|
349 |
+
"\n",
|
350 |
+
"SAMPLING_RATE = 16000\n",
|
351 |
+
"\n",
|
352 |
+
"import torch\n",
|
353 |
+
"torch.set_num_threads(1)\n",
|
354 |
+
"\n",
|
355 |
+
"from IPython.display import Audio\n",
|
356 |
+
"from pprint import pprint\n",
|
357 |
+
"# download example\n",
|
358 |
+
"torch.hub.download_url_to_file('https://models.silero.ai/vad_models/en.wav', 'en_example.wav')"
|
359 |
+
]
|
360 |
+
},
|
361 |
+
{
|
362 |
+
"cell_type": "code",
|
363 |
+
"execution_count": null,
|
364 |
+
"metadata": {
|
365 |
+
"id": "JfRKDZiRztFe"
|
366 |
+
},
|
367 |
+
"outputs": [],
|
368 |
+
"source": [
|
369 |
+
"USE_ONNX = False # change this to True if you want to test onnx model\n",
|
370 |
+
"if USE_ONNX:\n",
|
371 |
+
" !pip install -q onnxruntime\n",
|
372 |
+
" \n",
|
373 |
+
"model, utils = torch.hub.load(repo_or_dir='snakers4/silero-vad',\n",
|
374 |
+
" model='silero_lang_detector',\n",
|
375 |
+
" force_reload=True,\n",
|
376 |
+
" onnx=USE_ONNX)\n",
|
377 |
+
"\n",
|
378 |
+
"get_language, read_audio = utils"
|
379 |
+
]
|
380 |
+
},
|
381 |
+
{
|
382 |
+
"cell_type": "markdown",
|
383 |
+
"metadata": {
|
384 |
+
"heading_collapsed": true,
|
385 |
+
"hidden": true,
|
386 |
+
"id": "iC696eMX2Fwz"
|
387 |
+
},
|
388 |
+
"source": [
|
389 |
+
"## Full audio"
|
390 |
+
]
|
391 |
+
},
|
392 |
+
{
|
393 |
+
"cell_type": "code",
|
394 |
+
"execution_count": null,
|
395 |
+
"metadata": {
|
396 |
+
"hidden": true,
|
397 |
+
"id": "c8UYnYBF2Fw0"
|
398 |
+
},
|
399 |
+
"outputs": [],
|
400 |
+
"source": [
|
401 |
+
"wav = read_audio('en_example.wav', sampling_rate=SAMPLING_RATE)\n",
|
402 |
+
"lang = get_language(wav, model)\n",
|
403 |
+
"print(lang)"
|
404 |
+
]
|
405 |
+
}
|
406 |
+
],
|
407 |
+
"metadata": {
|
408 |
+
"colab": {
|
409 |
+
"name": "silero-vad.ipynb",
|
410 |
+
"provenance": []
|
411 |
+
},
|
412 |
+
"kernelspec": {
|
413 |
+
"display_name": "Python 3",
|
414 |
+
"language": "python",
|
415 |
+
"name": "python3"
|
416 |
+
},
|
417 |
+
"language_info": {
|
418 |
+
"codemirror_mode": {
|
419 |
+
"name": "ipython",
|
420 |
+
"version": 3
|
421 |
+
},
|
422 |
+
"file_extension": ".py",
|
423 |
+
"mimetype": "text/x-python",
|
424 |
+
"name": "python",
|
425 |
+
"nbconvert_exporter": "python",
|
426 |
+
"pygments_lexer": "ipython3",
|
427 |
+
"version": "3.8.8"
|
428 |
+
},
|
429 |
+
"toc": {
|
430 |
+
"base_numbering": 1,
|
431 |
+
"nav_menu": {},
|
432 |
+
"number_sections": true,
|
433 |
+
"sideBar": true,
|
434 |
+
"skip_h1_title": false,
|
435 |
+
"title_cell": "Table of Contents",
|
436 |
+
"title_sidebar": "Contents",
|
437 |
+
"toc_cell": false,
|
438 |
+
"toc_position": {},
|
439 |
+
"toc_section_display": true,
|
440 |
+
"toc_window_display": false
|
441 |
+
}
|
442 |
+
},
|
443 |
+
"nbformat": 4,
|
444 |
+
"nbformat_minor": 0
|
445 |
+
}
|
hub/snakers4_silero-vad_master/utils_vad.py
ADDED
@@ -0,0 +1,488 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import torch
|
2 |
+
import torchaudio
|
3 |
+
from typing import List
|
4 |
+
import torch.nn.functional as F
|
5 |
+
import warnings
|
6 |
+
|
7 |
+
languages = ['ru', 'en', 'de', 'es']
|
8 |
+
|
9 |
+
|
10 |
+
class OnnxWrapper():
|
11 |
+
|
12 |
+
def __init__(self, path, force_onnx_cpu=False):
|
13 |
+
import numpy as np
|
14 |
+
global np
|
15 |
+
import onnxruntime
|
16 |
+
if force_onnx_cpu and 'CPUExecutionProvider' in onnxruntime.get_available_providers():
|
17 |
+
self.session = onnxruntime.InferenceSession(path, providers=['CPUExecutionProvider'])
|
18 |
+
else:
|
19 |
+
self.session = onnxruntime.InferenceSession(path)
|
20 |
+
self.session.intra_op_num_threads = 1
|
21 |
+
self.session.inter_op_num_threads = 1
|
22 |
+
|
23 |
+
self.reset_states()
|
24 |
+
self.sample_rates = [8000, 16000]
|
25 |
+
|
26 |
+
def _validate_input(self, x, sr: int):
|
27 |
+
if x.dim() == 1:
|
28 |
+
x = x.unsqueeze(0)
|
29 |
+
if x.dim() > 2:
|
30 |
+
raise ValueError(f"Too many dimensions for input audio chunk {x.dim()}")
|
31 |
+
|
32 |
+
if sr != 16000 and (sr % 16000 == 0):
|
33 |
+
step = sr // 16000
|
34 |
+
x = x[::step]
|
35 |
+
sr = 16000
|
36 |
+
|
37 |
+
if sr not in self.sample_rates:
|
38 |
+
raise ValueError(f"Supported sampling rates: {self.sample_rates} (or multiply of 16000)")
|
39 |
+
|
40 |
+
if sr / x.shape[1] > 31.25:
|
41 |
+
raise ValueError("Input audio chunk is too short")
|
42 |
+
|
43 |
+
return x, sr
|
44 |
+
|
45 |
+
def reset_states(self, batch_size=1):
|
46 |
+
self._h = np.zeros((2, batch_size, 64)).astype('float32')
|
47 |
+
self._c = np.zeros((2, batch_size, 64)).astype('float32')
|
48 |
+
self._last_sr = 0
|
49 |
+
self._last_batch_size = 0
|
50 |
+
|
51 |
+
def __call__(self, x, sr: int):
|
52 |
+
|
53 |
+
x, sr = self._validate_input(x, sr)
|
54 |
+
batch_size = x.shape[0]
|
55 |
+
|
56 |
+
if not self._last_batch_size:
|
57 |
+
self.reset_states(batch_size)
|
58 |
+
if (self._last_sr) and (self._last_sr != sr):
|
59 |
+
self.reset_states(batch_size)
|
60 |
+
if (self._last_batch_size) and (self._last_batch_size != batch_size):
|
61 |
+
self.reset_states(batch_size)
|
62 |
+
|
63 |
+
if sr in [8000, 16000]:
|
64 |
+
ort_inputs = {'input': x.numpy(), 'h': self._h, 'c': self._c, 'sr': np.array(sr)}
|
65 |
+
ort_outs = self.session.run(None, ort_inputs)
|
66 |
+
out, self._h, self._c = ort_outs
|
67 |
+
else:
|
68 |
+
raise ValueError()
|
69 |
+
|
70 |
+
self._last_sr = sr
|
71 |
+
self._last_batch_size = batch_size
|
72 |
+
|
73 |
+
out = torch.tensor(out)
|
74 |
+
return out
|
75 |
+
|
76 |
+
def audio_forward(self, x, sr: int, num_samples: int = 512):
|
77 |
+
outs = []
|
78 |
+
x, sr = self._validate_input(x, sr)
|
79 |
+
|
80 |
+
if x.shape[1] % num_samples:
|
81 |
+
pad_num = num_samples - (x.shape[1] % num_samples)
|
82 |
+
x = torch.nn.functional.pad(x, (0, pad_num), 'constant', value=0.0)
|
83 |
+
|
84 |
+
self.reset_states(x.shape[0])
|
85 |
+
for i in range(0, x.shape[1], num_samples):
|
86 |
+
wavs_batch = x[:, i:i+num_samples]
|
87 |
+
out_chunk = self.__call__(wavs_batch, sr)
|
88 |
+
outs.append(out_chunk)
|
89 |
+
|
90 |
+
stacked = torch.cat(outs, dim=1)
|
91 |
+
return stacked.cpu()
|
92 |
+
|
93 |
+
|
94 |
+
class Validator():
|
95 |
+
def __init__(self, url, force_onnx_cpu):
|
96 |
+
self.onnx = True if url.endswith('.onnx') else False
|
97 |
+
torch.hub.download_url_to_file(url, 'inf.model')
|
98 |
+
if self.onnx:
|
99 |
+
import onnxruntime
|
100 |
+
if force_onnx_cpu and 'CPUExecutionProvider' in onnxruntime.get_available_providers():
|
101 |
+
self.model = onnxruntime.InferenceSession('inf.model', providers=['CPUExecutionProvider'])
|
102 |
+
else:
|
103 |
+
self.model = onnxruntime.InferenceSession('inf.model')
|
104 |
+
else:
|
105 |
+
self.model = init_jit_model(model_path='inf.model')
|
106 |
+
|
107 |
+
def __call__(self, inputs: torch.Tensor):
|
108 |
+
with torch.no_grad():
|
109 |
+
if self.onnx:
|
110 |
+
ort_inputs = {'input': inputs.cpu().numpy()}
|
111 |
+
outs = self.model.run(None, ort_inputs)
|
112 |
+
outs = [torch.Tensor(x) for x in outs]
|
113 |
+
else:
|
114 |
+
outs = self.model(inputs)
|
115 |
+
|
116 |
+
return outs
|
117 |
+
|
118 |
+
|
119 |
+
def read_audio(path: str,
|
120 |
+
sampling_rate: int = 16000):
|
121 |
+
|
122 |
+
wav, sr = torchaudio.load(path)
|
123 |
+
|
124 |
+
if wav.size(0) > 1:
|
125 |
+
wav = wav.mean(dim=0, keepdim=True)
|
126 |
+
|
127 |
+
if sr != sampling_rate:
|
128 |
+
transform = torchaudio.transforms.Resample(orig_freq=sr,
|
129 |
+
new_freq=sampling_rate)
|
130 |
+
wav = transform(wav)
|
131 |
+
sr = sampling_rate
|
132 |
+
|
133 |
+
assert sr == sampling_rate
|
134 |
+
return wav.squeeze(0)
|
135 |
+
|
136 |
+
|
137 |
+
def save_audio(path: str,
|
138 |
+
tensor: torch.Tensor,
|
139 |
+
sampling_rate: int = 16000):
|
140 |
+
torchaudio.save(path, tensor.unsqueeze(0), sampling_rate)
|
141 |
+
|
142 |
+
|
143 |
+
def init_jit_model(model_path: str,
|
144 |
+
device=torch.device('cpu')):
|
145 |
+
torch.set_grad_enabled(False)
|
146 |
+
model = torch.jit.load(model_path, map_location=device)
|
147 |
+
model.eval()
|
148 |
+
return model
|
149 |
+
|
150 |
+
|
151 |
+
def make_visualization(probs, step):
|
152 |
+
import pandas as pd
|
153 |
+
pd.DataFrame({'probs': probs},
|
154 |
+
index=[x * step for x in range(len(probs))]).plot(figsize=(16, 8),
|
155 |
+
kind='area', ylim=[0, 1.05], xlim=[0, len(probs) * step],
|
156 |
+
xlabel='seconds',
|
157 |
+
ylabel='speech probability',
|
158 |
+
colormap='tab20')
|
159 |
+
|
160 |
+
|
161 |
+
def get_speech_timestamps(audio: torch.Tensor,
|
162 |
+
model,
|
163 |
+
threshold: float = 0.5,
|
164 |
+
sampling_rate: int = 16000,
|
165 |
+
min_speech_duration_ms: int = 250,
|
166 |
+
min_silence_duration_ms: int = 100,
|
167 |
+
window_size_samples: int = 512,
|
168 |
+
speech_pad_ms: int = 30,
|
169 |
+
return_seconds: bool = False,
|
170 |
+
visualize_probs: bool = False):
|
171 |
+
|
172 |
+
"""
|
173 |
+
This method is used for splitting long audios into speech chunks using silero VAD
|
174 |
+
|
175 |
+
Parameters
|
176 |
+
----------
|
177 |
+
audio: torch.Tensor, one dimensional
|
178 |
+
One dimensional float torch.Tensor, other types are casted to torch if possible
|
179 |
+
|
180 |
+
model: preloaded .jit silero VAD model
|
181 |
+
|
182 |
+
threshold: float (default - 0.5)
|
183 |
+
Speech threshold. Silero VAD outputs speech probabilities for each audio chunk, probabilities ABOVE this value are considered as SPEECH.
|
184 |
+
It is better to tune this parameter for each dataset separately, but "lazy" 0.5 is pretty good for most datasets.
|
185 |
+
|
186 |
+
sampling_rate: int (default - 16000)
|
187 |
+
Currently silero VAD models support 8000 and 16000 sample rates
|
188 |
+
|
189 |
+
min_speech_duration_ms: int (default - 250 milliseconds)
|
190 |
+
Final speech chunks shorter min_speech_duration_ms are thrown out
|
191 |
+
|
192 |
+
min_silence_duration_ms: int (default - 100 milliseconds)
|
193 |
+
In the end of each speech chunk wait for min_silence_duration_ms before separating it
|
194 |
+
|
195 |
+
window_size_samples: int (default - 1536 samples)
|
196 |
+
Audio chunks of window_size_samples size are fed to the silero VAD model.
|
197 |
+
WARNING! Silero VAD models were trained using 512, 1024, 1536 samples for 16000 sample rate and 256, 512, 768 samples for 8000 sample rate.
|
198 |
+
Values other than these may affect model perfomance!!
|
199 |
+
|
200 |
+
speech_pad_ms: int (default - 30 milliseconds)
|
201 |
+
Final speech chunks are padded by speech_pad_ms each side
|
202 |
+
|
203 |
+
return_seconds: bool (default - False)
|
204 |
+
whether return timestamps in seconds (default - samples)
|
205 |
+
|
206 |
+
visualize_probs: bool (default - False)
|
207 |
+
whether draw prob hist or not
|
208 |
+
|
209 |
+
Returns
|
210 |
+
----------
|
211 |
+
speeches: list of dicts
|
212 |
+
list containing ends and beginnings of speech chunks (samples or seconds based on return_seconds)
|
213 |
+
"""
|
214 |
+
|
215 |
+
if not torch.is_tensor(audio):
|
216 |
+
try:
|
217 |
+
audio = torch.Tensor(audio)
|
218 |
+
except:
|
219 |
+
raise TypeError("Audio cannot be casted to tensor. Cast it manually")
|
220 |
+
|
221 |
+
if len(audio.shape) > 1:
|
222 |
+
for i in range(len(audio.shape)): # trying to squeeze empty dimensions
|
223 |
+
audio = audio.squeeze(0)
|
224 |
+
if len(audio.shape) > 1:
|
225 |
+
raise ValueError("More than one dimension in audio. Are you trying to process audio with 2 channels?")
|
226 |
+
|
227 |
+
if sampling_rate > 16000 and (sampling_rate % 16000 == 0):
|
228 |
+
step = sampling_rate // 16000
|
229 |
+
sampling_rate = 16000
|
230 |
+
audio = audio[::step]
|
231 |
+
warnings.warn('Sampling rate is a multiply of 16000, casting to 16000 manually!')
|
232 |
+
else:
|
233 |
+
step = 1
|
234 |
+
|
235 |
+
if sampling_rate == 8000 and window_size_samples > 768:
|
236 |
+
warnings.warn('window_size_samples is too big for 8000 sampling_rate! Better set window_size_samples to 256, 512 or 768 for 8000 sample rate!')
|
237 |
+
if window_size_samples not in [256, 512, 768, 1024, 1536]:
|
238 |
+
warnings.warn('Unusual window_size_samples! Supported window_size_samples:\n - [512, 1024, 1536] for 16000 sampling_rate\n - [256, 512, 768] for 8000 sampling_rate')
|
239 |
+
|
240 |
+
model.reset_states()
|
241 |
+
min_speech_samples = sampling_rate * min_speech_duration_ms / 1000
|
242 |
+
min_silence_samples = sampling_rate * min_silence_duration_ms / 1000
|
243 |
+
speech_pad_samples = sampling_rate * speech_pad_ms / 1000
|
244 |
+
|
245 |
+
audio_length_samples = len(audio)
|
246 |
+
|
247 |
+
speech_probs = []
|
248 |
+
for current_start_sample in range(0, audio_length_samples, window_size_samples):
|
249 |
+
chunk = audio[current_start_sample: current_start_sample + window_size_samples]
|
250 |
+
if len(chunk) < window_size_samples:
|
251 |
+
chunk = torch.nn.functional.pad(chunk, (0, int(window_size_samples - len(chunk))))
|
252 |
+
speech_prob = model(chunk, sampling_rate).item()
|
253 |
+
speech_probs.append(speech_prob)
|
254 |
+
|
255 |
+
triggered = False
|
256 |
+
speeches = []
|
257 |
+
current_speech = {}
|
258 |
+
neg_threshold = threshold - 0.15
|
259 |
+
temp_end = 0
|
260 |
+
|
261 |
+
for i, speech_prob in enumerate(speech_probs):
|
262 |
+
if (speech_prob >= threshold) and temp_end:
|
263 |
+
temp_end = 0
|
264 |
+
|
265 |
+
if (speech_prob >= threshold) and not triggered:
|
266 |
+
triggered = True
|
267 |
+
current_speech['start'] = window_size_samples * i
|
268 |
+
continue
|
269 |
+
|
270 |
+
if (speech_prob < neg_threshold) and triggered:
|
271 |
+
if not temp_end:
|
272 |
+
temp_end = window_size_samples * i
|
273 |
+
if (window_size_samples * i) - temp_end < min_silence_samples:
|
274 |
+
continue
|
275 |
+
else:
|
276 |
+
current_speech['end'] = temp_end
|
277 |
+
if (current_speech['end'] - current_speech['start']) > min_speech_samples:
|
278 |
+
speeches.append(current_speech)
|
279 |
+
temp_end = 0
|
280 |
+
current_speech = {}
|
281 |
+
triggered = False
|
282 |
+
continue
|
283 |
+
|
284 |
+
if current_speech and (audio_length_samples - current_speech['start']) > min_speech_samples:
|
285 |
+
current_speech['end'] = audio_length_samples
|
286 |
+
speeches.append(current_speech)
|
287 |
+
|
288 |
+
for i, speech in enumerate(speeches):
|
289 |
+
if i == 0:
|
290 |
+
speech['start'] = int(max(0, speech['start'] - speech_pad_samples))
|
291 |
+
if i != len(speeches) - 1:
|
292 |
+
silence_duration = speeches[i+1]['start'] - speech['end']
|
293 |
+
if silence_duration < 2 * speech_pad_samples:
|
294 |
+
speech['end'] += int(silence_duration // 2)
|
295 |
+
speeches[i+1]['start'] = int(max(0, speeches[i+1]['start'] - silence_duration // 2))
|
296 |
+
else:
|
297 |
+
speech['end'] = int(min(audio_length_samples, speech['end'] + speech_pad_samples))
|
298 |
+
speeches[i+1]['start'] = int(max(0, speeches[i+1]['start'] - speech_pad_samples))
|
299 |
+
else:
|
300 |
+
speech['end'] = int(min(audio_length_samples, speech['end'] + speech_pad_samples))
|
301 |
+
|
302 |
+
if return_seconds:
|
303 |
+
for speech_dict in speeches:
|
304 |
+
speech_dict['start'] = round(speech_dict['start'] / sampling_rate, 1)
|
305 |
+
speech_dict['end'] = round(speech_dict['end'] / sampling_rate, 1)
|
306 |
+
elif step > 1:
|
307 |
+
for speech_dict in speeches:
|
308 |
+
speech_dict['start'] *= step
|
309 |
+
speech_dict['end'] *= step
|
310 |
+
|
311 |
+
if visualize_probs:
|
312 |
+
make_visualization(speech_probs, window_size_samples / sampling_rate)
|
313 |
+
|
314 |
+
return speeches
|
315 |
+
|
316 |
+
|
317 |
+
def get_number_ts(wav: torch.Tensor,
|
318 |
+
model,
|
319 |
+
model_stride=8,
|
320 |
+
hop_length=160,
|
321 |
+
sample_rate=16000):
|
322 |
+
wav = torch.unsqueeze(wav, dim=0)
|
323 |
+
perframe_logits = model(wav)[0]
|
324 |
+
perframe_preds = torch.argmax(torch.softmax(perframe_logits, dim=1), dim=1).squeeze() # (1, num_frames_strided)
|
325 |
+
extended_preds = []
|
326 |
+
for i in perframe_preds:
|
327 |
+
extended_preds.extend([i.item()] * model_stride)
|
328 |
+
# len(extended_preds) is *num_frames_real*; for each frame of audio we know if it has a number in it.
|
329 |
+
triggered = False
|
330 |
+
timings = []
|
331 |
+
cur_timing = {}
|
332 |
+
for i, pred in enumerate(extended_preds):
|
333 |
+
if pred == 1:
|
334 |
+
if not triggered:
|
335 |
+
cur_timing['start'] = int((i * hop_length) / (sample_rate / 1000))
|
336 |
+
triggered = True
|
337 |
+
elif pred == 0:
|
338 |
+
if triggered:
|
339 |
+
cur_timing['end'] = int((i * hop_length) / (sample_rate / 1000))
|
340 |
+
timings.append(cur_timing)
|
341 |
+
cur_timing = {}
|
342 |
+
triggered = False
|
343 |
+
if cur_timing:
|
344 |
+
cur_timing['end'] = int(len(wav) / (sample_rate / 1000))
|
345 |
+
timings.append(cur_timing)
|
346 |
+
return timings
|
347 |
+
|
348 |
+
|
349 |
+
def get_language(wav: torch.Tensor,
|
350 |
+
model):
|
351 |
+
wav = torch.unsqueeze(wav, dim=0)
|
352 |
+
lang_logits = model(wav)[2]
|
353 |
+
lang_pred = torch.argmax(torch.softmax(lang_logits, dim=1), dim=1).item() # from 0 to len(languages) - 1
|
354 |
+
assert lang_pred < len(languages)
|
355 |
+
return languages[lang_pred]
|
356 |
+
|
357 |
+
|
358 |
+
def get_language_and_group(wav: torch.Tensor,
|
359 |
+
model,
|
360 |
+
lang_dict: dict,
|
361 |
+
lang_group_dict: dict,
|
362 |
+
top_n=1):
|
363 |
+
wav = torch.unsqueeze(wav, dim=0)
|
364 |
+
lang_logits, lang_group_logits = model(wav)
|
365 |
+
|
366 |
+
softm = torch.softmax(lang_logits, dim=1).squeeze()
|
367 |
+
softm_group = torch.softmax(lang_group_logits, dim=1).squeeze()
|
368 |
+
|
369 |
+
srtd = torch.argsort(softm, descending=True)
|
370 |
+
srtd_group = torch.argsort(softm_group, descending=True)
|
371 |
+
|
372 |
+
outs = []
|
373 |
+
outs_group = []
|
374 |
+
for i in range(top_n):
|
375 |
+
prob = round(softm[srtd[i]].item(), 2)
|
376 |
+
prob_group = round(softm_group[srtd_group[i]].item(), 2)
|
377 |
+
outs.append((lang_dict[str(srtd[i].item())], prob))
|
378 |
+
outs_group.append((lang_group_dict[str(srtd_group[i].item())], prob_group))
|
379 |
+
|
380 |
+
return outs, outs_group
|
381 |
+
|
382 |
+
|
383 |
+
class VADIterator:
|
384 |
+
def __init__(self,
|
385 |
+
model,
|
386 |
+
threshold: float = 0.5,
|
387 |
+
sampling_rate: int = 16000,
|
388 |
+
min_silence_duration_ms: int = 100,
|
389 |
+
speech_pad_ms: int = 30
|
390 |
+
):
|
391 |
+
|
392 |
+
"""
|
393 |
+
Class for stream imitation
|
394 |
+
|
395 |
+
Parameters
|
396 |
+
----------
|
397 |
+
model: preloaded .jit silero VAD model
|
398 |
+
|
399 |
+
threshold: float (default - 0.5)
|
400 |
+
Speech threshold. Silero VAD outputs speech probabilities for each audio chunk, probabilities ABOVE this value are considered as SPEECH.
|
401 |
+
It is better to tune this parameter for each dataset separately, but "lazy" 0.5 is pretty good for most datasets.
|
402 |
+
|
403 |
+
sampling_rate: int (default - 16000)
|
404 |
+
Currently silero VAD models support 8000 and 16000 sample rates
|
405 |
+
|
406 |
+
min_silence_duration_ms: int (default - 100 milliseconds)
|
407 |
+
In the end of each speech chunk wait for min_silence_duration_ms before separating it
|
408 |
+
|
409 |
+
speech_pad_ms: int (default - 30 milliseconds)
|
410 |
+
Final speech chunks are padded by speech_pad_ms each side
|
411 |
+
"""
|
412 |
+
|
413 |
+
self.model = model
|
414 |
+
self.threshold = threshold
|
415 |
+
self.sampling_rate = sampling_rate
|
416 |
+
|
417 |
+
if sampling_rate not in [8000, 16000]:
|
418 |
+
raise ValueError('VADIterator does not support sampling rates other than [8000, 16000]')
|
419 |
+
|
420 |
+
self.min_silence_samples = sampling_rate * min_silence_duration_ms / 1000
|
421 |
+
self.speech_pad_samples = sampling_rate * speech_pad_ms / 1000
|
422 |
+
self.reset_states()
|
423 |
+
|
424 |
+
def reset_states(self):
|
425 |
+
|
426 |
+
self.model.reset_states()
|
427 |
+
self.triggered = False
|
428 |
+
self.temp_end = 0
|
429 |
+
self.current_sample = 0
|
430 |
+
|
431 |
+
def __call__(self, x, return_seconds=False):
|
432 |
+
"""
|
433 |
+
x: torch.Tensor
|
434 |
+
audio chunk (see examples in repo)
|
435 |
+
|
436 |
+
return_seconds: bool (default - False)
|
437 |
+
whether return timestamps in seconds (default - samples)
|
438 |
+
"""
|
439 |
+
|
440 |
+
if not torch.is_tensor(x):
|
441 |
+
try:
|
442 |
+
x = torch.Tensor(x)
|
443 |
+
except:
|
444 |
+
raise TypeError("Audio cannot be casted to tensor. Cast it manually")
|
445 |
+
|
446 |
+
window_size_samples = len(x[0]) if x.dim() == 2 else len(x)
|
447 |
+
self.current_sample += window_size_samples
|
448 |
+
|
449 |
+
speech_prob = self.model(x, self.sampling_rate).item()
|
450 |
+
|
451 |
+
if (speech_prob >= self.threshold) and self.temp_end:
|
452 |
+
self.temp_end = 0
|
453 |
+
|
454 |
+
if (speech_prob >= self.threshold) and not self.triggered:
|
455 |
+
self.triggered = True
|
456 |
+
speech_start = self.current_sample - self.speech_pad_samples
|
457 |
+
return {'start': int(speech_start) if not return_seconds else round(speech_start / self.sampling_rate, 1)}
|
458 |
+
|
459 |
+
if (speech_prob < self.threshold - 0.15) and self.triggered:
|
460 |
+
if not self.temp_end:
|
461 |
+
self.temp_end = self.current_sample
|
462 |
+
if self.current_sample - self.temp_end < self.min_silence_samples:
|
463 |
+
return None
|
464 |
+
else:
|
465 |
+
speech_end = self.temp_end + self.speech_pad_samples
|
466 |
+
self.temp_end = 0
|
467 |
+
self.triggered = False
|
468 |
+
return {'end': int(speech_end) if not return_seconds else round(speech_end / self.sampling_rate, 1)}
|
469 |
+
|
470 |
+
return None
|
471 |
+
|
472 |
+
|
473 |
+
def collect_chunks(tss: List[dict],
|
474 |
+
wav: torch.Tensor):
|
475 |
+
chunks = []
|
476 |
+
for i in tss:
|
477 |
+
chunks.append(wav[i['start']: i['end']])
|
478 |
+
return torch.cat(chunks)
|
479 |
+
|
480 |
+
|
481 |
+
def drop_chunks(tss: List[dict],
|
482 |
+
wav: torch.Tensor):
|
483 |
+
chunks = []
|
484 |
+
cur_start = 0
|
485 |
+
for i in tss:
|
486 |
+
chunks.append((wav[cur_start: i['start']]))
|
487 |
+
cur_start = i['end']
|
488 |
+
return torch.cat(chunks)
|