1055 lines
34 KiB
Python
1055 lines
34 KiB
Python
# Copyright (c) 2021 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.
|
|
import re
|
|
import sys
|
|
import unittest
|
|
|
|
import numpy as np
|
|
import scipy.signal
|
|
from numpy import fft
|
|
from numpy.lib.stride_tricks import as_strided
|
|
from op_test import get_device_place, is_custom_device
|
|
|
|
import paddle
|
|
|
|
paddle.set_default_dtype('float64')
|
|
|
|
DEVICES = [paddle.CPUPlace()]
|
|
if paddle.is_compiled_with_cuda() or is_custom_device():
|
|
DEVICES.append(get_device_place())
|
|
TEST_CASE_NAME = 'test_case'
|
|
|
|
# Constrain STFT block sizes to 256 KB
|
|
MAX_MEM_BLOCK = 2**8 * 2**10
|
|
|
|
|
|
def fix_length(data, size, axis=-1, **kwargs):
|
|
kwargs.setdefault("mode", "constant")
|
|
|
|
n = data.shape[axis]
|
|
|
|
if n > size:
|
|
slices = [slice(None)] * data.ndim
|
|
slices[axis] = slice(0, size)
|
|
return data[tuple(slices)]
|
|
|
|
elif n < size:
|
|
lengths = [(0, 0)] * data.ndim
|
|
lengths[axis] = (0, size - n)
|
|
return np.pad(data, lengths, **kwargs)
|
|
|
|
return data
|
|
|
|
|
|
def tiny(x):
|
|
# Make sure we have an array view
|
|
x = np.asarray(x)
|
|
|
|
# Only floating types generate a tiny
|
|
if np.issubdtype(x.dtype, np.floating) or np.issubdtype(
|
|
x.dtype, np.complexfloating
|
|
):
|
|
dtype = x.dtype
|
|
else:
|
|
dtype = np.float32
|
|
|
|
return np.finfo(dtype).tiny
|
|
|
|
|
|
def normalize(S, norm=np.inf, axis=0, threshold=None, fill=None):
|
|
# Avoid div-by-zero
|
|
if threshold is None:
|
|
threshold = tiny(S)
|
|
|
|
elif threshold <= 0:
|
|
raise Exception(f"threshold={threshold} must be strictly positive")
|
|
|
|
if fill not in [None, False, True]:
|
|
raise Exception(f"fill={fill} must be None or boolean")
|
|
|
|
if not np.all(np.isfinite(S)):
|
|
raise Exception("Input must be finite")
|
|
|
|
# All norms only depend on magnitude, let's do that first
|
|
mag = np.abs(S).astype(np.float64)
|
|
|
|
# For max/min norms, filling with 1 works
|
|
fill_norm = 1
|
|
|
|
if norm == np.inf:
|
|
length = np.max(mag, axis=axis, keepdims=True)
|
|
|
|
elif norm == -np.inf:
|
|
length = np.min(mag, axis=axis, keepdims=True)
|
|
|
|
elif norm == 0:
|
|
if fill is True:
|
|
raise Exception("Cannot normalize with norm=0 and fill=True")
|
|
|
|
length = np.sum(mag > 0, axis=axis, keepdims=True, dtype=mag.dtype)
|
|
|
|
elif np.issubdtype(type(norm), np.number) and norm > 0:
|
|
length = np.sum(mag**norm, axis=axis, keepdims=True) ** (1.0 / norm)
|
|
|
|
if axis is None:
|
|
fill_norm = mag.size ** (-1.0 / norm)
|
|
else:
|
|
fill_norm = mag.shape[axis] ** (-1.0 / norm)
|
|
|
|
elif norm is None:
|
|
return S
|
|
|
|
else:
|
|
raise Exception(f"Unsupported norm: {norm!r}")
|
|
|
|
# indices where norm is below the threshold
|
|
small_idx = length < threshold
|
|
|
|
Snorm = np.empty_like(S)
|
|
if fill is None:
|
|
# Leave small indices un-normalized
|
|
length[small_idx] = 1.0
|
|
Snorm[:] = S / length
|
|
|
|
elif fill:
|
|
# If we have a non-zero fill value, we locate those entries by
|
|
# doing a nan-divide.
|
|
# If S was finite, then length is finite (except for small positions)
|
|
length[small_idx] = np.nan
|
|
Snorm[:] = S / length
|
|
Snorm[np.isnan(Snorm)] = fill_norm
|
|
else:
|
|
# Set small values to zero by doing an inf-divide.
|
|
# This is safe (by IEEE-754) as long as S is finite.
|
|
length[small_idx] = np.inf
|
|
Snorm[:] = S / length
|
|
|
|
return Snorm
|
|
|
|
|
|
def __window_ss_fill(x, win_sq, n_frames, hop_length): # pragma: no cover
|
|
"""Helper function for window sum-square calculation."""
|
|
|
|
n = len(x)
|
|
n_fft = len(win_sq)
|
|
for i in range(n_frames):
|
|
sample = i * hop_length
|
|
x[sample : min(n, sample + n_fft)] += win_sq[
|
|
: max(0, min(n_fft, n - sample))
|
|
]
|
|
|
|
|
|
def window_sumsquare(
|
|
window,
|
|
n_frames,
|
|
hop_length=512,
|
|
win_length=None,
|
|
n_fft=2048,
|
|
dtype=np.float32,
|
|
norm=None,
|
|
):
|
|
if win_length is None:
|
|
win_length = n_fft
|
|
|
|
n = n_fft + hop_length * (n_frames - 1)
|
|
x = np.zeros(n, dtype=dtype)
|
|
|
|
# Compute the squared window at the desired length
|
|
win_sq = get_window(window, win_length)
|
|
win_sq = normalize(win_sq, norm=norm) ** 2
|
|
win_sq = pad_center(win_sq, n_fft)
|
|
|
|
# Fill the envelope
|
|
__window_ss_fill(x, win_sq, n_frames, hop_length)
|
|
|
|
return x
|
|
|
|
|
|
def dtype_c2r(d, default=np.float32):
|
|
mapping = {
|
|
np.dtype(np.complex64): np.float32,
|
|
np.dtype(np.complex128): np.float64,
|
|
}
|
|
|
|
# If we're given a real type already, return it
|
|
dt = np.dtype(d)
|
|
if dt.kind == "f":
|
|
return dt
|
|
|
|
# Otherwise, try to map the dtype.
|
|
# If no match is found, return the default.
|
|
return np.dtype(mapping.get(np.dtype(d), default))
|
|
|
|
|
|
def dtype_r2c(d, default=np.complex64):
|
|
mapping = {
|
|
np.dtype(np.float32): np.complex64,
|
|
np.dtype(np.float64): np.complex128,
|
|
}
|
|
|
|
# If we're given a complex type already, return it
|
|
dt = np.dtype(d)
|
|
if dt.kind == "c":
|
|
return dt
|
|
|
|
# Otherwise, try to map the dtype.
|
|
# If no match is found, return the default.
|
|
return np.dtype(mapping.get(dt, default))
|
|
|
|
|
|
def frame(x, frame_length, hop_length, axis=-1):
|
|
if not isinstance(x, np.ndarray):
|
|
raise Exception(
|
|
f"Input must be of type numpy.ndarray, given type(x)={type(x)}"
|
|
)
|
|
|
|
if x.shape[axis] < frame_length:
|
|
raise Exception(
|
|
f"Input is too short (n={x.shape[axis]:d})"
|
|
f" for frame_length={frame_length:d}"
|
|
)
|
|
|
|
if hop_length < 1:
|
|
raise Exception(f"Invalid hop_length: {hop_length:d}")
|
|
|
|
if axis == -1 and not x.flags["F_CONTIGUOUS"]:
|
|
print(
|
|
f"librosa.util.frame called with axis={axis} "
|
|
"on a non-contiguous input. This will result in a copy."
|
|
)
|
|
x = np.asfortranarray(x)
|
|
elif axis == 0 and not x.flags["C_CONTIGUOUS"]:
|
|
print(
|
|
f"librosa.util.frame called with axis={axis} "
|
|
"on a non-contiguous input. This will result in a copy."
|
|
)
|
|
x = np.ascontiguousarray(x)
|
|
|
|
n_frames = 1 + (x.shape[axis] - frame_length) // hop_length
|
|
strides = np.asarray(x.strides)
|
|
|
|
new_stride = np.prod(strides[strides > 0] // x.itemsize) * x.itemsize
|
|
|
|
if axis == -1:
|
|
shape = [*list(x.shape)[:-1], frame_length, n_frames]
|
|
strides = [*list(strides), hop_length * new_stride]
|
|
|
|
elif axis == 0:
|
|
shape = [n_frames, frame_length, *list(x.shape)[1:]]
|
|
strides = [hop_length * new_stride, *list(strides)]
|
|
|
|
else:
|
|
raise Exception(f"Frame axis={axis} must be either 0 or -1")
|
|
|
|
return as_strided(x, shape=shape, strides=strides)
|
|
|
|
|
|
def pad_center(data, size, axis=-1, **kwargs):
|
|
kwargs.setdefault("mode", "constant")
|
|
|
|
n = data.shape[axis]
|
|
|
|
lpad = int((size - n) // 2)
|
|
|
|
lengths = [(0, 0)] * data.ndim
|
|
lengths[axis] = (lpad, int(size - n - lpad))
|
|
|
|
if lpad < 0:
|
|
raise Exception(
|
|
f"Target size ({size:d}) must be at least input size ({n:d})"
|
|
)
|
|
|
|
return np.pad(data, lengths, **kwargs)
|
|
|
|
|
|
def get_window(window, Nx, fftbins=True):
|
|
if callable(window):
|
|
return window(Nx)
|
|
|
|
elif isinstance(window, (str, tuple)) or np.isscalar(window):
|
|
# TODO: if we add custom window functions in librosa, call them here
|
|
|
|
return scipy.signal.get_window(window, Nx, fftbins=fftbins)
|
|
|
|
elif isinstance(window, (np.ndarray, list)):
|
|
if len(window) == Nx:
|
|
return np.asarray(window)
|
|
|
|
raise Exception(f"Window size mismatch: {len(window):d} != {Nx:d}")
|
|
else:
|
|
raise Exception(f"Invalid window specification: {window}")
|
|
|
|
|
|
def __overlap_add(y, ytmp, hop_length):
|
|
# numba-accelerated overlap add for inverse stft
|
|
# y is the pre-allocated output buffer
|
|
# ytmp is the windowed inverse-stft frames
|
|
# hop_length is the hop-length of the STFT analysis
|
|
|
|
n_fft = ytmp.shape[0]
|
|
for frame in range(ytmp.shape[1]):
|
|
sample = frame * hop_length
|
|
y[sample : (sample + n_fft)] += ytmp[:, frame]
|
|
|
|
|
|
def stft(
|
|
x,
|
|
n_fft=2048,
|
|
hop_length=None,
|
|
win_length=None,
|
|
window="hann",
|
|
center=True,
|
|
pad_mode="reflect",
|
|
):
|
|
y = x
|
|
input_rank = len(y.shape)
|
|
if input_rank == 2:
|
|
assert y.shape[0] == 1 # Only 1d input supported in librosa
|
|
y = y.squeeze(0)
|
|
dtype = None
|
|
|
|
# By default, use the entire frame
|
|
if win_length is None:
|
|
win_length = n_fft
|
|
|
|
# Set the default hop, if it's not already specified
|
|
if hop_length is None:
|
|
hop_length = int(win_length // 4)
|
|
|
|
fft_window = get_window(window, win_length, fftbins=True)
|
|
|
|
# Pad the window out to n_fft size
|
|
fft_window = pad_center(fft_window, n_fft)
|
|
|
|
# Reshape so that the window can be broadcast
|
|
fft_window = fft_window.reshape((-1, 1))
|
|
|
|
# Pad the time series so that frames are centered
|
|
if center:
|
|
if n_fft > y.shape[-1]:
|
|
print(
|
|
f"n_fft={n_fft} is too small for input signal of length={y.shape[-1]}"
|
|
)
|
|
|
|
y = np.pad(y, int(n_fft // 2), mode=pad_mode)
|
|
|
|
elif n_fft > y.shape[-1]:
|
|
raise Exception(
|
|
f"n_fft={n_fft} is too large for input signal of length={y.shape[-1]}"
|
|
)
|
|
|
|
# Window the time series.
|
|
y_frames = frame(y, frame_length=n_fft, hop_length=hop_length)
|
|
|
|
if dtype is None:
|
|
dtype = dtype_r2c(y.dtype)
|
|
|
|
# Pre-allocate the STFT matrix
|
|
stft_matrix = np.empty(
|
|
(int(1 + n_fft // 2), y_frames.shape[1]), dtype=dtype, order="F"
|
|
)
|
|
|
|
# how many columns can we fit within MAX_MEM_BLOCK?
|
|
n_columns = MAX_MEM_BLOCK // (stft_matrix.shape[0] * stft_matrix.itemsize)
|
|
n_columns = max(n_columns, 1)
|
|
|
|
for bl_s in range(0, stft_matrix.shape[1], n_columns):
|
|
bl_t = min(bl_s + n_columns, stft_matrix.shape[1])
|
|
|
|
stft_matrix[:, bl_s:bl_t] = fft.rfft(
|
|
fft_window * y_frames[:, bl_s:bl_t], axis=0
|
|
)
|
|
|
|
if input_rank == 2:
|
|
stft_matrix = np.expand_dims(stft_matrix, 0)
|
|
|
|
return stft_matrix
|
|
|
|
|
|
def istft(
|
|
x,
|
|
hop_length=None,
|
|
win_length=None,
|
|
window="hann",
|
|
center=True,
|
|
length=None,
|
|
):
|
|
stft_matrix = x
|
|
input_rank = len(stft_matrix.shape)
|
|
if input_rank == 3:
|
|
assert stft_matrix.shape[0] == 1 # Only 2d input supported in librosa
|
|
stft_matrix = stft_matrix.squeeze(0)
|
|
dtype = None
|
|
|
|
n_fft = 2 * (stft_matrix.shape[0] - 1)
|
|
|
|
# By default, use the entire frame
|
|
if win_length is None:
|
|
win_length = n_fft
|
|
|
|
# Set the default hop, if it's not already specified
|
|
if hop_length is None:
|
|
hop_length = int(win_length // 4)
|
|
|
|
ifft_window = get_window(window, win_length, fftbins=True)
|
|
|
|
# Pad out to match n_fft, and add a broadcasting axis
|
|
ifft_window = pad_center(ifft_window, n_fft)[:, np.newaxis]
|
|
|
|
# For efficiency, trim STFT frames according to signal length if available
|
|
if length:
|
|
if center:
|
|
padded_length = length + int(n_fft)
|
|
else:
|
|
padded_length = length
|
|
n_frames = min(
|
|
stft_matrix.shape[1], int(np.ceil(padded_length / hop_length))
|
|
)
|
|
else:
|
|
n_frames = stft_matrix.shape[1]
|
|
|
|
expected_signal_len = n_fft + hop_length * (n_frames - 1)
|
|
|
|
if dtype is None:
|
|
dtype = dtype_c2r(stft_matrix.dtype)
|
|
|
|
y = np.zeros(expected_signal_len, dtype=dtype)
|
|
|
|
n_columns = MAX_MEM_BLOCK // (stft_matrix.shape[0] * stft_matrix.itemsize)
|
|
n_columns = min(n_columns, 1)
|
|
|
|
frame = 0
|
|
for bl_s in range(0, n_frames, n_columns):
|
|
bl_t = min(bl_s + n_columns, n_frames)
|
|
|
|
# invert the block and apply the window function
|
|
ytmp = ifft_window * fft.irfft(stft_matrix[:, bl_s:bl_t], axis=0)
|
|
|
|
# Overlap-add the istft block starting at the i'th frame
|
|
__overlap_add(y[frame * hop_length :], ytmp, hop_length)
|
|
|
|
frame += bl_t - bl_s
|
|
|
|
# Normalize by sum of squared window
|
|
ifft_window_sum = window_sumsquare(
|
|
window,
|
|
n_frames,
|
|
win_length=win_length,
|
|
n_fft=n_fft,
|
|
hop_length=hop_length,
|
|
dtype=dtype,
|
|
)
|
|
|
|
approx_nonzero_indices = ifft_window_sum > tiny(ifft_window_sum)
|
|
y[approx_nonzero_indices] /= ifft_window_sum[approx_nonzero_indices]
|
|
|
|
if length is None:
|
|
# If we don't need to control length, just do the usual center trimming
|
|
# to eliminate padded data
|
|
if center:
|
|
y = y[int(n_fft // 2) : -int(n_fft // 2)]
|
|
else:
|
|
if center:
|
|
# If we're centering, crop off the first n_fft//2 samples
|
|
# and then trim/pad to the target length.
|
|
# We don't trim the end here, so that if the signal is zero-padded
|
|
# to a longer duration, the decay is smooth by windowing
|
|
start = int(n_fft // 2)
|
|
else:
|
|
# If we're not centering, start at 0 and trim/pad as necessary
|
|
start = 0
|
|
|
|
y = fix_length(y[start:], length)
|
|
|
|
if input_rank == 3:
|
|
y = np.expand_dims(y, 0)
|
|
|
|
return y
|
|
|
|
|
|
def frame_for_api_test(x, frame_length, hop_length, axis=-1):
|
|
if axis == -1 and not x.flags["C_CONTIGUOUS"]:
|
|
x = np.ascontiguousarray(x)
|
|
elif axis == 0 and not x.flags["F_CONTIGUOUS"]:
|
|
x = np.asfortranarray(x)
|
|
|
|
n_frames = 1 + (x.shape[axis] - frame_length) // hop_length
|
|
strides = np.asarray(x.strides)
|
|
|
|
if axis == -1:
|
|
shape = [*list(x.shape)[:-1], frame_length, n_frames]
|
|
strides = [*list(strides), hop_length * x.itemsize]
|
|
|
|
elif axis == 0:
|
|
shape = [n_frames, frame_length, *list(x.shape)[1:]]
|
|
strides = [hop_length * x.itemsize, *list(strides)]
|
|
|
|
else:
|
|
raise ValueError(f"Frame axis={axis} must be either 0 or -1")
|
|
|
|
return as_strided(x, shape=shape, strides=strides)
|
|
|
|
|
|
def overlap_add_for_api_test(x, hop_length, axis=-1):
|
|
assert axis in [0, -1], 'axis should be 0/-1.'
|
|
assert len(x.shape) >= 2, 'Input dims shoulb be >= 2.'
|
|
|
|
squeeze_output = False
|
|
if len(x.shape) == 2:
|
|
squeeze_output = True
|
|
dim = 0 if axis == -1 else -1
|
|
x = np.expand_dims(x, dim) # batch
|
|
|
|
n_frames = x.shape[axis]
|
|
frame_length = x.shape[1] if axis == 0 else x.shape[-2]
|
|
|
|
# Assure no gaps between frames.
|
|
assert 0 < hop_length <= frame_length, (
|
|
f'hop_length should be in (0, frame_length({frame_length})], but got {hop_length}.'
|
|
)
|
|
|
|
seq_length = (n_frames - 1) * hop_length + frame_length
|
|
|
|
reshape_output = False
|
|
if len(x.shape) > 3:
|
|
reshape_output = True
|
|
if axis == 0:
|
|
target_shape = [seq_length, *list(x.shape[2:])]
|
|
x = x.reshape(n_frames, frame_length, np.prod(x.shape[2:]))
|
|
else:
|
|
target_shape = [*list(x.shape[:-2]), seq_length]
|
|
x = x.reshape(np.prod(x.shape[:-2]), frame_length, n_frames)
|
|
|
|
if axis == 0:
|
|
x = x.transpose((2, 1, 0))
|
|
|
|
y = np.zeros(shape=[np.prod(x.shape[:-2]), seq_length], dtype=x.dtype)
|
|
for i in range(x.shape[0]):
|
|
for frame in range(x.shape[-1]):
|
|
sample = frame * hop_length
|
|
y[i, sample : sample + frame_length] += x[i, :, frame]
|
|
|
|
if axis == 0:
|
|
y = y.transpose((1, 0))
|
|
|
|
if reshape_output:
|
|
y = y.reshape(target_shape)
|
|
|
|
if squeeze_output:
|
|
y = y.squeeze(-1) if axis == 0 else y.squeeze(0)
|
|
|
|
return y
|
|
|
|
|
|
def place(devices, key='place'):
|
|
def decorate(cls):
|
|
module = sys.modules[cls.__module__].__dict__
|
|
raw_classes = {
|
|
k: v for k, v in module.items() if k.startswith(cls.__name__)
|
|
}
|
|
|
|
for raw_name, raw_cls in raw_classes.items():
|
|
for d in devices:
|
|
test_cls = dict(raw_cls.__dict__)
|
|
test_cls.update({key: d})
|
|
new_name = raw_name + '.' + d.__class__.__name__
|
|
module[new_name] = type(new_name, (raw_cls,), test_cls)
|
|
del module[raw_name]
|
|
return cls
|
|
|
|
return decorate
|
|
|
|
|
|
def setUpModule():
|
|
global rtol
|
|
global atol
|
|
# All test case will use float64 for compare precision, refs:
|
|
# https://github.com/PaddlePaddle/Paddle/wiki/Upgrade-OP-Precision-to-Float64
|
|
rtol = {
|
|
'float32': 1e-06,
|
|
'float64': 1e-7,
|
|
'complex64': 1e-06,
|
|
'complex128': 1e-7,
|
|
}
|
|
atol = {
|
|
'float32': 0.0,
|
|
'float64': 0.0,
|
|
'complex64': 0.0,
|
|
'complex128': 0.0,
|
|
}
|
|
|
|
|
|
def tearDownModule():
|
|
pass
|
|
|
|
|
|
def rand_x(
|
|
dims=1,
|
|
dtype='float64',
|
|
min_dim_len=1,
|
|
max_dim_len=10,
|
|
shape=None,
|
|
complex=False,
|
|
):
|
|
if shape is None:
|
|
shape = [
|
|
np.random.randint(min_dim_len, max_dim_len) for i in range(dims)
|
|
]
|
|
if complex:
|
|
return np.random.randn(*shape).astype(dtype) + 1.0j * np.random.randn(
|
|
*shape
|
|
).astype(dtype)
|
|
else:
|
|
return np.random.randn(*shape).astype(dtype)
|
|
|
|
|
|
def parameterize(attrs, input_values=None):
|
|
if isinstance(attrs, str):
|
|
attrs = [attrs]
|
|
input_dicts = (
|
|
attrs
|
|
if input_values is None
|
|
else [dict(zip(attrs, vals)) for vals in input_values]
|
|
)
|
|
|
|
def decorator(base_class):
|
|
test_class_module = sys.modules[base_class.__module__].__dict__
|
|
for idx, input_dict in enumerate(input_dicts):
|
|
test_class_dict = dict(base_class.__dict__)
|
|
test_class_dict.update(input_dict)
|
|
|
|
name = class_name(base_class, idx, input_dict)
|
|
|
|
test_class_module[name] = type(name, (base_class,), test_class_dict)
|
|
|
|
for method_name in list(base_class.__dict__):
|
|
if method_name.startswith("test"):
|
|
delattr(base_class, method_name)
|
|
return base_class
|
|
|
|
return decorator
|
|
|
|
|
|
def class_name(cls, num, params_dict):
|
|
suffix = to_safe_name(
|
|
next((v for v in params_dict.values() if isinstance(v, str)), "")
|
|
)
|
|
if TEST_CASE_NAME in params_dict:
|
|
suffix = to_safe_name(params_dict["test_case"])
|
|
return "{}_{}{}".format(cls.__name__, num, suffix and "_" + suffix)
|
|
|
|
|
|
def to_safe_name(s):
|
|
return str(re.sub("[^a-zA-Z0-9_]+", "_", s))
|
|
|
|
|
|
@place(DEVICES)
|
|
@parameterize(
|
|
(TEST_CASE_NAME, 'x', 'frame_length', 'hop_length', 'axis'),
|
|
[
|
|
('test_1d_input1', rand_x(1, np.float64, shape=[150]), 50, 15, 0),
|
|
('test_1d_input2', rand_x(1, np.float64, shape=[150]), 50, 15, -1),
|
|
('test_2d_input1', rand_x(2, np.float64, shape=[150, 8]), 50, 15, 0),
|
|
('test_2d_input2', rand_x(2, np.float64, shape=[8, 150]), 50, 15, -1),
|
|
('test_3d_input1', rand_x(3, np.float64, shape=[150, 4, 2]), 50, 15, 0),
|
|
('test_3d_input2', rand_x(3, np.float64, shape=[4, 2, 150]), 50, 15, -1),
|
|
]) # fmt: skip
|
|
class TestFrame(unittest.TestCase):
|
|
def test_frame(self):
|
|
np.testing.assert_allclose(
|
|
frame_for_api_test(
|
|
self.x, self.frame_length, self.hop_length, self.axis
|
|
),
|
|
paddle.signal.frame(
|
|
paddle.to_tensor(self.x),
|
|
self.frame_length,
|
|
self.hop_length,
|
|
self.axis,
|
|
),
|
|
rtol=rtol.get(str(self.x.dtype)),
|
|
atol=atol.get(str(self.x.dtype)),
|
|
)
|
|
|
|
|
|
@place(DEVICES)
|
|
@parameterize(
|
|
(TEST_CASE_NAME, 'x', 'frame_length', 'hop_length', 'axis'),
|
|
[
|
|
('test_1d_input1', rand_x(1, np.float64, shape=[150]), 50, 15, 0),
|
|
('test_1d_input2', rand_x(1, np.float64, shape=[150]), 50, 15, -1),
|
|
('test_2d_input1', rand_x(2, np.float64, shape=[150, 8]), 50, 15, 0),
|
|
('test_2d_input2', rand_x(2, np.float64, shape=[8, 150]), 50, 15, -1),
|
|
('test_3d_input1', rand_x(3, np.float64, shape=[150, 4, 2]), 50, 15, 0),
|
|
('test_3d_input2', rand_x(3, np.float64, shape=[4, 2, 150]), 50, 15, -1),
|
|
]) # fmt: skip
|
|
class TestFrameStatic(unittest.TestCase):
|
|
def test_frame_static(self):
|
|
paddle.enable_static()
|
|
mp, sp = paddle.static.Program(), paddle.static.Program()
|
|
with paddle.static.program_guard(mp, sp):
|
|
input = paddle.static.data(
|
|
'input', self.x.shape, dtype=self.x.dtype
|
|
)
|
|
output = (
|
|
paddle.signal.frame(
|
|
input, self.frame_length, self.hop_length, self.axis
|
|
),
|
|
)
|
|
exe = paddle.static.Executor(self.place)
|
|
exe.run(sp)
|
|
[output] = exe.run(mp, feed={'input': self.x}, fetch_list=[output])
|
|
paddle.disable_static()
|
|
|
|
np.testing.assert_allclose(
|
|
frame_for_api_test(
|
|
self.x, self.frame_length, self.hop_length, self.axis
|
|
),
|
|
output,
|
|
rtol=rtol.get(str(self.x.dtype)),
|
|
atol=atol.get(str(self.x.dtype)),
|
|
)
|
|
|
|
|
|
@place(DEVICES)
|
|
@parameterize(
|
|
(TEST_CASE_NAME, 'x', 'frame_length', 'hop_length', 'axis', 'expect_exception'),
|
|
[
|
|
('test_axis', rand_x(1, np.float64, shape=[150]), 50, 15, 2, ValueError),
|
|
('test_hop_length', rand_x(1, np.float64, shape=[150]), 50, 0, -1, ValueError),
|
|
('test_frame_length1', rand_x(2, np.float64, shape=[150, 8]), 0, 15, 0, ValueError),
|
|
('test_frame_length2', rand_x(2, np.float64, shape=[150, 8]), 151, 15, 0, ValueError),
|
|
]) # fmt: skip
|
|
class TestFrameException(unittest.TestCase):
|
|
def test_frame(self):
|
|
with self.assertRaises(self.expect_exception):
|
|
paddle.signal.frame(
|
|
paddle.to_tensor(self.x),
|
|
self.frame_length,
|
|
self.hop_length,
|
|
self.axis,
|
|
)
|
|
|
|
|
|
@place(DEVICES)
|
|
@parameterize(
|
|
(TEST_CASE_NAME, 'x', 'hop_length', 'axis'),
|
|
[
|
|
('test_2d_input1', rand_x(2, np.float64, shape=[3, 50]), 4, 0),
|
|
('test_2d_input2', rand_x(2, np.float64, shape=[50, 3]), 4, -1),
|
|
('test_3d_input1', rand_x(3, np.float64, shape=[5, 40, 2]), 10, 0),
|
|
('test_3d_input2', rand_x(3, np.float64, shape=[2, 40, 5]), 10, -1),
|
|
('test_4d_input1', rand_x(4, np.float64, shape=[8, 12, 5, 3]), 5, 0),
|
|
('test_4d_input2', rand_x(4, np.float64, shape=[3, 5, 12, 8]), 5, -1),
|
|
]) # fmt: skip
|
|
class TestOverlapAdd(unittest.TestCase):
|
|
def test_overlap_add(self):
|
|
np.testing.assert_allclose(
|
|
overlap_add_for_api_test(self.x, self.hop_length, self.axis),
|
|
paddle.signal.overlap_add(
|
|
paddle.to_tensor(self.x), self.hop_length, self.axis
|
|
),
|
|
rtol=rtol.get(str(self.x.dtype)),
|
|
atol=atol.get(str(self.x.dtype)),
|
|
)
|
|
|
|
|
|
@place(DEVICES)
|
|
@parameterize(
|
|
(TEST_CASE_NAME, 'x', 'hop_length', 'axis'),
|
|
[
|
|
('test_2d_input1', rand_x(2, np.float64, shape=[3, 50]), 4, 0),
|
|
('test_2d_input2', rand_x(2, np.float64, shape=[50, 3]), 4, -1),
|
|
('test_3d_input1', rand_x(3, np.float64, shape=[5, 40, 2]), 10, 0),
|
|
('test_3d_input2', rand_x(3, np.float64, shape=[2, 40, 5]), 10, -1),
|
|
('test_4d_input1', rand_x(4, np.float64, shape=[8, 12, 5, 3]), 5, 0),
|
|
('test_4d_input2', rand_x(4, np.float64, shape=[3, 5, 12, 8]), 5, -1),
|
|
]) # fmt: skip
|
|
class TestOverlapAddStatic(unittest.TestCase):
|
|
def test_overlap_add_static(self):
|
|
paddle.enable_static()
|
|
mp, sp = paddle.static.Program(), paddle.static.Program()
|
|
with paddle.static.program_guard(mp, sp):
|
|
input = paddle.static.data(
|
|
'input', self.x.shape, dtype=self.x.dtype
|
|
)
|
|
output = (
|
|
paddle.signal.overlap_add(input, self.hop_length, self.axis),
|
|
)
|
|
exe = paddle.static.Executor(self.place)
|
|
exe.run(sp)
|
|
[output] = exe.run(mp, feed={'input': self.x}, fetch_list=[output])
|
|
paddle.disable_static()
|
|
|
|
np.testing.assert_allclose(
|
|
overlap_add_for_api_test(self.x, self.hop_length, self.axis),
|
|
output,
|
|
rtol=rtol.get(str(self.x.dtype)),
|
|
atol=atol.get(str(self.x.dtype)),
|
|
)
|
|
|
|
|
|
@place(DEVICES)
|
|
@parameterize(
|
|
(TEST_CASE_NAME, 'x', 'hop_length', 'axis', 'expect_exception'),
|
|
[
|
|
('test_axis', rand_x(2, np.float64, shape=[3, 50]), 4, 2, ValueError),
|
|
('test_hop_length', rand_x(2, np.float64, shape=[50, 3]), -1, -1, ValueError),
|
|
]) # fmt: skip
|
|
class TestOverlapAddException(unittest.TestCase):
|
|
def test_overlap_add(self):
|
|
with self.assertRaises(self.expect_exception):
|
|
paddle.signal.overlap_add(
|
|
paddle.to_tensor(self.x), self.hop_length, self.axis
|
|
)
|
|
|
|
|
|
# ================= STFT
|
|
# common args
|
|
# x
|
|
# n_fft,
|
|
# hop_length=None,
|
|
# win_length=None,
|
|
# window=None,
|
|
# center=True,
|
|
# pad_mode='reflect',
|
|
|
|
# paddle only
|
|
# normalized=False,
|
|
# onesided=True,
|
|
|
|
# ================= ISTFT
|
|
# common args
|
|
# x,
|
|
# hop_length=None,
|
|
# win_length=None,
|
|
# window=None,
|
|
# center=True,
|
|
# length=None,
|
|
|
|
# paddle only
|
|
# n_fft,
|
|
# normalized=False,
|
|
# onesided=True,
|
|
# return_complex=False,
|
|
|
|
|
|
@place(DEVICES)
|
|
@parameterize(
|
|
(TEST_CASE_NAME, 'x', 'n_fft', 'hop_length', 'win_length', 'window', 'center', 'pad_mode', 'normalized', 'onesided'),
|
|
[
|
|
('test_1d_input', rand_x(1, np.float64, shape=[160000]), 512,
|
|
None, None, get_window('hann', 512), True, 'reflect', False, True),
|
|
('test_2d_input', rand_x(2, np.float64, shape=[1, 160000]), 512,
|
|
None, None, get_window('hann', 512), True, 'reflect', False, True),
|
|
('test_hop_length', rand_x(2, np.float64, shape=[1, 160000]), 512,
|
|
255, None, get_window('hann', 512), True, 'reflect', False, True),
|
|
('test_win_length', rand_x(2, np.float64, shape=[1, 160000]), 512,
|
|
255, 499, get_window('hann', 499), True, 'reflect', False, True),
|
|
('test_window', rand_x(2, np.float64, shape=[1, 160000]), 512,
|
|
None, None, None, True, 'reflect', False, True),
|
|
('test_center', rand_x(2, np.float64, shape=[1, 160000]), 512,
|
|
None, None, None, False, 'reflect', False, True),
|
|
]) # fmt: skip
|
|
class TestStft(unittest.TestCase):
|
|
def test_stft(self):
|
|
if self.window is None:
|
|
win_p = None
|
|
win_l = 'boxcar' # rectangular window
|
|
else:
|
|
win_p = paddle.to_tensor(self.window)
|
|
win_l = self.window
|
|
|
|
np.testing.assert_allclose(
|
|
stft(
|
|
self.x,
|
|
self.n_fft,
|
|
self.hop_length,
|
|
self.win_length,
|
|
win_l,
|
|
self.center,
|
|
self.pad_mode,
|
|
),
|
|
paddle.signal.stft(
|
|
paddle.to_tensor(self.x),
|
|
self.n_fft,
|
|
self.hop_length,
|
|
self.win_length,
|
|
win_p,
|
|
self.center,
|
|
self.pad_mode,
|
|
self.normalized,
|
|
self.onesided,
|
|
),
|
|
rtol=rtol.get(str(self.x.dtype)),
|
|
atol=atol.get(str(self.x.dtype)),
|
|
)
|
|
|
|
|
|
@place(DEVICES)
|
|
@parameterize(
|
|
(TEST_CASE_NAME, 'x', 'n_fft', 'hop_length', 'win_length', 'window', 'center', 'pad_mode', 'normalized', 'onesided', 'expect_exception'),
|
|
[
|
|
('test_dims', rand_x(1, np.float64, shape=[1, 2, 3]), 512,
|
|
None, None, None, True, 'reflect', False, True, AssertionError),
|
|
('test_hop_length', rand_x(1, np.float64, shape=[16000]), 512,
|
|
0, None, None, True, 'reflect', False, True, AssertionError),
|
|
('test_nfft1', rand_x(1, np.float64, shape=[16000]), 0,
|
|
None, None, None, True, 'reflect', False, True, AssertionError),
|
|
('test_nfft2', rand_x(1, np.float64, shape=[16000]), 16001,
|
|
None, None, None, True, 'reflect', False, True, AssertionError),
|
|
('test_win_length', rand_x(1, np.float64, shape=[16000]), 512,
|
|
None, 0, None, True, 'reflect', False, True, AssertionError),
|
|
('test_win_length', rand_x(1, np.float64, shape=[16000]), 512,
|
|
None, 513, None, True, 'reflect', False, True, AssertionError),
|
|
('test_pad_mode', rand_x(1, np.float64, shape=[16000]), 512,
|
|
None, None, None, True, 'nonsense', False, True, AssertionError),
|
|
('test_complex_onesided', rand_x(1, np.float64, shape=[16000], complex=True), 512,
|
|
None, None, None, False, 'reflect', False, True, AssertionError),
|
|
]) # fmt: skip
|
|
class TestStftException(unittest.TestCase):
|
|
def test_stft(self):
|
|
if self.window is None:
|
|
win_p = None
|
|
else:
|
|
win_p = paddle.to_tensor(self.window)
|
|
|
|
with self.assertRaises(self.expect_exception):
|
|
paddle.signal.stft(
|
|
paddle.to_tensor(self.x),
|
|
self.n_fft,
|
|
self.hop_length,
|
|
self.win_length,
|
|
win_p,
|
|
self.center,
|
|
self.pad_mode,
|
|
self.normalized,
|
|
self.onesided,
|
|
)
|
|
|
|
|
|
@place(DEVICES)
|
|
@parameterize(
|
|
(TEST_CASE_NAME, 'x', 'n_fft', 'hop_length', 'win_length', 'window', 'center', 'normalized', 'onesided', 'length', 'return_complex'),
|
|
[
|
|
('test_2d_input', rand_x(2, np.float64, shape=[257, 471], complex=True), 512,
|
|
None, None, get_window('hann', 512), True, False, True, None, False),
|
|
('test_3d_input', rand_x(3, np.float64, shape=[1, 257, 471], complex=True), 512,
|
|
None, None, get_window('hann', 512), True, False, True, None, False),
|
|
('test_hop_length', rand_x(3, np.float64, shape=[1, 257, 471], complex=True), 512,
|
|
99, None, get_window('hann', 512), True, False, True, None, False),
|
|
('test_win_length', rand_x(3, np.float64, shape=[1, 257, 471], complex=True), 512,
|
|
99, 299, get_window('hann', 299), True, False, True, None, False),
|
|
('test_window', rand_x(3, np.float64, shape=[1, 257, 471], complex=True), 512,
|
|
None, None, None, True, False, True, None, False),
|
|
('test_center', rand_x(3, np.float64, shape=[1, 257, 471], complex=True), 512,
|
|
None, None, None, False, False, True, None, False),
|
|
('test_length', rand_x(3, np.float64, shape=[1, 257, 471], complex=True), 512,
|
|
None, None, None, False, False, True, 1888, False),
|
|
]) # fmt: skip
|
|
class TestIstft(unittest.TestCase):
|
|
def test_istft(self):
|
|
if self.window is None:
|
|
win_p = None
|
|
win_l = 'boxcar' # rectangular window
|
|
else:
|
|
win_p = paddle.to_tensor(self.window)
|
|
win_l = self.window
|
|
|
|
np.testing.assert_allclose(
|
|
istft(
|
|
self.x,
|
|
self.hop_length,
|
|
self.win_length,
|
|
win_l,
|
|
self.center,
|
|
self.length,
|
|
),
|
|
paddle.signal.istft(
|
|
paddle.to_tensor(self.x),
|
|
self.n_fft,
|
|
self.hop_length,
|
|
self.win_length,
|
|
win_p,
|
|
self.center,
|
|
self.normalized,
|
|
self.onesided,
|
|
self.length,
|
|
self.return_complex,
|
|
),
|
|
rtol=rtol.get(str(self.x.dtype)),
|
|
atol=atol.get(str(self.x.dtype)),
|
|
)
|
|
|
|
|
|
@place(DEVICES)
|
|
@parameterize(
|
|
(TEST_CASE_NAME, 'x', 'n_fft', 'hop_length', 'win_length', 'window', 'center', 'normalized', 'onesided', 'length', 'return_complex', 'expect_exception'),
|
|
[
|
|
('test_dims', rand_x(4, np.float64, shape=[1, 2, 3, 4], complex=True), 512,
|
|
None, None, get_window('hann', 512), True, False, True, None, False, AssertionError),
|
|
('test_n_fft', rand_x(3, np.float64, shape=[1, 257, 471], complex=True), 257,
|
|
None, None, get_window('hann', 512), True, False, True, None, False, AssertionError),
|
|
('test_hop_length1', rand_x(3, np.float64, shape=[1, 257, 471], complex=True), 512,
|
|
0, None, get_window('hann', 512), True, False, True, None, False, AssertionError),
|
|
('test_hop_length2', rand_x(3, np.float64, shape=[1, 257, 471], complex=True), 512,
|
|
513, None, get_window('hann', 512), True, False, True, None, False, AssertionError),
|
|
('test_win_length1', rand_x(3, np.float64, shape=[1, 257, 471], complex=True), 512,
|
|
None, 0, get_window('hann', 512), True, False, True, None, False, AssertionError),
|
|
('test_win_length2', rand_x(3, np.float64, shape=[1, 257, 471], complex=True), 512,
|
|
None, 513, get_window('hann', 512), True, False, True, None, False, AssertionError),
|
|
('test_onesided1', rand_x(3, np.float64, shape=[1, 257, 471], complex=True), 20,
|
|
None, None, get_window('hann', 512), True, False, True, None, False, AssertionError),
|
|
('test_onesided2', rand_x(3, np.float64, shape=[1, 257, 471], complex=True), 256,
|
|
None, None, None, True, False, False, None, False, AssertionError),
|
|
('test_window', rand_x(3, np.float64, shape=[1, 512, 471], complex=True), 512,
|
|
None, 511, get_window('hann', 512), True, False, False, None, False, AssertionError),
|
|
('test_return_complex1', rand_x(3, np.float64, shape=[1, 257, 471], complex=True), 512,
|
|
None, None, get_window('hann', 512), True, False, True, None, True, AssertionError),
|
|
('test_return_complex2', rand_x(3, np.float64, shape=[1, 257, 471], complex=True), 512,
|
|
None, None, rand_x(1, np.float64, shape=[512], complex=True), True, False, True, None, False, AssertionError),
|
|
('test_NOLA', rand_x(3, np.float64, shape=[1, 257, 471], complex=True), 512,
|
|
512, None, get_window('hann', 512), True, False, True, None, False, ValueError),
|
|
]) # fmt: skip
|
|
class TestIstftException(unittest.TestCase):
|
|
def test_istft(self):
|
|
if self.window is None:
|
|
win_p = None
|
|
else:
|
|
win_p = paddle.to_tensor(self.window)
|
|
|
|
with self.assertRaises(self.expect_exception):
|
|
paddle.signal.istft(
|
|
paddle.to_tensor(self.x),
|
|
self.n_fft,
|
|
self.hop_length,
|
|
self.win_length,
|
|
win_p,
|
|
self.center,
|
|
self.normalized,
|
|
self.onesided,
|
|
self.length,
|
|
self.return_complex,
|
|
)
|
|
|
|
|
|
class TestIstftException_ZeroSize(unittest.TestCase):
|
|
def test_istft(self):
|
|
self.x = np.random.random([5, 0])
|
|
with self.assertRaises(AssertionError):
|
|
paddle.signal.istft(paddle.to_tensor(self.x), 512)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|