chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:40:42 +08:00
commit e25996e7db
15472 changed files with 3536181 additions and 0 deletions
@@ -0,0 +1,36 @@
# Copyright (c) 2026 PaddlePaddle Authors. 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 .functional import (
compute_fbank_matrix,
create_dct,
fft_frequencies,
hz_to_mel,
mel_frequencies,
mel_to_hz,
power_to_db,
resample,
)
from .window import get_window
__all__ = [
'compute_fbank_matrix',
'create_dct',
'fft_frequencies',
'hz_to_mel',
'mel_frequencies',
'mel_to_hz',
'power_to_db',
'get_window',
'resample',
]
@@ -0,0 +1,611 @@
# Copyright (c) 2022 PaddlePaddle Authors. 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.
# Modified from librosa(https://github.com/librosa/librosa)
from __future__ import annotations
import math
from typing import TYPE_CHECKING, Literal, TypeVar
import paddle
from paddle import Tensor
from paddle.base.framework import Variable
from paddle.pir import Value
if TYPE_CHECKING:
_TensorOrFloat = TypeVar("_TensorOrFloat", Tensor, float)
def hz_to_mel(freq: _TensorOrFloat, htk: bool = False) -> _TensorOrFloat:
"""Convert Hz to Mels.
Args:
freq (Union[Tensor, float]): The input tensor with arbitrary shape.
htk (bool, optional): Use htk scaling. Defaults to False.
Returns:
Union[Tensor, float]: Frequency in mels.
Examples:
.. code-block:: pycon
>>> import paddle
>>> val = 3.0
>>> htk_flag = True
>>> mel_paddle_tensor = paddle.audio.functional.hz_to_mel(paddle.to_tensor(val), htk_flag)
"""
if htk:
if isinstance(freq, (Tensor, Variable, Value)):
return 2595.0 * paddle.log10(1.0 + freq / 700.0)
else:
return 2595.0 * math.log10(1.0 + freq / 700.0)
# Fill in the linear part
f_min = 0.0
f_sp = 200.0 / 3
mels = (freq - f_min) / f_sp
# Fill in the log-scale part
min_log_hz = 1000.0 # beginning of log region (Hz)
min_log_mel = (min_log_hz - f_min) / f_sp # same (Mels)
logstep = math.log(6.4) / 27.0 # step size for log region
if isinstance(freq, (Tensor, Variable, Value)):
target = (
min_log_mel + paddle.log(freq / min_log_hz + 1e-10) / logstep
) # prevent nan with 1e-10
mask = (freq > min_log_hz).astype(freq.dtype)
mels = target * mask + mels * (
1 - mask
) # will replace by masked_fill OP in future
else:
if freq >= min_log_hz:
mels = min_log_mel + math.log(freq / min_log_hz + 1e-10) / logstep
return mels
def mel_to_hz(mel: _TensorOrFloat, htk: bool = False) -> _TensorOrFloat:
"""Convert mel bin numbers to frequencies.
Args:
mel (Union[float, Tensor]): The mel frequency represented as a tensor with arbitrary shape.
htk (bool, optional): Use htk scaling. Defaults to False.
Returns:
Union[float, Tensor]: Frequencies in Hz.
Examples:
.. code-block:: pycon
>>> import paddle
>>> val = 3.0
>>> htk_flag = True
>>> mel_paddle_tensor = paddle.audio.functional.mel_to_hz(paddle.to_tensor(val), htk_flag)
"""
if htk:
return 700.0 * (10.0 ** (mel / 2595.0) - 1.0)
f_min = 0.0
f_sp = 200.0 / 3
freqs = f_min + f_sp * mel
# And now the nonlinear scale
min_log_hz = 1000.0 # beginning of log region (Hz)
min_log_mel = (min_log_hz - f_min) / f_sp # same (Mels)
logstep = math.log(6.4) / 27.0 # step size for log region
if isinstance(mel, (Tensor, Variable, Value)):
target = min_log_hz * paddle.exp(logstep * (mel - min_log_mel))
mask = (mel > min_log_mel).astype(mel.dtype)
freqs = target * mask + freqs * (
1 - mask
) # will replace by masked_fill OP in future
else:
if mel >= min_log_mel:
freqs = min_log_hz * math.exp(logstep * (mel - min_log_mel))
return freqs
def mel_frequencies(
n_mels: int = 64,
f_min: float = 0.0,
f_max: float = 11025.0,
htk: bool = False,
dtype: str = 'float32',
) -> Tensor:
"""Compute mel frequencies.
Args:
n_mels (int, optional): Number of mel bins. Defaults to 64.
f_min (float, optional): Minimum frequency in Hz. Defaults to 0.0.
fmax (float, optional): Maximum frequency in Hz. Defaults to 11025.0.
htk (bool, optional): Use htk scaling. Defaults to False.
dtype (str, optional): The data type of the return frequencies. Defaults to 'float32'.
Returns:
Tensor: Tensor of n_mels frequencies in Hz with shape `(n_mels,)`.
Examples:
.. code-block:: pycon
>>> import paddle
>>> n_mels = 64
>>> f_min = 0.5
>>> f_max = 10000
>>> htk_flag = True
>>> paddle_mel_freq = paddle.audio.functional.mel_frequencies(n_mels, f_min, f_max, htk_flag, 'float64')
"""
# 'Center freqs' of mel bands - uniformly spaced between limits
min_mel = hz_to_mel(f_min, htk=htk)
max_mel = hz_to_mel(f_max, htk=htk)
mels = paddle.linspace(min_mel, max_mel, n_mels, dtype=dtype)
freqs = mel_to_hz(mels, htk=htk)
return freqs
def fft_frequencies(sr: int, n_fft: int, dtype: str = 'float32') -> Tensor:
"""Compute fourier frequencies.
Args:
sr (int): Sample rate.
n_fft (int): Number of fft bins.
dtype (str, optional): The data type of the return frequencies. Defaults to 'float32'.
Returns:
Tensor: FFT frequencies in Hz with shape `(n_fft//2 + 1,)`.
Examples:
.. code-block:: pycon
>>> import paddle
>>> sr = 16000
>>> n_fft = 128
>>> fft_freq = paddle.audio.functional.fft_frequencies(sr, n_fft)
"""
return paddle.linspace(0, float(sr) / 2, int(1 + n_fft // 2), dtype=dtype)
def compute_fbank_matrix(
sr: int,
n_fft: int,
n_mels: int = 64,
f_min: float = 0.0,
f_max: float | None = None,
htk: bool = False,
norm: Literal['slaney'] | float = 'slaney',
dtype: str = 'float32',
) -> Tensor:
"""Compute fbank matrix.
Args:
sr (int): Sample rate.
n_fft (int): Number of fft bins.
n_mels (int, optional): Number of mel bins. Defaults to 64.
f_min (float, optional): Minimum frequency in Hz. Defaults to 0.0.
f_max (Optional[float], optional): Maximum frequency in Hz. Defaults to None.
htk (bool, optional): Use htk scaling. Defaults to False.
norm (Union[str, float], optional): Type of normalization. Defaults to 'slaney'.
dtype (str, optional): The data type of the return matrix. Defaults to 'float32'.
Returns:
Tensor: Mel transform matrix with shape `(n_mels, n_fft//2 + 1)`.
Examples:
.. code-block:: pycon
>>> import paddle
>>> sr = 23
>>> n_fft = 51
>>> fbank = paddle.audio.functional.compute_fbank_matrix(sr, n_fft)
"""
if f_max is None:
f_max = float(sr) / 2
# Initialize the weights
weights = paddle.zeros((n_mels, int(1 + n_fft // 2)), dtype=dtype)
# Center freqs of each FFT bin
fftfreqs = fft_frequencies(sr=sr, n_fft=n_fft, dtype=dtype)
# 'Center freqs' of mel bands - uniformly spaced between limits
mel_f = mel_frequencies(
n_mels + 2, f_min=f_min, f_max=f_max, htk=htk, dtype=dtype
)
fdiff = mel_f[1:] - mel_f[:-1] # np.diff(mel_f)
ramps = mel_f.unsqueeze(1) - fftfreqs.unsqueeze(0)
# ramps = np.subtract.outer(mel_f, fftfreqs)
for i in range(n_mels):
# lower and upper slopes for all bins
lower = -ramps[i] / fdiff[i]
upper = ramps[i + 2] / fdiff[i + 1]
# .. then intersect them with each other and zero
weights[i] = paddle.maximum(
paddle.zeros_like(lower), paddle.minimum(lower, upper)
)
# Slaney-style mel is scaled to be approx constant energy per channel
if norm == 'slaney':
enorm = 2.0 / (mel_f[2 : n_mels + 2] - mel_f[:n_mels])
weights *= enorm.unsqueeze(1)
elif isinstance(norm, (int, float)):
weights = paddle.nn.functional.normalize(weights, p=norm, axis=-1)
return weights
def power_to_db(
spect: Tensor,
ref_value: float = 1.0,
amin: float = 1e-10,
top_db: float | None = 80.0,
) -> Tensor:
"""Convert a power spectrogram (amplitude squared) to decibel (dB) units. The function computes the scaling `10 * log10(x / ref)` in a numerically stable way.
Args:
spect (Tensor): STFT power spectrogram.
ref_value (float, optional): The reference value. If smaller than 1.0, the db level of the signal will be pulled up accordingly. Otherwise, the db level is pushed down. Defaults to 1.0.
amin (float, optional): Minimum threshold. Defaults to 1e-10.
top_db (Optional[float], optional): Threshold the output at `top_db` below the peak. Defaults to None.
Returns:
Tensor: Power spectrogram in db scale.
Examples:
.. code-block:: pycon
>>> import paddle
>>> val = 3.0
>>> decibel_paddle = paddle.audio.functional.power_to_db(paddle.to_tensor(val))
"""
if amin <= 0:
raise Exception("amin must be strictly positive")
if ref_value <= 0:
raise Exception("ref_value must be strictly positive")
ones = paddle.ones_like(spect)
log_spec = 10.0 * paddle.log10(paddle.maximum(ones * amin, spect))
log_spec -= 10.0 * math.log10(max(ref_value, amin))
if top_db is not None:
if top_db < 0:
raise Exception("top_db must be non-negative")
log_spec = paddle.maximum(log_spec, ones * (log_spec.max() - top_db))
return log_spec
def create_dct(
n_mfcc: int,
n_mels: int,
norm: Literal['ortho'] | None = 'ortho',
dtype: str = 'float32',
) -> Tensor:
"""Create a discrete cosine transform(DCT) matrix.
Args:
n_mfcc (int): Number of mel frequency cepstral coefficients.
n_mels (int): Number of mel filterbanks.
norm (Optional[str], optional): Normalization type. Defaults to 'ortho'.
dtype (str, optional): The data type of the return matrix. Defaults to 'float32'.
Returns:
Tensor: The DCT matrix with shape `(n_mels, n_mfcc)`.
Examples:
.. code-block:: pycon
>>> import paddle
>>> n_mfcc = 23
>>> n_mels = 257
>>> dct = paddle.audio.functional.create_dct(n_mfcc, n_mels)
"""
n = paddle.arange(n_mels, dtype=dtype)
k = paddle.arange(n_mfcc, dtype=dtype).unsqueeze(1)
dct = paddle.cos(
math.pi / float(n_mels) * (n + 0.5) * k
) # size (n_mfcc, n_mels)
if norm is None:
dct *= 2.0
else:
assert norm == "ortho"
dct[0] *= 1.0 / math.sqrt(2.0)
dct *= math.sqrt(2.0 / float(n_mels))
return dct.T
def _get_sinc_resample_kernel(
orig_freq: int,
new_freq: int,
gcd: int,
lowpass_filter_width: int = 6,
rolloff: float = 0.99,
resampling_method: Literal[
"sinc_interp_hann", "sinc_interp_kaiser"
] = "sinc_interp_hann",
beta: float | None = None,
dtype: paddle.dtype | None = None,
):
"""
Generate the sinc interpolation kernel for resampling.
This internal function computes the resampling kernel based on the sinc
interpolation formula with windowing. The kernel is used by
_apply_sinc_resample_kernel to perform the actual resampling.
Args:
orig_freq (int): Original sampling frequency.
new_freq (int): Target sampling frequency.
gcd (int): Greatest common divisor of orig_freq and new_freq.
lowpass_filter_width (int, optional): Controls the sharpness of the filter,
larger value means sharper but less efficient. Default: 6.
rolloff (float, optional): Roll-off frequency as a fraction of the Nyquist.
Lower values reduce anti-aliasing but also attenuate high frequencies.
Default: 0.99.
resampling_method (str, optional): Window method for filter design.
Options: ["sinc_interp_hann", "sinc_interp_kaiser"]. Default: "sinc_interp_hann".
beta (float, optional): Shape parameter for Kaiser window. Required only
when resampling_method="sinc_interp_kaiser". Default: None.
dtype (paddle.dtype, optional): Data type for kernel computation.
If None, uses float64 for computation and converts to float32 for output.
Default: None.
Returns:
tuple: (kernel, width)
- kernel (Tensor): Resampling kernel of shape (1, 1, kernel_width)
- width (int): Half-width of the filter in terms of input samples
Raises:
Exception: If frequencies are not integers.
ValueError: If resampling_method is invalid or lowpass_filter_width <= 0.
"""
if not (int(orig_freq) == orig_freq and int(new_freq) == new_freq):
raise ValueError(
"Frequencies must be of integer type to ensure quality resampling computation. "
"To work around this, manually convert both frequencies to integer values "
"that maintain their resampling rate ratio before passing them into the function. "
"Example: To downsample a 44100 hz waveform by a factor of 8, use "
"`orig_freq=8` and `new_freq=1` instead of `orig_freq=44100` and `new_freq=5512.5`. "
)
if resampling_method not in ["sinc_interp_hann", "sinc_interp_kaiser"]:
raise ValueError(f"Invalid resampling method: {resampling_method}")
orig_freq = int(orig_freq) // gcd
new_freq = int(new_freq) // gcd
if lowpass_filter_width <= 0:
raise ValueError("Low pass filter width should be positive.")
base_freq = min(orig_freq, new_freq)
# Perform antialiasing filtering by removing the highest frequencies.
base_freq *= rolloff
# Calculate filter width based on lowpass_filter_width and frequency ratio
width = math.ceil(lowpass_filter_width * orig_freq / base_freq)
idx_dtype = dtype if dtype is not None else paddle.float64
idx = (
paddle.arange(-width, width + orig_freq, dtype=idx_dtype)[None, None]
/ orig_freq
)
t = (
paddle.arange(0, -new_freq, -1, dtype=dtype)[:, None, None] / new_freq
+ idx
)
t *= base_freq
t = t.clip_(-lowpass_filter_width, lowpass_filter_width)
# we do not use built-in paddle windows here as we need to evaluate the window
# at specific positions, not over a regular grid.
if resampling_method == "sinc_interp_hann":
window = paddle.cos(t * math.pi / lowpass_filter_width / 2) ** 2
else:
# sinc_interp_kaiser
if beta is None:
beta = 14.769656459379492
beta_tensor = paddle.to_tensor(float(beta))
window = paddle.i0(
beta_tensor * paddle.sqrt(1 - (t / lowpass_filter_width) ** 2),
) / paddle.i0(beta_tensor)
t *= math.pi
scale = base_freq / orig_freq
kernels = paddle.where(
t == 0, paddle.to_tensor(1.0).cast(t.dtype), t.sin() / t
)
kernels *= window * scale
if dtype is None: # pragma: no cover
kernels = kernels.cast(paddle.float32)
return kernels, width
def _apply_sinc_resample_kernel(
waveform: Tensor,
orig_freq: int,
new_freq: int,
gcd: int,
kernel: Tensor,
width: int,
):
"""
Apply sinc interpolation resampling using precomputed kernel.
This internal function performs the actual resampling operation using the
kernel generated by _get_sinc_resample_kernel. It handles batch processing
and ensures correct output length.
Args:
waveform (Tensor): Input waveform of shape (..., time). Must be floating point.
orig_freq (int): Original sampling frequency.
new_freq (int): Target sampling frequency.
gcd (int): Greatest common divisor of orig_freq and new_freq.
kernel (Tensor): Resampling kernel from _get_sinc_resample_kernel.
width (int): Half-width of the filter from _get_sinc_resample_kernel.
Returns:
Tensor: Resampled waveform of shape (..., new_time).
"""
orig_freq = int(orig_freq) // gcd
new_freq = int(new_freq) // gcd
# pack batch
shape = waveform.shape
waveform = waveform.reshape([-1, shape[-1]])
num_wavs, length = waveform.shape
waveform = paddle.nn.functional.pad(waveform, (width, width + orig_freq))
resampled = paddle.nn.functional.conv1d(
waveform[:, None], kernel, stride=orig_freq
)
resampled = resampled.transpose([0, 2, 1]).reshape((num_wavs, -1))
target_length = paddle.ceil(
paddle.to_tensor(new_freq * length / orig_freq)
).astype(paddle.int64)
resampled = resampled[..., :target_length]
# unpack batch
resampled = resampled.reshape(shape[:-1] + resampled.shape[-1:])
return resampled
def resample(
waveform: Tensor,
orig_freq: int,
new_freq: int,
lowpass_filter_width: int = 6,
rolloff: float = 0.99,
resampling_method: Literal[
"sinc_interp_hann", "sinc_interp_kaiser"
] = "sinc_interp_hann",
beta: float | None = None,
) -> Tensor:
"""
Resample the waveform from orig_freq to new_freq using bandlimited interpolation.
This function implements resampling through sinc interpolation with windowing.
It first computes a resampling kernel based on the specified parameters, then
applies it to the input waveform using convolution. The algorithm handles both
upsampling and downsampling while minimizing aliasing artifacts.
Args:
waveform (Tensor): The input signal of dimension (..., time). Must be
floating point type (float32 or float64).
orig_freq (int): The original frequency of the signal. Must be positive.
new_freq (int): The desired target frequency. Must be positive.
lowpass_filter_width (int, optional): Controls the sharpness of the filter.
Larger values give sharper filtering but are less efficient.
Default: 6.
rolloff (float, optional): The roll-off frequency of the filter as a fraction
of the Nyquist frequency. Lower values reduce anti-aliasing but also
attenuate some high frequencies. Default: 0.99.
resampling_method (str, optional): The windowing method to use for filter
design. Options: "sinc_interp_hann" (Hann window) or "sinc_interp_kaiser"
(Kaiser window). Default: "sinc_interp_hann".
beta (float, optional): Shape parameter for the Kaiser window. Required only
when resampling_method="sinc_interp_kaiser". If not provided for Kaiser,
a default value of 14.769656459379492 is used. Default: None.
Returns:
Tensor: The waveform resampled to new_freq, with dimension (..., new_time).
Raises:
ValueError: If orig_freq or new_freq are not positive.
Exception: If frequencies are not integers (see note below).
TypeError: If waveform is not floating point.
Note:
- orig_freq and new_freq must be integers. For non-integer frequencies,
convert them to integers while maintaining the ratio.
- For repeated resampling with same parameters, use
:class:`paddle.audio.transforms.Resample` for better efficiency.
- Uses windowed sinc interpolation for high-quality audio resampling.
- This function does not support ONNX export now.
Examples:
.. code-block:: pycon
>>> import paddle
>>> from paddle.audio.functional import resample
>>> # Create a sample waveform (1 channel, 1000 samples at 16000 Hz)
>>> waveform = paddle.randn([1, 1000])
>>> # Downsample from 16000 Hz to 8000 Hz
>>> resampled = resample(waveform, 16000, 8000)
>>> print(resampled.shape)
paddle.Size([1, 500])
>>> # Upsample from 16000 Hz to 48000 Hz with custom filter width
>>> resampled = resample(waveform, 16000, 48000, lowpass_filter_width=12)
>>> print(resampled.shape)
paddle.Size([1, 3000])
>>> # Use Kaiser window resampling
>>> resampled = resample(waveform, 16000, 8000, resampling_method="sinc_interp_kaiser", beta=12.0)
>>> print(resampled.shape)
paddle.Size([1, 500])
>>> # Batch processing: multiple waveforms
>>> batch_waveforms = paddle.randn([4, 1, 1000]) # [batch, channels, time]
>>> resampled_batch = resample(batch_waveforms, 16000, 8000)
>>> print(resampled_batch.shape)
paddle.Size([4, 1, 500])
"""
if orig_freq <= 0.0 or new_freq <= 0.0:
raise ValueError(
"Original frequency and desired frequency should be positive integers"
)
if not waveform.is_floating_point():
raise TypeError(
f"Expected floating point type for waveform tensor, but received {waveform.dtype}."
)
if orig_freq == new_freq:
return waveform
gcd = math.gcd(int(orig_freq), int(new_freq))
kernel, width = _get_sinc_resample_kernel(
orig_freq,
new_freq,
gcd,
lowpass_filter_width,
rolloff,
resampling_method,
beta,
waveform.dtype,
)
resampled = _apply_sinc_resample_kernel(
waveform, orig_freq, new_freq, gcd, kernel, width
)
return resampled
+731
View File
@@ -0,0 +1,731 @@
# Copyright (c) 2022 PaddlePaddle Authors. 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
from __future__ import annotations
import math
import warnings
from typing import TYPE_CHECKING
import numpy as np
import paddle
if TYPE_CHECKING:
from paddle import Tensor
from paddle._typing import PlaceLike
from ..features.layers import _WindowLiteral
from paddle.base.framework import (
_current_expected_place,
_get_paddle_place,
_to_pinned_place,
in_dynamic_or_pir_mode,
)
class WindowFunctionRegister:
def __init__(self):
self._functions_dict = {}
def register(self, func=None):
def add_subfunction(func):
name = func.__name__
self._functions_dict[name] = func
return func
return add_subfunction
def get(self, name):
return self._functions_dict[name]
window_function_register = WindowFunctionRegister()
@window_function_register.register()
def _cat(x: list[Tensor], data_type: str) -> Tensor:
l = []
for t in x:
if np.isscalar(t) and not isinstance(t, str):
l.append(paddle.to_tensor([t], data_type))
else:
l.append(paddle.to_tensor(t, data_type))
return paddle.concat(l)
@window_function_register.register()
def _bartlett(M: int, sym: bool = True, dtype: str = 'float64') -> Tensor:
"""
Computes the Bartlett window.
This function is consistent with scipy.signal.windows.bartlett().
"""
if _len_guards(M):
return paddle.ones((M,), dtype=dtype)
M, needs_trunc = _extend(M, sym)
n = paddle.arange(0, M, dtype=dtype)
M = paddle.to_tensor(M, dtype=dtype)
w = paddle.where(
paddle.less_equal(n, (M - 1) / 2.0),
2.0 * n / (M - 1),
2.0 - 2.0 * n / (M - 1),
)
return _truncate(w, needs_trunc)
@window_function_register.register()
def _kaiser(
M: int, beta: float, sym: bool = True, dtype: str = 'float64'
) -> Tensor:
"""Compute the Kaiser window.
This function is consistent with scipy.signal.windows.kaiser().
"""
if _len_guards(M):
return paddle.ones((M,), dtype=dtype)
M, needs_trunc = _extend(M, sym)
beta = paddle.to_tensor(beta, dtype=dtype)
n = paddle.arange(0, M, dtype=dtype)
M = paddle.to_tensor(M, dtype=dtype)
alpha = (M - 1) / 2.0
w = paddle.i0(
beta * paddle.sqrt(1 - ((n - alpha) / alpha) ** 2.0)
) / paddle.i0(beta)
return _truncate(w, needs_trunc)
@window_function_register.register()
def _nuttall(M: int, sym: bool = True, dtype: str = 'float64') -> Tensor:
"""Nuttall window.
This function is consistent with scipy.signal.windows.nuttall().
"""
a = paddle.to_tensor(
[0.3635819, 0.4891775, 0.1365995, 0.0106411], dtype=dtype
)
return _general_cosine(M, a=a, sym=sym, dtype=dtype)
@window_function_register.register()
def _acosh(x: Tensor | float) -> Tensor:
if isinstance(x, float):
return math.log(x + math.sqrt(x**2 - 1))
return paddle.log(x + paddle.sqrt(paddle.square(x) - 1))
@window_function_register.register()
def _extend(M: int, sym: bool) -> bool:
"""Extend window by 1 sample if needed for DFT-even symmetry."""
if not sym:
return M + 1, True
else:
return M, False
@window_function_register.register()
def _len_guards(M: int) -> bool:
"""Handle small or incorrect window lengths."""
if int(M) != M or M < 0:
raise ValueError('Window length M must be a non-negative integer')
return M <= 1
@window_function_register.register()
def _truncate(w: Tensor, needed: bool) -> Tensor:
"""Truncate window by 1 sample if needed for DFT-even symmetry."""
if needed:
return w[:-1]
else:
return w
@window_function_register.register()
def _general_gaussian(
M: int, p, sig, sym: bool = True, dtype: str = 'float64'
) -> Tensor:
"""Compute a window with a generalized Gaussian shape.
This function is consistent with scipy.signal.windows.general_gaussian().
"""
if _len_guards(M):
return paddle.ones((M,), dtype=dtype)
M, needs_trunc = _extend(M, sym)
n = paddle.arange(0, M, dtype=dtype) - (M - 1.0) / 2.0
w = paddle.exp(-0.5 * paddle.abs(n / sig) ** (2 * p))
return _truncate(w, needs_trunc)
@window_function_register.register()
def _general_cosine(
M: int, a: list[float], sym: bool = True, dtype: str = 'float64'
) -> Tensor:
"""Compute a generic weighted sum of cosine terms window.
This function is consistent with scipy.signal.windows.general_cosine().
"""
if _len_guards(M):
return paddle.ones((M,), dtype=dtype)
M, needs_trunc = _extend(M, sym)
fac = paddle.linspace(-math.pi, math.pi, M, dtype=dtype)
w = paddle.zeros((M,), dtype=dtype)
for k in range(len(a)):
w += a[k] * paddle.cos(k * fac)
return _truncate(w, needs_trunc)
@window_function_register.register()
def _general_hamming(
M: int, alpha: float, sym: bool = True, dtype: str = 'float64'
) -> Tensor:
"""Compute a generalized Hamming window.
This function is consistent with scipy.signal.windows.general_hamming()
"""
return _general_cosine(M, [alpha, 1.0 - alpha], sym, dtype=dtype)
@window_function_register.register()
def _taylor(
M: int, nbar=4, sll=30, norm=True, sym: bool = True, dtype: str = 'float64'
) -> Tensor:
"""Compute a Taylor window.
The Taylor window taper function approximates the Dolph-Chebyshev window's
constant sidelobe level for a parameterized number of near-in sidelobes.
"""
if _len_guards(M):
return paddle.ones((M,), dtype=dtype)
M, needs_trunc = _extend(M, sym)
# Original text uses a negative sidelobe level parameter and then negates
# it in the calculation of B. To keep consistent with other methods we
# assume the sidelobe level parameter to be positive.
B = 10 ** (sll / 20)
A = _acosh(B) / math.pi
s2 = nbar**2 / (A**2 + (nbar - 0.5) ** 2)
ma = paddle.arange(1, nbar, dtype=dtype)
Fm = paddle.empty((nbar - 1,), dtype=dtype)
signs = paddle.empty_like(ma)
signs[::2] = 1
signs[1::2] = -1
m2 = ma * ma
for mi in range(len(ma)):
number = signs[mi] * paddle.prod(
1 - m2[mi] / s2 / (A**2 + (ma - 0.5) ** 2)
)
if mi == 0:
denom = 2 * paddle.prod(1 - m2[mi] / m2[mi + 1 :])
elif mi == len(ma) - 1:
denom = 2 * paddle.prod(1 - m2[mi] / m2[:mi])
else:
denom = (
2
* paddle.prod(1 - m2[mi] / m2[:mi])
* paddle.prod(1 - m2[mi] / m2[mi + 1 :])
)
Fm[mi] = number / denom
def W(n):
return 1 + 2 * paddle.matmul(
Fm.unsqueeze(0),
paddle.cos(2 * math.pi * ma.unsqueeze(1) * (n - M / 2.0 + 0.5) / M),
)
w = W(paddle.arange(0, M, dtype=dtype))
# normalize (Note that this is not described in the original text [1])
if norm:
scale = 1.0 / W((M - 1) / 2)
w *= scale
w = w.squeeze()
return _truncate(w, needs_trunc)
@window_function_register.register()
def _hamming(M: int, sym: bool = True, dtype: str = 'float64') -> Tensor:
"""Compute a Hamming window.
The Hamming window is a taper formed by using a raised cosine with
non-zero endpoints, optimized to minimize the nearest side lobe.
"""
return _general_hamming(M, 0.54, sym, dtype=dtype)
@window_function_register.register()
def _hann(M: int, sym: bool = True, dtype: str = 'float64') -> Tensor:
"""Compute a Hann window.
The Hann window is a taper formed by using a raised cosine or sine-squared
with ends that touch zero.
"""
return _general_hamming(M, 0.5, sym, dtype=dtype)
@window_function_register.register()
def _tukey(
M: int, alpha=0.5, sym: bool = True, dtype: str = 'float64'
) -> Tensor:
"""Compute a Tukey window.
The Tukey window is also known as a tapered cosine window.
"""
if _len_guards(M):
return paddle.ones((M,), dtype=dtype)
if alpha <= 0:
return paddle.ones((M,), dtype=dtype)
elif alpha >= 1.0:
return _hann(M, sym=sym)
M, needs_trunc = _extend(M, sym)
n = paddle.arange(0, M, dtype=dtype)
width = int(alpha * (M - 1) / 2.0)
n1 = n[0 : width + 1]
n2 = n[width + 1 : M - width - 1]
n3 = n[M - width - 1 :]
w1 = 0.5 * (1 + paddle.cos(math.pi * (-1 + 2.0 * n1 / alpha / (M - 1))))
w2 = paddle.ones(n2.shape, dtype=dtype)
w3 = 0.5 * (
1
+ paddle.cos(math.pi * (-2.0 / alpha + 1 + 2.0 * n3 / alpha / (M - 1)))
)
w = paddle.concat([w1, w2, w3])
return _truncate(w, needs_trunc)
@window_function_register.register()
def _gaussian(
M: int, std: float, sym: bool = True, dtype: str = 'float64'
) -> Tensor:
"""Compute a Gaussian window.
The Gaussian widows has a Gaussian shape defined by the standard deviation(std).
"""
if _len_guards(M):
return paddle.ones((M,), dtype=dtype)
M, needs_trunc = _extend(M, sym)
n = paddle.arange(0, M, dtype=dtype) - (M - 1.0) / 2.0
sig2 = 2 * std * std
w = paddle.exp(-(n**2) / sig2)
return _truncate(w, needs_trunc)
@window_function_register.register()
def _exponential(
M: int, center=None, tau=1.0, sym: bool = True, dtype: str = 'float64'
) -> Tensor:
"""Compute an exponential (or Poisson) window."""
if sym and center is not None:
raise ValueError("If sym==True, center must be None.")
if _len_guards(M):
return paddle.ones((M,), dtype=dtype)
M, needs_trunc = _extend(M, sym)
if center is None:
center = (M - 1) / 2
n = paddle.arange(0, M, dtype=dtype)
w = paddle.exp(-paddle.abs(n - center) / tau)
return _truncate(w, needs_trunc)
@window_function_register.register()
def _triang(M: int, sym: bool = True, dtype: str = 'float64') -> Tensor:
"""Compute a triangular window."""
if _len_guards(M):
return paddle.ones((M,), dtype=dtype)
M, needs_trunc = _extend(M, sym)
n = paddle.arange(1, (M + 1) // 2 + 1, dtype=dtype)
if M % 2 == 0:
w = (2 * n - 1.0) / M
w = paddle.concat([w, w[::-1]])
else:
w = 2 * n / (M + 1.0)
w = paddle.concat([w, w[-2::-1]])
return _truncate(w, needs_trunc)
@window_function_register.register()
def _bohman(M: int, sym: bool = True, dtype: str = 'float64') -> Tensor:
"""Compute a Bohman window.
The Bohman window is the autocorrelation of a cosine window.
"""
if _len_guards(M):
return paddle.ones((M,), dtype=dtype)
M, needs_trunc = _extend(M, sym)
fac = paddle.abs(paddle.linspace(-1, 1, M, dtype=dtype)[1:-1])
w = (1 - fac) * paddle.cos(math.pi * fac) + 1.0 / math.pi * paddle.sin(
math.pi * fac
)
w = _cat([0, w, 0], dtype)
return _truncate(w, needs_trunc)
@window_function_register.register()
def _blackman(M: int, sym: bool = True, dtype: str = 'float64') -> Tensor:
"""Compute a Blackman window.
The Blackman window is a taper formed by using the first three terms of
a summation of cosines. It was designed to have close to the minimal
leakage possible. It is close to optimal, only slightly worse than a
Kaiser window.
"""
return _general_cosine(M, [0.42, 0.50, 0.08], sym, dtype=dtype)
@window_function_register.register()
def _cosine(M: int, sym: bool = True, dtype: str = 'float64') -> Tensor:
"""Compute a window with a simple cosine shape."""
if _len_guards(M):
return paddle.ones((M,), dtype=dtype)
M, needs_trunc = _extend(M, sym)
w = paddle.sin(math.pi / M * (paddle.arange(0, M, dtype=dtype) + 0.5))
return _truncate(w, needs_trunc)
def get_window(
window: _WindowLiteral | tuple[_WindowLiteral, float],
win_length: int,
fftbins: bool = True,
dtype: str | None = 'float64',
) -> Tensor:
"""Return a window of a given length and type.
Args:
window (Union[str, Tuple[str, float]]): The window function applied to the signal before the Fourier transform. Supported window functions: 'hamming', 'hann', 'gaussian', 'general_gaussian', 'exponential', 'triang', 'bohman', 'blackman', 'cosine', 'tukey', 'taylor', 'bartlett', 'kaiser', 'nuttall'.
win_length (int): Number of samples.
fftbins (bool, optional): If True, create a "periodic" window. Otherwise, create a "symmetric" window, for use in filter design. Defaults to True.
dtype (str, optional): The data type of the return window. Defaults to 'float64'.
Returns:
Tensor: The window represented as a tensor.
Examples:
.. code-block:: pycon
>>> import paddle
>>> n_fft = 512
>>> cosine_window = paddle.audio.functional.get_window('cosine', n_fft)
>>> std = 7
>>> gaussian_window = paddle.audio.functional.get_window(('gaussian', std), n_fft)
"""
if dtype is None:
dtype = 'float32'
sym = not fftbins
args = ()
if isinstance(window, tuple):
winstr = window[0]
if len(window) > 1:
args = window[1:]
elif isinstance(window, str):
if window in ['gaussian', 'exponential', 'kaiser']:
raise ValueError(
"The '" + window + "' window needs one or "
"more parameters -- pass a tuple."
)
else:
winstr = window
else:
raise ValueError(f"{type(window)} as window type is not supported.")
try:
winfunc = window_function_register.get('_' + winstr)
except KeyError as e:
raise ValueError("Unknown window type.") from e
params = (win_length, *args)
kwargs = {'sym': sym}
return winfunc(*params, dtype=dtype, **kwargs)
def _apply_window_postprocess(
w: Tensor,
*,
layout: str | None = None,
device: PlaceLike | None = None,
pin_memory: bool = False,
requires_grad: bool = False,
) -> Tensor:
if layout not in (None, 'strided'):
raise RuntimeError(
"Window functions only support layout='strided' or None"
)
if layout is not None:
warnings.warn("layout only supports 'strided' in Paddle; ignored")
if in_dynamic_or_pir_mode():
device = (
_get_paddle_place(device)
if device is not None
else _current_expected_place()
)
if pin_memory and paddle.in_dynamic_mode() and device is not None:
device = _to_pinned_place(device)
w = w.to(device=device)
if pin_memory and paddle.in_dynamic_mode():
w = w.pin_memory()
if requires_grad is True:
w.stop_gradient = False
return w
def hamming_window(
window_length: int,
periodic: bool = True,
alpha: float = 0.54,
beta: float = 0.46,
*,
dtype: str = 'float32',
layout: str | None = None,
device: PlaceLike | None = None,
pin_memory: bool = False,
requires_grad: bool = False,
):
"""
Compute a generalized Hamming window.
Args:
window_length (int): The size of the returned window. Must be positive.
periodic (bool, optional): If True, returns a window for use as a periodic function; if False, returns a symmetric window. Defaults to True.
alpha (float, optional): The coefficient α in the equation above. Defaults to 0.54.
beta (float, optional): The coefficient β in the equation above. Defaults to 0.46.
dtype (str, optional): The data type of the returned tensor. Defaults to 'float32'.
layout (str, optional): Only included for API consistency with PyTorch; ignored in Paddle. Defaults to None.
device(PlaceLike|None, optional): The desired device of returned tensor.
if None, uses the current device for the default tensor type (see paddle.device.set_device()).
device will be the CPU for CPU tensor types and the current CUDA device for CUDA tensor types. Default: None.
pin_memory(bool, optional): If set, return tensor would be allocated in the pinned memory. Works only for CPU tensors. Default: False
requires_grad(bool, optional): If autograd should record operations on the returned tensor. Default: False.
Returns:
Tensor: A 1-D tensor of shape `(window_length,)` containing the Hamming window.
Examples:
.. code-block:: pycon
>>> import paddle
>>> win = paddle.hamming_window(400, requires_grad=True)
>>> win = paddle.hamming_window(256, alpha=0.5, beta=0.5)
"""
w0 = get_window('hamming', window_length, fftbins=periodic, dtype=dtype)
alpha0, beta0 = 0.54, 0.46
B = beta / beta0
A = alpha - B * alpha0
w = A + B * w0
return _apply_window_postprocess(
w,
layout=layout,
device=device,
pin_memory=pin_memory,
requires_grad=requires_grad,
)
def hann_window(
window_length: int,
periodic: bool = True,
*,
dtype: str = 'float32',
layout: str | None = None,
device: PlaceLike | None = None,
pin_memory: bool = False,
requires_grad: bool = False,
):
"""
Compute a Hann window.
Args:
window_length (int): The size of the returned window. Must be positive.
periodic (bool, optional): If True, returns a window for use as a periodic function; if False, returns a symmetric window. Defaults to True.
dtype (str, optional): The data type of the returned tensor. Defaults to 'float32'.
layout (str, optional): Only included for API consistency with PyTorch; ignored in Paddle. Defaults to None.
device(PlaceLike|None, optional): The desired device of returned tensor.
if None, uses the current device for the default tensor type (see paddle.device.set_device()).
device will be the CPU for CPU tensor types and the current CUDA device for CUDA tensor types. Default: None.
pin_memory(bool, optional): If set, return tensor would be allocated in the pinned memory. Works only for CPU tensors. Default: False
requires_grad(bool, optional): If autograd should record operations on the returned tensor. Default: False.
Returns:
Tensor: A 1-D tensor of shape `(window_length,)` containing the Hann window.
Examples:
.. code-block:: pycon
>>> import paddle
>>> win = paddle.hann_window(512)
>>> win = paddle.hann_window(512, requires_grad=True)
"""
w = get_window('hann', window_length, fftbins=periodic, dtype=dtype)
return _apply_window_postprocess(
w,
layout=layout,
device=device,
pin_memory=pin_memory,
requires_grad=requires_grad,
)
def kaiser_window(
window_length: int,
periodic: bool = True,
beta: float = 12.0,
*,
dtype: str | None = 'float32',
layout: str | None = None,
device: PlaceLike | None = None,
pin_memory: bool = False,
requires_grad: bool = False,
out: Tensor | None = None,
):
"""
Compute a Kaiser window.
Args:
window_length (int): The size of the returned window. Must be positive.
periodic (bool, optional): If True, returns a window for use as a periodic function; if False, returns a symmetric window. Defaults to True.
beta (float, optional): Shape parameter for the window. Defaults to 12.0.
dtype (str, optional): The data type of the returned tensor. Defaults to 'float32'.
layout (str, optional): Only included for API consistency with PyTorch; ignored in Paddle. Defaults to None.
device(PlaceLike|None, optional): The desired device of returned tensor.
if None, uses the current device for the default tensor type (see paddle.device.set_device()).
device will be the CPU for CPU tensor types and the current CUDA device for CUDA tensor types. Default: None.
pin_memory(bool, optional): If set, return tensor would be allocated in the pinned memory. Works only for CPU tensors. Default: False
requires_grad(bool, optional): If autograd should record operations on the returned tensor. Default: False.
out(Tensor|None, optional): The output tensor. Default: None.
Returns:
Tensor: A 1-D tensor of shape `(window_length,)` containing the Kaiser window.
Examples:
.. code-block:: pycon
>>> import paddle
>>> win = paddle.kaiser_window(400, beta=8.6)
>>> win = paddle.kaiser_window(400, requires_grad=True)
"""
w = get_window(
('kaiser', beta), window_length, fftbins=periodic, dtype=dtype
)
w = _apply_window_postprocess(
w,
layout=layout,
device=device,
pin_memory=pin_memory,
requires_grad=requires_grad,
)
return paddle.assign(w, out) if out is not None else w
def blackman_window(
window_length: int,
periodic: bool = True,
*,
dtype: str = 'float32',
layout: str | None = None,
device: PlaceLike | None = None,
pin_memory: bool = False,
requires_grad: bool = False,
):
"""
Compute a Blackman window.
Args:
window_length (int): The size of the returned window. Must be positive.
periodic (bool, optional): If True, returns a window for use as a periodic function; if False, returns a symmetric window. Defaults to True.
dtype (str, optional): The data type of the returned tensor. Defaults to 'float32'.
layout (str, optional): Only included for API consistency with PyTorch; ignored in Paddle. Defaults to None.
device(PlaceLike|None, optional): The desired device of returned tensor.
if None, uses the current device for the default tensor type (see paddle.device.set_device()).
device will be the CPU for CPU tensor types and the current CUDA device for CUDA tensor types. Default: None.
pin_memory(bool, optional): If set, return tensor would be allocated in the pinned memory. Works only for CPU tensors. Default: False
requires_grad(bool, optional): If autograd should record operations on the returned tensor. Default: False.
Returns:
Tensor: A 1-D tensor of shape `(window_length,)` containing the Blackman window.
Examples:
.. code-block:: pycon
>>> import paddle
>>> win = paddle.blackman_window(256)
>>> win = paddle.blackman_window(256, requires_grad=True)
"""
w = get_window('blackman', window_length, fftbins=periodic, dtype=dtype)
return _apply_window_postprocess(
w,
layout=layout,
device=device,
pin_memory=pin_memory,
requires_grad=requires_grad,
)
def bartlett_window(
window_length: int,
periodic: bool = True,
*,
dtype: str = 'float32',
layout: str | None = None,
device: PlaceLike | None = None,
pin_memory: bool = False,
requires_grad: bool = False,
):
"""
Compute a Bartlett window.
Args:
window_length (int): The size of the returned window. Must be positive.
periodic (bool, optional): If True, returns a window for use as a periodic function; if False, returns a symmetric window. Defaults to True.
dtype (str, optional): The data type of the returned tensor. Defaults to 'float32'.
layout (str, optional): Only included for API consistency with PyTorch; ignored in Paddle. Defaults to None.
device(PlaceLike|None, optional): The desired device of returned tensor.
if None, uses the current device for the default tensor type (see paddle.device.set_device()).
device will be the CPU for CPU tensor types and the current CUDA device for CUDA tensor types. Default: None.
pin_memory(bool, optional): If set, return tensor would be allocated in the pinned memory. Works only for CPU tensors. Default: False
requires_grad(bool, optional): If autograd should record operations on the returned tensor. Default: False.
Returns:
Tensor: A 1-D tensor of shape `(window_length,)` containing the Bartlett window.
Examples:
.. code-block:: pycon
>>> import paddle
>>> n_fft = 512
>>> win = paddle.bartlett_window(n_fft)
>>> win = paddle.bartlett_window(n_fft, requires_grad=True)
"""
w = get_window('bartlett', window_length, fftbins=periodic, dtype=dtype)
return _apply_window_postprocess(
w,
layout=layout,
device=device,
pin_memory=pin_memory,
requires_grad=requires_grad,
)