Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/mlsafir.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ on:
pull_request:
paths:
- src/vocoder/mlsa.rs
- src/mlpg_adjust/**

jobs:
collect:
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ mod tests {

assert_eq!(speech.len(), 74880);
approx::assert_abs_diff_eq!(speech[2000], 2.3158134981607754e-5, epsilon = 1.0e-10);
approx::assert_abs_diff_eq!(speech[30000], 6459.375032316974, epsilon = 1.0e-10);
approx::assert_abs_diff_eq!(speech[30000], 6459.375032318177, epsilon = 1.0e-10);
}

// これ,名詞,代名詞,一般,*,*,*,これ,コレ,コレ,0/2,C3,-1
Expand Down Expand Up @@ -127,7 +127,7 @@ mod tests {

assert_eq!(speech.len(), 100800);
approx::assert_abs_diff_eq!(speech[2000], 17.15977345625943, epsilon = 1.0e-10);
approx::assert_abs_diff_eq!(speech[30000], 2566.2058730889985, epsilon = 1.0e-10);
approx::assert_abs_diff_eq!(speech[30000], 2566.205873089126, epsilon = 1.0e-10);
approx::assert_abs_diff_eq!(speech[70000], -1898.2890228814217, epsilon = 1.0e-10);
approx::assert_abs_diff_eq!(speech[100799], -13.514971382534956, epsilon = 1.0e-10);
}
Expand Down
207 changes: 96 additions & 111 deletions src/mlpg_adjust/mask.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>>,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

duration_ranges の方がいいかも? なんなら estimator 側でこれを吐くようにしてもいいかも

// contiguous ranges joined together
pub(super) voiced_ranges: Vec<Range<usize>>,
}
Comment on lines +10 to 15

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The 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>(
Expand All @@ -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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self.0self.mask() 以外に特に理由はないので戻してもよいです.
本当は mask() を経由せず self.voiced_ranges.flat_map() みたいにしたいのですが,flat_map の中で if の左右のイテレータの型が合わないせいでうまくいかないです.

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()))

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The 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![]);
}
}
Loading
Loading