nvidia-nemo--speech
ba4be087d5
Create PR to main with cherry-pick from release / cherry-pick (push) Failing after 0s
CICD NeMo / pre-flight (push) Failing after 0s
CICD NeMo / configure (push) Has been skipped
Build, validate, and release Neural Modules / pre-flight (push) Failing after 1s
CICD NeMo / code-linting (push) Has been skipped
Build, validate, and release Neural Modules / release (push) Has been skipped
Build, validate, and release Neural Modules / release-summary (push) Has been cancelled
CICD NeMo / cicd-test-container-build (push) Has been cancelled
CICD NeMo / cicd-import-tests (push) Has been cancelled
CICD NeMo / L0_Setup_Test_Data_And_Models (push) Has been cancelled
CICD NeMo / cicd-main-unit-tests (push) Has been cancelled
CICD NeMo / cicd-main-speech (push) Has been cancelled
CICD NeMo / Nemo_CICD_Test (push) Has been cancelled
CICD NeMo / Coverage (e2e) (push) Has been cancelled
CICD NeMo / Coverage (unit-test) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
CICD NeMo / cicd-wait-in-queue (push) Has been cancelled
115 行
3.9 KiB
Python
115 行
3.9 KiB
Python
# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
|
|
import torch
|
|
from nemo.collections.asr.inference.streaming.framing.request import Frame, RequestOptions
|
|
from nemo.collections.asr.inference.streaming.framing.stream import Stream
|
|
from nemo.collections.asr.inference.utils.audio_io import read_audio
|
|
|
|
|
|
class MonoStream(Stream):
|
|
"""
|
|
Streamer for mono wav files.
|
|
Iterates over the frames of the audio file
|
|
"""
|
|
|
|
def __init__(self, rate: int, frame_size_in_secs: float, stream_id: int, pad_last_frame: bool = False):
|
|
"""
|
|
Initialize the MonoStream
|
|
Args:
|
|
rate (int): sampling rate
|
|
frame_size_in_secs (int): frame length in seconds
|
|
stream_id (int): stream id
|
|
"""
|
|
|
|
self.rate = rate
|
|
self.frame_size = int(frame_size_in_secs * rate)
|
|
self.pad_last_frame = pad_last_frame
|
|
|
|
self.samples = None
|
|
self.n_samples = None
|
|
self.options = None
|
|
super().__init__(stream_id)
|
|
|
|
def load_audio(self, audio: str | torch.Tensor, options: RequestOptions | None = None) -> None:
|
|
"""
|
|
Load the audio file either from a file or from a torch tensor
|
|
Args:
|
|
audio (str | torch.Tensor): audio file path or torch tensor of audio samples
|
|
options (RequestOptions | None): optional options for the request
|
|
"""
|
|
if isinstance(audio, str):
|
|
# Read the audio file and convert to mono
|
|
self.samples = read_audio(audio, target_sr=self.rate, mono=True)
|
|
else:
|
|
self.samples = audio
|
|
self.n_samples = len(self.samples)
|
|
self.frame_count = 0 # Reset frame count
|
|
self.options = options
|
|
|
|
def __iter__(self):
|
|
"""Returns the frame iterator object"""
|
|
self.start = 0
|
|
self.frame_count = 0
|
|
return self
|
|
|
|
def __next__(self) -> list[Frame]:
|
|
"""
|
|
Get the next frame in the stream
|
|
Returns:
|
|
list[Frame]: The next frame in the stream
|
|
"""
|
|
if self.samples is None:
|
|
raise RuntimeError("No audio samples loaded. Please call load_audio() first.")
|
|
|
|
if self.start < self.n_samples:
|
|
|
|
end = min(self.start + self.frame_size, self.n_samples)
|
|
|
|
# Check if this is the last frame
|
|
is_end = False
|
|
chunk_length = end - self.start
|
|
if (end - self.start < self.frame_size) or (end == self.n_samples):
|
|
is_end = True
|
|
|
|
# Pad the last frame if needed
|
|
if not is_end:
|
|
chunk_samples = self.samples[self.start : end]
|
|
else:
|
|
if self.pad_last_frame:
|
|
chunk_samples = torch.zeros(self.frame_size)
|
|
chunk_samples[:chunk_length] = self.samples[self.start : end]
|
|
else:
|
|
chunk_samples = self.samples[self.start : end]
|
|
|
|
# Package the frame
|
|
is_first = self.frame_count == 0
|
|
frame = Frame(
|
|
samples=chunk_samples,
|
|
stream_id=self.stream_id,
|
|
is_first=is_first,
|
|
is_last=is_end,
|
|
length=chunk_length,
|
|
options=self.options if is_first else None,
|
|
)
|
|
|
|
self.frame_count += 1
|
|
self.start += frame.size
|
|
|
|
return [frame]
|
|
|
|
# End of stream
|
|
raise StopIteration
|