class_docstr: Parametric multichannel Wiener filter, with an adjustable
  tradeoff between noise reduction and speech distortion.
  It supports automatic reference channel selection based
  on the estimated output SNR.
  
  Args:
      beta: Parameter of the parameteric filter, tradeoff between noise reduction
            and speech distortion (0: MVDR, 1: MWF).
      rank: Rank assumption for the speech covariance matrix.
      postfilter: Optional postfilter. If None, no postfilter is applied.
      ref_channel: Optional, reference channel. If None, it will be estimated automatically.
      ref_hard: If true, estimate a hard (one-hot) reference. If false, a soft reference.
      ref_hard_use_grad: If true, use straight-through gradient when using the hard reference
      ref_subband_weighting: If true, use subband weighting when estimating reference channel
      num_subbands: Optional, used to determine the parameter size for reference estimation
      diag_reg: Optional, diagonal regularization for the multichannel filter
      eps: Small regularization constant to avoid division by zero
  
  References:
      - Souden et al, On Optimal Frequency-Domain Multichannel Linear Filtering for Noise Reduction, 2010
docstr: Return processed signal.
  The output has either one channel (M=1) if a ref_channel is selected,
  or the same number of channels as the input (M=C) if ref_channel is None.
  
  Args:
      input: Input signal, complex tensor with shape (B, C, F, T)
      mask_s: Mask for the desired signal, shape (B, F, T)
      mask_n: Mask for the undesired noise, shape (B, F, T)
  
  Returns:
      Processed signal, shape (B, M, F, T)
docstr_tok: ["Return","processed","signal",".","The","output","has","either","one","channel","(","M=1",")","if","a","ref_channel","is","selected",",","or","the","same","number","of","channels","as","the","input","(","M=C",")","if","ref_channel","is","None",".","Args",":","input",":","Input","signal",",","complex","tensor","with","shape","(","B",",","C",",","F",",","T",")","mask_s",":","Mask","for","the","desired","signal",",","shape","(","B",",","F",",","T",")","mask_n",":","Mask","for","the","undesired","noise",",","shape","(","B",",","F",",","T",")","Returns",":","Processed","signal",",","shape","(","B",",","M",",","F",",","T",")"]
code: def forward(
      self,
      input: torch.Tensor,
      mask_s: torch.Tensor,
      mask_n: torch.Tensor,
  ) -> torch.Tensor:
      """Return processed signal.
      The output has either one channel (M=1) if a ref_channel is selected,
      or the same number of channels as the input (M=C) if ref_channel is None.
  
      Args:
          input: Input signal, complex tensor with shape (B, C, F, T)
          mask_s: Mask for the desired signal, shape (B, F, T)
          mask_n: Mask for the undesired noise, shape (B, F, T)
  
      Returns:
          Processed signal, shape (B, M, F, T)
      """
      iodtype = input.dtype
  
      with torch.cuda.amp.autocast(enabled=False):
          # Convert to double
          input = input.cdouble()
          mask_s = mask_s.double()
          mask_n = mask_n.double()
  
          # Calculate signal statistics
          psd_s = self.psd(input, mask_s)
          psd_n = self.psd(input, mask_n)
  
          if self.rank == "one":
              # Calculate filter W using (18) in [1]
              # Diagonal regularization
              if self.diag_reg:
                  psd_n = self.apply_diag_reg(psd_n)
  
              # MIMO filter
              # (B, F, C, C)
              W = torch.linalg.solve(psd_n, psd_s)
              lam = self.trace(W, keepdim=True).real
              W = W / (self.beta + lam + self.eps)
          elif self.rank == "full":
              # Calculate filter W using (15) in [1]
              psd_sn = psd_s + self.beta * psd_n
  
              if self.diag_reg:
                  psd_sn = self.apply_diag_reg(psd_sn)
  
              # MIMO filter
              # (B, F, C, C)
              W = torch.linalg.solve(psd_sn, psd_s)
          else:
              raise RuntimeError(f"Unexpected rank {self.rank}")
  
          if torch.jit.isinstance(self.ref_channel, int):
              # Fixed ref channel
              # (B, F, C, 1)
              W = W[..., self.ref_channel].unsqueeze(-1)
          elif self.ref_estimator is not None:
              # Estimate ref channel tensor (one-hot or soft across C)
              # (B, C)
              ref_channel_tensor = self.ref_estimator(
                  W=W, psd_s=psd_s, psd_n=psd_n
              ).to(W.dtype)
              # Weighting across channels
              # (B, F, C, 1)
              W = torch.sum(
                  W * ref_channel_tensor[:, None, None, :],
                  dim=-1,
                  keepdim=True,
              )
  
          output = self.apply_filter(input=input, filter=W)
  
          # Optional: postfilter
          if self.postfilter == "ban":
              output = self.apply_ban(
                  input=output, filter=W, psd_n=psd_n
              )
  
      return output.to(iodtype)
  
imports: import random
  from typing import Callable, Optional
  import torch
  from nemo.collections.asr.parts.submodules.multi_head_attention import MultiHeadAttention
  from nemo.core.classes import NeuralModule, typecheck
  from nemo.core.neural_types import AudioSignal, FloatType, NeuralType, SpectrogramType
  from nemo.utils import logging
label_desc: Use image node_id 8 for calling the ParametricMultichannelWienerFilter obj's underlying member method code with example usage: obj.forward(input, mask_s, mask_n) and returns: output