/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package gui;

import data.IInstrument;
import data.IScore;
import data.ITonality;
import database.DatabaseInterface;
import java.awt.Rectangle;
import java.io.File;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.table.DefaultTableModel;
import jm.music.data.Note;
import jm.music.data.Part;
import jm.music.data.Phrase;
import jm.music.data.Score;
import jm.music.tools.Mod;
import jm.util.Play;
import jm.util.Read;
import jm.util.Write;
import midi.KeyFinder;
import midi.MIDIAnalyser;
import midi.MIDIPlayer;

/**
 *
 * @author Tom
 */
public class ConvertDialog extends javax.swing.JDialog {
   private String user = ""; //username
   private String pw = ""; //password
   private String url = ""; //DB-Url
   private String path = ""; //path to open up filechooser
   private HashMap<String, Integer> categories = new HashMap<>(); //List or categories ordered
   private Score scr = new Score(); //input MIDI
   private ArrayList<IInstrument> iinew; //Instruments of new genre
   private ArrayList<IInstrument> iiold; //Instruments of old genre
   private Score newScr = null; //converted MIDI
   private IScore iScr; //converted MIDI
   private String[] transposeStep = {"C","C#","D","Eb","E","F","F#","G","Ab","A","Bb","B"};
   private boolean StartStop = true;
   
   /**
    * Creates new form ConvertDialog
    */
   public ConvertDialog(java.awt.Frame parent, boolean modal, String user, String pw, String url, String path) {
      super(parent, modal);
      initComponents();
      
      this.path = path;
      this.user = user;
      this.pw = pw;
      this.url = url;
      
      btnPlay.setEnabled(false);
      btnStop.setEnabled(false);
      btnSave.setEnabled(false);
      btnPlayOriginal.setEnabled(false);
      
      Rectangle parentBounds = parent.getBounds();
      this.setBounds((int)(parentBounds.getWidth()/2-getBounds().getWidth()/2) + (int)parentBounds.getX(), 
                     (int)(parentBounds.getHeight()/2-getBounds().getHeight()/2) + (int)parentBounds.getY(), 
                     (int)getBounds().getWidth(), (int)getBounds().getHeight());
      
      DatabaseInterface dbi = new DatabaseInterface(user, pw, url);
      LinkedHashMap<String, Integer> cat = dbi.getCategoriesOrdered();
      Iterator it = cat.entrySet().iterator();
      while (it.hasNext()) {
         Map.Entry pairs = (Map.Entry)it.next();
         cmbPrevGenre.addItem(pairs.getKey());
         cmbGenre.addItem(pairs.getKey());
         categories.put(pairs.getKey().toString(), (int)pairs.getValue());
      }
      iiold = dbi.getInstruments(categories.get(cmbPrevGenre.getSelectedItem().toString()));
      iinew = dbi.getInstruments(categories.get(cmbGenre.getSelectedItem().toString()));
      
      if (categories.containsKey(cmbGenre.getSelectedItem().toString())) {
         int i = categories.get(cmbGenre.getSelectedItem().toString());
         
         //insert all Keys with Counts in combobox
         ArrayList<ITonality> ito = dbi.getTonalities(i);
         for (ITonality t : ito) {
            if (t.isMajor()) {
               cmbKey.addItem(t.getKey() +" major (" +t.getCount() +")");
            } else {
               cmbKey.addItem(t.getKey() +" minor (" +t.getCount() +")");
            }
         }
      }
      dbi.closeConnection();
   }

