Clicko777/RVC_HFv2
0
1# File under the MIT license, see https://github.com/adefossez/julius/LICENSE for details.2# Author: adefossez, 20203"""4FIR windowed sinc lowpass filters.5"""6 7import math8from typing import Sequence, Optional9 10import torch11from torch.nn import functional as F12 13from .core import sinc14from .fftconv import fft_conv1d15from .utils import simple_repr16 17 18class LowPassFilters(torch.nn.Module):19 """20 Bank of low pass filters. Note that a high pass or band pass filter can easily21 be implemented by substracting a same signal processed with low pass filters with different22 frequencies (see `julius.bands.SplitBands` for instance).23 This uses a windowed sinc filter, very similar to the one used in24 `julius.resample`. However, because we do not change the sample rate here,25 this filter can be much more efficiently implemented using the FFT convolution from26 `julius.fftconv`.27 28 Args:29 cutoffs (list[float]): list of cutoff frequencies, in [0, 0.5] expressed as `f/f_s` where30 f_s is the samplerate and `f` is the cutoff frequency.31 The upper limit is 0.5, because a signal sampled at `f_s` contains only32 frequencies under `f_s / 2`.33 stride (int): how much to decimate the output. Keep in mind that decimation34 of the output is only acceptable if the cutoff frequency is under `1/ (2 * stride)`35 of the original sampling rate.36 pad (bool): if True, appropriately pad the input with zero over the edge. If `stride=1`,37 the output will have the same length as the input.38 zeros (float): Number of zero crossings to keep.39 Controls the receptive field of the Finite Impulse Response filter.40 For lowpass filters with low cutoff frequency, e.g. 40Hz at 44.1kHz,41 it is a bad idea to set this to a high value.42 This is likely appropriate for most use. Lower values43 will result in a faster filter, but with a slower attenuation around the44 cutoff frequency.45 fft (bool or None): if True, uses `julius.fftconv` rather than PyTorch convolutions.46 If False, uses PyTorch convolutions. If None, either one will be chosen automatically47 depending on the effective filter size.48 49 50 ..warning::51 All the filters will use the same filter size, aligned on the lowest52 frequency provided. If you combine a lot of filters with very diverse frequencies, it might53 be more efficient to split them over multiple modules with similar frequencies.54 55 ..note::56 A lowpass with a cutoff frequency of 0 is defined as the null function57 by convention here. This allows for a highpass with a cutoff of 0 to58 be equal to identity, as defined in `julius.filters.HighPassFilters`.59 60 Shape:61 62 - Input: `[*, T]`63 - Output: `[F, *, T']`, with `T'=T` if `pad` is True and `stride` is 1, and64 `F` is the numer of cutoff frequencies.65 66 >>> lowpass = LowPassFilters([1/4])67 >>> x = torch.randn(4, 12, 21, 1024)68 >>> list(lowpass(x).shape)69 [1, 4, 12, 21, 1024]70 """71 72 def __init__(self, cutoffs: Sequence[float], stride: int = 1, pad: bool = True,73 zeros: float = 8, fft: Optional[bool] = None):74 super().__init__()75 self.cutoffs = list(cutoffs)76 if min(self.cutoffs) < 0:77 raise ValueError("Minimum cutoff must be larger than zero.")78 if max(self.cutoffs) > 0.5:79 raise ValueError("A cutoff above 0.5 does not make sense.")80 self.stride = stride81 self.pad = pad82 self.zeros = zeros83 self.half_size = int(zeros / min([c for c in self.cutoffs if c > 0]) / 2)84 if fft is None:85 fft = self.half_size > 3286 self.fft = fft87 window = torch.hann_window(2 * self.half_size + 1, periodic=False)88 time = torch.arange(-self.half_size, self.half_size + 1)89 filters = []90 for cutoff in cutoffs:91 if cutoff == 0:92 filter_ = torch.zeros_like(time)93 else:94 filter_ = 2 * cutoff * window * sinc(2 * cutoff * math.pi * time)95 # Normalize filter to have sum = 1, otherwise we will have a small leakage96 # of the constant component in the input signal.97 filter_ /= filter_.sum()98 filters.append(filter_)99 self.register_buffer("filters", torch.stack(filters)[:, None])100 101 def forward(self, input):102 shape = list(input.shape)103 input = input.view(-1, 1, shape[-1])104 if self.pad:105 input = F.pad(input, (self.half_size, self.half_size), mode='replicate')106 if self.fft:107 out = fft_conv1d(input, self.filters, stride=self.stride)108 else:109 out = F.conv1d(input, self.filters, stride=self.stride)110 shape.insert(0, len(self.cutoffs))111 shape[-1] = out.shape[-1]112 return out.permute(1, 0, 2).reshape(shape)113 114 def __repr__(self):115 return simple_repr(self)116 117 118class LowPassFilter(torch.nn.Module):119 """120 Same as `LowPassFilters` but applies a single low pass filter.121 122 Shape:123 124 - Input: `[*, T]`125 - Output: `[*, T']`, with `T'=T` if `pad` is True and `stride` is 1.126 127 >>> lowpass = LowPassFilter(1/4, stride=2)128 >>> x = torch.randn(4, 124)129 >>> list(lowpass(x).shape)130 [4, 62]131 """132 133 def __init__(self, cutoff: float, stride: int = 1, pad: bool = True,134 zeros: float = 8, fft: Optional[bool] = None):135 super().__init__()136 self._lowpasses = LowPassFilters([cutoff], stride, pad, zeros, fft)137 138 @property139 def cutoff(self):140 return self._lowpasses.cutoffs[0]141 142 @property143 def stride(self):144 return self._lowpasses.stride145 146 @property147 def pad(self):148 return self._lowpasses.pad149 150 @property151 def zeros(self):152 return self._lowpasses.zeros153 154 @property155 def fft(self):156 return self._lowpasses.fft157 158 def forward(self, input):159 return self._lowpasses(input)[0]160 161 def __repr__(self):162 return simple_repr(self)163 164 165def lowpass_filters(input: torch.Tensor, cutoffs: Sequence[float],166 stride: int = 1, pad: bool = True,167 zeros: float = 8, fft: Optional[bool] = None):168 """169 Functional version of `LowPassFilters`, refer to this class for more information.170 """171 return LowPassFilters(cutoffs, stride, pad, zeros, fft).to(input)(input)172 173 174def lowpass_filter(input: torch.Tensor, cutoff: float,175 stride: int = 1, pad: bool = True,176 zeros: float = 8, fft: Optional[bool] = None):177 """178 Same as `lowpass_filters` but with a single cutoff frequency.179 Output will not have a dimension inserted in the front.180 """181 return lowpass_filters(input, [cutoff], stride, pad, zeros, fft)[0]182 