BEST-RQ-2 / feature_extraction_audio.py
ltuncay's picture
Add Transformers loading for the existing AECC 2026 encoder
86dc2b6 verified
Raw
History Blame Contribute Delete
4.73 kB
# MIT License
#
# Copyright (c) 2026 audio-embeddings contributors
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
"""Prepare mono waveforms without changing the learned frontend's numerics."""
from __future__ import annotations
from typing import Any
import numpy as np
import torch
from transformers.feature_extraction_sequence_utils import SequenceFeatureExtractor
from transformers.feature_extraction_utils import BatchFeature
class AudioEmbeddingFeatureExtractor(SequenceFeatureExtractor):
model_input_names = ["input_values", "attention_mask"]
def __init__(
self,
sampling_rate: int = 16000,
padding_value: float = 0.0,
return_attention_mask: bool = True,
**kwargs: Any,
) -> None:
if not isinstance(sampling_rate, int) or sampling_rate <= 0:
raise ValueError("sampling_rate must be a positive integer")
kwargs.pop("feature_size", None)
super().__init__(
feature_size=1,
sampling_rate=sampling_rate,
padding_value=padding_value,
return_attention_mask=return_attention_mask,
**kwargs,
)
def __call__(
self,
raw_speech: Any,
*,
sampling_rate: int | None = None,
padding: bool | str = True,
max_length: int | None = None,
truncation: bool = False,
pad_to_multiple_of: int | None = None,
return_attention_mask: bool | None = None,
return_tensors: str | None = "pt",
) -> BatchFeature:
if sampling_rate != self.sampling_rate:
raise ValueError(
f"Pass sampling_rate={self.sampling_rate}; got {sampling_rate}. "
"Resample audio to the model rate before calling the feature extractor."
)
if isinstance(raw_speech, torch.Tensor):
raw_speech = raw_speech.detach().cpu().float().numpy()
if isinstance(raw_speech, np.ndarray):
if raw_speech.ndim not in {1, 2}:
raise ValueError(
"Expected mono audio [samples] or a batch [batch, samples]"
)
batch = [raw_speech] if raw_speech.ndim == 1 else list(raw_speech)
elif isinstance(raw_speech, (list, tuple)) and len(raw_speech):
batch = [raw_speech] if np.isscalar(raw_speech[0]) else list(raw_speech)
else:
raise ValueError("Provide a nonempty waveform or batch of mono waveforms")
waveforms = []
for waveform in batch:
if isinstance(waveform, torch.Tensor):
waveform = waveform.detach().cpu().float().numpy()
array = np.asarray(waveform, dtype=np.float32)
if array.ndim != 1 or array.size == 0 or not np.isfinite(array).all():
raise ValueError(
"Each waveform must be a nonempty, finite, mono 1-D array"
)
waveforms.append(array)
if not waveforms:
raise ValueError("The audio batch cannot be empty")
if max_length is not None and max_length <= 0:
raise ValueError("max_length must be positive")
return self.pad(
BatchFeature({"input_values": waveforms}),
padding=padding,
max_length=max_length,
truncation=truncation,
pad_to_multiple_of=pad_to_multiple_of,
return_attention_mask=(
self.return_attention_mask
if return_attention_mask is None
else return_attention_mask
),
return_tensors=return_tensors,
)
AudioEmbeddingFeatureExtractor.register_for_auto_class("AutoFeatureExtractor")