   /**
    * This method is called from within the constructor to initialize the form.
    * WARNING: Do NOT modify this code. The content of this method is always
    * regenerated by the Form Editor.
    */
   @SuppressWarnings("unchecked")
   // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
   private void initComponents() {

      panUp = new javax.swing.JPanel();
      jLabel1 = new javax.swing.JLabel();
      txtMidi = new javax.swing.JTextField();
      btnMidi = new javax.swing.JButton();
      jLabel2 = new javax.swing.JLabel();
      cmbPrevGenre = new javax.swing.JComboBox();
      cmbGenre = new javax.swing.JComboBox();
      jLabel4 = new javax.swing.JLabel();
      btnPlayOriginal = new javax.swing.JButton();
      panMiddle = new javax.swing.JPanel();
      jLabel3 = new javax.swing.JLabel();
      cmbKey = new javax.swing.JComboBox();
      jScrollPane1 = new javax.swing.JScrollPane();
      tabParts = new javax.swing.JTable();
      panDown = new javax.swing.JPanel();
      btnCancel = new javax.swing.JButton();
      btnConvert = new javax.swing.JButton();
      btnStop = new javax.swing.JButton();
      btnPlay = new javax.swing.JButton();
      btnSave = new javax.swing.JButton();

      setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
      setTitle("Convert MIDI Genre");
      addWindowListener(new java.awt.event.WindowAdapter() {
         public void windowClosing(java.awt.event.WindowEvent evt) {
            formWindowClosing(evt);
         }
      });

      panUp.setBorder(javax.swing.BorderFactory.createEtchedBorder());

      jLabel1.setText("MIDI:");

      btnMidi.setText("...");
      btnMidi.addActionListener(new java.awt.event.ActionListener() {
         public void actionPerformed(java.awt.event.ActionEvent evt) {
            btnMidiActionPerformed(evt);
         }
      });

      jLabel2.setText("previous Genre:");

      cmbPrevGenre.addActionListener(new java.awt.event.ActionListener() {
         public void actionPerformed(java.awt.event.ActionEvent evt) {
            cmbPrevGenreActionPerformed(evt);
         }
      });

      cmbGenre.addActionListener(new java.awt.event.ActionListener() {
         public void actionPerformed(java.awt.event.ActionEvent evt) {
            cmbGenreActionPerformed(evt);
         }
      });

      jLabel4.setText("Target-Genre:");

      btnPlayOriginal.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/play.png"))); // NOI18N
      btnPlayOriginal.setToolTipText("Play");
      btnPlayOriginal.setFocusable(false);
      btnPlayOriginal.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
      btnPlayOriginal.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
      btnPlayOriginal.addActionListener(new java.awt.event.ActionListener() {
         public void actionPerformed(java.awt.event.ActionEvent evt) {
            btnPlayOriginalActionPerformed(evt);
         }
      });

      javax.swing.GroupLayout panUpLayout = new javax.swing.GroupLayout(panUp);
      panUp.setLayout(panUpLayout);
      panUpLayout.setHorizontalGroup(
         panUpLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
         .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panUpLayout.createSequentialGroup()
            .addGap(30, 30, 30)
            .addGroup(panUpLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
               .addComponent(jLabel2)
               .addComponent(jLabel4)
               .addGroup(panUpLayout.createSequentialGroup()
                  .addComponent(jLabel1)
                  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                  .addComponent(btnPlayOriginal, javax.swing.GroupLayout.PREFERRED_SIZE, 31, javax.swing.GroupLayout.PREFERRED_SIZE)))
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
            .addGroup(panUpLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
               .addComponent(txtMidi)
               .addComponent(cmbGenre, javax.swing.GroupLayout.Alignment.LEADING, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
               .addComponent(cmbPrevGenre, javax.swing.GroupLayout.Alignment.LEADING, 0, 289, Short.MAX_VALUE))
            .addGap(18, 18, 18)
            .addComponent(btnMidi, javax.swing.GroupLayout.PREFERRED_SIZE, 29, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addGap(39, 39, 39))
      );
      panUpLayout.setVerticalGroup(
         panUpLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
         .addGroup(panUpLayout.createSequentialGroup()
            .addGap(14, 14, 14)
            .addGroup(panUpLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
               .addGroup(panUpLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                  .addComponent(jLabel1)
                  .addComponent(txtMidi, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                  .addComponent(btnMidi))
               .addComponent(btnPlayOriginal, javax.swing.GroupLayout.PREFERRED_SIZE, 31, javax.swing.GroupLayout.PREFERRED_SIZE))
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
            .addGroup(panUpLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
               .addComponent(jLabel2)
               .addComponent(cmbPrevGenre, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
            .addGroup(panUpLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
               .addComponent(cmbGenre, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
               .addComponent(jLabel4))
            .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
      );

      panMiddle.setBorder(javax.swing.BorderFactory.createEtchedBorder());

      jLabel3.setText("Key:");

      tabParts.setModel(new javax.swing.table.DefaultTableModel(
         new Object [][] {

         },
         new String [] {
            "Part", "Instrument", "mapped Instrument"
         }
      ) {
         Class[] types = new Class [] {
            java.lang.Integer.class, java.lang.String.class, java.lang.String.class
         };
         boolean[] canEdit = new boolean [] {
            false, false, false
         };

         public Class getColumnClass(int columnIndex) {
            return types [columnIndex];
         }

         public boolean isCellEditable(int rowIndex, int columnIndex) {
            return canEdit [columnIndex];
         }
      });
      jScrollPane1.setViewportView(tabParts);

      javax.swing.GroupLayout panMiddleLayout = new javax.swing.GroupLayout(panMiddle);
      panMiddle.setLayout(panMiddleLayout);
      panMiddleLayout.setHorizontalGroup(
         panMiddleLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
         .addGroup(panMiddleLayout.createSequentialGroup()
            .addGap(32, 32, 32)
            .addComponent(jLabel3)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
            .addGroup(panMiddleLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
               .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
               .addComponent(cmbKey, 0, 344, Short.MAX_VALUE))
            .addGap(84, 84, 84))
      );
      panMiddleLayout.setVerticalGroup(
         panMiddleLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
         .addGroup(panMiddleLayout.createSequentialGroup()
            .addContainerGap()
            .addGroup(panMiddleLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
               .addComponent(jLabel3)
               .addComponent(cmbKey, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
            .addGap(18, 18, 18)
            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 251, Short.MAX_VALUE)
            .addContainerGap())
      );

      panDown.setBorder(javax.swing.BorderFactory.createEtchedBorder());

      btnCancel.setText("Close");
      btnCancel.addActionListener(new java.awt.event.ActionListener() {
         public void actionPerformed(java.awt.event.ActionEvent evt) {
            btnCancelActionPerformed(evt);
         }
      });

      btnConvert.setText("Convert");
      btnConvert.addActionListener(new java.awt.event.ActionListener() {
         public void actionPerformed(java.awt.event.ActionEvent evt) {
            btnConvertActionPerformed(evt);
         }
      });

      btnStop.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/stop.png"))); // NOI18N
      btnStop.setToolTipText("Stop");
      btnStop.setFocusable(false);
      btnStop.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
      btnStop.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
      btnStop.addActionListener(new java.awt.event.ActionListener() {
         public void actionPerformed(java.awt.event.ActionEvent evt) {
            btnStopActionPerformed(evt);
         }
      });

      btnPlay.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/play.png"))); // NOI18N
      btnPlay.setToolTipText("Play");
      btnPlay.setFocusable(false);
      btnPlay.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
      btnPlay.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
      btnPlay.addActionListener(new java.awt.event.ActionListener() {
         public void actionPerformed(java.awt.event.ActionEvent evt) {
            btnPlayActionPerformed(evt);
         }
      });

      btnSave.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/save.png"))); // NOI18N
      btnSave.setToolTipText("Save");
      btnSave.setFocusable(false);
      btnSave.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
      btnSave.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
      btnSave.addActionListener(new java.awt.event.ActionListener() {
         public void actionPerformed(java.awt.event.ActionEvent evt) {
            btnSaveActionPerformed(evt);
         }
      });

      javax.swing.GroupLayout panDownLayout = new javax.swing.GroupLayout(panDown);
      panDown.setLayout(panDownLayout);
      panDownLayout.setHorizontalGroup(
         panDownLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
         .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panDownLayout.createSequentialGroup()
            .addGap(65, 65, 65)
            .addComponent(btnPlay, javax.swing.GroupLayout.PREFERRED_SIZE, 31, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(btnStop, javax.swing.GroupLayout.PREFERRED_SIZE, 31, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(btnSave, javax.swing.GroupLayout.PREFERRED_SIZE, 31, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
            .addComponent(btnConvert)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(btnCancel)
            .addGap(24, 24, 24))
      );
      panDownLayout.setVerticalGroup(
         panDownLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
         .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panDownLayout.createSequentialGroup()
            .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
            .addGroup(panDownLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
               .addComponent(btnSave, javax.swing.GroupLayout.PREFERRED_SIZE, 31, javax.swing.GroupLayout.PREFERRED_SIZE)
               .addComponent(btnPlay, javax.swing.GroupLayout.PREFERRED_SIZE, 31, javax.swing.GroupLayout.PREFERRED_SIZE)
               .addComponent(btnStop, javax.swing.GroupLayout.PREFERRED_SIZE, 31, javax.swing.GroupLayout.PREFERRED_SIZE)
               .addGroup(panDownLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                  .addComponent(btnCancel)
                  .addComponent(btnConvert)))
            .addContainerGap())
      );

      javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
      getContentPane().setLayout(layout);
      layout.setHorizontalGroup(
         layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
         .addComponent(panUp, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
         .addComponent(panMiddle, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
         .addComponent(panDown, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
      );
      layout.setVerticalGroup(
         layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
         .addGroup(layout.createSequentialGroup()
            .addComponent(panUp, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(panMiddle, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(panDown, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
      );

      pack();
   }// </editor-fold>//GEN-END:initComponents

   private void btnConvertActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnConvertActionPerformed
      if (txtMidi.getText().equals("")) {
         JOptionPane.showMessageDialog(this, "Please select a MIDI-File to convert.", "Error", JOptionPane.ERROR_MESSAGE);
      } else {
         newScr = scr.copy();
         newScr.setPPQN(scr.getPPQN());
         
         DefaultTableModel tableModel = (DefaultTableModel)tabParts.getModel();
         for (int i=0; i < tableModel.getRowCount(); i++) {
            int instr = getInstrumentFromGroup(tableModel.getValueAt(i, 1).toString());
            if (instr != 200) { //alles ausser percussion anpassen
               newScr.getPart(i).setInstrument(instr);
            }
         }
         //auf die gewuenschte Tonart transponieren
         newScr = TransposeScore(newScr);
         MIDIAnalyser analyser = new MIDIAnalyser();
         iScr = analyser.analyseTask(newScr);
         btnPlay.setEnabled(true);
         btnSave.setEnabled(true);
      }
   }//GEN-LAST:event_btnConvertActionPerformed
   
   /**
    * transponiert den Score in eine andere Tonart
    * @param s der Score
    * @return der transponierte Score
    */
   private Score TransposeScore(Score s) {
      double[] noteDurations = {0,0,0,0,0,0,0,0,0,0,0,0};
      double duration;
      DecimalFormat roundDuration = new DecimalFormat("#.##");
      
      for (Part p : scr.getPartArray()) {
         for (Phrase ip : p.getPhraseArray()) {
            for (Note in : ip.getNoteArray()) {
               if (in.getPitch() != Integer.MIN_VALUE) {
                  //1.0 = Viertel, 0.5 = Achtel, ....
                  duration = in.getRhythmValue()/scr.getPPQN(); 
                  duration = Double.valueOf(roundDuration.format(duration).replace(",", "."));
                  switch (in.getNote())
                  {
                      case "C":
                          noteDurations[0] += duration;
                          break;
                      case "C#":
                          noteDurations[1] += duration;
                          break;
                      case "D":
                          noteDurations[2] += duration;
                          break;
                      case "Eb":
                          noteDurations[3] += duration;
                          break;
                      case "E":
                          noteDurations[4] += duration;
                          break;
                      case "F":
                          noteDurations[5] += duration;
                          break;
                      case "F#":
                          noteDurations[6] += duration;
                          break;
                      case "G":
                          noteDurations[7] += duration;
                          break;
                      case "Ab":
                          noteDurations[8] += duration;
                          break;
                      case "A":
                          noteDurations[9] += duration;
                          break;
                      case "Bb":
                          noteDurations[10] += duration;
                          break;
                      case "B":
                          noteDurations[11] += duration;
                          break;
                  }
               }
            }
         }
      }

      //krumhansl key finding algorithm
      KeyFinder kf = new KeyFinder(noteDurations);
      
      String oldKey = kf.getKey().substring(kf.getKey().indexOf(" ")+1);
      //String oldMajor = kf.getKey().substring(0,kf.getKey().indexOf(" "));
      String newKey = cmbKey.getSelectedItem().toString().substring(0, cmbKey.getSelectedItem().toString().indexOf(" "));
      String newMajor = cmbKey.getSelectedItem().toString().substring(cmbKey.getSelectedItem().toString().indexOf(" ")+1,cmbKey.getSelectedItem().toString().lastIndexOf(" "));
      
      int Steps = getHalftoneSteps(oldKey, newKey);
      //transpose all channel except channel 9 (percussion)
      for (Part p : s.getPartArray()) {
         if (p.getChannel() != 9) {
            Mod.transpose(p, Steps);
         }
      }
      
      //set major, minor
      if (newMajor.equalsIgnoreCase("major")) {
         s.setKeyQuality(0); //0 = major, 1 = minor
      } else {
         s.setKeyQuality(1); //0 = major, 1 = minor
      }
      return s;
   }
   
   /**
    * replace key by MIDI-key-name
    * @param s key to check
    * @return the correspondent key or the key passed through
    */
   private String FormatKeys(String s) {
      //{"C","C#","D","Eb","E","F","F#","G","Ab","A","Bb","B"};
      switch (s) {
         case "A#":
            s = "Bb";
            break;
         case "D#":
            s = "Eb";
            break;
         case "G#":
            s = "Ab";
            break;
      }
      return s;
   }
   
   /**
    * gibt die Anzahl der Halbtoene zurueck um von der Ursprungstonart in die Zieltonart zu gelangen
    * der geringere Unterschied wird verwendet (man kann auf beide Seiten transponieren, nur die Oktave aendert sich)
    * @param From Ursprungstonart
    * @param To Zieltonart
    * @return Verschiebung in Halbtoenen
    */
   private int getHalftoneSteps(String From, String To) {
      int StepOld = 0;
      int StepNew = 0;
      int x1, x2;
      
      From = FormatKeys(From);
      To = FormatKeys(To);
      
      for (int i=0; i<transposeStep.length; i++) {
         if (transposeStep[i].equalsIgnoreCase(From)) {
            StepOld = i;
         }
         if (transposeStep[i].equalsIgnoreCase(To)) {
            StepNew = i;
         }
      }
      x1 = (StepOld - StepNew);
      x2 = (StepNew - StepOld);
      
      if (x1 < 0){
         x1 += 12;
      }else{
         x1 *= -1;
      }
      if (x2 < 0){
         x2 += 12;
      }else{
         x1 *= -1;
      }
      if (Math.abs(x1) < Math.abs(x2)) {
         return x1;
      }else{
         return x2;
      }
   }
   
   private void btnCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCancelActionPerformed
      dispose();
   }//GEN-LAST:event_btnCancelActionPerformed

   /**
    * oeffnet einen Filechooser zur Auswahl eines MIDI-Files
    * @param evt 
    */
   private void btnMidiActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnMidiActionPerformed
      JFileChooser fc = new JFileChooser(path);

      fc.setMultiSelectionEnabled(false);
      FileNameExtensionFilter filter = new FileNameExtensionFilter("MIDIs","mid", "MID");
      fc.setFileFilter(filter);
      fc.setAcceptAllFileFilterUsed(false);

      if (fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
          path = fc.getSelectedFile().toString();
          txtMidi.setText(fc.getSelectedFile().getName().substring(0, fc.getSelectedFile().getName().length()-4));
          Read.midi(scr,path);
          scr.setTitle(txtMidi.getText());
          fillTable();
          btnPlayOriginal.setEnabled(true);
      }
   }//GEN-LAST:event_btnMidiActionPerformed

   /**
    * befuellt die Combobox mit den Kategorien
    * @param evt 
    */
   private void cmbPrevGenreActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cmbPrevGenreActionPerformed
      if (categories.containsKey(cmbPrevGenre.getSelectedItem().toString())) {
         DatabaseInterface dbi = new DatabaseInterface(user, pw, url);
         iiold = dbi.getInstruments(categories.get(cmbPrevGenre.getSelectedItem().toString()));
         dbi.closeConnection();
         fillTable();
      }
   }//GEN-LAST:event_cmbPrevGenreActionPerformed

   /**
    * fuellt die Comboboxen im Mittelteil mit Keys und Instrumenten
    * @param evt 
    */
   private void cmbGenreActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cmbGenreActionPerformed
      if (categories.containsKey(cmbGenre.getSelectedItem().toString())) {
         int i = categories.get(cmbGenre.getSelectedItem().toString());
         
         //insert all Keys with Counts in combobox
         cmbKey.removeAllItems();
         DatabaseInterface dbi = new DatabaseInterface(user, pw, url);
         ArrayList<ITonality> it = dbi.getTonalities(i);
         iinew = dbi.getInstruments(i);
         dbi.closeConnection();
         for (ITonality t : it) {
            if (t.isMajor()) {
               cmbKey.addItem(t.getKey() +" major (" +t.getCount() +")");
            } else {
               cmbKey.addItem(t.getKey() +" minor (" +t.getCount() +")");
            }
         }
         fillTable();
      }
   }//GEN-LAST:event_cmbGenreActionPerformed

   private void formWindowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosing
      try {
            MIDIPlayer.stop();
        }
        catch (NullPointerException npe) //Player wurde nie gespielt
        {
            //do nothing
        }
      try {
         Play.stopMidi();
      }
      catch (NullPointerException npe) //Player wurde nie gespielt
      {
          //do nothing
      }
   }//GEN-LAST:event_formWindowClosing

   private void btnPlayActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnPlayActionPerformed
      if (iScr != null) {
         MIDIPlayer.play(iScr);
         btnStop.setEnabled(true);
      }
   }//GEN-LAST:event_btnPlayActionPerformed

   private void btnStopActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnStopActionPerformed
      try {
         MIDIPlayer.stop();
     }
     catch (NullPointerException npe) //Player wurde nie gespielt
     {
         //do nothing
     }
   }//GEN-LAST:event_btnStopActionPerformed

   private void btnPlayOriginalActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnPlayOriginalActionPerformed
      if (!txtMidi.getText().equalsIgnoreCase("")) {
         if (StartStop) {
            if (scr != null) {
               StartStop = false;
               btnPlayOriginal.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/stop.png")));
               Play.midi(scr, false);
            }
         } else {
            try {
               StartStop = true;
               btnPlayOriginal.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/play.png")));
               Play.stopMidi();
            }
            catch (NullPointerException npe) //Player wurde nie gespielt
            {
                //do nothing
            }
         }
      }
   }//GEN-LAST:event_btnPlayOriginalActionPerformed

   /**
    * speichert das konvertierte MIDI-File ab
    * @param evt 
    */
   private void btnSaveActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnSaveActionPerformed
      JFileChooser fc = new JFileChooser(path);
      fc.setMultiSelectionEnabled(false);
      FileNameExtensionFilter filter = new FileNameExtensionFilter("MIDIs","mid", "MID");
      fc.setFileFilter(filter);
      fc.setAcceptAllFileFilterUsed(false);
      fc.setSelectedFile(new File(txtMidi.getText() +"_converted.mid"));
      
      int userSelection = fc.showSaveDialog(this);

      if (userSelection == JFileChooser.APPROVE_OPTION) {
          String fileName = fc.getSelectedFile().getAbsolutePath();
          if (!fileName.toLowerCase().endsWith(".mid")) {
             fileName += ".mid";
          }
          Write.midi(iScr.getMusicStructure(),fileName);
      }
   }//GEN-LAST:event_btnSaveActionPerformed

   /**
    * befuellt die Tabelle mit den neuen gemappten Instrumentengruppen (pro Kanal)
    */
   private void fillTable() {
      //insert all Parts in table
      if (cmbGenre.getSelectedItem() != null && cmbPrevGenre.getSelectedItem() != null) {
         DefaultTableModel tableModel = (DefaultTableModel)tabParts.getModel();
         //table loeschen
         int rowCount = tableModel.getRowCount();
         for(int l = 0; l < rowCount; l++)
         {
            tableModel.removeRow(0);
         }
         if (scr != null) {
            int j=0;
            for (Part p : scr.getPartArray()) {
               if (p.getChannel() == 9) {
                  tableModel.insertRow(tableModel.getRowCount(), new Object[]{j,"Percussion","Percussion"});
               } else {
                  String newInst = getTargetInstrument(p.getInstrument());
                  tableModel.insertRow(tableModel.getRowCount(), new Object[]{j,newInst,getInstrumentGroup(p.getInstrument())});
               }
               j++;
            }
         }
      }
   }
   
   /**
    * liefert die neue Instrumentengruppe des Instruments des MIDI-Kanals
    * @param instrument Instrument des MIDI-Kanals
    * @return gemappte Instrumentengruppe
    */
   private String getTargetInstrument(int instrument){
      if (cmbGenre.getSelectedItem() != null && cmbPrevGenre.getSelectedItem() != null) {
         String instr = getInstrumentGroup(instrument);
         for (int i=0; i<iiold.size(); i++) {
            if (iiold.get(i).getInstrument().equalsIgnoreCase(instr)) {
               //an Stelle i in iinew das Instrument nehmen, wenn geordnet
               if (iinew.size() > i) {
                  return iinew.get(i).getInstrument();
               } else {
                  return "";
               }
            }
         }
      }
      return "";
   }
   
   /**
    * gibt das erste Instrument der Instrumentengruppe zurueck
    * @param group Instrumentengruppe (MIDI)
    * @return erste Instrument der Gruppe
    */
   private int getInstrumentFromGroup(String group){
      switch (group) {
         case "Piano":
            return 1;
         case "Chromatic Percussion":
            return 9;
         case "Organ":
            return 17;
         case "Guitar":
            return 25;
         case "Bass":
            return 33;
         case "Strings":
            return 41;
         case "Ensemble":
            return 49;
         case "Brass":
            return 57;
         case "Reed":
            return 65;
         case "Pipe":
            return 73;
         case "Synth Lead":
            return 81;
         case "Synth Pad":
            return 89;
         case "Synth Effects":
            return 97;
         case "Ethnic":
            return 105;
         case "Percussive":
            return 113;
         case "Sound Effects":
            return 121;
         default:
            return 200;            
      }
   }
   
   /**
    * liefert die Instrumentengruppe zum uebergebenen Instrument
    * @param instrument Instrument (MIDI)
    * @return Instrumentengruppe (MIDI)
    */
   private String getInstrumentGroup(int instrument) {
      if (instrument >= 0 && instrument <= 8) { //Piano
         return "Piano";
      } else if (instrument >= 9 && instrument <= 16) { //Chromatic Percussion
         return "Chromatic Percussion";
      } else if (instrument >= 17 && instrument <= 24) { //Organ
         return "Organ";
      } else if (instrument >= 25 && instrument <= 32) { //Guitar
         return "Guitar";
      } else if (instrument >= 33 && instrument <= 40) { //Bass
         return "Bass";
      } else if (instrument >= 41 && instrument <= 48) { //Strings
         return "Strings";
      } else if (instrument >= 49 && instrument <= 56) { //Ensemble
         return "Ensemble";
      } else if (instrument >= 57 && instrument <= 64) { //Brass
         return "Brass";
      } else if (instrument >= 65 && instrument <= 72) { //Reed
         return "Reed";
      } else if (instrument >= 73 && instrument <= 80) { //Pipe
         return "Pipe";
      } else if (instrument >= 81 && instrument <= 88) { //Synth Lead
         return "Synth Lead";
      } else if (instrument >= 89 && instrument <= 96) { //Synth Pad
         return "Synth Pad";
      } else if (instrument >= 97 && instrument <= 104) { //Synth Effects
         return "Synth Effects";
      } else if (instrument >= 105 && instrument <= 112) { //Ethnic
         return "Ethnic";
      } else if (instrument >= 113 && instrument <= 120) { //Percussive
         return "Percussive";
      } else if (instrument >= 121 && instrument <= 128) { //Sound Effects
         return "Sound Effects";
      } else { //Percussions
         return "Percussions";
      }
   }
   
   /**
    * @param args the command line arguments
    */
   public static void main(String args[]) {
      /* Set the Nimbus look and feel */
      //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
       * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
       */
      try {
         for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
            if ("Nimbus".equals(info.getName())) {
               javax.swing.UIManager.setLookAndFeel(info.getClassName());
               break;
            }
         }
      } catch (ClassNotFoundException ex) {
         java.util.logging.Logger.getLogger(ConvertDialog.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
      } catch (InstantiationException ex) {
         java.util.logging.Logger.getLogger(ConvertDialog.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
      } catch (IllegalAccessException ex) {
         java.util.logging.Logger.getLogger(ConvertDialog.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
      } catch (javax.swing.UnsupportedLookAndFeelException ex) {
         java.util.logging.Logger.getLogger(ConvertDialog.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
      }
      //</editor-fold>

      /* Create and display the dialog */
      java.awt.EventQueue.invokeLater(new Runnable() {
         public void run() {
            ConvertDialog dialog = new ConvertDialog(new javax.swing.JFrame(), true, null, null, null, null);
            dialog.addWindowListener(new java.awt.event.WindowAdapter() {
               @Override
               public void windowClosing(java.awt.event.WindowEvent e) {
                  System.exit(0);
               }
            });
            dialog.setVisible(true);
         }
      });
   }
   // Variables declaration - do not modify//GEN-BEGIN:variables
   private javax.swing.JButton btnCancel;
   private javax.swing.JButton btnConvert;
   private javax.swing.JButton btnMidi;
   private javax.swing.JButton btnPlay;
   private javax.swing.JButton btnPlayOriginal;
   private javax.swing.JButton btnSave;
   private javax.swing.JButton btnStop;
   private javax.swing.JComboBox cmbGenre;
   private javax.swing.JComboBox cmbKey;
   private javax.swing.JComboBox cmbPrevGenre;
   private javax.swing.JLabel jLabel1;
   private javax.swing.JLabel jLabel2;
   private javax.swing.JLabel jLabel3;
   private javax.swing.JLabel jLabel4;
   private javax.swing.JScrollPane jScrollPane1;
   private javax.swing.JPanel panDown;
   private javax.swing.JPanel panMiddle;
   private javax.swing.JPanel panUp;
   private javax.swing.JTable tabParts;
   private javax.swing.JTextField txtMidi;
   // End of variables declaration//GEN-END:variables
}
