gt:                         throw (ThreadDeath) t;
context: /*
   * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
   *
   * Distributable under LGPL license.
   * See terms of license at gnu.org.
   */
  package org.jitsi.impl.neomedia.jmfext.media.renderer.audio;
  
  import android.media.*;
  
  import javax.media.*;
  import javax.media.format.AudioFormat; // disambiguation
  
  import org.jitsi.impl.neomedia.device.*;
  import net.java.sip.communicator.util.*;
  import org.jitsi.impl.neomedia.*;
  import org.jitsi.service.neomedia.codec.*;
  
  /**
   * Implements an audio <tt>Renderer</tt> which uses {@link AudioTrack}.
   *
   * @author Lyubomir Marinov
   */
  public class AudioTrackRenderer
      extends AbstractAudioRenderer<AudioSystem>
  {
      /**
       * The <tt>Logger</tt> used by the <tt>AudioTrackRenderer</tt> class and its
       * instances for logging output.
       */
      private static final Logger logger
          = Logger.getLogger(AudioTrackRenderer.class);
  
      private static final int ABSTRACT_VOLUME_CONTROL_PERCENT_RANGE
          = (AbstractVolumeControl.MAX_VOLUME_PERCENT
                  - AbstractVolumeControl.MIN_VOLUME_PERCENT)
              / 100;
  
      /**
       * The length of the valid volume value range accepted by
       * <tt>AudioTrack</tt> instances.
       */
      private static final float AUDIO_TRACK_VOLUME_RANGE;
  
      /**
       * The latency in milliseconds to be incurred by
       * <tt>AudioTrackRenderer</tt>.
       */
      private static final int LATENCY = 0;
  
      /**
       * The maximum valid volume value accepted by <tt>AudioTrack</tt> instances.
       */
      private static final float MAX_AUDIO_TRACK_VOLUME
          = AudioTrack.getMaxVolume();
  
      /**
       * The minimum valid volume value accepted by <tt>AudioTrack</tt> instances.
       */
      private static final float MIN_AUDIO_TRACK_VOLUME
          = AudioTrack.getMinVolume();
  
      /**
       * The human-readable name of the <tt>AudioTrackRenderer</tt> FMJ plug-in.
       */
      private static final String PLUGIN_NAME
          = "android.media.AudioTrack Renderer";
  
      /**
       * The indicator which determines whether the gain specified by
       * {@link #gainControl} is to be applied in a software manner using
       * {@link AbstractVolumeControl#applyGain(GainControl, byte[], int, int)} or
       * in a hardware manner using
       * {@link AudioTrack#setStereoVolume(float, float)}.
       */
      private static final boolean USE_SOFTWARE_GAIN = false;
  
      static
      {
          AUDIO_TRACK_VOLUME_RANGE
              = Math.abs(MAX_AUDIO_TRACK_VOLUME - MIN_AUDIO_TRACK_VOLUME);
      }
  
      /**
       * The <tt>AudioTrack</tt> which implements the output device represented by
       * this <tt>Renderer</tt> and renders to it.
       */
      private AudioTrack audioTrack;
  
      /**
       * The length in bytes of media data to be written into {@link #audioTrack}
       * via a single call to {@link AudioTrack#write(byte[], int, int)}.
       */
      private int audioTrackWriteLengthInBytes;
  
      /**
       * The <tt>GainControl</tt> through which the volume/gain of rendered media
       * is controlled.
       */
      private final GainControl gainControl;
  
      /**
       * The value of {@link GainControl#getLevel()} of {@link #gainControl} which
       * has been applied to {@link #audioTrack} using
       * {@link AudioTrack#setStereoVolume(float, float)}.
       */
      private float gainControlLevelAppliedToAudioTrack = -1;
  
      /**
       * The buffer into which media data is written during the execution of
       * {@link #process(Buffer)} and from which media data is read into
       * {@link #audioTrack} in order to incur latency.
       */
      private byte[] latency;
  
      /**
       * The zero-based index in {@link #latency} at which the beginning of
       * (actual/valid) media data is contained.
       */
      private int latencyHead;
  
      /**
       * The number of bytes in {@link #latency} containing (actual/valid) media
       * data.
       */
      private int latencyLength;
  
      /**
       * The <tt>Thread</tt> which reads from {@link #latency} and writes into
       * {@link #audioTrack}.
       */
      private Thread latencyThread;
  
      /**
       * The indicator which determines whether this <tt>AudioTrackRenderer</tt>
       * is to set the priority of the thread in which its
       * {@link #process(Buffer)} method is executed.
       */
      private boolean setThreadPriority = true;
  
      /**
       * The type of audio stream in the terms of {@link AudioManager} to be
       * rendered to the output device represented by this
       * <tt>AudioTrackRenderer</tt>.
       */
      private int streamType;
  
      /**
       * The list of <tt>Format</tt>s of media data supported as input by this
       * <tt>Renderer</tt>.
       */
      private Format[] supportedInputFormats;
  
      /**
       * Initializes a new <tt>AudioTrackRenderer</tt> instance.
       */
      public AudioTrackRenderer()
      {
          this(true);
      }
  
      /**
       * Initializes a new <tt>AudioTrackRenderer</tt> instance.
       *
       * @param enableGainControl <tt>true</tt> to enable controlling the
       * volume/gain of the rendered media; otherwise, <tt>false</tt>
       */
      public AudioTrackRenderer(boolean enableGainControl)
      {
          super(AudioSystem.getAudioSystem(
              AudioSystem.LOCATOR_PROTOCOL_AUDIORECORD));
  
          if (enableGainControl)
          {
              MediaServiceImpl mediaServiceImpl
                  = NeomediaActivator.getMediaServiceImpl();
  
              gainControl
                  = (mediaServiceImpl == null)
                      ? null
                      : (GainControl) mediaServiceImpl.getOutputVolumeControl();
          }
          else
              gainControl = null;
      }
  
      /**
       * Implements {@link PlugIn#close()}. Closes this {@link PlugIn} and
       * releases its resources.
       *
       * @see PlugIn#close()
       */
      public synchronized void close()
      {
          if (audioTrack != null)
          {
              audioTrack.release();
              audioTrack = null;
  
              setThreadPriority = true;
  
              boolean interrupted = false;
  
              while (latencyThread != null)
              {
                  try
                  {
                      wait(20);
                  }
                  catch (InterruptedException ie)
                  {
                      interrupted = true;
                  }
              }
              if (interrupted)
                  Thread.currentThread().interrupt();
              latency = null;
              latencyHead = 0;
              latencyLength = 0;
          }
      }
  
      /**
       * Gets the descriptive/human-readable name of this FMJ plug-in.
       *
       * @return the descriptive/human-readable name of this FMJ plug-in
       */
      public String getName()
      {
          return PLUGIN_NAME;
      }
  
      /**
       * Gets the type of audio stream in the terms of {@link AudioManager} to be
       * rendered to the output device represented by this
       * <tt>AudioTrackRenderer</tt>.
       *
       * @return the type of audio stream in the terms of <tt>AudioManager</tt> to
       * be rendered to the output device represented by this
       * <tt>AudioTrackRenderer</tt>
       */
      private int getStreamType()
      {
          return streamType;
      }
  
      /**
       * Implements {@link Renderer#getSupportedInputFormats()}. Gets the list of
       * input <tt>Format</tt>s supported by this <tt>Renderer</tt>.
       *
       * @return the list of input <tt>Format</tt>s supported by this
       * <tt>Renderer</tt>
       * @see Renderer#getSupportedInputFormats()
       */
      public Format[] getSupportedInputFormats()
      {
          if (supportedInputFormats == null)
          {
              double[] supportedInputSampleRates
                  = new double[1 + Constants.AUDIO_SAMPLE_RATES.length];
              int supportedInputSampleRateCount = 0;
  
              supportedInputSampleRates[supportedInputSampleRateCount]
                      = AudioTrack.getNativeOutputSampleRate(getStreamType());
              supportedInputSampleRateCount++;
              System.arraycopy(
                      Constants.AUDIO_SAMPLE_RATES, 0,
                      supportedInputSampleRates, supportedInputSampleRateCount,
                      Constants.AUDIO_SAMPLE_RATES.length);
              supportedInputSampleRateCount
                  += Constants.AUDIO_SAMPLE_RATES.length;
  
              supportedInputFormats
                  = new Format[2 * supportedInputSampleRateCount];
              for (int i = 0; i < supportedInputSampleRateCount; i++)
              {
                  double sampleRate = supportedInputSampleRates[i];
  
                  supportedInputFormats[2 * i]
                          = new AudioFormat(
                                  AudioFormat.LINEAR,
                                  sampleRate,
                                  16 /* sampleSizeInBits */,
                                  Format.NOT_SPECIFIED /* channels */,
                                  AudioFormat.LITTLE_ENDIAN,
                                  AudioFormat.SIGNED,
                                  Format.NOT_SPECIFIED /* frameSizeInBits */,
                                  Format.NOT_SPECIFIED /* frameRate */,
                                  Format.byteArray);
                  supportedInputFormats[2 * i + 1]
                          = new AudioFormat(
                                  AudioFormat.LINEAR,
                                  sampleRate,
                                  8 /* sampleSizeInBits */,
                                  Format.NOT_SPECIFIED /* channels */,
                                  AudioFormat.LITTLE_ENDIAN,
                                  AudioFormat.SIGNED,
                                  Format.NOT_SPECIFIED /* frameSizeInBits */,
                                  Format.NOT_SPECIFIED /* frameRate */,
                                  Format.byteArray);
              }
          }
          return supportedInputFormats.clone();
      }
  
      /**
       * Implements {@link PlugIn#open()}. Opens this {@link PlugIn} and acquires
       * the resources that it needs to operate.
       *
       * @throws ResourceUnavailableException if any of the required resources
       * cannot be acquired
       * @see PlugIn#open()
       */
      public synchronized void open()
          throws ResourceUnavailableException
      {
          if (audioTrack == null)
          {
              AudioFormat inputFormat = this.inputFormat;
              double sampleRate = inputFormat.getSampleRate();
              int channels = inputFormat.getChannels();
              int channelConfig;
  
              if (channels == Format.NOT_SPECIFIED)
                  channels = 1;
              switch (channels)
              {
              case 1:
                  channelConfig = android.media.AudioFormat.CHANNEL_OUT_MONO;
                  break;
              case 2:
                  channelConfig = android.media.AudioFormat.CHANNEL_OUT_STEREO;
                  break;
              default:
                  throw new ResourceUnavailableException("channels");
              }
  
              int sampleSizeInBits = inputFormat.getSampleSizeInBits();
              int audioFormat;
  
              switch (sampleSizeInBits)
              {
              case 8:
                  audioFormat = android.media.AudioFormat.ENCODING_PCM_8BIT;
                  break;
              case 16:
                  audioFormat = android.media.AudioFormat.ENCODING_PCM_16BIT;
                  break;
              default:
                  throw new ResourceUnavailableException("sampleSizeInBits");
              }
  
              int bytesPerMillisecond
                  = (int)
                      Math.round(
                              (sampleRate / 1000)
                                  * channels
                                  * (sampleSizeInBits / 8));
  
              audioTrackWriteLengthInBytes
                  = 20 /* milliseconds */ * bytesPerMillisecond;
  
              /*
               * Give the AudioTrack a large enough buffer size in bytes in case
               * it remedies cracking.
               */
              int audioTrackBufferSizeInBytes
                  = 5 * audioTrackWriteLengthInBytes;
  
              /*
               * Apart from the thread in which #process(Buffer) is executed, use
               * the thread priority for the thread which will create the
               * AudioTrack.
               */
              org.jitsi.impl.neomedia.jmfext.media.protocol
                      .audiorecord.DataSource.setThreadPriority();
              audioTrack
                  = new AudioTrack(
                          getStreamType(),
                          (int) sampleRate,
                          channelConfig,
                          audioFormat,
                          Math.max(
                                  audioTrackBufferSizeInBytes,
                                  AudioTrack.getMinBufferSize(
                                          (int) sampleRate,
                                          channelConfig,
                                          audioFormat)),
                          AudioTrack.MODE_STREAM);
  
              setThreadPriority = true;
  
              if (USE_SOFTWARE_GAIN)
              {
                  /*
                   * Set the volume of the audioTrack to the maximum value because
                   * there is volume control via neomedia.
                   */
                  float volume = MAX_AUDIO_TRACK_VOLUME;
                  int setStereoVolume
                      = audioTrack.setStereoVolume(volume, volume);
  
                  if (setStereoVolume != AudioTrack.SUCCESS)
                  {
                      logger.warn(
                              "AudioTrack.setStereoVolume(float, float) failed"
                                  + " with return value "
                                  + setStereoVolume);
                  }
              }
              else
              {
                  /*
                   * The level specified by gainControl has not been applied to
                   * audioTrack yet.
                   */
                  gainControlLevelAppliedToAudioTrack = -1;
              }
  
              /* Incur latency if requested. */
              latency
                  = (LATENCY > 0)
                      ? new byte[2 * LATENCY * bytesPerMillisecond]
                      : null;
              latencyHead = 0;
              latencyLength = 0;
  
              if (latency == null)
                  latencyThread = null;
              else
              {
                  latencyThread
                      = new Thread()
                              {
                                  @Override
                                  public void run()
                                  {
                                      runInLatencyThread();
                                  }
                              };
                  latencyThread.setDaemon(true);
                  latencyThread.setName("AudioTrackRenderer.LatencyThread");
                  try
                  {
                      latencyThread.start();
                  }
                  catch (Throwable t)
                  {
                      latencyThread = null;
  
                      if (t instanceof ThreadDeath)
  