class_docstr: Resampling from the sample rate `old_sr` to `new_sr`.
class_docstr_tok: ["Resampling","from","the","sample","rate","`","old_sr","`","to","`","new_sr","`","."]
code: def _init_kernels(self):
      if self.old_sr == self.new_sr:
          return
  
      kernels = []
      sr = min(self.new_sr, self.old_sr)
      # rolloff will perform antialiasing filtering by removing the highest frequencies.
      # At first I thought I only needed this when downsampling, but when upsampling
      # you will get edge artifacts without this, the edge is equivalent to zero padding,
      # which will add high freq artifacts.
      sr *= self.rolloff
  
      # The key idea of the algorithm is that x(t) can be exactly reconstructed from x[i] (tensor)
      # using the sinc interpolation formula:
      #   x(t) = sum_i x[i] sinc(pi * old_sr * (i / old_sr - t))
      # We can then sample the function x(t) with a different sample rate:
      #    y[j] = x(j / new_sr)
      # or,
      #    y[j] = sum_i x[i] sinc(pi * old_sr * (i / old_sr - j / new_sr))
  
      # We see here that y[j] is the convolution of x[i] with a specific filter, for which
      # we take an FIR approximation, stopping when we see at least `zeros` zeros crossing.
      # But y[j+1] is going to have a different set of weights and so on, until y[j + new_sr].
      # Indeed:
      # y[j + new_sr] = sum_i x[i] sinc(pi * old_sr * ((i / old_sr - (j + new_sr) / new_sr))
      #               = sum_i x[i] sinc(pi * old_sr * ((i - old_sr) / old_sr - j / new_sr))
      #               = sum_i x[i + old_sr] sinc(pi * old_sr * (i / old_sr - j / new_sr))
      # so y[j+new_sr] uses the same filter as y[j], but on a shifted version of x by `old_sr`.
      # This will explain the F.conv1d after, with a stride of old_sr.
      self._width = math.ceil(self.zeros * self.old_sr / sr)
      # If old_sr is still big after GCD reduction, most filters will be very unbalanced, i.e.,
      # they will have a lot of almost zero values to the left or to the right...
      # There is probably a way to evaluate those filters more efficiently, but this is kept for
      # future work.
      idx = torch.arange(
          -self._width, self._width + self.old_sr
      ).float()
      for i in range(self.new_sr):
          t = (-i / self.new_sr + idx / self.old_sr) * sr
          t = t.clamp_(-self.zeros, self.zeros)
          t *= math.pi
          window = torch.cos(t / self.zeros / 2) ** 2
          kernel = sinc(t) * window
          # Renormalize kernel to ensure a constant signal is preserved.
          kernel.div_(kernel.sum())
          kernels.append(kernel)
  
      self.register_buffer(
          "kernel", torch.stack(kernels).view(self.new_sr, 1, -1)
      )
  
file_path: julius/julius/resample.py
imports: import math
  from typing import Optional
  import torch
  from torch.nn import functional
  from .core import sinc
  from .utils import simple_repr
label_desc: Use image node_id 2 for calling the ResampleFrac obj's underlying member method code with example usage: obj._init_kernels() without return types