CoolFace
Apppublic

Aluode/PerceptionLabPortable

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
audio_utils.py1225 linesDownload Raw Back to transformers
1# Copyright 2023 The HuggingFace Inc. team and the librosa & torchaudio authors.2#3# Licensed under the Apache License, Version 2.0 (the "License");4# you may not use this file except in compliance with the License.5# You may obtain a copy of the License at6#7#     http://www.apache.org/licenses/LICENSE-2.08#9# Unless required by applicable law or agreed to in writing, software10# distributed under the License is distributed on an "AS IS" BASIS,11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12# See the License for the specific language governing permissions and13# limitations under the License.14"""15Audio processing functions to extract features from audio waveforms. This code is pure numpy to support all frameworks16and remove unnecessary dependencies.17"""18 19import base6420import importlib21import io22import os23import warnings24from collections.abc import Sequence25from io import BytesIO26from typing import TYPE_CHECKING, Any, Optional, Union27 28 29if TYPE_CHECKING:30    import torch31import numpy as np32import requests33from packaging import version34 35from .utils import (36    is_librosa_available,37    is_numpy_array,38    is_soundfile_available,39    is_torch_tensor,40    is_torchcodec_available,41    requires_backends,42)43 44 45if is_soundfile_available():46    import soundfile as sf47 48if is_librosa_available():49    import librosa50 51    # TODO: @eustlb, we actually don't need librosa but soxr is installed with librosa52    import soxr53 54if is_torchcodec_available():55    TORCHCODEC_VERSION = version.parse(importlib.metadata.version("torchcodec"))56 57AudioInput = Union[np.ndarray, "torch.Tensor", Sequence[np.ndarray], Sequence["torch.Tensor"]]58 59 60def load_audio(audio: Union[str, np.ndarray], sampling_rate=16000, timeout=None) -> np.ndarray:61    """62    Loads `audio` to an np.ndarray object.63 64    Args:65        audio (`str` or `np.ndarray`):66            The audio to be loaded to the numpy array format.67        sampling_rate (`int`, *optional*, defaults to 16000):68            The sampling rate to be used when loading the audio. It should be same as the69            sampling rate the model you will be using further was trained with.70        timeout (`float`, *optional*):71            The timeout value in seconds for the URL request.72 73    Returns:74        `np.ndarray`: A numpy array representing the audio.75    """76    if isinstance(audio, str):77        # Try to load with `torchcodec` but do not enforce users to install it. If not found78        # fallback to `librosa`. If using an audio-only model, most probably `torchcodec` won't be79        # needed. Do not raise any errors if not installed or versions do not match80        if is_torchcodec_available() and TORCHCODEC_VERSION >= version.parse("0.3.0"):81            audio = load_audio_torchcodec(audio, sampling_rate=sampling_rate)82        else:83            audio = load_audio_librosa(audio, sampling_rate=sampling_rate, timeout=timeout)84    elif not isinstance(audio, np.ndarray):85        raise TypeError(86            "Incorrect format used for `audio`. Should be an url linking to an audio, a local path, or numpy array."87        )88    return audio89 90 91def load_audio_torchcodec(audio: Union[str, np.ndarray], sampling_rate=16000) -> np.ndarray:92    """93    Loads `audio` to an np.ndarray object using `torchcodec`.94 95    Args:96        audio (`str` or `np.ndarray`):97            The audio to be loaded to the numpy array format.98        sampling_rate (`int`, *optional*, defaults to 16000):99            The sampling rate to be used when loading the audio. It should be same as the100            sampling rate the model you will be using further was trained with.101 102    Returns:103        `np.ndarray`: A numpy array representing the audio.104    """105    # Lazy import so that issues in torchcodec compatibility don't crash the whole library106    requires_backends(load_audio_torchcodec, ["torchcodec"])107    from torchcodec.decoders import AudioDecoder108 109    # Set `num_channels` to `1` which is what most models expects and the default in librosa110    decoder = AudioDecoder(audio, sample_rate=sampling_rate, num_channels=1)111    audio = decoder.get_all_samples().data[0].numpy()  # NOTE: feature extractors don't accept torch tensors112    return audio113 114 115def load_audio_librosa(audio: Union[str, np.ndarray], sampling_rate=16000, timeout=None) -> np.ndarray:116    """117    Loads `audio` to an np.ndarray object using `librosa`.118 119    Args:120        audio (`str` or `np.ndarray`):121            The audio to be loaded to the numpy array format.122        sampling_rate (`int`, *optional*, defaults to 16000):123            The sampling rate to be used when loading the audio. It should be same as the124            sampling rate the model you will be using further was trained with.125        timeout (`float`, *optional*):126            The timeout value in seconds for the URL request.127 128    Returns:129        `np.ndarray`: A numpy array representing the audio.130    """131    requires_backends(load_audio_librosa, ["librosa"])132 133    # Load audio from URL (e.g https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2-Audio/audio/translate_to_chinese.wav)134    if audio.startswith("http://") or audio.startswith("https://"):135        audio = librosa.load(BytesIO(requests.get(audio, timeout=timeout).content), sr=sampling_rate)[0]136    elif os.path.isfile(audio):137        audio = librosa.load(audio, sr=sampling_rate)[0]138    return audio139 140 141def load_audio_as(142    audio: str,143    return_format: str,144    timeout: Optional[int] = None,145    force_mono: bool = False,146    sampling_rate: Optional[int] = None,147) -> Union[str, dict[str, Any], io.BytesIO, None]:148    """149    Load audio from either a local file path or URL and return in specified format.150 151    Args:152        audio (`str`): Either a local file path or a URL to an audio file153        return_format (`str`): Format to return the audio in:154            - "base64": Base64 encoded string155            - "dict": Dictionary with data and format156            - "buffer": BytesIO object157        timeout (`int`, *optional*): Timeout for URL requests in seconds158        force_mono (`bool`): Whether to convert stereo audio to mono159        sampling_rate (`int`, *optional*): If provided, the audio will be resampled to the specified sampling rate.160 161    Returns:162        `Union[str, Dict[str, Any], io.BytesIO, None]`:163            - `str`: Base64 encoded audio data (if return_format="base64")164            - `dict`: Dictionary with 'data' (base64 encoded audio data) and 'format' keys (if return_format="dict")165            - `io.BytesIO`: BytesIO object containing audio data (if return_format="buffer")166    """167    # TODO: @eustlb, we actually don't need librosa but soxr is installed with librosa168    requires_backends(load_audio_as, ["librosa"])169 170    if return_format not in ["base64", "dict", "buffer"]:171        raise ValueError(f"Invalid return_format: {return_format}. Must be 'base64', 'dict', or 'buffer'")172 173    try:174        # Load audio bytes from URL or file175        audio_bytes = None176        if audio.startswith(("http://", "https://")):177            response = requests.get(audio, timeout=timeout)178            response.raise_for_status()179            audio_bytes = response.content180        elif os.path.isfile(audio):181            with open(audio, "rb") as audio_file:182                audio_bytes = audio_file.read()183        else:184            raise ValueError(f"File not found: {audio}")185 186        # Process audio data187        with io.BytesIO(audio_bytes) as audio_file:188            with sf.SoundFile(audio_file) as f:189                audio_array = f.read(dtype="float32")190                original_sr = f.samplerate191                audio_format = f.format192                if sampling_rate is not None and sampling_rate != original_sr:193                    # Resample audio to target sampling rate194                    audio_array = soxr.resample(audio_array, original_sr, sampling_rate, quality="HQ")195                else:196                    sampling_rate = original_sr197 198        # Convert to mono if needed199        if force_mono and audio_array.ndim != 1:200            audio_array = audio_array.mean(axis=1)201 202        buffer = io.BytesIO()203        sf.write(buffer, audio_array, sampling_rate, format=audio_format.upper())204        buffer.seek(0)205 206        if return_format == "buffer":207            return buffer208        elif return_format == "base64":209            return base64.b64encode(buffer.read()).decode("utf-8")210        elif return_format == "dict":211            return {212                "data": base64.b64encode(buffer.read()).decode("utf-8"),213                "format": audio_format.lower(),214            }215 216    except Exception as e:217        raise ValueError(f"Error loading audio: {e}")218 219 220def is_valid_audio(audio):221    return is_numpy_array(audio) or is_torch_tensor(audio)222 223 224def is_valid_list_of_audio(audio):225    return audio and all(is_valid_audio(audio_i) for audio_i in audio)226 227 228def make_list_of_audio(229    audio: Union[list[AudioInput], AudioInput],230) -> AudioInput:231    """232    Ensure that the output is a list of audio.233    Args:234        audio (`Union[list[AudioInput], AudioInput]`):235            The input audio.236    Returns:237        list: A list of audio.238    """239    # If it's a list of audios, it's already in the right format240    if isinstance(audio, (list, tuple)) and is_valid_list_of_audio(audio):241        return audio242 243    # If it's a single audio, convert it to a list of244    if is_valid_audio(audio):245        return [audio]246 247    raise ValueError("Invalid input type. Must be a single audio or a list of audio")248 249 250def hertz_to_mel(freq: Union[float, np.ndarray], mel_scale: str = "htk") -> Union[float, np.ndarray]:251    """252    Convert frequency from hertz to mels.253 254    Args:255        freq (`float` or `np.ndarray`):256            The frequency, or multiple frequencies, in hertz (Hz).257        mel_scale (`str`, *optional*, defaults to `"htk"`):258            The mel frequency scale to use, `"htk"`, `"kaldi"` or `"slaney"`.259 260    Returns:261        `float` or `np.ndarray`: The frequencies on the mel scale.262    """263 264    if mel_scale not in ["slaney", "htk", "kaldi"]:265        raise ValueError('mel_scale should be one of "htk", "slaney" or "kaldi".')266 267    if mel_scale == "htk":268        return 2595.0 * np.log10(1.0 + (freq / 700.0))269    elif mel_scale == "kaldi":270        return 1127.0 * np.log(1.0 + (freq / 700.0))271 272    min_log_hertz = 1000.0273    min_log_mel = 15.0274    logstep = 27.0 / np.log(6.4)275    mels = 3.0 * freq / 200.0276 277    if isinstance(freq, np.ndarray):278        log_region = freq >= min_log_hertz279        mels[log_region] = min_log_mel + np.log(freq[log_region] / min_log_hertz) * logstep280    elif freq >= min_log_hertz:281        mels = min_log_mel + np.log(freq / min_log_hertz) * logstep282 283    return mels284 285 286def mel_to_hertz(mels: Union[float, np.ndarray], mel_scale: str = "htk") -> Union[float, np.ndarray]:287    """288    Convert frequency from mels to hertz.289 290    Args:291        mels (`float` or `np.ndarray`):292            The frequency, or multiple frequencies, in mels.293        mel_scale (`str`, *optional*, `"htk"`):294            The mel frequency scale to use, `"htk"`, `"kaldi"` or `"slaney"`.295 296    Returns:297        `float` or `np.ndarray`: The frequencies in hertz.298    """299 300    if mel_scale not in ["slaney", "htk", "kaldi"]:301        raise ValueError('mel_scale should be one of "htk", "slaney" or "kaldi".')302 303    if mel_scale == "htk":304        return 700.0 * (np.power(10, mels / 2595.0) - 1.0)305    elif mel_scale == "kaldi":306        return 700.0 * (np.exp(mels / 1127.0) - 1.0)307 308    min_log_hertz = 1000.0309    min_log_mel = 15.0310    logstep = np.log(6.4) / 27.0311    freq = 200.0 * mels / 3.0312 313    if isinstance(mels, np.ndarray):314        log_region = mels >= min_log_mel315        freq[log_region] = min_log_hertz * np.exp(logstep * (mels[log_region] - min_log_mel))316    elif mels >= min_log_mel:317        freq = min_log_hertz * np.exp(logstep * (mels - min_log_mel))318 319    return freq320 321 322def hertz_to_octave(freq: Union[float, np.ndarray], tuning: float = 0.0, bins_per_octave: int = 12):323    """324    Convert frequency from hertz to fractional octave numbers.325    Adapted from *librosa*.326 327    Args:328        freq (`float` or `np.ndarray`):329            The frequency, or multiple frequencies, in hertz (Hz).330        tuning (`float`, defaults to `0.`):331            Tuning deviation from the Stuttgart pitch (A440) in (fractional) bins per octave.332        bins_per_octave (`int`, defaults to `12`):333            Number of bins per octave.334 335    Returns:336        `float` or `np.ndarray`: The frequencies on the octave scale.337    """338    stuttgart_pitch = 440.0 * 2.0 ** (tuning / bins_per_octave)339    octave = np.log2(freq / (float(stuttgart_pitch) / 16))340    return octave341 342 343def _create_triangular_filter_bank(fft_freqs: np.ndarray, filter_freqs: np.ndarray) -> np.ndarray:344    """345    Creates a triangular filter bank.346 347    Adapted from *torchaudio* and *librosa*.348 349    Args:350        fft_freqs (`np.ndarray` of shape `(num_frequency_bins,)`):351            Discrete frequencies of the FFT bins in Hz.352        filter_freqs (`np.ndarray` of shape `(num_mel_filters,)`):353            Center frequencies of the triangular filters to create, in Hz.354 355    Returns:356        `np.ndarray` of shape `(num_frequency_bins, num_mel_filters)`357    """358    filter_diff = np.diff(filter_freqs)359    slopes = np.expand_dims(filter_freqs, 0) - np.expand_dims(fft_freqs, 1)360    down_slopes = -slopes[:, :-2] / filter_diff[:-1]361    up_slopes = slopes[:, 2:] / filter_diff[1:]362    return np.maximum(np.zeros(1), np.minimum(down_slopes, up_slopes))363 364 365def chroma_filter_bank(366    num_frequency_bins: int,367    num_chroma: int,368    sampling_rate: int,369    tuning: float = 0.0,370    power: Optional[float] = 2.0,371    weighting_parameters: Optional[tuple[float, float]] = (5.0, 2.0),372    start_at_c_chroma: bool = True,373):374    """375    Creates a chroma filter bank, i.e a linear transformation to project spectrogram bins onto chroma bins.376 377    Adapted from *librosa*.378 379    Args:380        num_frequency_bins (`int`):381            Number of frequencies used to compute the spectrogram (should be the same as in `stft`).382        num_chroma (`int`):383            Number of chroma bins (i.e pitch classes).384        sampling_rate (`float`):385            Sample rate of the audio waveform.386        tuning (`float`):387            Tuning deviation from A440 in fractions of a chroma bin.388        power (`float`, *optional*, defaults to 2.0):389            If 12.0, normalizes each column with their L2 norm. If 1.0, normalizes each column with their L1 norm.390        weighting_parameters (`tuple[float, float]`, *optional*, defaults to `(5., 2.)`):391            If specified, apply a Gaussian weighting parameterized by the first element of the tuple being the center and392            the second element being the Gaussian half-width.393        start_at_c_chroma (`bool`, *optional*, defaults to `True`):394            If True, the filter bank will start at the 'C' pitch class. Otherwise, it will start at 'A'.395    Returns:396        `np.ndarray` of shape `(num_frequency_bins, num_chroma)`397    """398    # Get the FFT bins, not counting the DC component399    frequencies = np.linspace(0, sampling_rate, num_frequency_bins, endpoint=False)[1:]400 401    freq_bins = num_chroma * hertz_to_octave(frequencies, tuning=tuning, bins_per_octave=num_chroma)402 403    # make up a value for the 0 Hz bin = 1.5 octaves below bin 1404    # (so chroma is 50% rotated from bin 1, and bin width is broad)405    freq_bins = np.concatenate(([freq_bins[0] - 1.5 * num_chroma], freq_bins))406 407    bins_width = np.concatenate((np.maximum(freq_bins[1:] - freq_bins[:-1], 1.0), [1]))408 409    chroma_filters = np.subtract.outer(freq_bins, np.arange(0, num_chroma, dtype="d")).T410 411    num_chroma2 = np.round(float(num_chroma) / 2)412 413    # Project into range -num_chroma/2 .. num_chroma/2414    # add on fixed offset of 10*num_chroma to ensure all values passed to415    # rem are positive416    chroma_filters = np.remainder(chroma_filters + num_chroma2 + 10 * num_chroma, num_chroma) - num_chroma2417 418    # Gaussian bumps - 2*D to make them narrower419    chroma_filters = np.exp(-0.5 * (2 * chroma_filters / np.tile(bins_width, (num_chroma, 1))) ** 2)420 421    # normalize each column422    if power is not None:423        chroma_filters = chroma_filters / np.sum(chroma_filters**power, axis=0, keepdims=True) ** (1.0 / power)424 425    # Maybe apply scaling for fft bins426    if weighting_parameters is not None:427        center, half_width = weighting_parameters428        chroma_filters *= np.tile(429            np.exp(-0.5 * (((freq_bins / num_chroma - center) / half_width) ** 2)),430            (num_chroma, 1),431        )432 433    if start_at_c_chroma:434        chroma_filters = np.roll(chroma_filters, -3 * (num_chroma // 12), axis=0)435 436    # remove aliasing columns, copy to ensure row-contiguity437    return np.ascontiguousarray(chroma_filters[:, : int(1 + num_frequency_bins / 2)])438 439 440def mel_filter_bank(441    num_frequency_bins: int,442    num_mel_filters: int,443    min_frequency: float,444    max_frequency: float,445    sampling_rate: int,446    norm: Optional[str] = None,447    mel_scale: str = "htk",448    triangularize_in_mel_space: bool = False,449) -> np.ndarray:450    """451    Creates a frequency bin conversion matrix used to obtain a mel spectrogram. This is called a *mel filter bank*, and452    various implementation exist, which differ in the number of filters, the shape of the filters, the way the filters453    are spaced, the bandwidth of the filters, and the manner in which the spectrum is warped. The goal of these454    features is to approximate the non-linear human perception of the variation in pitch with respect to the frequency.455 456    Different banks of mel filters were introduced in the literature. The following variations are supported:457 458    - MFCC FB-20: introduced in 1980 by Davis and Mermelstein, it assumes a sampling frequency of 10 kHz and a speech459      bandwidth of `[0, 4600]` Hz.460    - MFCC FB-24 HTK: from the Cambridge HMM Toolkit (HTK) (1995) uses a filter bank of 24 filters for a speech461      bandwidth of `[0, 8000]` Hz. This assumes sampling rate ≥ 16 kHz.462    - MFCC FB-40: from the Auditory Toolbox for MATLAB written by Slaney in 1998, assumes a sampling rate of 16 kHz and463      speech bandwidth of `[133, 6854]` Hz. This version also includes area normalization.464    - HFCC-E FB-29 (Human Factor Cepstral Coefficients) of Skowronski and Harris (2004), assumes a sampling rate of465      12.5 kHz and speech bandwidth of `[0, 6250]` Hz.466 467    This code is adapted from *torchaudio* and *librosa*. Note that the default parameters of torchaudio's468    `melscale_fbanks` implement the `"htk"` filters while librosa uses the `"slaney"` implementation.469 470    Args:471        num_frequency_bins (`int`):472            Number of frequency bins (should be the same as `n_fft // 2 + 1` where `n_fft` is the size of the Fourier Transform used to compute the spectrogram).473        num_mel_filters (`int`):474            Number of mel filters to generate.475        min_frequency (`float`):476            Lowest frequency of interest in Hz.477        max_frequency (`float`):478            Highest frequency of interest in Hz. This should not exceed `sampling_rate / 2`.479        sampling_rate (`int`):480            Sample rate of the audio waveform.481        norm (`str`, *optional*):482            If `"slaney"`, divide the triangular mel weights by the width of the mel band (area normalization).483        mel_scale (`str`, *optional*, defaults to `"htk"`):484            The mel frequency scale to use, `"htk"`, `"kaldi"` or `"slaney"`.485        triangularize_in_mel_space (`bool`, *optional*, defaults to `False`):486            If this option is enabled, the triangular filter is applied in mel space rather than frequency space. This487            should be set to `true` in order to get the same results as `torchaudio` when computing mel filters.488 489    Returns:490        `np.ndarray` of shape (`num_frequency_bins`, `num_mel_filters`): Triangular filter bank matrix. This is a491        projection matrix to go from a spectrogram to a mel spectrogram.492    """493    if norm is not None and norm != "slaney":494        raise ValueError('norm must be one of None or "slaney"')495 496    if num_frequency_bins < 2:497        raise ValueError(f"Require num_frequency_bins: {num_frequency_bins} >= 2")498 499    if min_frequency > max_frequency:500        raise ValueError(f"Require min_frequency: {min_frequency} <= max_frequency: {max_frequency}")501 502    # center points of the triangular mel filters503    mel_min = hertz_to_mel(min_frequency, mel_scale=mel_scale)504    mel_max = hertz_to_mel(max_frequency, mel_scale=mel_scale)505    mel_freqs = np.linspace(mel_min, mel_max, num_mel_filters + 2)506    filter_freqs = mel_to_hertz(mel_freqs, mel_scale=mel_scale)507 508    if triangularize_in_mel_space:509        # frequencies of FFT bins in Hz, but filters triangularized in mel space510        fft_bin_width = sampling_rate / ((num_frequency_bins - 1) * 2)511        fft_freqs = hertz_to_mel(fft_bin_width * np.arange(num_frequency_bins), mel_scale=mel_scale)512        filter_freqs = mel_freqs513    else:514        # frequencies of FFT bins in Hz515        fft_freqs = np.linspace(0, sampling_rate // 2, num_frequency_bins)516 517    mel_filters = _create_triangular_filter_bank(fft_freqs, filter_freqs)518 519    if norm is not None and norm == "slaney":520        # Slaney-style mel is scaled to be approx constant energy per channel521        enorm = 2.0 / (filter_freqs[2 : num_mel_filters + 2] - filter_freqs[:num_mel_filters])522        mel_filters *= np.expand_dims(enorm, 0)523 524    if (mel_filters.max(axis=0) == 0.0).any():525        warnings.warn(526            "At least one mel filter has all zero values. "527            f"The value for `num_mel_filters` ({num_mel_filters}) may be set too high. "528            f"Or, the value for `num_frequency_bins` ({num_frequency_bins}) may be set too low."529        )530 531    return mel_filters532 533 534def optimal_fft_length(window_length: int) -> int:535    """536    Finds the best FFT input size for a given `window_length`. This function takes a given window length and, if not537    already a power of two, rounds it up to the next power or two.538 539    The FFT algorithm works fastest when the length of the input is a power of two, which may be larger than the size540    of the window or analysis frame. For example, if the window is 400 samples, using an FFT input size of 512 samples541    is more optimal than an FFT size of 400 samples. Using a larger FFT size does not affect the detected frequencies,542    it simply gives a higher frequency resolution (i.e. the frequency bins are smaller).543    """544    return 2 ** int(np.ceil(np.log2(window_length)))545 546 547def window_function(548    window_length: int,549    name: str = "hann",550    periodic: bool = True,551    frame_length: Optional[int] = None,552    center: bool = True,553) -> np.ndarray:554    """555    Returns an array containing the specified window. This window is intended to be used with `stft`.556 557    The following window types are supported:558 559        - `"boxcar"`: a rectangular window560        - `"hamming"`: the Hamming window561        - `"hann"`: the Hann window562        - `"povey"`: the Povey window563 564    Args:565        window_length (`int`):566            The length of the window in samples.567        name (`str`, *optional*, defaults to `"hann"`):568            The name of the window function.569        periodic (`bool`, *optional*, defaults to `True`):570            Whether the window is periodic or symmetric.571        frame_length (`int`, *optional*):572            The length of the analysis frames in samples. Provide a value for `frame_length` if the window is smaller573            than the frame length, so that it will be zero-padded.574        center (`bool`, *optional*, defaults to `True`):575            Whether to center the window inside the FFT buffer. Only used when `frame_length` is provided.576 577    Returns:578        `np.ndarray` of shape `(window_length,)` or `(frame_length,)` containing the window.579    """580    length = window_length + 1 if periodic else window_length581 582    if name == "boxcar":583        window = np.ones(length)584    elif name in ["hamming", "hamming_window"]:585        window = np.hamming(length)586    elif name in ["hann", "hann_window"]:587        window = np.hanning(length)588    elif name == "povey":589        window = np.power(np.hanning(length), 0.85)590    else:591        raise ValueError(f"Unknown window function '{name}'")592 593    if periodic:594        window = window[:-1]595 596    if frame_length is None:597        return window598 599    if window_length > frame_length:600        raise ValueError(601            f"Length of the window ({window_length}) may not be larger than frame_length ({frame_length})"602        )603 604    padded_window = np.zeros(frame_length)605    offset = (frame_length - window_length) // 2 if center else 0606    padded_window[offset : offset + window_length] = window607    return padded_window608 609 610# TODO This method does not support batching yet as we are mainly focused on inference.611def spectrogram(612    waveform: np.ndarray,613    window: np.ndarray,614    frame_length: int,615    hop_length: int,616    fft_length: Optional[int] = None,617    power: Optional[float] = 1.0,618    center: bool = True,619    pad_mode: str = "reflect",620    onesided: bool = True,621    dither: float = 0.0,622    preemphasis: Optional[float] = None,623    mel_filters: Optional[np.ndarray] = None,624    mel_floor: float = 1e-10,625    log_mel: Optional[str] = None,626    reference: float = 1.0,627    min_value: float = 1e-10,628    db_range: Optional[float] = None,629    remove_dc_offset: bool = False,630    dtype: np.dtype = np.float32,631) -> np.ndarray:632    """633    Calculates a spectrogram over one waveform using the Short-Time Fourier Transform.634 635    This function can create the following kinds of spectrograms:636 637      - amplitude spectrogram (`power = 1.0`)638      - power spectrogram (`power = 2.0`)639      - complex-valued spectrogram (`power = None`)640      - log spectrogram (use `log_mel` argument)641      - mel spectrogram (provide `mel_filters`)642      - log-mel spectrogram (provide `mel_filters` and `log_mel`)643 644    How this works:645 646      1. The input waveform is split into frames of size `frame_length` that are partially overlapping by `frame_length647         - hop_length` samples.648      2. Each frame is multiplied by the window and placed into a buffer of size `fft_length`.649      3. The DFT is taken of each windowed frame.650      4. The results are stacked into a spectrogram.651 652    We make a distinction between the following "blocks" of sample data, each of which may have a different lengths:653 654      - The analysis frame. This is the size of the time slices that the input waveform is split into.655      - The window. Each analysis frame is multiplied by the window to avoid spectral leakage.656      - The FFT input buffer. The length of this determines how many frequency bins are in the spectrogram.657 658    In this implementation, the window is assumed to be zero-padded to have the same size as the analysis frame. A659    padded window can be obtained from `window_function()`. The FFT input buffer may be larger than the analysis frame,660    typically the next power of two.661 662    Note: This function is not optimized for speed yet. It should be mostly compatible with `librosa.stft` and663    `torchaudio.functional.transforms.Spectrogram`, although it is more flexible due to the different ways spectrograms664    can be constructed.665 666    Args:667        waveform (`np.ndarray` of shape `(length,)`):668            The input waveform. This must be a single real-valued, mono waveform.669        window (`np.ndarray` of shape `(frame_length,)`):670            The windowing function to apply, including zero-padding if necessary. The actual window length may be671            shorter than `frame_length`, but we're assuming the array has already been zero-padded.672        frame_length (`int`):673            The length of the analysis frames in samples. With librosa this is always equal to `fft_length` but we also674            allow smaller sizes.675        hop_length (`int`):676            The stride between successive analysis frames in samples.677        fft_length (`int`, *optional*):678            The size of the FFT buffer in samples. This determines how many frequency bins the spectrogram will have.679            For optimal speed, this should be a power of two. If `None`, uses `frame_length`.680        power (`float`, *optional*, defaults to 1.0):681            If 1.0, returns the amplitude spectrogram. If 2.0, returns the power spectrogram. If `None`, returns682            complex numbers.683        center (`bool`, *optional*, defaults to `True`):684            Whether to pad the waveform so that frame `t` is centered around time `t * hop_length`. If `False`, frame685            `t` will start at time `t * hop_length`.686        pad_mode (`str`, *optional*, defaults to `"reflect"`):687            Padding mode used when `center` is `True`. Possible values are: `"constant"` (pad with zeros), `"edge"`688            (pad with edge values), `"reflect"` (pads with mirrored values).689        onesided (`bool`, *optional*, defaults to `True`):690            If True, only computes the positive frequencies and returns a spectrogram containing `fft_length // 2 + 1`691            frequency bins. If False, also computes the negative frequencies and returns `fft_length` frequency bins.692        dither (`float`, *optional*, defaults to 0.0):693            Adds dithering. In other words, adds a small Gaussian noise to each frame.694            E.g. use 4.0 to add dithering with a normal distribution centered695            around 0.0 with standard deviation 4.0, 0.0 means no dithering.696            Dithering has similar effect as `mel_floor`. It reduces the high log_mel_fbank697            values for signals with hard-zero sections, when VAD cutoff is present in the signal.698        preemphasis (`float`, *optional*)699            Coefficient for a low-pass filter that applies pre-emphasis before the DFT.700        mel_filters (`np.ndarray` of shape `(num_freq_bins, num_mel_filters)`, *optional*):701            The mel filter bank. If supplied, applies a this filter bank to create a mel spectrogram.702        mel_floor (`float`, *optional*, defaults to 1e-10):703            Minimum value of mel frequency banks.704        log_mel (`str`, *optional*):705            How to convert the spectrogram to log scale. Possible options are: `None` (don't convert), `"log"` (take706            the natural logarithm) `"log10"` (take the base-10 logarithm), `"dB"` (convert to decibels). Can only be707            used when `power` is not `None`.708        reference (`float`, *optional*, defaults to 1.0):709            Sets the input spectrogram value that corresponds to 0 dB. For example, use `np.max(spectrogram)` to set710            the loudest part to 0 dB. Must be greater than zero.711        min_value (`float`, *optional*, defaults to `1e-10`):712            The spectrogram will be clipped to this minimum value before conversion to decibels, to avoid taking713            `log(0)`. For a power spectrogram, the default of `1e-10` corresponds to a minimum of -100 dB. For an714            amplitude spectrogram, the value `1e-5` corresponds to -100 dB. Must be greater than zero.715        db_range (`float`, *optional*):716            Sets the maximum dynamic range in decibels. For example, if `db_range = 80`, the difference between the717            peak value and the smallest value will never be more than 80 dB. Must be greater than zero.718        remove_dc_offset (`bool`, *optional*):719            Subtract mean from waveform on each frame, applied before pre-emphasis. This should be set to `true` in720            order to get the same results as `torchaudio.compliance.kaldi.fbank` when computing mel filters.721        dtype (`np.dtype`, *optional*, defaults to `np.float32`):722            Data type of the spectrogram tensor. If `power` is None, this argument is ignored and the dtype will be723            `np.complex64`.724 725    Returns:726        `nd.array` containing a spectrogram of shape `(num_frequency_bins, length)` for a regular spectrogram or shape727        `(num_mel_filters, length)` for a mel spectrogram.728    """729    window_length = len(window)730 731    if fft_length is None:732        fft_length = frame_length733 734    if frame_length > fft_length:735        raise ValueError(f"frame_length ({frame_length}) may not be larger than fft_length ({fft_length})")736 737    if window_length != frame_length:738        raise ValueError(f"Length of the window ({window_length}) must equal frame_length ({frame_length})")739 740    if hop_length <= 0:741        raise ValueError("hop_length must be greater than zero")742 743    if waveform.ndim != 1:744        raise ValueError(f"Input waveform must have only one dimension, shape is {waveform.shape}")745 746    if np.iscomplexobj(waveform):747        raise ValueError("Complex-valued input waveforms are not currently supported")748 749    if power is None and mel_filters is not None:750        raise ValueError(751            "You have provided `mel_filters` but `power` is `None`. Mel spectrogram computation is not yet supported for complex-valued spectrogram."752            "Specify `power` to fix this issue."753        )754 755    # center pad the waveform756    if center:757        padding = [(int(frame_length // 2), int(frame_length // 2))]758        waveform = np.pad(waveform, padding, mode=pad_mode)759 760    # promote to float64, since np.fft uses float64 internally761    waveform = waveform.astype(np.float64)762    window = window.astype(np.float64)763 764    # split waveform into frames of frame_length size765    num_frames = int(1 + np.floor((waveform.size - frame_length) / hop_length))766 767    num_frequency_bins = (fft_length // 2) + 1 if onesided else fft_length768    spectrogram = np.empty((num_frames, num_frequency_bins), dtype=np.complex64)769 770    # rfft is faster than fft771    fft_func = np.fft.rfft if onesided else np.fft.fft772    buffer = np.zeros(fft_length)773 774    timestep = 0775    for frame_idx in range(num_frames):776        buffer[:frame_length] = waveform[timestep : timestep + frame_length]777 778        if dither != 0.0:779            buffer[:frame_length] += dither * np.random.randn(frame_length)780 781        if remove_dc_offset:782            buffer[:frame_length] = buffer[:frame_length] - buffer[:frame_length].mean()783 784        if preemphasis is not None:785            buffer[1:frame_length] -= preemphasis * buffer[: frame_length - 1]786            buffer[0] *= 1 - preemphasis787 788        buffer[:frame_length] *= window789 790        spectrogram[frame_idx] = fft_func(buffer)791        timestep += hop_length792 793    # note: ** is much faster than np.power794    if power is not None:795        spectrogram = np.abs(spectrogram, dtype=np.float64) ** power796 797    spectrogram = spectrogram.T798 799    if mel_filters is not None:800        spectrogram = np.maximum(mel_floor, np.dot(mel_filters.T, spectrogram))801 802    if power is not None and log_mel is not None:803        if log_mel == "log":804            spectrogram = np.log(spectrogram)805        elif log_mel == "log10":806            spectrogram = np.log10(spectrogram)807        elif log_mel == "dB":808            if power == 1.0:809                spectrogram = amplitude_to_db(spectrogram, reference, min_value, db_range)810            elif power == 2.0:811                spectrogram = power_to_db(spectrogram, reference, min_value, db_range)812            else:813                raise ValueError(f"Cannot use log_mel option '{log_mel}' with power {power}")814        else:815            raise ValueError(f"Unknown log_mel option: {log_mel}")816 817        spectrogram = np.asarray(spectrogram, dtype)818 819    return spectrogram820 821 822def spectrogram_batch(823    waveform_list: list[np.ndarray],824    window: np.ndarray,825    frame_length: int,826    hop_length: int,827    fft_length: Optional[int] = None,828    power: Optional[float] = 1.0,829    center: bool = True,830    pad_mode: str = "reflect",831    onesided: bool = True,832    dither: float = 0.0,833    preemphasis: Optional[float] = None,834    mel_filters: Optional[np.ndarray] = None,835    mel_floor: float = 1e-10,836    log_mel: Optional[str] = None,837    reference: float = 1.0,838    min_value: float = 1e-10,839    db_range: Optional[float] = None,840    remove_dc_offset: bool = False,841    dtype: np.dtype = np.float32,842) -> list[np.ndarray]:843    """844    Calculates spectrograms for a list of waveforms using the Short-Time Fourier Transform, optimized for batch processing.845    This function extends the capabilities of the `spectrogram` function to handle multiple waveforms efficiently by leveraging broadcasting.846 847    It supports generating various types of spectrograms:848 849        - amplitude spectrogram (`power = 1.0`)850        - power spectrogram (`power = 2.0`)851        - complex-valued spectrogram (`power = None`)852        - log spectrogram (use `log_mel` argument)853        - mel spectrogram (provide `mel_filters`)854        - log-mel spectrogram (provide `mel_filters` and `log_mel`)855 856    How this works:857 858        1. The input waveform is split into frames of size `frame_length` that are partially overlapping by `frame_length859            - hop_length` samples.860        2. Each frame is multiplied by the window and placed into a buffer of size `fft_length`.861        3. The DFT is taken of each windowed frame.862        4. The results are stacked into a spectrogram.863 864    We make a distinction between the following "blocks" of sample data, each of which may have a different lengths:865 866      - The analysis frame. This is the size of the time slices that the input waveform is split into.867      - The window. Each analysis frame is multiplied by the window to avoid spectral leakage.868      - The FFT input buffer. The length of this determines how many frequency bins are in the spectrogram.869 870    In this implementation, the window is assumed to be zero-padded to have the same size as the analysis frame. A871    padded window can be obtained from `window_function()`. The FFT input buffer may be larger than the analysis frame,872    typically the next power of two.873 874    Note: This function is designed for efficient batch processing of multiple waveforms but retains compatibility with individual waveform processing methods like `librosa.stft`.875 876    Args:877        waveform_list (`list[np.ndarray]` with arrays of shape `(length,)`):878            The list of input waveforms, each a single-channel (mono) signal.879        window (`np.ndarray` of shape `(frame_length,)`):880            The windowing function to apply, including zero-padding if necessary.881        frame_length (`int`):882            The length of each frame for analysis.883        hop_length (`int`):884            The step size between successive frames.885        fft_length (`int`, *optional*):886            The size of the FFT buffer, defining frequency bin resolution.887        power (`float`, *optional*, defaults to 1.0):888            Determines the type of spectrogram: 1.0 for amplitude, 2.0 for power, None for complex.889        center (`bool`, *optional*, defaults to `True`):890            Whether to center-pad the waveform frames.891        pad_mode (`str`, *optional*, defaults to `"reflect"`):892            The padding strategy when `center` is `True`.893        onesided (`bool`, *optional*, defaults to `True`):894            If True, returns a one-sided spectrogram for real input signals.895        dither (`float`, *optional*, defaults to 0.0):896            Adds dithering. In other words, adds a small Gaussian noise to each frame.897            E.g. use 4.0 to add dithering with a normal distribution centered898            around 0.0 with standard deviation 4.0, 0.0 means no dithering.899        preemphasis (`float`, *optional*):900            Applies a pre-emphasis filter to each frame.901        mel_filters (`np.ndarray`, *optional*):902            Mel filter bank for converting to mel spectrogram.903        mel_floor (`float`, *optional*, defaults to 1e-10):904            Floor value for mel spectrogram to avoid log(0).905        log_mel (`str`, *optional*):906            Specifies log scaling strategy; options are None, "log", "log10", "dB".907        reference (`float`, *optional*, defaults to 1.0):908            Reference value for dB conversion in log_mel.909        min_value (`float`, *optional*, defaults to 1e-10):910            Minimum floor value for log scale conversions.911        db_range (`float`, *optional*):912            Dynamic range for dB scale spectrograms.913        remove_dc_offset (`bool`, *optional*):914            Whether to remove the DC offset from each frame.915        dtype (`np.dtype`, *optional*, defaults to `np.float32`):916            Data type of the output spectrogram.917 918    Returns:919        list[`np.ndarray`]: A list of spectrogram arrays, one for each input waveform.920    """921    window_length = len(window)922 923    if fft_length is None:924        fft_length = frame_length925 926    if frame_length > fft_length:927        raise ValueError(f"frame_length ({frame_length}) may not be larger than fft_length ({fft_length})")928 929    if window_length != frame_length:930        raise ValueError(f"Length of the window ({window_length}) must equal frame_length ({frame_length})")931 932    if hop_length <= 0:933        raise ValueError("hop_length must be greater than zero")934 935    # Check the dimensions of the waveform , and if waveform is complex936    for waveform in waveform_list:937        if waveform.ndim != 1:938            raise ValueError(f"Input waveform must have only one dimension, shape is {waveform.shape}")939        if np.iscomplexobj(waveform):940            raise ValueError("Complex-valued input waveforms are not currently supported")941    # Center pad the waveform942    if center:943        padding = [(int(frame_length // 2), int(frame_length // 2))]944        waveform_list = [945            np.pad(946                waveform,947                padding,948                mode=pad_mode,949            )950            for waveform in waveform_list951        ]952    original_waveform_lengths = [953        len(waveform) for waveform in waveform_list954    ]  # these lengths will be used to remove padding later955 956    # Batch pad the waveform957    max_length = max(original_waveform_lengths)958    padded_waveform_batch = np.array(959        [960            np.pad(waveform, (0, max_length - len(waveform)), mode="constant", constant_values=0)961            for waveform in waveform_list962        ],963        dtype=dtype,964    )965 966    # Promote to float64, since np.fft uses float64 internally967    padded_waveform_batch = padded_waveform_batch.astype(np.float64)968    window = window.astype(np.float64)969 970    # Split waveform into frames of frame_length size971    num_frames = int(1 + np.floor((padded_waveform_batch.shape[1] - frame_length) / hop_length))972    # these lengths will be used to remove padding later973    true_num_frames = [int(1 + np.floor((length - frame_length) / hop_length)) for length in original_waveform_lengths]974    num_batches = padded_waveform_batch.shape[0]975 976    num_frequency_bins = (fft_length // 2) + 1 if onesided else fft_length977    spectrogram = np.empty((num_batches, num_frames, num_frequency_bins), dtype=np.complex64)978 979    # rfft is faster than fft980    fft_func = np.fft.rfft if onesided else np.fft.fft981    buffer = np.zeros((num_batches, fft_length))982 983    for frame_idx in range(num_frames):984        timestep = frame_idx * hop_length985        buffer[:, :frame_length] = padded_waveform_batch[:, timestep : timestep + frame_length]986 987        if dither != 0.0:988            buffer[:, :frame_length] += dither * np.random.randn(*buffer[:, :frame_length].shape)989 990        if remove_dc_offset:991            buffer[:, :frame_length] -= buffer[:, :frame_length].mean(axis=1, keepdims=True)992 993        if preemphasis is not None:994            buffer[:, 1:frame_length] -= preemphasis * buffer[:, : frame_length - 1]995            buffer[:, 0] *= 1 - preemphasis996 997        buffer[:, :frame_length] *= window998 999        spectrogram[:, frame_idx] = fft_func(buffer)1000 1001    # Note: ** is much faster than np.power1002    if power is not None:1003        spectrogram = np.abs(spectrogram, dtype=np.float64) ** power1004 1005    # Apply mel filters if provided1006    if mel_filters is not None:1007        result = np.tensordot(spectrogram, mel_filters.T, axes=([2], [1]))1008        spectrogram = np.maximum(mel_floor, result)1009 1010    # Convert to log scale if specified1011    if power is not None and log_mel is not None:1012        if log_mel == "log":1013            spectrogram = np.log(spectrogram)1014        elif log_mel == "log10":1015            spectrogram = np.log10(spectrogram)1016        elif log_mel == "dB":1017            if power == 1.0:1018                spectrogram = amplitude_to_db_batch(spectrogram, reference, min_value, db_range)1019            elif power == 2.0:1020                spectrogram = power_to_db_batch(spectrogram, reference, min_value, db_range)1021            else:1022                raise ValueError(f"Cannot use log_mel option '{log_mel}' with power {power}")1023        else:1024            raise ValueError(f"Unknown log_mel option: {log_mel}")1025 1026        spectrogram = np.asarray(spectrogram, dtype)1027 1028    spectrogram_list = [spectrogram[i, : true_num_frames[i], :].T for i in range(len(true_num_frames))]1029 1030    return spectrogram_list1031 1032 1033def power_to_db(1034    spectrogram: np.ndarray,1035    reference: float = 1.0,1036    min_value: float = 1e-10,1037    db_range: Optional[float] = None,1038) -> np.ndarray:1039    """1040    Converts a power spectrogram to the decibel scale. This computes `10 * log10(spectrogram / reference)`, using basic1041    logarithm properties for numerical stability.1042 1043    The motivation behind applying the log function on the (mel) spectrogram is that humans do not hear loudness on a1044    linear scale. Generally to double the perceived volume of a sound we need to put 8 times as much energy into it.1045    This means that large variations in energy may not sound all that different if the sound is loud to begin with.1046    This compression operation makes the (mel) spectrogram features match more closely what humans actually hear.1047 1048    Based on the implementation of `librosa.power_to_db`.1049 1050    Args:1051        spectrogram (`np.ndarray`):1052            The input power (mel) spectrogram. Note that a power spectrogram has the amplitudes squared!1053        reference (`float`, *optional*, defaults to 1.0):1054            Sets the input spectrogram value that corresponds to 0 dB. For example, use `np.max(spectrogram)` to set1055            the loudest part to 0 dB. Must be greater than zero.1056        min_value (`float`, *optional*, defaults to `1e-10`):1057            The spectrogram will be clipped to this minimum value before conversion to decibels, to avoid taking1058            `log(0)`. The default of `1e-10` corresponds to a minimum of -100 dB. Must be greater than zero.1059        db_range (`float`, *optional*):1060            Sets the maximum dynamic range in decibels. For example, if `db_range = 80`, the difference between the1061            peak value and the smallest value will never be more than 80 dB. Must be greater than zero.1062 1063    Returns:1064        `np.ndarray`: the spectrogram in decibels1065    """1066    if reference <= 0.0:1067        raise ValueError("reference must be greater than zero")1068    if min_value <= 0.0:1069        raise ValueError("min_value must be greater than zero")1070 1071    reference = max(min_value, reference)1072 1073    spectrogram = np.clip(spectrogram, a_min=min_value, a_max=None)1074    spectrogram = 10.0 * (np.log10(spectrogram) - np.log10(reference))1075 1076    if db_range is not None:1077        if db_range <= 0.0:1078            raise ValueError("db_range must be greater than zero")1079        spectrogram = np.clip(spectrogram, a_min=spectrogram.max() - db_range, a_max=None)1080 1081    return spectrogram1082 1083 1084def power_to_db_batch(1085    spectrogram: np.ndarray,1086    reference: float = 1.0,1087    min_value: float = 1e-10,1088    db_range: Optional[float] = None,1089) -> np.ndarray:1090    """1091    Converts a batch of power spectrograms to the decibel scale. This computes `10 * log10(spectrogram / reference)`,1092    using basic logarithm properties for numerical stability.1093 1094    This function supports batch processing, where each item in the batch is an individual power (mel) spectrogram.1095 1096    Args:1097        spectrogram (`np.ndarray`):1098            The input batch of power (mel) spectrograms. Expected shape is (batch_size, *spectrogram_shape).1099            Note that a power spectrogram has the amplitudes squared!1100        reference (`float`, *optional*, defaults to 1.0):1101            Sets the input spectrogram value that corresponds to 0 dB. For example, use `np.max(spectrogram)` to set1102            the loudest part to 0 dB. Must be greater than zero.1103        min_value (`float`, *optional*, defaults to `1e-10`):1104            The spectrogram will be clipped to this minimum value before conversion to decibels, to avoid taking1105            `log(0)`. The default of `1e-10` corresponds to a minimum of -100 dB. Must be greater than zero.1106        db_range (`float`, *optional*):1107            Sets the maximum dynamic range in decibels. For example, if `db_range = 80`, the difference between the1108            peak value and the smallest value will never be more than 80 dB. Must be greater than zero.1109 1110    Returns:1111        `np.ndarray`: the batch of spectrograms in decibels1112    """1113    if reference <= 0.0:1114        raise ValueError("reference must be greater than zero")1115    if min_value <= 0.0:1116        raise ValueError("min_value must be greater than zero")1117 1118    reference = max(min_value, reference)1119 1120    spectrogram = np.clip(spectrogram, a_min=min_value, a_max=None)1121    spectrogram = 10.0 * (np.log10(spectrogram) - np.log10(reference))1122 1123    if db_range is not None:1124        if db_range <= 0.0:1125            raise ValueError("db_range must be greater than zero")1126        # Apply db_range clipping per batch item1127        max_values = spectrogram.max(axis=(1, 2), keepdims=True)1128        spectrogram = np.clip(spectrogram, a_min=max_values - db_range, a_max=None)1129 1130    return spectrogram1131 1132 1133def amplitude_to_db(1134    spectrogram: np.ndarray,1135    reference: float = 1.0,1136    min_value: float = 1e-5,1137    db_range: Optional[float] = None,1138) -> np.ndarray:1139    """1140    Converts an amplitude spectrogram to the decibel scale. This computes `20 * log10(spectrogram / reference)`, using1141    basic logarithm properties for numerical stability.1142 1143    The motivation behind applying the log function on the (mel) spectrogram is that humans do not hear loudness on a1144    linear scale. Generally to double the perceived volume of a sound we need to put 8 times as much energy into it.1145    This means that large variations in energy may not sound all that different if the sound is loud to begin with.1146    This compression operation makes the (mel) spectrogram features match more closely what humans actually hear.1147 1148    Args:1149        spectrogram (`np.ndarray`):1150            The input amplitude (mel) spectrogram.1151        reference (`float`, *optional*, defaults to 1.0):1152            Sets the input spectrogram value that corresponds to 0 dB. For example, use `np.max(spectrogram)` to set1153            the loudest part to 0 dB. Must be greater than zero.1154        min_value (`float`, *optional*, defaults to `1e-5`):1155            The spectrogram will be clipped to this minimum value before conversion to decibels, to avoid taking1156            `log(0)`. The default of `1e-5` corresponds to a minimum of -100 dB. Must be greater than zero.1157        db_range (`float`, *optional*):1158            Sets the maximum dynamic range in decibels. For example, if `db_range = 80`, the difference between the1159            peak value and the smallest value will never be more than 80 dB. Must be greater than zero.1160 1161    Returns:1162        `np.ndarray`: the spectrogram in decibels1163    """1164    if reference <= 0.0:1165        raise ValueError("reference must be greater than zero")1166    if min_value <= 0.0:1167        raise ValueError("min_value must be greater than zero")1168 1169    reference = max(min_value, reference)1170 1171    spectrogram = np.clip(spectrogram, a_min=min_value, a_max=None)1172    spectrogram = 20.0 * (np.log10(spectrogram) - np.log10(reference))1173 1174    if db_range is not None:1175        if db_range <= 0.0:1176            raise ValueError("db_range must be greater than zero")1177        spectrogram = np.clip(spectrogram, a_min=spectrogram.max() - db_range, a_max=None)1178 1179    return spectrogram1180 1181 1182def amplitude_to_db_batch(1183    spectrogram: np.ndarray, reference: float = 1.0, min_value: float = 1e-5, db_range: Optional[float] = None1184) -> np.ndarray:1185    """1186    Converts a batch of amplitude spectrograms to the decibel scale. This computes `20 * log10(spectrogram / reference)`,1187    using basic logarithm properties for numerical stability.1188 1189    The function supports batch processing, where each item in the batch is an individual amplitude (mel) spectrogram.1190 1191    Args:1192        spectrogram (`np.ndarray`):1193            The input batch of amplitude (mel) spectrograms. Expected shape is (batch_size, *spectrogram_shape).1194        reference (`float`, *optional*, defaults to 1.0):1195            Sets the input spectrogram value that corresponds to 0 dB. For example, use `np.max(spectrogram)` to set1196            the loudest part to 0 dB. Must be greater than zero.1197        min_value (`float`, *optional*, defaults to `1e-5`):1198            The spectrogram will be clipped to this minimum value before conversion to decibels, to avoid taking1199            `log(0)`. The default of `1e-5` corresponds to a minimum of -100 dB. Must be greater than zero.1200        db_range (`float`, *optional*):

Showing the first 1,200 of 1225 lines. Download the file for the rest.

Aluode/PerceptionLabPortable · CoolFace