gt:                 ret = new MidiChannel[16];
context: /*
   * Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.  Sun designates this
   * particular file as subject to the "Classpath" exception as provided
   * by Sun in the LICENSE file that accompanied this code.
   *
   * This code is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   * version 2 for more details (a copy is included in the LICENSE file that
   * accompanied this code).
   *
   * You should have received a copy of the GNU General Public License version
   * 2 along with this work; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   *
   * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   * CA 95054 USA or visit www.sun.com if you need additional information or
   * have any questions.
   */
  
  package com.sun.media.sound;
  
  import java.io.File;
  import java.io.IOException;
  import java.io.InputStream;
  import java.lang.ref.WeakReference;
  import java.security.AccessControlException;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  import javax.sound.midi.Instrument;
  import javax.sound.midi.MidiChannel;
  import javax.sound.midi.MidiDevice;
  import javax.sound.midi.MidiSystem;
  import javax.sound.midi.MidiUnavailableException;
  import javax.sound.midi.Patch;
  import javax.sound.midi.Receiver;
  import javax.sound.midi.Soundbank;
  import javax.sound.midi.Transmitter;
  import javax.sound.midi.VoiceStatus;
  import javax.sound.sampled.AudioFormat;
  import javax.sound.sampled.AudioInputStream;
  import javax.sound.sampled.AudioSystem;
  import javax.sound.sampled.LineUnavailableException;
  import javax.sound.sampled.SourceDataLine;
  
  /**
   * The software synthesizer class.
   *
   * @author Karl Helgason
   */
  public class SoftSynthesizer implements AudioSynthesizer,
          ReferenceCountingDevice {
  
      protected static class WeakAudioStream extends InputStream
      {
          private volatile AudioInputStream stream;
          public SoftAudioPusher pusher = null;
          public AudioInputStream jitter_stream = null;
          public SourceDataLine sourceDataLine = null;
          public volatile long silent_samples = 0;
          private int framesize = 0;
          private WeakReference<AudioInputStream> weak_stream_link;
          private AudioFloatConverter converter;
          private float[] silentbuffer = null;
          private int samplesize;
  
          public void setInputStream(AudioInputStream stream)
          {
              this.stream = stream;
          }
  
          public int available() throws IOException {
              AudioInputStream local_stream = stream;
              if(local_stream != null)
                  return local_stream.available();
              return 0;
          }
  
          public int read() throws IOException {
               byte[] b = new byte[1];
               if (read(b) == -1)
                    return -1;
               return b[0] & 0xFF;
          }
  
          public int read(byte[] b, int off, int len) throws IOException {
               AudioInputStream local_stream = stream;
               if(local_stream != null)
                   return local_stream.read(b, off, len);
               else
               {
                   int flen = len / samplesize;
                   if(silentbuffer == null || silentbuffer.length < flen)
                       silentbuffer = new float[flen];
                   converter.toByteArray(silentbuffer, flen, b, off);
  
                   silent_samples += (long)((len / framesize));
  
                   if(pusher != null)
                   if(weak_stream_link.get() == null)
                   {
                       Runnable runnable = new Runnable()
                       {
                           SoftAudioPusher _pusher = pusher;
                           AudioInputStream _jitter_stream = jitter_stream;
                           SourceDataLine _sourceDataLine = sourceDataLine;
                           public void run()
                           {
                               _pusher.stop();
                               if(_jitter_stream != null)
                                  try {
                                      _jitter_stream.close();
                                  } catch (IOException e) {
                                      e.printStackTrace();
                                  }
                               if(_sourceDataLine != null)
                                   _sourceDataLine.close();
                           }
                       };
                       pusher = null;
                       jitter_stream = null;
                       sourceDataLine = null;
                       new Thread(runnable).start();
                   }
                   return len;
               }
          }
  
          public WeakAudioStream(AudioInputStream stream) {
              this.stream = stream;
              weak_stream_link = new WeakReference<AudioInputStream>(stream);
              converter = AudioFloatConverter.getConverter(stream.getFormat());
              samplesize = stream.getFormat().getFrameSize() / stream.getFormat().getChannels();
              framesize = stream.getFormat().getFrameSize();
          }
  
          public AudioInputStream getAudioInputStream()
          {
              return new AudioInputStream(this, stream.getFormat(), AudioSystem.NOT_SPECIFIED);
          }
  
          public void close() throws IOException
          {
              AudioInputStream astream  = weak_stream_link.get();
              if(astream != null)
                  astream.close();
          }
      }
  
      private static class Info extends MidiDevice.Info {
          public Info() {
              super(INFO_NAME, INFO_VENDOR, INFO_DESCRIPTION, INFO_VERSION);
          }
      }
  
      protected static final String INFO_NAME = "Gervill";
      protected static final String INFO_VENDOR = "OpenJDK";
      protected static final String INFO_DESCRIPTION = "Software MIDI Synthesizer";
      protected static final String INFO_VERSION = "1.0";
      protected final static MidiDevice.Info info = new Info();
  
      private static SourceDataLine testline = null;
  
      private static Soundbank defaultSoundBank = null;
  
      protected WeakAudioStream weakstream = null;
  
      protected Object control_mutex = this;
  
      protected int voiceIDCounter = 0;
  
      // 0: default
      // 1: DLS Voice Allocation
      protected int voice_allocation_mode = 0;
  
      protected boolean reverb_light = true;
      protected boolean reverb_on = true;
      protected boolean chorus_on = true;
      protected boolean agc_on = true;
  
      protected SoftChannel[] channels;
      protected SoftChannelProxy[] external_channels = null;
  
      private boolean largemode = false;
  
      // 0: GM Mode off (default)
      // 1: GM Level 1
      // 2: GM Level 2
      private int gmmode = 0;
  
      private int deviceid = 0;
  
      private AudioFormat format = new AudioFormat(44100, 16, 2, true, false);
  
      private SourceDataLine sourceDataLine = null;
  
      private SoftAudioPusher pusher = null;
      private AudioInputStream pusher_stream = null;
  
      private float controlrate = 147f;
  
      private boolean open = false;
      private boolean implicitOpen = false;
  
      private String resamplerType = "linear";
      private SoftResampler resampler = new SoftLinearResampler();
  
      private int number_of_midi_channels = 16;
      private int maxpoly = 64;
      private long latency = 200000; // 200 msec
      private boolean jitter_correction = false;
  
      private SoftMainMixer mainmixer;
      private SoftVoice[] voices;
  
      private Map<String, SoftTuning> tunings
              = new HashMap<String, SoftTuning>();
      private Map<String, SoftInstrument> inslist
              = new HashMap<String, SoftInstrument>();
      private Map<String, ModelInstrument> availlist
              = new HashMap<String, ModelInstrument>();
      private Map<String, ModelInstrument> loadedlist
              = new HashMap<String, ModelInstrument>();
  
      private ArrayList<Receiver> recvslist = new ArrayList<Receiver>();
  
      private void getBuffers(ModelInstrument instrument,
              List<ModelByteBuffer> buffers) {
          for (ModelPerformer performer : instrument.getPerformers()) {
              if (performer.getOscillators() != null) {
                  for (ModelOscillator osc : performer.getOscillators()) {
                      if (osc instanceof ModelByteBufferWavetable) {
                          ModelByteBufferWavetable w = (ModelByteBufferWavetable)osc;
                          ModelByteBuffer buff = w.getBuffer();
                          if (buff != null)
                              buffers.add(buff);
                          buff = w.get8BitExtensionBuffer();
                          if (buff != null)
                              buffers.add(buff);
                      }
                  }
              }
          }
      }
  
      private boolean loadSamples(List<ModelInstrument> instruments) {
          if (largemode)
              return true;
          List<ModelByteBuffer> buffers = new ArrayList<ModelByteBuffer>();
          for (ModelInstrument instrument : instruments)
              getBuffers(instrument, buffers);
          try {
              ModelByteBuffer.loadAll(buffers);
          } catch (IOException e) {
              return false;
          }
          return true;
      }
  
      private boolean loadInstruments(List<ModelInstrument> instruments) {
          if (!isOpen())
              return false;
          if (!loadSamples(instruments))
              return false;
  
          synchronized (control_mutex) {
              if (channels != null)
                  for (SoftChannel c : channels)
                      c.current_instrument = null;
              for (Instrument instrument : instruments) {
                  String pat = patchToString(instrument.getPatch());
                  availlist.remove(pat);
                  SoftInstrument softins
                          = new SoftInstrument((ModelInstrument) instrument);
                  inslist.put(pat, softins);
                  loadedlist.put(pat, (ModelInstrument) instrument);
              }
          }
  
          return true;
      }
  
      private void processPropertyInfo(Map<String, Object> info) {
          AudioSynthesizerPropertyInfo[] items = getPropertyInfo(info);
  
          String resamplerType = (String)items[0].value;
          if (resamplerType.equalsIgnoreCase("point"))
          {
              this.resampler = new SoftPointResampler();
              this.resamplerType = "point";
          }
          else if (resamplerType.equalsIgnoreCase("linear"))
          {
              this.resampler = new SoftLinearResampler2();
              this.resamplerType = "linear";
          }
          else if (resamplerType.equalsIgnoreCase("linear1"))
          {
              this.resampler = new SoftLinearResampler();
              this.resamplerType = "linear1";
          }
          else if (resamplerType.equalsIgnoreCase("linear2"))
          {
              this.resampler = new SoftLinearResampler2();
              this.resamplerType = "linear2";
          }
          else if (resamplerType.equalsIgnoreCase("cubic"))
          {
              this.resampler = new SoftCubicResampler();
              this.resamplerType = "cubic";
          }
          else if (resamplerType.equalsIgnoreCase("lanczos"))
          {
              this.resampler = new SoftLanczosResampler();
              this.resamplerType = "lanczos";
          }
          else if (resamplerType.equalsIgnoreCase("sinc"))
          {
              this.resampler = new SoftSincResampler();
              this.resamplerType = "sinc";
          }
  
          setFormat((AudioFormat)items[2].value);
          controlrate = (Float)items[1].value;
          latency = (Long)items[3].value;
          deviceid = (Integer)items[4].value;
          maxpoly = (Integer)items[5].value;
          reverb_on = (Boolean)items[6].value;
          chorus_on = (Boolean)items[7].value;
          agc_on = (Boolean)items[8].value;
          largemode = (Boolean)items[9].value;
          number_of_midi_channels = (Integer)items[10].value;
          jitter_correction = (Boolean)items[11].value;
          reverb_light = (Boolean)items[12].value;
      }
  
      private String patchToString(Patch patch) {
          if (patch instanceof ModelPatch && ((ModelPatch) patch).isPercussion())
              return "p." + patch.getProgram() + "." + patch.getBank();
          else
              return patch.getProgram() + "." + patch.getBank();
      }
  
      private void setFormat(AudioFormat format) {
          if (format.getChannels() > 2) {
              throw new IllegalArgumentException(
                      "Only mono and stereo audio supported.");
          }
          if (AudioFloatConverter.getConverter(format) == null)
              throw new IllegalArgumentException("Audio format not supported.");
          this.format = format;
      }
  
      protected void removeReceiver(Receiver recv) {
          boolean perform_close = false;
          synchronized (control_mutex) {
              if (recvslist.remove(recv)) {
                  if (implicitOpen && recvslist.isEmpty())
                      perform_close = true;
              }
          }
          if (perform_close)
              close();
      }
  
      protected SoftMainMixer getMainMixer() {
          if (!isOpen())
              return null;
          return mainmixer;
      }
  
      protected SoftInstrument findInstrument(int program, int bank, int channel) {
  
          // Add support for GM2 banks 0x78 and 0x79
          // as specified in DLS 2.2 in Section 1.4.6
          // which allows using percussion and melodic instruments
          // on all channels
          if (bank >> 7 == 0x78 || bank >> 7 == 0x79) {
              SoftInstrument current_instrument
                      = inslist.get(program + "." + bank);
              if (current_instrument != null)
                  return current_instrument;
  
              String p_plaf;
              if (bank >> 7 == 0x78)
                  p_plaf = "p.";
              else
                  p_plaf = "";
  
              // Instrument not found fallback to MSB:bank, LSB:0
              current_instrument = inslist.get(p_plaf + program + "."
                      + ((bank & 128) << 7));
              if (current_instrument != null)
                  return current_instrument;
              // Instrument not found fallback to MSB:0, LSB:bank
              current_instrument = inslist.get(p_plaf + program + "."
                      + (bank & 128));
              if (current_instrument != null)
                  return current_instrument;
              // Instrument not found fallback to MSB:0, LSB:0
              current_instrument = inslist.get(p_plaf + program + ".0");
              if (current_instrument != null)
                  return current_instrument;
              // Instrument not found fallback to MSB:0, LSB:0, program=0
              current_instrument = inslist.get(p_plaf + program + "0.0");
              if (current_instrument != null)
                  return current_instrument;
              return null;
          }
  
          // Channel 10 uses percussion instruments
          String p_plaf;
          if (channel == 9)
              p_plaf = "p.";
          else
              p_plaf = "";
  
          SoftInstrument current_instrument
                  = inslist.get(p_plaf + program + "." + bank);
          if (current_instrument != null)
              return current_instrument;
          // Instrument not found fallback to MSB:0, LSB:0
          current_instrument = inslist.get(p_plaf + program + ".0");
          if (current_instrument != null)
              return current_instrument;
          // Instrument not found fallback to MSB:0, LSB:0, program=0
          current_instrument = inslist.get(p_plaf + "0.0");
          if (current_instrument != null)
              return current_instrument;
          return null;
      }
  
      protected int getVoiceAllocationMode() {
          return voice_allocation_mode;
      }
  
      protected int getGeneralMidiMode() {
          return gmmode;
      }
  
      protected void setGeneralMidiMode(int gmmode) {
          this.gmmode = gmmode;
      }
  
      protected int getDeviceID() {
          return deviceid;
      }
  
      protected float getControlRate() {
          return controlrate;
      }
  
      protected SoftVoice[] getVoices() {
          return voices;
      }
  
      protected SoftTuning getTuning(Patch patch) {
          String t_id = patchToString(patch);
          SoftTuning tuning = tunings.get(t_id);
          if (tuning == null) {
              tuning = new SoftTuning(patch);
              tunings.put(t_id, tuning);
          }
          return tuning;
      }
  
      public long getLatency() {
          synchronized (control_mutex) {
              return latency;
          }
      }
  
      public AudioFormat getFormat() {
          synchronized (control_mutex) {
              return format;
          }
      }
  
      public int getMaxPolyphony() {
          synchronized (control_mutex) {
              return maxpoly;
          }
      }
  
      public MidiChannel[] getChannels() {
  
          synchronized (control_mutex) {
              // if (external_channels == null) => the synthesizer is not open,
              // create 16 proxy channels
              // otherwise external_channels has the same length as channels array
              if (external_channels == null) {
                  external_channels = new SoftChannelProxy[16];
                  for (int i = 0; i < external_channels.length; i++)
                      external_channels[i] = new SoftChannelProxy();
              }
              MidiChannel[] ret;
              if (isOpen())
                  ret = new MidiChannel[channels.length];
              else
  