Repository: lhotse-speech/lhotse

Issue:
Unnecessary padding to audio samples in OnTheFlyFeatures
See the code below
https://github.com/lhotse-speech/lhotse/blob/81b3a0d842787ec476ead2c255e5be0ee89eb0cb/lhotse/dataset/input_strategies.py#L233

https://github.com/lhotse-speech/lhotse/blob/81b3a0d842787ec476ead2c255e5be0ee89eb0cb/lhotse/dataset/input_strategies.py#L239-L242

The extra padding has the following side effects:
 - (1) Waste CPU during feature extraction.
 - (2) It affects the result of the last feature frame. That means the features of an utterance vary
with respect to the batch size (`--max-duration`).




Discussion:
Good catch, I guess it makes sense to first extract the features and then pad them. I'll make the relevant changes later (if you need it sooner feel free to do it).

Resolving patch:
diff --git a/lhotse/dataset/collation.py b/lhotse/dataset/collation.py
--- a/lhotse/dataset/collation.py
+++ b/lhotse/dataset/collation.py
@@ -276,6 +276,20 @@ def maybe_pad(
 """
 
 
+def read_audio_from_cuts(
+        cuts: Iterable[Cut], executor: Optional[Executor] = None
+) -> List[torch.Tensor]:
+    map_fn = map if executor is None else executor.map
+    return list(map_fn(_read_audio, cuts))
+
+
+def read_features_from_cuts(
+        cuts: Iterable[Cut], executor: Optional[Executor] = None
+) -> List[torch.Tensor]:
+    map_fn = map if executor is None else executor.map
+    return list(map_fn(_read_features, cuts))
+
+
 def _read_audio(cut: Cut) -> torch.Tensor:
     return torch.from_numpy(cut.load_audio()[0])
 
diff --git a/lhotse/dataset/input_strategies.py b/lhotse/dataset/input_strategies.py
--- a/lhotse/dataset/input_strategies.py
+++ b/lhotse/dataset/input_strategies.py
@@ -7,8 +7,9 @@
 
 from lhotse import CutSet, FeatureExtractor
 from lhotse.cut import compute_supervisions_frame_mask
-from lhotse.dataset.collation import collate_audio, collate_features, collate_vectors
-from lhotse.utils import compute_num_frames, ifnone, supervision_to_frames, supervision_to_samples
+from lhotse.dataset.collation import collate_audio, collate_features, collate_matrices, collate_vectors, \
+    read_audio_from_cuts
+from lhotse.utils import LOG_EPSILON, compute_num_frames, ifnone, supervision_to_frames, supervision_to_samples
 
 
 class BatchIO:
@@ -230,21 +231,22 @@ def __call__(self, cuts: CutSet) -> Tuple[torch.Tensor, torch.IntTensor]:
 
         :return: a tensor with collated features, and a tensor of ``num_frames`` of each cut before padding.
         """
-        audio, _ = collate_audio(cuts, executor=_get_executor(self.num_workers))
+        audios = read_audio_from_cuts(cuts, executor=_get_executor(self.num_workers))
 
         for tfnm in self.wave_transforms:
-            audio = tfnm(audio)
+            for idx in range(len(audios)):
+                audios[idx] = tfnm(audios[idx])
 
         features_single = []
         for idx, cut in enumerate(cuts):
-            samples = audio[idx].numpy()
+            samples = audios[idx].numpy()
             try:
                 features = self.extractor.extract(samples, cuts[idx].sampling_rate)
             except:
                 logging.error(f"Error while extracting the features for cut with ID {cut.id} -- details:\n{cut}")
                 raise
             features_single.append(torch.from_numpy(features))
-        features_batch = torch.stack(features_single)
+        features_batch = collate_matrices(features_single, padding_value=LOG_EPSILON)
 
         feature_lens = torch.tensor([
             compute_num_frames(


Test patch: