/** Redo the last undone operation */
 public void performRedo() {
   if (!canRedo()) return;
   Object lastOperation = redoOperations.get(redoOperations.size() - 1);
   if (lastOperation instanceof Integer) {
     Integer last = (Integer) redoOperations.get(redoOperations.size() - 1);
     int action = last.intValue();
     CutListElement el;
     switch (action) {
       case EditAction.PASTE:
       case EditAction.TRIM:
         el = (CutListElement) redoElements.get(redoElements.size() - 1);
         redoElements.remove(el);
         cutList.addElement(el);
       case EditAction.CUT:
       case EditAction.DELETE:
         el = (CutListElement) redoElements.get(redoElements.size() - 1);
         redoElements.remove(el);
         cutList.addElement(el);
         break;
       default:
         break;
     }
   } else if (lastOperation instanceof CutListElement) {
     cutList.addElement((CutListElement) lastOperation);
     redoElements.remove(lastOperation);
   }
   undoOperations.add(lastOperation);
   redoOperations.remove(lastOperation);
 }
 /** Trim this WaveTab´s data to the current selection */
 public void performTrim() {
   Selection sel = waveDisplay.getSelection();
   int pos = sel.getLeft();
   cutList.addElement(new DeleteElement(0, sel.getLeft()));
   cutList.addElement(
       new DeleteElement(
           sel.getRight() - sel.getLeft(), cutList.getLength() - sel.getRight() + sel.getLeft()));
   waveDisplay.setSelection(new Selection(0, cutList.getLength()));
   waveDisplay.setPosition(0);
 }
 /** Delete the current selection */
 public void performDelete() {
   Selection sel = waveDisplay.getSelection();
   int pos = sel.getLeft();
   cutList.addElement(new DeleteElement(sel.getLeft(), sel.getRight() - sel.getLeft()));
   waveDisplay.setSelection(new Selection(0));
   waveDisplay.setPosition(pos);
 }
  /** Insert the clipboard content of the editor at the current position */
  public void performPaste() throws DifferentChannelException {
    WaveTab sourceTab = editor.getCopySource();
    if (sourceTab == null) return;

    if (sourceTab.getChannels() != getChannels()) {
      throw new DifferentChannelException();
    }

    CutListSource source = sourceTab.getCopySource();

    int pos = waveDisplay.getSelection().getLeft();
    performDelete();
    cutList.addElement(new InsertElement(source, pos));
  }
  /**
   * Apply the given effect to this WaveTab´s audio data
   *
   * @param effect The effect to apply
   */
  public void applyEffect(Effect effect) {
    Selection sel = waveDisplay.getSelection();
    if (sel.getLeft() == sel.getRight())
      waveDisplay.setSelection(new Selection(0, getTotalLength()));
    Thread thread = null;
    try {
      AudioInputStream stream = getAudioInputStream();
      int sourceChannels = stream.getFormat().getChannels();
      stream = AudioManager.getStereoInputStream(stream);
      final FXUnit unit = new FXUnit(effect);
      if (effect.needsAnalysis()) {
        Analyzer a = new Analyzer(unit, stream);
        ProgressMonitor monitor =
            new ProgressMonitor(getShell(), a, "Analyzing...", "Analyzing audio data");
        monitor.start();
        stream = AudioManager.getStereoInputStream(getAudioInputStream());
      }

      final SourceDataLine sourceLine = unit.getEffectSourceLine();
      sourceLine.open();
      sourceLine.start();
      final TargetDataLine targetLine = unit.getEffectTargetLine();
      targetLine.open();
      targetLine.start();
      if (!stream.getFormat().equals(sourceLine.getFormat())) {
        if (AudioSystem.isConversionSupported(sourceLine.getFormat(), stream.getFormat()))
          stream = AudioSystem.getAudioInputStream(sourceLine.getFormat(), stream);
        else {
          editor.errorMessage(
              "Unable to apply effect:\nFormat conversion from "
                  + stream.getFormat()
                  + " to "
                  + sourceLine.getFormat()
                  + " not supported.");
          return;
        }
      }

      final AudioInputStream inStream = stream;
      thread =
          new Thread() {
            public void run() {
              int numBytesRead = 0;
              byte[] buffer = new byte[sourceLine.getBufferSize()];
              while (numBytesRead != -1 && !getItem().isDisposed()) {
                try {
                  numBytesRead = inStream.read(buffer, 0, buffer.length);
                } catch (IOException e1) {
                  e1.printStackTrace();
                  numBytesRead = -1;
                }
                if (numBytesRead > 0) {
                  sourceLine.write(buffer, 0, numBytesRead);
                }
                try {
                  Thread.sleep(0, 1);
                } catch (InterruptedException e) {
                }
              }
            }
          };
      thread.start();

      AudioInputStream in = new AudioInputStream(targetLine);
      if (sourceChannels == 1) in = AudioManager.getMonoInputStream(in);
      File tempFile = File.createTempFile("gmtmp_", ".wav");
      AudioFormat tempFormat =
          new AudioFormat(
              fileFormat.getFormat().getSampleRate(),
              16,
              fileFormat.getFormat().getChannels(),
              true,
              false);
      AudioFileOutputStream out =
          AudioManager.getDefault()
              .getAudioFileOutputStream(
                  tempFile, tempFormat, AudioFileFormat.Type.WAVE, null, null, null);
      if (!in.getFormat().equals(out.getFormat()))
        in = AudioSystem.getAudioInputStream(out.getFormat(), in);
      SaveFileThread saver =
          new SaveFileThread(
              in, out, (int) inStream.getFrameLength(), in.getFormat().getFrameSize(), true);
      ProgressMonitor monitor =
          new ProgressMonitor(
              getShell(), saver, "Apply Effect", "Applying " + effect.getName() + " to Selection");
      monitor.start();

      File tempPeak = File.createTempFile("gmtmp_", ".gmpk");
      CreatePeakFileThread peak =
          new CreatePeakFileThread(AudioSystem.getAudioInputStream(tempFile), tempPeak);
      monitor =
          new ProgressMonitor(
              getShell(), peak, "Creating peak file", "Creating peak file for applied effect.");
      monitor.start();

      PeakWaveForm pwf = new PeakWaveForm(tempPeak);
      AudioFileWaveForm awf = new AudioFileWaveForm(tempFile, pwf, 32 * 1024, 25);
      CutListSource newSource = new AudioFileSource(tempFile, awf);

      sel = waveDisplay.getSelection();
      int left = sel.getLeft();
      int right = sel.getRight();

      ReplaceElement el =
          new ReplaceElement(
              effect.getName(), newSource, left, right - left, fileFormat.getFormat());
      cutList.addElement(el);
      undoOperations.add(el);
      redoOperations.clear();
      thread.stop();
    } catch (NotReadyException e) {
      e.printStackTrace();
      editor.errorMessage(e.getMessage());
      if (thread != null) thread.stop();
    } catch (NotFinishedException e) {
      e.printStackTrace();
      editor.errorMessage(e.getMessage());
      if (thread != null) thread.stop();
    } catch (LineUnavailableException e) {
      e.printStackTrace();
      editor.errorMessage(e.getMessage());
      if (thread != null) thread.stop();
    } catch (IOException e) {
      e.printStackTrace();
      editor.errorMessage(e.getMessage());
      if (thread != null) thread.stop();
    } catch (UnsupportedAudioFileException e) {
      e.printStackTrace();
      editor.errorMessage(e.getMessage());
      if (thread != null) thread.stop();
    }
  }