use ndarray::{Array2, Array3, ArrayView2, ArrayViewMut3, s};
use crate::inference::{InferenceError, TensorShapeError};
use super::prepare_weights;
use super::tensor::array3_slice_mut;
use super::{
CHUNK_SPEAKER_BATCH_SIZE, FBANK_BATCH_SIZE, FBANK_FEATURES, FBANK_FRAMES, MASK_FRAMES,
MULTI_MASK_BATCH_SIZE, NUM_SPEAKERS, PRIMARY_BATCH_SIZE, SplitTailInput, should_use_clean_mask,
};
pub(super) struct EmbeddingBuffers {
pub(super) multi_mask_fbank_buffer: Array3<f32>,
pub(super) multi_mask_masks_buffer: Array2<f32>,
pub(super) split_waveform_buffer: Array3<f32>,
pub(super) split_fbank_batch_buffer: Array3<f32>,
pub(super) split_feature_batch_buffer: Array3<f32>,
pub(super) split_weights_batch_buffer: Array2<f32>,
pub(super) split_primary_feature_batch_buffer: Array3<f32>,
pub(super) split_primary_weights_batch_buffer: Array2<f32>,
}
impl EmbeddingBuffers {
pub(super) fn fresh() -> Self {
Self {
multi_mask_fbank_buffer: Array3::zeros((
MULTI_MASK_BATCH_SIZE,
FBANK_FRAMES,
FBANK_FEATURES,
)),
multi_mask_masks_buffer: Array2::zeros((
MULTI_MASK_BATCH_SIZE * NUM_SPEAKERS,
MASK_FRAMES,
)),
split_waveform_buffer: Array3::zeros((1, 1, 160_000)),
split_fbank_batch_buffer: Array3::zeros((FBANK_BATCH_SIZE, 1, 160_000)),
split_feature_batch_buffer: Array3::zeros((
CHUNK_SPEAKER_BATCH_SIZE,
FBANK_FRAMES,
FBANK_FEATURES,
)),
split_weights_batch_buffer: Array2::zeros((CHUNK_SPEAKER_BATCH_SIZE, 589)),
split_primary_feature_batch_buffer: Array3::zeros((
PRIMARY_BATCH_SIZE,
FBANK_FRAMES,
FBANK_FEATURES,
)),
split_primary_weights_batch_buffer: Array2::zeros((PRIMARY_BATCH_SIZE, 589)),
}
}
pub(super) fn fill_split_waveform(&mut self, audio: &[f32], window_samples: usize) {
prepare_waveform(
0,
audio,
window_samples,
&mut self.split_waveform_buffer.view_mut(),
);
}
pub(super) fn fill_fbank_batch(&mut self, audios: &[&[f32]], window_samples: usize) {
self.split_fbank_batch_buffer.fill(0.0);
for (idx, audio) in audios.iter().enumerate() {
let copy_len = audio.len().min(window_samples);
self.split_fbank_batch_buffer
.slice_mut(s![idx, 0, ..copy_len])
.assign(&ndarray::ArrayView1::from(&audio[..copy_len]));
}
}
pub(super) fn fill_tail_single(
&mut self,
fbank: &Array2<f32>,
weights: &[f32],
mask_frames: usize,
) {
self.split_feature_batch_buffer
.slice_mut(s![0, ..fbank.nrows(), ..fbank.ncols()])
.assign(fbank);
prepare_weights(
0,
weights,
mask_frames,
&mut self.split_weights_batch_buffer.view_mut(),
);
}
pub(super) fn fill_tail_batch(
&mut self,
fbank: &Array2<f32>,
segmentations: &ArrayView2<'_, f32>,
clean_masks: &Array2<f32>,
num_samples: usize,
mask_frames: usize,
min_num_samples: usize,
) -> Result<(), InferenceError> {
self.split_feature_batch_buffer
.slice_mut(s![0, ..fbank.nrows(), ..fbank.ncols()])
.assign(fbank);
let row_stride = FBANK_FRAMES * FBANK_FEATURES;
let fbank_elems = fbank.nrows() * fbank.ncols();
let buf = array3_slice_mut(
&mut self.split_feature_batch_buffer,
"split feature batch buffer",
)?;
for speaker_idx in 1..segmentations.ncols() {
buf.copy_within(0..fbank_elems, speaker_idx * row_stride);
}
for speaker_idx in 0..segmentations.ncols() {
let mask_col = segmentations.column(speaker_idx);
let clean_col = clean_masks.column(speaker_idx);
let use_clean =
should_use_clean_mask(&clean_col, mask_col.len(), num_samples, min_num_samples);
let weights: Vec<f32> = if use_clean {
clean_col.iter().copied().collect()
} else {
mask_col.iter().copied().collect()
};
prepare_weights(
speaker_idx,
&weights,
mask_frames,
&mut self.split_weights_batch_buffer.view_mut(),
);
}
Ok(())
}
pub(super) fn fill_tail_primary(
&mut self,
inputs: &[SplitTailInput<'_>],
mask_frames: usize,
) -> Result<(), InferenceError> {
if inputs.len() > PRIMARY_BATCH_SIZE {
return Err(InferenceError::BatchTooLarge {
context: "primary tail batch",
rows: inputs.len(),
capacity: PRIMARY_BATCH_SIZE,
});
}
let row_stride = FBANK_FRAMES * FBANK_FEATURES;
for (batch_idx, input) in inputs.iter().enumerate() {
debug_assert_eq!(input.fbank.ncols(), FBANK_FEATURES);
if batch_idx > 0 && std::ptr::eq(input.fbank, inputs[batch_idx - 1].fbank) {
let buf = array3_slice_mut(
&mut self.split_primary_feature_batch_buffer,
"split primary feature batch buffer",
)?;
let prev_start = (batch_idx - 1) * row_stride;
buf.copy_within(prev_start..prev_start + row_stride, batch_idx * row_stride);
} else {
self.split_primary_feature_batch_buffer
.slice_mut(s![batch_idx, ..input.fbank.nrows(), ..input.fbank.ncols()])
.assign(input.fbank);
}
prepare_weights(
batch_idx,
input.weights,
mask_frames,
&mut self.split_primary_weights_batch_buffer.view_mut(),
);
}
if inputs.len() < PRIMARY_BATCH_SIZE {
self.split_primary_weights_batch_buffer
.slice_mut(s![inputs.len().., ..])
.fill(0.0);
}
Ok(())
}
pub(super) fn fill_multi_mask(
&mut self,
fbanks: &[&Array2<f32>],
masks: &[&[f32]],
mask_frames: usize,
) -> Result<(), InferenceError> {
let num_fbanks = fbanks.len();
let num_masks = masks.len();
if num_fbanks > MULTI_MASK_BATCH_SIZE {
return Err(InferenceError::BatchTooLarge {
context: "multi-mask batch",
rows: num_fbanks,
capacity: MULTI_MASK_BATCH_SIZE,
});
}
let expected_masks =
num_fbanks
.checked_mul(NUM_SPEAKERS)
.ok_or(TensorShapeError::Overflow {
context: "multi-mask batch",
})?;
if num_masks != expected_masks {
return Err(InferenceError::MaskCountMismatch {
fbanks: num_fbanks,
expected: expected_masks,
actual: num_masks,
});
}
let fbank_row_stride = FBANK_FRAMES * FBANK_FEATURES;
for (idx, fbank) in fbanks.iter().enumerate() {
self.multi_mask_fbank_buffer
.slice_mut(s![idx, ..fbank.nrows(), ..fbank.ncols()])
.assign(fbank);
}
for (idx, mask) in masks.iter().enumerate() {
prepare_weights(
idx,
mask,
mask_frames,
&mut self.multi_mask_masks_buffer.view_mut(),
);
}
if num_fbanks < MULTI_MASK_BATCH_SIZE {
let start = num_fbanks * fbank_row_stride;
let buf = array3_slice_mut(
&mut self.multi_mask_fbank_buffer,
"multi-mask fbank scratch buffer",
)?;
buf[start..].fill(0.0);
}
if num_masks < MULTI_MASK_BATCH_SIZE * NUM_SPEAKERS {
self.multi_mask_masks_buffer
.slice_mut(s![num_masks.., ..])
.fill(0.0);
}
Ok(())
}
}
pub(super) fn prepare_waveform(
batch_idx: usize,
audio: &[f32],
window_samples: usize,
waveform_buffer: &mut ArrayViewMut3<f32>,
) {
let copy_len = audio.len().min(window_samples);
waveform_buffer
.slice_mut(s![batch_idx, 0, ..copy_len])
.assign(&ndarray::ArrayView1::from(&audio[..copy_len]));
if copy_len < window_samples {
waveform_buffer
.slice_mut(s![batch_idx, 0, copy_len..])
.fill(0.0);
}
}
#[cfg(test)]
mod tests {
use ndarray::array;
use super::prepare_weights;
#[test]
fn prepare_weights_clears_tail_when_mask_is_shorter_than_buffer() {
let mut buffer = ndarray::Array2::from_elem((2, 4), 9.0);
prepare_weights(0, &[1.0, 2.0], 4, &mut buffer.view_mut());
prepare_weights(1, &[3.0, 4.0, 5.0, 6.0, 7.0], 4, &mut buffer.view_mut());
assert_eq!(buffer, array![[1.0, 2.0, 0.0, 0.0], [3.0, 4.0, 5.0, 6.0]]);
}
}