-
Notifications
You must be signed in to change notification settings - Fork 4
perf: optimize MlpgAdjust #116
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
5bad6db
9ec19d8
95d34f4
ba541c5
5f5a6d4
6e01bd6
692907a
61cce5d
5b3d9d1
51ba4d2
14e17f7
8b7efe3
6c4d16c
c6402d4
f3b0655
e32bc6f
cd499a9
8d818af
41debeb
55d2a5a
0571e5e
3ebd385
5af0a0e
7122aaa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ on: | |
| pull_request: | ||
| paths: | ||
| - src/vocoder/mlsa.rs | ||
| - src/mlpg_adjust/** | ||
|
|
||
| jobs: | ||
| collect: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,33 +2,55 @@ | |
| //! | ||
| //! The unvoiced frames are determined using multi-space probability distribution (MSD) parameter in stream. | ||
|
|
||
| use crate::model::StreamParameter; | ||
| use std::ops::Range; | ||
|
|
||
| use super::IterExt; | ||
| use crate::model::StreamParameter; | ||
|
|
||
| /// Mask for unvoiced frames | ||
| pub struct Mask(Vec<bool>); | ||
|
|
||
| impl FromIterator<bool> for Mask { | ||
| fn from_iter<I: IntoIterator<Item = bool>>(iter: I) -> Self { | ||
| Self(iter.into_iter().collect()) | ||
| } | ||
| pub struct Mask { | ||
| // matches the length of `durations` | ||
| pub(super) ranged_durations: Vec<Range<usize>>, | ||
| // contiguous ranges joined together | ||
| pub(super) voiced_ranges: Vec<Range<usize>>, | ||
| } | ||
|
Comment on lines
+10
to
15
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. もともと IterExt::duration で双方を引き伸ばして IterExt::filter_by で join していたのが非効率に思ったので,ここでいう ranged_durations や voiced_ranges に refactor しました.voiced_ranges はしばしば1つしかないので,その辺の SIMD 最適化を期待するものです. |
||
|
|
||
| impl Mask { | ||
| /// Create mask from `msd` field in stream with lengths of `durations`. | ||
| pub fn create(stream: &StreamParameter, threshold: f64, durations: &[usize]) -> Self { | ||
| Self( | ||
| stream | ||
| .iter() | ||
| .map(|(_, msd)| *msd > threshold) | ||
| .duration(durations) | ||
| .collect(), | ||
| ) | ||
| let mut i = 0; | ||
| let mut ranged_durations = Vec::with_capacity(durations.len()); | ||
| let mut voiced_ranges: Vec<Range<usize>> = Vec::new(); | ||
| for ((_, msd), duration) in stream.iter().zip(durations) { | ||
| let range = i..(i + duration); | ||
| if *msd > threshold { | ||
| if let Some(last) = voiced_ranges.last_mut() | ||
| && last.end == range.start | ||
| { | ||
| last.end = range.end; | ||
| } else { | ||
| voiced_ranges.push(range.clone()); | ||
| } | ||
| } | ||
| ranged_durations.push(range); | ||
| i += duration; | ||
| } | ||
| Self { | ||
| ranged_durations, | ||
| voiced_ranges, | ||
| } | ||
| } | ||
| pub fn len(&self) -> usize { | ||
| self.ranged_durations.last().map_or(0, |r| r.end) | ||
| } | ||
| /// Get the internal mask. | ||
| pub fn mask(&self) -> &[bool] { | ||
| &self.0 | ||
| pub fn voiced_len(&self) -> usize { | ||
| self.voiced_ranges.iter().map(|r| r.len()).sum() | ||
| } | ||
| fn mask(&self) -> Vec<bool> { | ||
| let mut out = vec![false; self.len()]; | ||
| for range in &self.voiced_ranges { | ||
| out[range.clone()].fill(true); | ||
| } | ||
| out | ||
| } | ||
| /// Fill back the masked region with `default` and returns an iterator of full-length sequence. | ||
| pub fn fill<'a, T: 'a + Clone>( | ||
|
|
@@ -37,124 +59,87 @@ impl Mask { | |
| default: T, | ||
| ) -> impl 'a + Iterator<Item = T> { | ||
| let mut iter = masked.into_iter(); | ||
| self.0.iter().map(move |&mask| { | ||
| if mask { | ||
| iter.next().expect( | ||
| "Length of `masked` must be the same as the number of `true`'s in mask.", | ||
| ) | ||
|
Comment on lines
-40
to
-44
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| self.mask().into_iter().map(move |is_voiced| { | ||
| if is_voiced { | ||
| iter.next().unwrap() | ||
| } else { | ||
| default.clone() | ||
| } | ||
| }) | ||
| } | ||
| /// Get distances from left- and right-boundaries. | ||
| pub fn boundary_distances(&self) -> Vec<(usize, usize)> { | ||
| if self.0.is_empty() { | ||
| return vec![]; | ||
| } | ||
| } | ||
|
|
||
| let mut result = vec![(0, 0); self.0.len()]; | ||
| #[derive(Debug)] | ||
| pub struct IntoIter<'a> { | ||
| ranged_durations: std::slice::Iter<'a, Range<usize>>, | ||
| voiced_ranges: std::slice::Iter<'a, Range<usize>>, | ||
| next_voiced_range: Option<&'a Range<usize>>, | ||
| } | ||
|
|
||
| let mut left = 0; | ||
| for (frame, mask) in self.0.iter().enumerate() { | ||
| if *mask { | ||
| result[frame].0 = frame - left; | ||
| } else { | ||
| // current position will be cut off | ||
| left = frame + 1; | ||
| } | ||
| impl<'a> IntoIter<'a> { | ||
| pub fn new(mask: &'a Mask) -> Self { | ||
| let ranged_durations = mask.ranged_durations.iter(); | ||
| let mut voiced_ranges = mask.voiced_ranges.iter(); | ||
| let next_voiced_range = voiced_ranges.next(); | ||
| Self { | ||
| ranged_durations, | ||
| voiced_ranges, | ||
| next_voiced_range, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| let mut right = self.0.len() - 1; | ||
| for (frame, mask) in self.0.iter().enumerate().rev() { | ||
| if *mask { | ||
| result[frame].1 = right - frame; | ||
| } else { | ||
| // current position will be cut off | ||
| if frame == 0 { | ||
| break; | ||
| } | ||
| right = frame - 1; | ||
| } | ||
| impl Iterator for IntoIter<'_> { | ||
| type Item = (Range<usize>, Option<Range<usize>>); | ||
|
|
||
| fn next(&mut self) -> Option<Self::Item> { | ||
| let range = self.ranged_durations.next()?; | ||
| let voiced_range = self | ||
| .next_voiced_range | ||
| .filter(|r| r.start <= range.start && range.end <= r.end); | ||
| if voiced_range.is_some_and(|r| r.end == range.end) { | ||
| self.next_voiced_range = self.voiced_ranges.next(); | ||
| } | ||
| Some((range.clone(), voiced_range.cloned())) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 今の range とそれを含む voiced_range という組み合わせは window が voiced_range に含まれるかによって分岐する create_parameters の要請です.下手に Iterator にするのではなく普通に専用メソッドにする説はあります. |
||
| } | ||
| } | ||
|
|
||
| impl<'a> IntoIterator for &'a Mask { | ||
| type Item = <IntoIter<'a> as Iterator>::Item; | ||
| type IntoIter = IntoIter<'a>; | ||
|
|
||
| result | ||
| fn into_iter(self) -> Self::IntoIter { | ||
| IntoIter::new(self) | ||
| } | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| #[allow( | ||
| clippy::single_range_in_vec_init, | ||
| reason = "intended vec of single range" | ||
| )] | ||
| mod tests { | ||
| use super::Mask; | ||
|
|
||
| #[test] | ||
| fn fill() { | ||
| assert_eq!( | ||
| Mask(vec![false, false, true, true, false, true]) | ||
| .fill([0, 1, 2], 5) | ||
| .collect::<Vec<_>>(), | ||
| Mask { | ||
| ranged_durations: vec![0..2, 2..4, 4..5, 5..6], | ||
| voiced_ranges: vec![2..4, 5..6] | ||
| } | ||
| .fill([0, 1, 2], 5) | ||
| .collect::<Vec<_>>(), | ||
| vec![5, 5, 0, 1, 5, 2] | ||
| ); | ||
| assert_eq!( | ||
| Mask(vec![false, false]).fill([0, 1], 5).collect::<Vec<_>>(), | ||
| Mask { | ||
| ranged_durations: vec![0..2], | ||
| voiced_ranges: vec![] | ||
| } | ||
| .fill([0, 1], 5) | ||
| .collect::<Vec<_>>(), | ||
| vec![5, 5] | ||
| ); | ||
| } | ||
| #[test] | ||
| fn boundary_distances() { | ||
| assert_eq!( | ||
| Mask(vec![ | ||
| true, true, true, true, true, true, true, true, true, true | ||
| ]) | ||
| .boundary_distances(), | ||
| vec![ | ||
| (0, 9), | ||
| (1, 8), | ||
| (2, 7), | ||
| (3, 6), | ||
| (4, 5), | ||
| (5, 4), | ||
| (6, 3), | ||
| (7, 2), | ||
| (8, 1), | ||
| (9, 0) | ||
| ], | ||
| ); | ||
| assert_eq!( | ||
| Mask(vec![ | ||
| true, true, true, false, false, true, true, true, true, true | ||
| ]) | ||
| .boundary_distances(), | ||
| vec![ | ||
| (0, 2), | ||
| (1, 1), | ||
| (2, 0), | ||
| (0, 0), | ||
| (0, 0), | ||
| (0, 4), | ||
| (1, 3), | ||
| (2, 2), | ||
| (3, 1), | ||
| (4, 0) | ||
| ] | ||
| ); | ||
| assert_eq!( | ||
| Mask(vec![ | ||
| true, true, true, false, true, false, false, false, false, false | ||
| ]) | ||
| .boundary_distances(), | ||
| vec![ | ||
| (0, 2), | ||
| (1, 1), | ||
| (2, 0), | ||
| (0, 0), | ||
| (0, 0), | ||
| (0, 0), | ||
| (0, 0), | ||
| (0, 0), | ||
| (0, 0), | ||
| (0, 0) | ||
| ] | ||
| ); | ||
| assert_eq!(Mask(vec![]).boundary_distances(), vec![]); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
duration_ranges の方がいいかも? なんなら estimator 側でこれを吐くようにしてもいいかも