class_docstr: Gradually change the volume up or down over a random time span. Also known as
  fade in and fade out. The fade works on a logarithmic scale, which is natural to
  human hearing.
  
  The way this works is that it picks two gains: a first gain and a second gain.
  Then it picks a time range for the transition between those two gains.
  Note that this transition can start before the audio starts and/or end after the
  audio ends, so the output audio can start or end in the middle of a transition.
  The gain starts at the first gain and is held constant until the transition start.
  Then it transitions to the second gain. Then that gain is held constant until the
  end of the sound.
class_docstr_tok: ["Gradually","change","the","volume","up","or","down","over","a","random","time","span",".","Also","known","as","fade","in","and","fade","out",".","The","fade","works","on","a","logarithmic","scale",",","which","is","natural","to","human","hearing",".","The","way","this","works","is","that","it","picks","two","gains",":","a","first","gain","and","a","second","gain",".","Then","it","picks","a","time","range","for","the","transition","between","those","two","gains",".","Note","that","this","transition","can","start","before","the","audio","starts","and\/or","end","after","the","audio","ends",",","so","the","output","audio","can","start","or","end","in","the","middle","of","a","transition",".","The","gain","starts","at","the","first","gain","and","is","held","constant","until","the","transition","start",".","Then","it","transitions","to","the","second","gain",".","Then","that","gain","is","held","constant","until","the","end","of","the","sound","."]
code: def apply(self, samples: NDArray[np.float32], sample_rate: int):
      num_samples = samples.shape[-1]
      fade_mask = get_fade_mask(
          start_level_db=self.parameters["start_gain_db"],
          end_level_db=self.parameters["end_gain_db"],
          fade_time_samples=self.parameters["fade_time_samples"],
      )
      start_sample_index = self.parameters["t0"]
      end_sample_index = (
          start_sample_index + self.parameters["fade_time_samples"]
      )
      if start_sample_index < 0:
          # crop fade_mask: shave off a chunk in the beginning
          num_samples_to_shave_off = abs(start_sample_index)
          fade_mask = fade_mask[num_samples_to_shave_off:]
          start_sample_index = 0
  
      if end_sample_index > num_samples:
          # crop fade_mask: shave off a chunk in the end
          num_samples_to_shave_off = end_sample_index - num_samples
          fade_mask = fade_mask[
              : fade_mask.shape[-1] - num_samples_to_shave_off
          ]
          end_sample_index = num_samples
  
      samples = np.copy(samples)
  
      samples[..., start_sample_index:end_sample_index] *= fade_mask
      if start_sample_index > 0:
          samples[
              ..., :start_sample_index
          ] *= convert_decibels_to_amplitude_ratio(
              self.parameters["start_gain_db"]
          )
      if end_sample_index < num_samples:
          samples[
              ..., end_sample_index:
          ] *= convert_decibels_to_amplitude_ratio(
              self.parameters["end_gain_db"]
          )
      return samples
  
file_path: audiomentations/audiomentations/augmentations/gain_transition.py
imports: import random
  import warnings
  from typing import Union
  import numpy
  from numpy.typing import NDArray
  from audiomentations.core.transforms_interface import BaseWaveformTransform
  from audiomentations.core.utils import convert_decibels_to_amplitude_ratio
label_desc: Use image node_id 3 for calling the GainTransition obj's underlying member method code with example usage: obj.apply(samples, sample_rate) and returns: samples