public static void main(String[] args) throws Exception {
    SoftSynthesizer synth = new SoftSynthesizer();
    synth.openStream(new AudioFormat(44100, 16, 1, true, false), null);

    SoftAudioBuffer in1 = new SoftAudioBuffer(250, synth.getFormat());
    SoftAudioBuffer out1 = new SoftAudioBuffer(250, synth.getFormat());

    float[] testdata1 = new float[in1.getSize()];
    float[] n1a = in1.array();
    float[] out1a = out1.array();
    for (int i = 0; i < n1a.length; i++) {
      testdata1[i] = (float) Math.sin(i * 0.3) * 0.9f;
      n1a[i] = testdata1[i];
      out1a[i] = 1;
    }

    SoftLimiter limiter = new SoftLimiter();
    limiter.init(44100, 147);
    limiter.setMixMode(false);
    limiter.setInput(0, in1);
    limiter.setOutput(0, out1);
    limiter.processControlLogic();
    limiter.processAudio();
    limiter.processControlLogic();
    limiter.processAudio();
    // Limiter should delay audio by one buffer,
    // and there should almost no different in output v.s. input
    for (int i = 0; i < n1a.length; i++) {
      if (Math.abs(out1a[i] - testdata1[i]) > 0.00001) throw new Exception("input != output");
    }

    synth.close();
  }
  protected void noteOn(int noteNumber, int velocity) {

    sustain = false;
    sostenuto = false;
    portamento = false;

    soundoff = false;
    on = true;
    active = true;
    started = true;
    // volume = velocity;

    noteOn_noteNumber = noteNumber;
    noteOn_velocity = velocity;

    lastMuteValue = 0;
    lastSoloMuteValue = 0;

    setNote(noteNumber);

    if (performer.forcedKeynumber) co_noteon_keynumber[0] = 0;
    else co_noteon_keynumber[0] = tunedKey * (1f / 128f);
    if (performer.forcedVelocity) co_noteon_velocity[0] = 0;
    else co_noteon_velocity[0] = velocity * (1f / 128f);
    co_mixer_active[0] = 0;
    co_mixer_gain[0] = 0;
    co_mixer_pan[0] = 0;
    co_mixer_balance[0] = 0;
    co_mixer_reverb[0] = 0;
    co_mixer_chorus[0] = 0;
    co_osc_pitch[0] = 0;
    co_filter_freq[0] = 0;
    co_filter_q[0] = 0;
    co_filter_type[0] = 0;
    co_noteon_on[0] = 1;

    eg.reset();
    lfo.reset();
    filter_left.reset();
    filter_right.reset();

    objects.put("master", synthesizer.getMainMixer().co_master);
    objects.put("eg", eg);
    objects.put("lfo", lfo);
    objects.put("noteon", co_noteon);
    objects.put("osc", co_osc);
    objects.put("mixer", co_mixer);
    objects.put("filter", co_filter);

    connections = performer.connections;

    if (connections_last == null || connections_last.length < connections.length) {
      connections_last = new double[connections.length];
    }
    if (connections_src == null || connections_src.length < connections.length) {
      connections_src = new double[connections.length][][];
      connections_src_kc = new int[connections.length][];
    }
    if (connections_dst == null || connections_dst.length < connections.length) {
      connections_dst = new double[connections.length][];
    }
    for (int i = 0; i < connections.length; i++) {
      ModelConnectionBlock conn = connections[i];
      connections_last[i] = 0;
      if (conn.getSources() != null) {
        ModelSource[] srcs = conn.getSources();
        if (connections_src[i] == null || connections_src[i].length < srcs.length) {
          connections_src[i] = new double[srcs.length][];
          connections_src_kc[i] = new int[srcs.length];
        }
        double[][] src = connections_src[i];
        int[] src_kc = connections_src_kc[i];
        connections_src[i] = src;
        for (int j = 0; j < srcs.length; j++) {
          src_kc[j] = getValueKC(srcs[j].getIdentifier());
          src[j] = getValue(srcs[j].getIdentifier());
        }
      }

      if (conn.getDestination() != null)
        connections_dst[i] = getValue(conn.getDestination().getIdentifier());
      else connections_dst[i] = null;
    }

    for (int i = 0; i < connections.length; i++) processConnection(i);

    if (extendedConnectionBlocks != null) {
      for (ModelConnectionBlock connection : extendedConnectionBlocks) {
        double value = 0;

        if (softchannel.keybasedcontroller_active == null) {
          for (ModelSource src : connection.getSources()) {
            double x = getValue(src.getIdentifier())[0];
            ModelTransform t = src.getTransform();
            if (t == null) value += x;
            else value += t.transform(x);
          }
        } else {
          for (ModelSource src : connection.getSources()) {
            double x = getValue(src.getIdentifier())[0];
            x = processKeyBasedController(x, getValueKC(src.getIdentifier()));
            ModelTransform t = src.getTransform();
            if (t == null) value += x;
            else value += t.transform(x);
          }
        }

        ModelDestination dest = connection.getDestination();
        ModelTransform t = dest.getTransform();
        if (t != null) value = t.transform(value);
        getValue(dest.getIdentifier())[0] += value;
      }
    }

    eg.init(synthesizer);
    lfo.init(synthesizer);
  }
  protected void processControlLogic() {
    if (stopping) {
      active = false;
      stopping = false;
      audiostarted = false;
      if (osc_stream != null)
        try {
          osc_stream.close();
        } catch (IOException e) {
          // e.printStackTrace();
        }

      if (stealer_channel != null) {
        stealer_channel.initVoice(
            this,
            stealer_performer,
            stealer_voiceID,
            stealer_noteNumber,
            stealer_velocity,
            stealer_extendedConnectionBlocks,
            stealer_channelmixer,
            stealer_releaseTriggered);
        stealer_releaseTriggered = false;
        stealer_channel = null;
        stealer_performer = null;
        stealer_voiceID = -1;
        stealer_noteNumber = 0;
        stealer_velocity = 0;
        stealer_extendedConnectionBlocks = null;
        stealer_channelmixer = null;
      }
    }
    if (started) {
      audiostarted = true;

      ModelOscillator osc = performer.oscillators[0];

      osc_stream_off_transmitted = false;
      if (osc instanceof ModelWavetable) {
        try {
          resampler.open((ModelWavetable) osc, synthesizer.getFormat().getSampleRate());
          osc_stream = resampler;
        } catch (IOException e) {
          // e.printStackTrace();
        }
      } else {
        osc_stream = osc.open(synthesizer.getFormat().getSampleRate());
      }
      osc_attenuation = osc.getAttenuation();
      osc_stream_nrofchannels = osc.getChannels();
      if (osc_buff == null || osc_buff.length < osc_stream_nrofchannels)
        osc_buff = new float[osc_stream_nrofchannels][];

      if (osc_stream != null)
        osc_stream.noteOn(softchannel, this, noteOn_noteNumber, noteOn_velocity);
    }
    if (audiostarted) {
      if (portamento) {
        double note_delta = tunedKey - (co_noteon_keynumber[0] * 128);
        double note_delta_a = Math.abs(note_delta);
        if (note_delta_a < 0.0000000001) {
          co_noteon_keynumber[0] = tunedKey * (1.0 / 128.0);
          portamento = false;
        } else {
          if (note_delta_a > softchannel.portamento_time)
            note_delta = Math.signum(note_delta) * softchannel.portamento_time;
          co_noteon_keynumber[0] += note_delta * (1.0 / 128.0);
        }

        int[] c = performer.midi_connections[4];
        if (c == null) return;
        for (int i = 0; i < c.length; i++) processConnection(c[i]);
      }

      eg.processControlLogic();
      lfo.processControlLogic();

      for (int i = 0; i < performer.ctrl_connections.length; i++)
        processConnection(performer.ctrl_connections[i]);

      osc_stream.setPitch((float) co_osc_pitch[0]);

      int filter_type = (int) co_filter_type[0];
      double filter_freq;

      if (co_filter_freq[0] == 13500.0) filter_freq = 19912.126958213175;
      else
        filter_freq = 440.0 * Math.exp(((co_filter_freq[0]) - 6900.0) * (Math.log(2.0) / 1200.0));
      /*
      filter_freq = 440.0 * Math.pow(2.0,
      ((co_filter_freq[0]) - 6900.0) / 1200.0);*/
      /*
       * double velocity = co_noteon_velocity[0]; if(velocity < 0.5)
       * filter_freq *= ((velocity * 2)*0.75 + 0.25);
       */

      double q = co_filter_q[0] / 10.0;
      filter_left.setFilterType(filter_type);
      filter_left.setFrequency(filter_freq);
      filter_left.setResonance(q);
      filter_right.setFilterType(filter_type);
      filter_right.setFrequency(filter_freq);
      filter_right.setResonance(q);
      /*
      float gain = (float) Math.pow(10,
      (-osc_attenuation + co_mixer_gain[0]) / 200.0);
       */
      float gain = (float) Math.exp((-osc_attenuation + co_mixer_gain[0]) * (Math.log(10) / 200.0));

      if (co_mixer_gain[0] <= -960) gain = 0;

      if (soundoff) {
        stopping = true;
        gain = 0;
        /*
         * if(co_mixer_gain[0] > -960)
         *   co_mixer_gain[0] -= 960;
         */
      }

      volume = (int) (Math.sqrt(gain) * 128);

      // gain *= 0.2;

      double pan = co_mixer_pan[0] * (1.0 / 1000.0);
      // System.out.println("pan = " + pan);
      if (pan < 0) pan = 0;
      else if (pan > 1) pan = 1;

      if (pan == 0.5) {
        out_mixer_left = gain * 0.7071067811865476f;
        out_mixer_right = out_mixer_left;
      } else {
        out_mixer_left = gain * (float) Math.cos(pan * Math.PI * 0.5);
        out_mixer_right = gain * (float) Math.sin(pan * Math.PI * 0.5);
      }

      double balance = co_mixer_balance[0] * (1.0 / 1000.0);
      if (balance != 0.5) {
        if (balance > 0.5) out_mixer_left *= (1 - balance) * 2;
        else out_mixer_right *= balance * 2;
      }

      if (synthesizer.reverb_on) {
        out_mixer_effect1 = (float) (co_mixer_reverb[0] * (1.0 / 1000.0));
        out_mixer_effect1 *= gain;
      } else out_mixer_effect1 = 0;
      if (synthesizer.chorus_on) {
        out_mixer_effect2 = (float) (co_mixer_chorus[0] * (1.0 / 1000.0));
        out_mixer_effect2 *= gain;
      } else out_mixer_effect2 = 0;
      out_mixer_end = co_mixer_active[0] < 0.5;

      if (!on)
        if (!osc_stream_off_transmitted) {
          osc_stream_off_transmitted = true;
          if (osc_stream != null) osc_stream.noteOff(noteOff_velocity);
        }
    }
    if (started) {
      last_out_mixer_left = out_mixer_left;
      last_out_mixer_right = out_mixer_right;
      last_out_mixer_effect1 = out_mixer_effect1;
      last_out_mixer_effect2 = out_mixer_effect2;
      started = false;
    }
  }
 public SoftVoice(SoftSynthesizer synth) {
   synthesizer = synth;
   filter_left = new SoftFilter(synth.getFormat().getSampleRate());
   filter_right = new SoftFilter(synth.getFormat().getSampleRate());
   nrofchannels = synth.getFormat().getChannels();
 }