diff --git a/Makefile b/Makefile index 39f73fb..45f778c 100644 --- a/Makefile +++ b/Makefile @@ -85,8 +85,8 @@ tool-clean: update: tool . .venv/bin/activate && python -m pip install --upgrade pip - @for package in $$(./tools/taplo/taplo get -f pyproject.toml project.optional-dependencies.dev); do \ - . .venv/bin/activate && python -m pip install --upgrade $$package; \ + @./tools/taplo/taplo get -f pyproject.toml project.optional-dependencies.dev | while read -r package; do \ + . .venv/bin/activate && python -m pip install --upgrade "$$package"; \ done clean: dist-clean doc-clean test-clean tool-clean diff --git a/diffsptk/functional.py b/diffsptk/functional.py index ea5f45c..ffb9d29 100644 --- a/diffsptk/functional.py +++ b/diffsptk/functional.py @@ -1138,33 +1138,6 @@ def hilbert(x: Tensor, fft_length: int | None = None, dim: int = -1) -> Tensor: return nn.HilbertTransform._func(x, fft_length=fft_length, dim=dim) -def hilbert2( - x: Tensor, - fft_length: ArrayLike[int] | int | None = None, - dim: ArrayLike[int] = (-2, -1), -) -> Tensor: - """Compute the analytic signal using the Hilbert transform. - - Parameters - ---------- - x : Tensor [shape=(..., T1, T2, ...)] - The input signal. - - fft_length : int, list[int], or None - The number of FFT bins. If None, set to (:math:`T1`, :math:`T2`). - - dim : list[int] - The dimensions along which to take the Hilbert transform. - - Returns - ------- - out : Tensor [shape=(..., T1, T2, ...)] - The analytic signal. - - """ - return nn.TwoDimensionalHilbertTransform._func(x, fft_length=fft_length, dim=dim) - - def histogram( x: Tensor, n_bin: int = 10, diff --git a/diffsptk/modules/__init__.py b/diffsptk/modules/__init__.py index 951ea72..3f8e1ec 100644 --- a/diffsptk/modules/__init__.py +++ b/diffsptk/modules/__init__.py @@ -62,7 +62,6 @@ from .griffin import GriffinLim from .grpdelay import GroupDelay from .hilbert import HilbertTransform -from .hilbert2 import TwoDimensionalHilbertTransform from .histogram import Histogram from .ialaw import ALawExpansion from .ica import IndependentComponentAnalysis diff --git a/diffsptk/modules/excite.py b/diffsptk/modules/excite.py index bd29711..31649ff 100644 --- a/diffsptk/modules/excite.py +++ b/diffsptk/modules/excite.py @@ -148,15 +148,16 @@ def _forward( s = torch.cumsum(q.double(), dim=-1) bias, _ = torch.cummax(s * ~mask, dim=-1) phase = (s - bias).to(p.dtype) - if isinstance(init_phase, str): - if init_phase == "zeros": - pass - elif init_phase == "random": - phase += torch.rand_like(p[..., :1]) - else: - raise ValueError(f"init_phase {init_phase} is not supported.") + if not isinstance(init_phase, str): + shift = init_phase / TAU + elif init_phase == "zeros": + shift = 0.0 + elif init_phase == "random": + shift = torch.rand_like(p[..., :1]) else: - phase += init_phase / TAU + raise ValueError(f"init_phase {init_phase} is not supported.") + if isinstance(shift, torch.Tensor) or shift != 0.0: + phase += shift # Generate excitation signal using phase. if polarity == "auto": @@ -170,15 +171,19 @@ def _forward( def get_pulse_pos(p): r = torch.ceil(p) - r = F.pad(r, (1, 0)) return torch.ge(torch.diff(r), 1) + if isinstance(shift, float): + padded_phase = F.pad(phase, (1, 0), value=shift) + else: + padded_phase = torch.cat([shift, phase], dim=-1) + if unipolar: - pulse_pos = get_pulse_pos(phase) + pulse_pos = get_pulse_pos(padded_phase) e[pulse_pos] = torch.sqrt(p[pulse_pos]) else: - pulse_pos1 = get_pulse_pos(phase) - pulse_pos2 = get_pulse_pos(0.5 * phase) + pulse_pos1 = get_pulse_pos(padded_phase) + pulse_pos2 = get_pulse_pos(0.5 * padded_phase) e[pulse_pos1] = torch.sqrt(p[pulse_pos1]) e[pulse_pos1 & ~pulse_pos2] *= -1 elif voiced_region == "sinusoidal": diff --git a/diffsptk/modules/hilbert2.py b/diffsptk/modules/hilbert2.py deleted file mode 100644 index 79e60c7..0000000 --- a/diffsptk/modules/hilbert2.py +++ /dev/null @@ -1,133 +0,0 @@ -# ------------------------------------------------------------------------ # -# Copyright 2022 SPTK Working Group # -# # -# 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 torch - -from ..typing import ArrayLike, Precomputed -from ..utils.private import filter_values, to -from .base import BaseFunctionalModule -from .hilbert import HilbertTransform - - -class TwoDimensionalHilbertTransform(BaseFunctionalModule): - """2-D Hilbert transform module. - - Parameters - ---------- - fft_length : int >= 1 or tuple[int, int] - The number of FFT bins. - - dim : tuple[int, int] - The dimension along which to take the Hilbert transform. - - device : torch.device or None - The device of this module. - - dtype : torch.dtype or None - The data type of this module. - - """ - - def __init__( - self, - fft_length: ArrayLike[int] | int, - dim: ArrayLike[int] = (-2, -1), - device: torch.device | None = None, - dtype: torch.dtype | None = None, - ) -> None: - super().__init__() - - self.values, _, tensors = self._precompute(**filter_values(locals())) - self.register_buffer("h", tensors[0]) - - def forward(self, x: torch.Tensor) -> torch.Tensor: - """Compute the analytic signal using the Hilbert transform. - - Parameters - ---------- - x : Tensor [shape=(..., T1, T2, ...)] - The input signal. - - Returns - ------- - out : Tensor [shape=(..., T1, T2, ...)] - The analytic signal, where the real part is the input signal and the - imaginary part is the Hilbert transform of the input signal. - - Examples - -------- - >>> import diffsptk - >>> hilbert2 = diffsptk.TwoDimensionalHilbertTransform((1, 4)) - >>> x = diffsptk.ramp(3).unsqueeze(0) - >>> z = hilbert2(x) - >>> z.real - tensor([[0., 1., 2., 3.]]) - >>> z.imag - tensor([[ 1., -1., -1., 1.]]) - - """ - return self._forward(x, *self.values, **self._buffers) - - @staticmethod - def _func( - x: torch.Tensor, fft_length: ArrayLike[int] | int | None, dim: ArrayLike[int] - ) -> torch.Tensor: - values, _, tensors = TwoDimensionalHilbertTransform._precompute( - (x.size(dim[0]), x.size(dim[1])) if fft_length is None else fft_length, - dim, - device=x.device, - dtype=x.dtype, - ) - return TwoDimensionalHilbertTransform._forward(x, *values, *tensors) - - @staticmethod - def _takes_input_size() -> bool: - return True - - @staticmethod - def _check(dim: ArrayLike[int]) -> None: - if len(dim) != 2: - raise ValueError("dim must have length 2.") - - @staticmethod - def _precompute( - fft_length: ArrayLike[int] | int, - dim: ArrayLike[int], - device: torch.device | None, - dtype: torch.dtype | None, - ) -> Precomputed: - TwoDimensionalHilbertTransform._check(dim) - if isinstance(fft_length, int): - fft_length = (fft_length, fft_length) - _, _, h1 = HilbertTransform._precompute( - fft_length[0], None, device=device, dtype=torch.double - ) - _, _, h2 = HilbertTransform._precompute( - fft_length[1], None, device=device, dtype=torch.double - ) - h = h1[0].unsqueeze(1) * h2[0].unsqueeze(0) - return (dim,), None, (to(h, dtype=dtype),) - - @staticmethod - def _forward(x: torch.Tensor, dim: ArrayLike[int], h: torch.Tensor) -> torch.Tensor: - L = h.size(dim[0]), h.size(dim[1]) - target_shape = [1] * x.dim() - target_shape[dim[0]] = L[0] - target_shape[dim[1]] = L[1] - h = h.view(*target_shape) - X = torch.fft.fft2(x, s=L, dim=dim) - z = torch.fft.ifft2(X * h, s=L, dim=dim) - return z diff --git a/docs/source/modules/hilbert.rst b/docs/source/modules/hilbert.rst index 53c7ecd..7055fbf 100644 --- a/docs/source/modules/hilbert.rst +++ b/docs/source/modules/hilbert.rst @@ -7,7 +7,3 @@ hilbert :members: .. autofunction:: diffsptk.functional.hilbert - -.. seealso:: - - :ref:`hilbert2` diff --git a/docs/source/modules/hilbert2.rst b/docs/source/modules/hilbert2.rst deleted file mode 100644 index 0e5a863..0000000 --- a/docs/source/modules/hilbert2.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. _hilbert2: - -hilbert2 -======== - -.. autoclass:: diffsptk.TwoDimensionalHilbertTransform - :members: - -.. autofunction:: diffsptk.functional.hilbert2 - -.. seealso:: - - :ref:`hilbert` diff --git a/tests/test_hilbert2.py b/tests/test_hilbert2.py deleted file mode 100644 index 3020cf9..0000000 --- a/tests/test_hilbert2.py +++ /dev/null @@ -1,53 +0,0 @@ -# ------------------------------------------------------------------------ # -# Copyright 2022 SPTK Working Group # -# # -# 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 pytest -from scipy.signal import hilbert2 as scipy_hilbert2 - -import diffsptk -import tests.utils as U - - -@pytest.mark.parametrize("module", [False, True]) -@pytest.mark.parametrize("L", [(16, 8), 8, None]) -def test_compatibility(device, dtype, module, L): - if module and L is None: - return - - hilbert2 = U.choice( - module, - diffsptk.TwoDimensionalHilbertTransform, - diffsptk.functional.hilbert2, - {"fft_length": L, "device": device, "dtype": dtype}, - ) - - def func(x): - return scipy_hilbert2(x, N=L) - - if isinstance(L, int): - L = (L, L) - elif L is None: - L = (16, 8) - - U.check_confidence( - device, - dtype, - hilbert2, - func, - L, - ) - - U.check_differentiability(device, dtype, [lambda x: x.real, hilbert2], L)