class_docstr: This algorithm is using the resampling method proposed in [1].
  Instead of convoluting the STFT results with a gigantic CQT kernel covering the full frequency
  spectrum, we make a small CQT kernel covering only the top octave.
  Then we keep downsampling the input audio by a factor of 2 to convoluting it with the
  small CQT kernel. Everytime the input audio is downsampled, the CQT relative to the downsampled
  input is equavalent to the next lower octave.
  The kernel creation process is still same as the 1992 algorithm. Therefore, we can reuse the code
  from the 1992 alogrithm [2]
  [1] Schörkhuber, Christian. “CONSTANT-Q TRANSFORM TOOLBOX FOR MUSIC PROCESSING.” (2010).
  [2] Brown, Judith C.C. and Miller Puckette. “An efficient algorithm for the calculation of a
  constant Q transform.” (1992).
  early downsampling factor is to downsample the input audio to reduce the CQT kernel size.
  The result with and without early downsampling are more or less the same except in the very low
  frequency region where freq < 40Hz.
class_docstr_tok: ["This","algorithm","is","using","the","resampling","method","proposed","in","[","1","]",".","Instead","of","convoluting","the","STFT","results","with","a","gigantic","CQT","kernel","covering","the","full","frequency","spectrum",",","we","make","a","small","CQT","kernel","covering","only","the","top","octave",".","Then","we","keep","downsampling","the","input","audio","by","a","factor","of","2","to","convoluting","it","with","the","small","CQT","kernel",".","Everytime","the","input","audio","is","downsampled",",","the","CQT","relative","to","the","downsampled","input","is","equavalent","to","the","next","lower","octave",".","The","kernel","creation","process","is","still","same","as","the","1992","algorithm",".","Therefore",",","we","can","reuse","the","code","from","the","1992","alogrithm","[","2","]","[","1","]","Sch\u00f6rkhuber",",","Christian",".","\u201c","CONSTANT-Q","TRANSFORM","TOOLBOX","FOR","MUSIC","PROCESSING.","\u201d","(","2010",")",".","[","2","]","Brown",",","Judith","C.C",".","and","Miller","Puckette",".","\u201c","An","efficient","algorithm","for","the","calculation","of","a","constant","Q","transform.","\u201d","(","1992",")",".","early","downsampling","factor","is","to","downsample","the","input","audio","to","reduce","the","CQT","kernel","size",".","The","result","with","and","without","early","downsampling","are","more","or","less","the","same","except","in","the","very","low","frequency","region","where","freq","<","40Hz","."]
code: def __init__(
      self,
      sr=22050,
      hop_length=512,
      fmin=32.70,
      fmax=None,
      n_bins=84,
      bins_per_octave=12,
      norm=True,
      basis_norm=1,
      window="hann",
      pad_mode="reflect",
      trainable_STFT=False,
      filter_scale=1,
      trainable_CQT=False,
      output_format="Magnitude",
      earlydownsample=True,
      verbose=True,
  ):
      super().__init__()
  
      self.norm = norm  # Now norm is used to normalize the final CQT result by dividing n_fft
      # basis_norm is for normalizing basis
      self.hop_length = hop_length
      self.pad_mode = pad_mode
      self.n_bins = n_bins
      self.output_format = output_format
      self.earlydownsample = earlydownsample  # TODO: activate early downsampling later if possible
  
      # This will be used to calculate filter_cutoff and creating CQT kernels
      Q = float(filter_scale) / (2 ** (1 / bins_per_octave) - 1)
  
      # Creating lowpass filter and make it a torch tensor
      if verbose == True:
          print("Creating low pass filter ...", end="\r")
      start = time()
      lowpass_filter = torch.tensor(
          create_lowpass_filter(
              band_center=0.5,
              kernelLength=256,
              transitionBandwidth=0.001,
          )
      )
  
      # Broadcast the tensor to the shape that fits conv1d
      self.register_buffer(
          "lowpass_filter", lowpass_filter[None, None, :]
      )
  
      if verbose == True:
          print(
              "Low pass filter created, time used = {:.4f} seconds".format(
                  time() - start
              )
          )
  
      # Calculate num of filter requires for the kernel
      # n_octaves determines how many resampling requires for the CQT
      n_filters = min(bins_per_octave, n_bins)
      self.n_octaves = int(np.ceil(float(n_bins) / bins_per_octave))
      # print("n_octaves = ", self.n_octaves)
  
      # Calculate the lowest frequency bin for the top octave kernel
      self.fmin_t = fmin * 2 ** (self.n_octaves - 1)
      remainder = n_bins % bins_per_octave
      # print("remainder = ", remainder)
  
      if remainder == 0:
          # Calculate the top bin frequency
          fmax_t = self.fmin_t * 2 ** (
              (bins_per_octave - 1) / bins_per_octave
          )
      else:
          # Calculate the top bin frequency
          fmax_t = self.fmin_t * 2 ** (
              (remainder - 1) / bins_per_octave
          )
  
      self.fmin_t = fmax_t / 2 ** (
          1 - 1 / bins_per_octave
      )  # Adjusting the top minium bins
      if fmax_t > sr / 2:
          raise ValueError(
              "The top bin {}Hz has exceeded the Nyquist frequency, \
                                please reduce the n_bins".format(
                  fmax_t
              )
          )
  
      if (
          self.earlydownsample == True
      ):  # Do early downsampling if this argument is True
          if verbose == True:
              print("Creating early downsampling filter ...", end="\r")
          start = time()
          (
              sr,
              self.hop_length,
              self.downsample_factor,
              early_downsample_filter,
              self.earlydownsample,
          ) = get_early_downsample_params(
              sr, hop_length, fmax_t, Q, self.n_octaves, verbose
          )
  
          self.register_buffer(
              "early_downsample_filter", early_downsample_filter
          )
          if verbose == True:
              print(
                  "Early downsampling filter created, \
                              time used = {:.4f} seconds".format(
                      time() - start
                  )
              )
      else:
          self.downsample_factor = 1.0
  
      # Preparing CQT kernels
      if verbose == True:
          print("Creating CQT kernels ...", end="\r")
  
      start = time()
      # print("Q = {}, fmin_t = {}, n_filters = {}".format(Q, self.fmin_t, n_filters))
      basis, self.n_fft, _, _ = create_cqt_kernels(
          Q,
          sr,
          self.fmin_t,
          n_filters,
          bins_per_octave,
          norm=basis_norm,
          topbin_check=False,
      )
  
      # This is for the normalization in the end
      freqs = fmin * 2.0 ** (
          np.r_[0:n_bins] / np.double(bins_per_octave)
      )
      self.frequencies = freqs
  
      lenghts = np.ceil(Q * sr / freqs)
      lenghts = torch.tensor(lenghts).float()
      self.register_buffer("lenghts", lenghts)
  
      self.basis = basis
      fft_basis = fft(basis)[
          :, : self.n_fft // 2 + 1
      ]  # Convert CQT kenral from time domain to freq domain
  
      # These cqt_kernel is already in the frequency domain
      cqt_kernels_real = torch.tensor(fft_basis.real)
      cqt_kernels_imag = torch.tensor(fft_basis.imag)
  
      if verbose == True:
          print(
              "CQT kernels created, time used = {:.4f} seconds".format(
                  time() - start
              )
          )
  
      # print("Getting cqt kernel done, n_fft = ",self.n_fft)
      # Preparing kernels for Short-Time Fourier Transform (STFT)
      # We set the frequency range in the CQT filter instead of here.
  
      if verbose == True:
          print("Creating STFT kernels ...", end="\r")
  
      start = time()
      (
          kernel_sin,
          kernel_cos,
          self.bins2freq,
          _,
          window,
      ) = create_fourier_kernels(
          self.n_fft, window="ones", freq_scale="no"
      )
      wsin = kernel_sin * window
      wcos = kernel_cos * window
  
      wsin = torch.tensor(wsin)
      wcos = torch.tensor(wcos)
  
      if verbose == True:
          print(
              "STFT kernels created, time used = {:.4f} seconds".format(
                  time() - start
              )
          )
  
      if trainable_STFT:
          wsin = nn.Parameter(wsin, requires_grad=trainable_STFT)
          wcos = nn.Parameter(wcos, requires_grad=trainable_STFT)
          self.register_parameter("wsin", wsin)
          self.register_parameter("wcos", wcos)
      else:
          self.register_buffer("wsin", wsin)
          self.register_buffer("wcos", wcos)
  
      if trainable_CQT:
          cqt_kernels_real = nn.Parameter(
              cqt_kernels_real, requires_grad=trainable_CQT
          )
          cqt_kernels_imag = nn.Parameter(
              cqt_kernels_imag, requires_grad=trainable_CQT
          )
          self.register_parameter("cqt_kernels_real", cqt_kernels_real)
          self.register_parameter("cqt_kernels_imag", cqt_kernels_imag)
      else:
          self.register_buffer("cqt_kernels_real", cqt_kernels_real)
          self.register_buffer("cqt_kernels_imag", cqt_kernels_imag)
  
          # If center==True, the STFT window will be put in the middle, and paddings at the beginning
      # and ending are required.
      if self.pad_mode == "constant":
          self.padding = nn.ConstantPad1d(self.n_fft // 2, 0)
      elif self.pad_mode == "reflect":
          self.padding = nn.ReflectionPad1d(self.n_fft // 2)
  
imports: import torch.nn
  import torch
  import numpy
  from time import time
  from ..utils import *
  from ..utils import *
label_desc: Use image node_id 1 to create a new CQT2010 object from inherited base classes: nn with example: obj = CQT2010(sr, hop_length, fmin, fmax, n_bins, bins_per_octave, norm, basis_norm, window, pad_mode, trainable_STFT, filter_scale, trainable_CQT, output_format, earlydownsample, verbose)