class_docstr: Select a subsection (or "part") of the audio and repeat that part a number of times.
  This can be useful when simulating scenarios where a short audio snippet gets
  repeated, for example:
  
  * Repetitions of some musical note or sound in a rhythmical way
  * A person stutters or says the same word (with variations) multiple times on a row
  * A mechanical noise with periodic repetitions
  * A "skip in the record" or a "stuck needle" effect, reminiscent of vinyl records or
      CDs when they repeatedly play a short section due to a scratch or other
      imperfection.
  * Digital audio glitches, such as a buffer underrun in video games,
      where the current audio frame gets looped continuously due to system overloads
      or a software crash.
  
  Note that the length of inputs you give it must be compatible with the part
  duration range and crossfade duration. If you give it an input audio array that is
  too short, a `UserWarning` will be raised and no operation is applied to the signal.
class_docstr_tok: ["Select","a","subsection","(","or","``","part","''",")","of","the","audio","and","repeat","that","part","a","number","of","times",".","This","can","be","useful","when","simulating","scenarios","where","a","short","audio","snippet","gets","repeated",",","for","example",":","*","Repetitions","of","some","musical","note","or","sound","in","a","rhythmical","way","*","A","person","stutters","or","says","the","same","word","(","with","variations",")","multiple","times","on","a","row","*","A","mechanical","noise","with","periodic","repetitions","*","A","``","skip","in","the","record","''","or","a","``","stuck","needle","''","effect",",","reminiscent","of","vinyl","records","or","CDs","when","they","repeatedly","play","a","short","section","due","to","a","scratch","or","other","imperfection",".","*","Digital","audio","glitches",",","such","as","a","buffer","underrun","in","video","games",",","where","the","current","audio","frame","gets","looped","continuously","due","to","system","overloads","or","a","software","crash",".","Note","that","the","length","of","inputs","you","give","it","must","be","compatible","with","the","part","duration","range","and","crossfade","duration",".","If","you","give","it","an","input","audio","array","that","is","too","short",",","a","`","UserWarning","`","will","be","raised","and","no","operation","is","applied","to","the","signal","."]
code: def apply(self, samples: NDArray[np.float32], sample_rate: int):
      crossfade_length = 0
      half_crossfade_length = 0
      equal_energy_fade_in_mask = None
      equal_energy_fade_out_mask = None
      last_crossfade_type = "equal_energy"
      if self.crossfade:
          crossfade_length = self.get_crossfade_length(sample_rate)
          half_crossfade_length = crossfade_length // 2
          (
              equal_energy_fade_in_mask,
              equal_energy_fade_out_mask,
          ) = get_crossfade_mask_pair(crossfade_length)
  
      if self.crossfade:
          part = samples[
              ...,
              self.parameters["part_start_index"]
              - half_crossfade_length : self.parameters[
                  "part_start_index"
              ]
              + self.parameters["part_num_samples"]
              + half_crossfade_length,
          ]
      else:
          part = samples[
              ...,
              self.parameters["part_start_index"] : self.parameters[
                  "part_start_index"
              ]
              + self.parameters["part_num_samples"],
          ]
  
      repeats_start_index = (
          self.parameters["part_start_index"]
          + self.parameters["part_num_samples"]
      )
  
      last_repetition_end_index = repeats_start_index
      parts = []
      for i in range(self.parameters["repeats"]):
          start_idx = last_repetition_end_index
          if self.crossfade:
              if i == 0:
                  start_idx -= half_crossfade_length
              else:
                  start_idx -= crossfade_length
  
          part_array = np.copy(part)
  
          if self.part_transform:
              part_array = self.part_transform(part_array, sample_rate)
              if (
                  self.crossfade
                  and part_array.shape[-1] < 2 * crossfade_length
              ):
                  raise ValueError(
                      "Applying a part_transform that makes a part shorter than 2 *"
                      " crossfade_duration is not supported"
                  )
  
          last_repetition_end_index = start_idx + part_array.shape[-1]
  
          if self.crossfade:
              part_array[
                  ..., :crossfade_length
              ] *= equal_energy_fade_in_mask
  
              fade_out_crossfade_type = "equal_energy"
              is_last_part = i == self.parameters["repeats"] - 1
              if is_last_part and self.mode == "insert":
                  if self.part_transform is None:
                      fade_out_crossfade_type = "equal_gain"
                  else:
                      # If the part was transformed, check if it still correlates with
                      # the original signal in the seam. If it does, we use equal-gain
                      # crossfading instead of equal-energy.
                      correlation_coefficient = np.corrcoef(
                          part_array[..., -crossfade_length:],
                          samples[
                              ...,
                              self.parameters["part_start_index"]
                              + self.parameters["part_num_samples"]
                              - half_crossfade_length : self.parameters[
                                  "part_start_index"
                              ]
                              + self.parameters["part_num_samples"]
                              + half_crossfade_length,
                          ],
                      )[0, 1]
                      if abs(correlation_coefficient) > 0.5:
                          fade_out_crossfade_type = "equal_gain"
              if is_last_part:
                  last_crossfade_type = fade_out_crossfade_type
              if fade_out_crossfade_type == "equal_energy":
                  part_array[
                      ..., -crossfade_length:
                  ] *= equal_energy_fade_out_mask
              else:
                  _, equal_gain_fade_out_mask = get_crossfade_mask_pair(
                      crossfade_length, equal_energy=False
                  )
                  part_array[
                      ..., -crossfade_length:
                  ] *= equal_gain_fade_out_mask
  
          parts.append(
              {
                  "array": part_array,
                  "start_idx": start_idx,
                  "end_idx": last_repetition_end_index,
              }
          )
          if (
              self.mode == "replace"
              and last_repetition_end_index > samples.shape[-1]
          ):
              break
  
      result_length = samples.shape[-1]
      if self.mode == "insert":
          result_length += (
              parts[-1]["end_idx"]
              - parts[0]["start_idx"]
              - crossfade_length
          )
  
      if samples.ndim == 1:
          result_shape = (result_length,)
      else:
          result_shape = (samples.shape[0], result_length)
  
      result_placeholder = np.zeros(
          shape=result_shape, dtype=np.float32
      )
  
      if self.crossfade:
          # Fade out the signal where the first repetition takes over
          result_placeholder[
              ..., : repeats_start_index - half_crossfade_length
          ] = samples[
              ..., : repeats_start_index - half_crossfade_length
          ]
          result_placeholder[
              ...,
              repeats_start_index
              - half_crossfade_length : repeats_start_index
              + half_crossfade_length,
          ] = (
              equal_energy_fade_out_mask
              * samples[
                  ...,
                  repeats_start_index
                  - half_crossfade_length : repeats_start_index
                  + half_crossfade_length,
              ]
          )
      else:
          result_placeholder[..., :repeats_start_index] = samples[
              ..., :repeats_start_index
          ]
  
      # Add all repetitions except the last one
      for part in parts[:-1]:
          result_placeholder[
              ..., part["start_idx"] : part["end_idx"]
          ] += part["array"]
  
      # Add the (potentially truncated) last repetition
      if (
          self.mode == "replace"
          and parts[-1]["end_idx"] > samples.shape[-1]
      ):
          truncated_part_length = (
              samples.shape[-1] - parts[-1]["start_idx"]
          )
          truncated_part = parts[-1]["array"][
              ..., :truncated_part_length
          ]
          result_placeholder[
              ...,
              parts[-1]["start_idx"] : parts[-1]["start_idx"]
              + truncated_part_length,
          ] += truncated_part
      else:
          result_placeholder[
              ..., parts[-1]["start_idx"] : parts[-1]["end_idx"]
          ] += parts[-1]["array"]
  
      del parts
  
      if self.mode == "insert":
          if self.crossfade:
              if last_crossfade_type == "equal_energy":
                  fade_in_mask = equal_energy_fade_in_mask
              else:
                  fade_in_mask, _ = get_crossfade_mask_pair(
                      crossfade_length, equal_energy=False
                  )
              result_placeholder[
                  ...,
                  last_repetition_end_index
                  - crossfade_length : last_repetition_end_index,
              ] += (
                  fade_in_mask
                  * samples[
                      ...,
                      -(result_length - last_repetition_end_index)
                      - crossfade_length : -(
                          result_length - last_repetition_end_index
                      ),
                  ]
              )
  
          result_placeholder[..., last_repetition_end_index:] = samples[
              ..., -(result_length - last_repetition_end_index) :
          ]
      else:
          if self.crossfade:
              last_crossfade_start_idx = (
                  last_repetition_end_index - crossfade_length
              )
              if last_crossfade_start_idx < samples.shape[-1]:
                  last_crossfade_length = crossfade_length
                  if last_repetition_end_index > samples.shape[-1]:
                      # Truncate crossfade
                      last_crossfade_length = (
                          samples.shape[-1] - last_crossfade_start_idx
                      )
  
                  result_placeholder[
                      ...,
                      last_crossfade_start_idx : last_crossfade_start_idx
                      + last_crossfade_length,
                  ] += (
                      equal_energy_fade_in_mask[:last_crossfade_length]
                      * samples[
                          ...,
                          last_crossfade_start_idx : last_crossfade_start_idx
                          + last_crossfade_length,
                      ]
                  )
  
          if last_repetition_end_index < result_length:
              result_placeholder[
                  ..., last_repetition_end_index:
              ] = samples[..., last_repetition_end_index:]
      return result_placeholder
  
file_path: audiomentations/audiomentations/augmentations/repeat_part.py
imports: import random
  import warnings
  from typing import Optional, Callable
  import numpy
  from numpy.typing import NDArray
  from audiomentations.core.transforms_interface import BaseWaveformTransform
  from audiomentations.core.utils import get_crossfade_mask_pair
label_desc: Use image node_id 4 for calling the RepeatPart obj's underlying member method code with example usage: obj.apply(samples, sample_rate) and returns: result_placeholder