L0SG's picture
Upload Nemotron-Labs-Audex-2B
5e79b62 verified
Raw
History Blame Contribute Delete
5.26 kB
# coding=utf-8
# Copyright (c) 2026, 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.
from __future__ import annotations
from collections.abc import AsyncIterable, AsyncIterator, Iterable, Iterator, Sequence
from typing import TYPE_CHECKING, Any
import torch
from transformers import AutoModel
if TYPE_CHECKING:
from .modeling_audex_causal_speech_decoder import AudexCausalSpeechDecoderModel
def freeze_model(model: torch.nn.Module) -> torch.nn.Module:
model.eval()
for param in model.parameters():
param.requires_grad = False
return model
def load_audex_causal_speech_decoder(
model_path: str,
*,
device: torch.device | str = "cuda",
dtype: torch.dtype | str | None = None,
) -> AudexCausalSpeechDecoderModel:
kwargs = {"trust_remote_code": True}
if dtype is not None:
kwargs["torch_dtype"] = dtype
model = AutoModel.from_pretrained(model_path, **kwargs)
return freeze_model(model.to(device))
def _decoder_device(decoder: AudexCausalSpeechDecoderModel) -> torch.device:
return next(decoder.parameters()).device
def _embed_speech_token_frames(
decoder: AudexCausalSpeechDecoderModel,
token_frames: Sequence[Sequence[int]],
device: torch.device,
) -> torch.Tensor:
indices = torch.tensor(token_frames, dtype=torch.long, device=device).unsqueeze(0)
return decoder.audex_speech_token_embedder.get_output_from_indices(indices)
def decode_speech_token_frames(
token_frames: Iterable[Sequence[int]],
decoder: AudexCausalSpeechDecoderModel,
*,
device: torch.device | str | None = None,
chunk_frames: int = 1,
) -> Iterator[torch.Tensor]:
if chunk_frames <= 0:
raise ValueError(f"chunk_frames must be positive, got {chunk_frames}")
device = torch.device(device) if device is not None else _decoder_device(decoder)
cache = decoder.create_cache()
buffer: list[list[int]] = []
lookahead_steps = decoder.lookahead_steps
for token_frame in token_frames:
buffer.append(list(token_frame))
while len(buffer) > lookahead_steps:
emit_frames = min(chunk_frames, len(buffer) - lookahead_steps)
wav = _decode_buffered_frames(decoder, cache, buffer, emit_frames, device, flush=False)
del buffer[:emit_frames]
yield wav
while buffer:
emit_frames = min(chunk_frames, len(buffer))
wav = _decode_buffered_frames(decoder, cache, buffer, emit_frames, device, flush=True)
del buffer[:emit_frames]
yield wav
async def decode_speech_token_stream(
token_stream: AsyncIterable[Sequence[int]],
decoder: AudexCausalSpeechDecoderModel,
*,
device: torch.device | str | None = None,
chunk_frames: int = 1,
) -> AsyncIterator[torch.Tensor]:
if chunk_frames <= 0:
raise ValueError(f"chunk_frames must be positive, got {chunk_frames}")
device = torch.device(device) if device is not None else _decoder_device(decoder)
cache = decoder.create_cache()
buffer: list[list[int]] = []
lookahead_steps = decoder.lookahead_steps
async for token_frame in token_stream:
buffer.append(list(token_frame))
while len(buffer) > lookahead_steps:
emit_frames = min(chunk_frames, len(buffer) - lookahead_steps)
wav = _decode_buffered_frames(decoder, cache, buffer, emit_frames, device, flush=False)
del buffer[:emit_frames]
yield wav
while buffer:
emit_frames = min(chunk_frames, len(buffer))
wav = _decode_buffered_frames(decoder, cache, buffer, emit_frames, device, flush=True)
del buffer[:emit_frames]
yield wav
def _decode_buffered_frames(
decoder: AudexCausalSpeechDecoderModel,
cache: Any,
buffer: Sequence[Sequence[int]],
emit_frames: int,
device: torch.device,
*,
flush: bool,
) -> torch.Tensor:
with torch.inference_mode():
vq_emb = _embed_speech_token_frames(decoder, buffer[:emit_frames], device)
lookahead_vq_emb = None
if decoder.lookahead_steps > 0:
future_frames = buffer[emit_frames : emit_frames + decoder.lookahead_steps]
future_parts = []
if future_frames:
future_parts.append(_embed_speech_token_frames(decoder, future_frames, device))
missing_frames = decoder.lookahead_steps - len(future_frames) if flush else 0
if missing_frames > 0:
future_parts.append(vq_emb.new_zeros(vq_emb.size(0), missing_frames, vq_emb.size(-1)))
lookahead_vq_emb = torch.cat(future_parts, dim=1) if future_parts else None
return decoder.decode_cached(vq_emb, cache, lookahead_vq_emb=lookahead_vq_emb)