1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
use crate::morph::{morph_opening, morph_closing};
///
/// Voice activity as computed by either WebRtcVad or Silero
///
pub struct VoiceActivity {
pub data: Vec<bool>,
pub chunk_millis: usize
}
impl VoiceActivity {
///
/// Cleans voice-activity data (EXPERIMENTAL)
///
/// This operation successively employs the mathematical morphological 'erosion'
/// and 'dilation` operators to clean the output of the voice-activity detector.
/// The result is a clone of the original `VoiceActivity` instance having
/// cleaner/fewer timespans.
///
/// The `opening_radius` and `closing_radius` parameters represent the kernel radii
/// of the mathematical morphological operators. Each radius determines a window
/// of size `(2r+1)*CHUNK_MILLIS` milliseconds. Any errant spans smaller than this
/// window will be removed and any gaps larger than this window will be filled.
///
pub fn clean(self: &Self, opening_radius: usize, closing_radius: usize) -> Self {
// Clone voice-activity buffer and add padding
fn pad(data: &[bool], radius: usize) -> Vec<bool> {
let orig_len = data.len();
let clone_len = orig_len + radius * 2;
let mut clone = vec![false; clone_len];
clone.truncate(radius);
clone.extend_from_slice(&data);
clone.resize(clone_len, false);
clone
}
// Remove padding
fn unpad(data: &[bool], radius: usize) -> Vec<bool> {
data[radius .. data.len()-radius].to_vec()
}
let data = self.data.to_owned();
// Perform morphological 'closing' operation to fill gaps
let data = if opening_radius > 0 {
let opening_clone = pad(&data, opening_radius);
unpad(&morph_opening(&opening_clone, opening_radius), opening_radius)
} else {
data
};
// Perform morphological 'opening' operation to remove noise
let data = if closing_radius > 0 {
let closing_clone = pad(&data, closing_radius);
unpad(&morph_closing(&closing_clone, closing_radius), closing_radius)
} else {
data
};
VoiceActivity { data, chunk_millis: self.chunk_millis }
}
}