Source code for diffsptk.core.dfs

# ------------------------------------------------------------------------ #
# 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 numpy as np
import torch.nn as nn
import torch.nn.functional as F

from ..misc.utils import iir
from ..misc.utils import numpy_to_torch
from ..misc.utils import to_3d


[docs]class InfiniteImpulseResponseDigitalFilter(nn.Module): """See `this page <https://sp-nitech.github.io/sptk/latest/main/dfs.html>`_ for details. Parameters ---------- b : List [shape=(M+1,)] Numerator coefficients. a : List [shape=(N+1,)] Denominator coefficients. ir_length : int >= 1 [scalar] Length of impulse response (valid only if **mode** is 'fir'). mode : ['fir', 'iir'] If 'fir', filter is approximated by a finite impulse response. """ def __init__(self, b=None, a=None, ir_length=None, mode="fir"): super(InfiniteImpulseResponseDigitalFilter, self).__init__() self.mode = mode if b is None: b = [1] if a is None: a = [1] b = np.asarray(b) a = np.asarray(a) if self.mode == "fir": # Pre-compute impulse response. if ir_length is None: ir_length = len(b) assert 1 <= ir_length d = np.zeros(max(len(b), len(a))) h = np.empty(ir_length) a0 = a[0] a1 = a[1:] for t in range(ir_length): x = a0 if t == 0 else 0 y = x - np.sum(d[: len(a1)] * a1) d = np.roll(d, 1) d[0] = y y = np.sum(d[: len(b)] * b) h[t] = y h = h.reshape(1, 1, -1) self.register_buffer("h", numpy_to_torch(h).flip(-1)) elif self.mode == "iir": self.register_buffer("b", numpy_to_torch(b)) self.register_buffer("a", numpy_to_torch(a)) else: raise ValueError(f"mode {mode} is not supported")
[docs] def forward(self, x, b=None, a=None): """Apply an IIR digital filter. Parameters ---------- x : Tensor [shape=(..., T)] Input waveform. b : Tensor [shape=(M+1,)] Numerator coefficients. a : Tensor [shape=(N+1,)] Denominator coefficients. Returns ------- y : Tensor [shape=(..., T)] Filtered waveform. Examples -------- >>> x = diffsptk.ramp(4) >>> dfs = diffsptk.IIR(b=[1, -0.97]) >>> y = dfs(x) >>> y tensor([0.0000, 1.0000, 1.0300, 1.0600, 1.0900]) """ if self.mode == "fir": y = self._forward_fir(x, b, a) elif self.mode == "iir": y = self._forward_iir(x, b, a) else: raise RuntimeError return y
def _forward_fir(self, x, b=None, a=None): if a is None and b is None: h = self.h elif a is None and b is not None: h = b.view(1, 1, -1).flip(-1) else: raise ValueError("Denominator coefficients must be set via constructor") y = to_3d(x) y = F.pad(y, (h.size(-1) - 1, 0)) y = F.conv1d(y, h) y = y.view_as(x) return y def _forward_iir(self, x, b=None, a=None): if b is None: b = self.b if a is None: a = self.a y = iir(x, b, a) return y