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

import data.AverageMIDIFileData;
import data.Beat;
import data.BeatTransition;
import data.IScore;
import data.MIDIFileData;
import data.MotifData;
import midi.MidiQuantizer;
import data.MultiMotifData;
import database.DatabaseInterface;
import java.awt.Cursor;
import java.awt.Desktop;
import java.awt.Toolkit;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.Properties;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import jm.music.data.Note;
import jm.music.data.Part;
import jm.music.data.Phrase;
import jm.music.data.Score;
import jm.util.Read;
import midi.KeyFinder;
import midi.MIDIAnalyser;
import midi.MIDIPlayer;
import midi.MidiToSheetMusic;
import motif.PolyphonMotifDetection;
/**
 *
 * @author Tom
 */
public class AnalyserForm extends javax.swing.JFrame{
    private boolean bSubmotifs = true;
    public static String path;
    private static Score scr = new Score();
    private int other = 0;
    private ArrayList<MIDIFileData> almfd = new ArrayList<>(); //MIDI-FileData
    private ArrayList<AverageMIDIFileData> avgData = new ArrayList<>(); //average MIDI-FileData
    private int highNote = 0; //Highest Note
    private int lowNote = 127; //Lowest Note
    private double longestNote = 0.0; //Longest Note
    private double shortestNote  = 100.0; //shortest Note
    private int ascending = 0; //Upward movements
    private int descending = 0; //Downward movements
    private int prev = 60;
    private int minMotifSize = 3, maxMotifSize = 7, allowedInterval = 1, maxMotifExtention = 3;
    private int categoryId = -1;
    private String categoryName = "";
    private boolean startDetect = false;
    private int fileCount = 0, currentFileCount = 0;
    private Thread thread = null;
    private int userId = -1;
    private MIDIAnalyser midiAnalyser;
    private DefaultTableModel tableModel;
    private IScore iScore;
    private SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
    private SimpleDateFormat sdf2 = new SimpleDateFormat("mm:ss");
    private Calendar curTime, startTime;
    private double[] noteDurations = {0,0,0,0,0,0,0,0,0,0,0,0};
    private int AllRep = 0, AllSeq = 0, AllKrebs = 0, AllMirror = 0, AllPolyRep = 0, AllPolySeq = 0, AllPolyKrebs = 0, AllPolyMirror = 0;
    private int[][] beatTransitions = new int[12][12]; //Array fuer die Taktuebergaenge (Zeilen-/Spaltenreihenfolge 0-11 : C, C#, D, Eb, E, F, F#, G, Ab, A, Bb, B)
    private boolean goOn = false;
    private boolean AllInOne = false;
    private AllInOneStatusDialog aiosd = null;
    public static String username = null;
    public static String password = null;
    public static String DB_USER;// = "PBMC_user";
    public static String DB_PASSWORD;// = "PBMC_pass";
    public static String DB_URL;// = "localhost or serveradress";
    public static boolean saveLogin = false;
    public static boolean loggedIn = false;
    
    /**
     * Creates new form AnalyserForm
     */
    public AnalyserForm() {
        initComponents();
        
        path = "";
        Properties prop = null;
        try {
            prop = new Properties();
            prop.load(new FileInputStream("app.properties"));
        } catch (IOException ex) {
        }
        
        if(prop != null)
        {
            path = prop.getProperty("PATH");

            username = prop.getProperty("USERNAME");
            password = prop.getProperty("PASSWORD");
            
            DB_USER = prop.getProperty("DB_USER");
            DB_PASSWORD = prop.getProperty("DB_PASS");
            DB_URL = prop.getProperty("DB_URL");
        }
        
        if(username != null && password != null && DB_USER != null && DB_PASSWORD != null && DB_URL != null)
        {
            DatabaseInterface databaseInterface = new DatabaseInterface(DB_USER, DB_PASSWORD, DB_URL);
            if(databaseInterface.isDatabaseConnectionUp())
            {
                if(databaseInterface.login(username, password.getBytes()))
                {
                    loggedIn = true;
                    saveLogin = true;
                }
                databaseInterface.closeConnection();
            }
        }
        
        proBar.setStringPainted(true);
        proBar.setIndeterminate(false);
        
        tabData.setEnabledAt(3, false);
        
        //make a "no wrap" TextPane
        txtLog = new javax.swing.JTextPane()
        {
            @Override
            public boolean getScrollableTracksViewportWidth()
            {
                return getUI().getPreferredSize(this).width 
                    <= getParent().getSize().width;
            }
        };
        txtLog.setFont(new java.awt.Font("Courier New", 0, 12));
        jScrollPane5.setViewportView(txtLog);
        
        //aktiviert/deaktiviert die Buttons und Menuepunkte je nach aktivem Tab
         ChangeListener changeListener = new ChangeListener() {
            @Override
          public void stateChanged(ChangeEvent changeEvent) {
            JTabbedPane sourceTabbedPane = (JTabbedPane) changeEvent.getSource();
            int index = sourceTabbedPane.getSelectedIndex();
            switch (sourceTabbedPane.getTitleAt(index)) {
               case "Data":
                  btnSheet.setEnabled(true);
                  btnNotes.setEnabled(true);
                  btnInstruments.setEnabled(true);
                  mNoteSheet.setEnabled(true);
                  mNotes.setEnabled(true);
                  mInstruments.setEnabled(true);
                  mComposer.setEnabled(true);
                  break;
               case "Average":
                  btnSheet.setEnabled(false);
                  btnNotes.setEnabled(true);
                  btnInstruments.setEnabled(false);
                  mNoteSheet.setEnabled(false);
                  mNotes.setEnabled(true);
                  mInstruments.setEnabled(false);
                  mComposer.setEnabled(true);
                  break;
               case "Console":
                  btnSheet.setEnabled(false);
                  btnNotes.setEnabled(false);
                  btnInstruments.setEnabled(false);
                  mNoteSheet.setEnabled(false);
                  mNotes.setEnabled(false);
                  mInstruments.setEnabled(false);
                  mComposer.setEnabled(true);
                  break;
               case "Log":
                  btnSheet.setEnabled(false);
                  btnNotes.setEnabled(false);
                  btnInstruments.setEnabled(false);
                  mNoteSheet.setEnabled(false);
                  mNotes.setEnabled(false);
                  mInstruments.setEnabled(false);
                  mComposer.setEnabled(true);
            }
          }
         };
         tabData.addChangeListener(changeListener);
        
        //laden des zuletzt ausgewaehlten Verzeichnisses
        if (path != null && !path.equalsIgnoreCase("")){
            tFiles.setEnabled(true);

            DefaultMutableTreeNode root = (DefaultMutableTreeNode)tFiles.getModel().getRoot();
            root.removeAllChildren();

            addNodes(root, new File(path));

            tFiles.updateUI();
            tFiles.expandRow(0);
        }
    }
    
    /**
     * 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() {

        jToolBar1 = new javax.swing.JToolBar();
        btnPlay = new javax.swing.JButton();
        btnStop = new javax.swing.JButton();
        jSeparator5 = new javax.swing.JToolBar.Separator();
        btnAnalyse = new javax.swing.JButton();
        btnNotes = new javax.swing.JButton();
        btnSheet = new javax.swing.JButton();
        btnInstruments = new javax.swing.JButton();
        btnClear = new javax.swing.JButton();
        jSeparator6 = new javax.swing.JToolBar.Separator();
        btnDatabaseAdd = new javax.swing.JButton();
        jScrollPane1 = new javax.swing.JScrollPane();
        tFiles = new javax.swing.JTree();
        panData = new javax.swing.JPanel();
        tabData = new javax.swing.JTabbedPane();
        jScrollPane2 = new javax.swing.JScrollPane();
        tAnalyse = new javax.swing.JTable();
        jScrollPane3 = new javax.swing.JScrollPane();
        tAverage = new javax.swing.JTable();
        jScrollPane4 = new javax.swing.JScrollPane();
        tConsole = new javax.swing.JTable();
        jScrollPane5 = new javax.swing.JScrollPane();
        txtLog = new javax.swing.JTextPane();
        proBar = new javax.swing.JProgressBar();
        jMenuBar1 = new javax.swing.JMenuBar();
        mFile = new javax.swing.JMenu();
        mChoose = new javax.swing.JMenuItem();
        jSeparator1 = new javax.swing.JPopupMenu.Separator();
        mClose = new javax.swing.JMenuItem();
        mAnalyse = new javax.swing.JMenu();
        start = new javax.swing.JMenuItem();
        mNotes = new javax.swing.JMenuItem();
        mNoteSheet = new javax.swing.JMenuItem();
        mInstruments = new javax.swing.JMenuItem();
        clear = new javax.swing.JMenuItem();
        mMotifs = new javax.swing.JMenu();
        mAdd = new javax.swing.JMenuItem();
        mMotifViewer = new javax.swing.JMenuItem();
        mComposer = new javax.swing.JMenu();
        mNewCategory = new javax.swing.JMenuItem();
        mCompose = new javax.swing.JMenuItem();
        mConvert = new javax.swing.JMenuItem();
        mLog = new javax.swing.JMenu();
        mChkLog = new javax.swing.JCheckBoxMenuItem();
        mnuHelp = new javax.swing.JMenu();
        mHelp = new javax.swing.JMenuItem();

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        setTitle("MIDI Analyser");
        setIconImage(Toolkit.getDefaultToolkit()
            .getImage(getClass().getResource("/icons/noteIcon.png")));
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(java.awt.event.WindowEvent evt) {
                formWindowClosing(evt);
            }
        });

        jToolBar1.setRollover(true);

        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);
            }
        });
        jToolBar1.add(btnPlay);

        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);
            }
        });
        jToolBar1.add(btnStop);
        jToolBar1.add(jSeparator5);

        btnAnalyse.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/icon_lupe.png"))); // NOI18N
        btnAnalyse.setToolTipText("Analyse MIDI");
        btnAnalyse.setFocusable(false);
        btnAnalyse.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        btnAnalyse.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        btnAnalyse.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnAnalyseActionPerformed(evt);
            }
        });
        jToolBar1.add(btnAnalyse);

        btnNotes.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/matrix.png"))); // NOI18N
        btnNotes.setToolTipText("Show Notes");
        btnNotes.setFocusable(false);
        btnNotes.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        btnNotes.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        btnNotes.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnNotesActionPerformed(evt);
            }
        });
        jToolBar1.add(btnNotes);

        btnSheet.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/Note.png"))); // NOI18N
        btnSheet.setToolTipText("Show Notesheet");
        btnSheet.setFocusable(false);
        btnSheet.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        btnSheet.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        btnSheet.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnSheetActionPerformed(evt);
            }
        });
        jToolBar1.add(btnSheet);

        btnInstruments.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/instruments.png"))); // NOI18N
        btnInstruments.setToolTipText("Show Instruments");
        btnInstruments.setFocusable(false);
        btnInstruments.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        btnInstruments.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        btnInstruments.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnInstrumentsActionPerformed(evt);
            }
        });
        jToolBar1.add(btnInstruments);

        btnClear.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/Trash.png"))); // NOI18N
        btnClear.setToolTipText("Clear Table");
        btnClear.setFocusable(false);
        btnClear.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        btnClear.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        btnClear.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnClearActionPerformed(evt);
            }
        });
        jToolBar1.add(btnClear);
        jToolBar1.add(jSeparator6);

        btnDatabaseAdd.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/database_add.png"))); // NOI18N
        btnDatabaseAdd.setToolTipText("Add motifs to database");
        btnDatabaseAdd.setFocusable(false);
        btnDatabaseAdd.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        btnDatabaseAdd.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        btnDatabaseAdd.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btnDatabaseAddActionPerformed(evt);
            }
        });
        jToolBar1.add(btnDatabaseAdd);

        javax.swing.tree.DefaultMutableTreeNode treeNode1 = new javax.swing.tree.DefaultMutableTreeNode("MIDIs");
        tFiles.setModel(new javax.swing.tree.DefaultTreeModel(treeNode1));
        tFiles.setEnabled(false);
        jScrollPane1.setViewportView(tFiles);

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

            },
            new String [] {
                "ID", "Title", "Key", "Rests", "Lowest Note", "Highest Note", "Shortest Note", "Longest Note", "Upwards", "Downwards"
            }
        ) {
            Class[] types = new Class [] {
                java.lang.Integer.class, java.lang.String.class, java.lang.String.class, java.lang.Integer.class, java.lang.Integer.class, java.lang.Integer.class, java.lang.Double.class, java.lang.Double.class, java.lang.Integer.class, java.lang.Integer.class
            };
            boolean[] canEdit = new boolean [] {
                false, false, false, false, false, false, false, false, false, false
            };

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

            public boolean isCellEditable(int rowIndex, int columnIndex) {
                return canEdit [columnIndex];
            }
        });
        tAnalyse.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                tAnalyseMouseClicked(evt);
            }
            public void mouseExited(java.awt.event.MouseEvent evt) {
                tAnalyseMouseExited(evt);
            }
        });
        jScrollPane2.setViewportView(tAnalyse);

        tabData.addTab("Data", jScrollPane2);

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

            },
            new String [] {
                "ID", "Title", "Rests", "Lowest Note", "Highest Note", "Shortest Note", "Longest Note", "Upwards", "Downwards"
            }
        ) {
            Class[] types = new Class [] {
                java.lang.Integer.class, java.lang.String.class, java.lang.Integer.class, java.lang.Integer.class, java.lang.Integer.class, java.lang.Double.class, java.lang.Double.class, java.lang.Integer.class, java.lang.Integer.class
            };
            boolean[] canEdit = new boolean [] {
                false, false, false, false, false, false, false, false, false
            };

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

            public boolean isCellEditable(int rowIndex, int columnIndex) {
                return canEdit [columnIndex];
            }
        });
        tAverage.setAutoscrolls(false);
        jScrollPane3.setViewportView(tAverage);

        tabData.addTab("Average", jScrollPane3);

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

            },
            new String [] {
                "File", "Repetition", "Sequence", "Reversion", "Inversion", "Timestamp", "Timespan"
            }
        ) {
            Class[] types = new Class [] {
                java.lang.String.class, java.lang.Integer.class, java.lang.Integer.class, java.lang.Integer.class, java.lang.Integer.class, java.lang.String.class, java.lang.String.class
            };
            boolean[] canEdit = new boolean [] {
                false, false, false, false, false, false, false
            };

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

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

        tabData.addTab("Console", jScrollPane4);

        txtLog.setFont(new java.awt.Font("Courier New", 0, 10)); // NOI18N
        jScrollPane5.setViewportView(txtLog);

        tabData.addTab("Log", jScrollPane5);

        javax.swing.GroupLayout panDataLayout = new javax.swing.GroupLayout(panData);
        panData.setLayout(panDataLayout);
        panDataLayout.setHorizontalGroup(
            panDataLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(tabData, javax.swing.GroupLayout.DEFAULT_SIZE, 744, Short.MAX_VALUE)
        );
        panDataLayout.setVerticalGroup(
            panDataLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(tabData)
        );

        mFile.setText("File");

        mChoose.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/folder.png"))); // NOI18N
        mChoose.setText("Choose Directory");
        mChoose.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                mChooseActionPerformed(evt);
            }
        });
        mFile.add(mChoose);
        mFile.add(jSeparator1);

        mClose.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/Close.png"))); // NOI18N
        mClose.setText("Close");
        mClose.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                mCloseActionPerformed(evt);
            }
        });
        mFile.add(mClose);

        jMenuBar1.add(mFile);

        mAnalyse.setText("Analyse");

        start.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/icon_lupe.png"))); // NOI18N
        start.setText("Analyse MIDI");
        start.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                startActionPerformed(evt);
            }
        });
        mAnalyse.add(start);

        mNotes.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/matrix.png"))); // NOI18N
        mNotes.setText("Show Notes");
        mNotes.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                mNotesActionPerformed(evt);
            }
        });
        mAnalyse.add(mNotes);

        mNoteSheet.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/Note.png"))); // NOI18N
        mNoteSheet.setText("Notesheet");
        mNoteSheet.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                mNoteSheetActionPerformed(evt);
            }
        });
        mAnalyse.add(mNoteSheet);

        mInstruments.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/instruments.png"))); // NOI18N
        mInstruments.setText("Show Instruments");
        mInstruments.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                mInstrumentsActionPerformed(evt);
            }
        });
        mAnalyse.add(mInstruments);

        clear.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/Trash.png"))); // NOI18N
        clear.setText("Clear Table");
        clear.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                clearActionPerformed(evt);
            }
        });
        mAnalyse.add(clear);

        jMenuBar1.add(mAnalyse);

        mMotifs.setText("Motifs");

        mAdd.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/database_add.png"))); // NOI18N
        mAdd.setText("Add motifs to database");
        mAdd.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                mAddActionPerformed(evt);
            }
        });
        mMotifs.add(mAdd);

        mMotifViewer.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/Note.png"))); // NOI18N
        mMotifViewer.setText("View Motifs");
        mMotifViewer.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                mMotifViewerActionPerformed(evt);
            }
        });
        mMotifs.add(mMotifViewer);

        jMenuBar1.add(mMotifs);

        mComposer.setText("Composer");

        mNewCategory.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/magic_wand.png"))); // NOI18N
        mNewCategory.setText("Store New Category");
        mNewCategory.setPreferredSize(new java.awt.Dimension(165, 30));
        mNewCategory.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                mNewCategoryActionPerformed(evt);
            }
        });
        mComposer.add(mNewCategory);

        mCompose.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/irc-voice.png"))); // NOI18N
        mCompose.setText("Compose");
        mCompose.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                mComposeActionPerformed(evt);
            }
        });
        mComposer.add(mCompose);

        mConvert.setIcon(new javax.swing.ImageIcon(getClass().getResource("/icons/convert.png"))); // NOI18N
        mConvert.setText("Convert");
        mConvert.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                mConvertActionPerformed(evt);
            }
        });
        mComposer.add(mConvert);

        jMenuBar1.add(mComposer);

        mLog.setText("Log");

        mChkLog.setText("show Log");
        mChkLog.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                mChkLogActionPerformed(evt);
            }
        });
        mLog.add(mChkLog);

        jMenuBar1.add(mLog);

        mnuHelp.setText("Help");
        mnuHelp.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                mnuHelpMouseClicked(evt);
            }
        });
        mnuHelp.addMenuListener(new javax.swing.event.MenuListener() {
            public void menuCanceled(javax.swing.event.MenuEvent evt) {
            }
            public void menuSelected(javax.swing.event.MenuEvent evt) {
                mnuHelpMenuSelected(evt);
            }
            public void menuDeselected(javax.swing.event.MenuEvent evt) {
            }
        });

        mHelp.setText("show Help");
        mHelp.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                mHelpActionPerformed(evt);
            }
        });
        mnuHelp.add(mHelp);

        jMenuBar1.add(mnuHelp);

        setJMenuBar(jMenuBar1);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGap(0, 0, 0)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                    .addComponent(jScrollPane1)
                    .addComponent(jToolBar1, javax.swing.GroupLayout.DEFAULT_SIZE, 274, Short.MAX_VALUE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(proBar, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(panData, 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()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                    .addComponent(jToolBar1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(proBar, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 710, Short.MAX_VALUE)
                    .addComponent(panData, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents
    
    /**
     * setzt die Logindaten
     * @param successful true, wenn erfolgreich
     * @param user Benutzername
     * @param pass Passwort
     * @param save true, wenn der Login gespeichert werden soll
     */
    public void setLoginData(boolean successful, String user, String pass, boolean save)
    {
        if(successful)
        {
            loggedIn = true;
            username = user;
            password = pass;
        }
        else
        {
            loggedIn = false;
        }
        
        saveLogin = save;
    }
    
    /**
     * liefert den Benutzernamen des eingeloggten Benutzers
     * @return Benutzername des zur Zeit eingeloggten Benutzers
     */
    public String getLoggedInUsername()
    {
        return username;
    }
    
    /**
     * Befuellt den JTree mit allen Ordnern und MIDIs des gewaehlten Pfads
     * @param evt 
     */
    private void mChooseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mChooseActionPerformed
        JFileChooser fc = new JFileChooser(path);

        fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        fc.setAcceptAllFileFilterUsed(false);

        if (fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
            path = fc.getSelectedFile().toString();
            clearFileTreeView();
            tFiles.setEnabled(true);

            DefaultMutableTreeNode root = (DefaultMutableTreeNode)tFiles.getModel().getRoot();
            root.removeAllChildren();

            addNodes(root, new File(path));

            tFiles.updateUI();
            tFiles.expandRow(0);
        }
    }//GEN-LAST:event_mChooseActionPerformed

    private void mCloseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mCloseActionPerformed
        dispose();
    }//GEN-LAST:event_mCloseActionPerformed

    /**
     * Startet die Analyse der gewählten MIDI-Dateien in einem eigenen Thread
     * @param evt 
     */
    private void startActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_startActionPerformed
       TreePath[] paths = tFiles.getSelectionPaths();
        
        if (paths != null)
        {
            countFiles();
            currentFileCount = 0;
            proBar.setMaximum(fileCount);
            setButtons(false);
            setCursor(new Cursor(Cursor.WAIT_CURSOR));
            thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    analyseFiles();
                }
            });
            thread.start();
        }
        else
        {
            JOptionPane.showMessageDialog(this, "You have to select at least one entry which contains a MIDI-file", "Error", JOptionPane.ERROR_MESSAGE);
        }
    }//GEN-LAST:event_startActionPerformed

    /**
     * analysiert die im JTree ausgewaehlten MIDI-Dateien
     */
    private void analyseFiles() {
        TreePath[] paths = tFiles.getSelectionPaths();
        String pathToMidi;
        for (TreePath p : paths) {
            pathToMidi = getPathFromFile(p);
            if (pathToMidi.toLowerCase().endsWith(".mid"))
            {
                currentFileCount++;
                //Progressbar updaten
                SwingUtilities.invokeLater(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        proBar.setString("file " +currentFileCount +" of " +fileCount +" files.");
                        proBar.setValue(currentFileCount);
                    }
                });
                analyseMIDI(pathToMidi);
            }
            else //Ordner ist ausgewaehlt => verwende alle Subordner und beinhalteten MIDIs
            {
                DefaultMutableTreeNode tn = (DefaultMutableTreeNode)p.getLastPathComponent();
                getSelectedFolder(tn,true);
            }
        }
        updateTable(paths[0].getLastPathComponent().toString());
        setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
        tabData.setSelectedIndex(0); //Data sichtbar
        setButtons(true);
        goOn = true;
    }
    
    private void clearActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_clearActionPerformed
        btnClearActionPerformed(evt);
    }//GEN-LAST:event_clearActionPerformed

    private void btnPlayActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnPlayActionPerformed
        play();
    }//GEN-LAST:event_btnPlayActionPerformed

    private void btnStopActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnStopActionPerformed
        stop();
    }//GEN-LAST:event_btnStopActionPerformed

    private void btnAnalyseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnAnalyseActionPerformed
        startActionPerformed(evt);
    }//GEN-LAST:event_btnAnalyseActionPerformed

    /**
     * oeffnet pro ausgewaehlter Zeile in Tabelle tAnalyse ein Fenster mit dem dazugehoerigen Notenblatt
     * ist der Menuepunkt "Log" aktiviert, werden die Notendaten jedes Midifiles im Log ausgegeben
     * @param evt 
     */
    private void btnSheetActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnSheetActionPerformed
        int[] iRows = tAnalyse.getSelectedRows();
        
        if (tAnalyse.getSelectedRowCount() > 0){
            if (mChkLog.isSelected()){
                tabData.setSelectedIndex(3); //show Log
            }
            for (int i=0; i<tAnalyse.getSelectedRowCount(); i++)
            {
                int j = (int)tAnalyse.getModel().getValueAt(iRows[i], 0);
                //Midi-File quantisieren, in Takte unterteilen und als Notenblatt anzeigen  
                Score score = new Score();
                score.setTitle(almfd.get(j).getFilepath().substring(almfd.get(j).getFilepath().lastIndexOf("\\")+1));
                Read.midi(score, almfd.get(j).getFilepath());

                MidiQuantizer mq;
                if (mChkLog.isSelected()){
                    mq = new MidiQuantizer(txtLog);
                } else {
                    mq = new MidiQuantizer();
                }
                //quantisierten und in Takte unterteilten Score als Notenblatt darstellen
                MidiToSheetMusic mtsm = new MidiToSheetMusic(mq.getQuantizedScoreInBeats(score) ,score.getNumerator(), score.getDenominator(), score.getTitle());
                JFrame jf = mtsm.getNoteSheetFrame();
                if (jf.getWidth() > this.getWidth()){
                    jf.setSize(this.getWidth(), jf.getHeight());
                }
                if (jf.getHeight() > this.getHeight()){
                    jf.setSize(jf.getWidth(), this.getHeight());
                }
                jf.setVisible(true); //Notenblatt anzeigen
            }
        } else {
            JOptionPane.showMessageDialog(this, "You have to select at least one row", "Error", JOptionPane.ERROR_MESSAGE);
        }
    }//GEN-LAST:event_btnSheetActionPerformed

    /**
     * leert die Tabelle des zur Zeit ausgewaehlten Tabs
     * @param evt 
     */
    private void btnClearActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnClearActionPerformed
        switch (tabData.getSelectedIndex())
        {
            case 0:
                clearTableView(tAnalyse);
                almfd = new ArrayList<>();
                break;
            case 1:
                clearTableView(tAverage);
                avgData = new ArrayList<>();
                break;
            case 2:
                clearTableView(tConsole);
                break;
            case 3:
                txtLog.setText("");
                break;
        }
    }//GEN-LAST:event_btnClearActionPerformed

    /**
     * aktualisiert die Properties-Datei,
     * stoppt den MIDIPlayer und stoppt gegebenenfalls den Thread
     * @param evt 
     */
    private void formWindowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosing
        Properties prop = new Properties();
        
        prop.setProperty("PATH", path);
        
        if(saveLogin)
        {
            prop.setProperty("USERNAME", username);
            prop.setProperty("PASSWORD", password);
        }
        
        prop.setProperty("DB_USER", DB_USER == null ? "" : DB_USER);
        prop.setProperty("DB_PASS", DB_PASSWORD == null ? "" : DB_PASSWORD);
        prop.setProperty("DB_URL", DB_URL == null ? "" : DB_URL);
            
        try {
            prop.store(new FileOutputStream("app.properties"), null);
        } catch (IOException ex) {
            Logger.getLogger(AnalyserForm.class.getName()).log(Level.SEVERE, null, ex);
        }
        stop();
        if (thread != null && thread.isAlive())
            thread.stop();
    }//GEN-LAST:event_formWindowClosing

    private void mNoteSheetActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mNoteSheetActionPerformed
        btnSheetActionPerformed(evt);
    }//GEN-LAST:event_mNoteSheetActionPerformed

    /**
     * zaehlt die Dateien des aktuellen Ordners und seiner Unterordner
     */
    private void countFiles() {
        fileCount = 0;
        TreePath[] paths = tFiles.getSelectionPaths();
        for (TreePath p : paths) {
            if (getPathFromFile(p).toLowerCase().endsWith(".mid"))
            {
                fileCount++;
            }
            else //Ordner ist ausgewaehlt => zaehle alle Subordner und beinhalteten MIDIs
            {
                DefaultMutableTreeNode tn = (DefaultMutableTreeNode)p.getLastPathComponent();
                getFolderFileCount(tn);
            }
        }
    }
    
    /**
     * zaehlt alle im Ordner befindlicher Dateien (inklusive Unterordner)
     * @param tn Der aktuelle TreeNode
     */
    private void getFolderFileCount(DefaultMutableTreeNode tn) {
        for (int i=0; i<tn.getChildCount(); i++)
        {
            DefaultMutableTreeNode t = (DefaultMutableTreeNode)tn.getChildAt(i);
            TreeNode[] tno = t.getPath();
            if (tno[tno.length-1].toString().toLowerCase().endsWith(".mid"))
            {
                fileCount++;
            }
            else
            {
                getFolderFileCount(t);
            }
        }
    }
    
    /**
     * speichert die detektierten Motive aller gewaehlten MIDI-Dateien in der DB
     * @param evt 
     */
    private void btnDatabaseAddActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnDatabaseAddActionPerformed
        TreePath[] paths = tFiles.getSelectionPaths();
        startDetect = false;
        
        if (paths != null)
        {
            countFiles();
            currentFileCount = 0;
            proBar.setString("");
            proBar.setValue(0);
            proBar.setMaximum(fileCount);
            DetectionDialogLarge detectionDialogLarge = new DetectionDialogLarge(this, true);
            detectionDialogLarge.addWindowListener(new WindowAdapter() {
                @Override
                public void windowClosed(WindowEvent e) {
                    if (startDetect)
                    {
                        thread = new Thread(new Runnable() {
                            @Override
                            public void run() {
                                startWork();
                            }
                        });
                        thread.start();
                    }
                }
            });
            if(loggedIn)
            {
                detectionDialogLarge.initCategories(username);
                detectionDialogLarge.setVisible(true);
            }
            else
            {
                DatabaseLoginDialogAnalyser loginDialog = new DatabaseLoginDialogAnalyser(this, true, detectionDialogLarge); 
                loginDialog.setVisible(true);
            }
        }
        else
        {
            JOptionPane.showMessageDialog(this, "You have to select at least one entry which contains a MIDI-file", "Error", JOptionPane.ERROR_MESSAGE);
        }
    }//GEN-LAST:event_btnDatabaseAddActionPerformed

   /**
    * oeffnet ein Fenster pro gewaehlter Zeile und zeigt die verwendeten Noten/Pitches an
    */
   private void btnNotesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnNotesActionPerformed
      switch (tabData.getSelectedIndex())
      {
          case 0:
               if (tAnalyse.getSelectedRows().length == 0) {
                  JOptionPane.showMessageDialog(this, "You have to select at least one row", "Error", JOptionPane.ERROR_MESSAGE);
               }
               for (int i : tAnalyse.getSelectedRows()) {
                  int j = (int)tAnalyse.getModel().getValueAt(i,0);
                  MatrixDialog maf = new MatrixDialog(this, false, almfd.get(j).getPitches(),(String)tAnalyse.getModel().getValueAt(i,1));
                  maf.setVisible(true);
               }
              break;
          case 1:
               if (tAverage.getSelectedRows().length == 0) {
                  JOptionPane.showMessageDialog(this, "You have to select at least one row", "Error", JOptionPane.ERROR_MESSAGE);
               }
               for (int i : tAverage.getSelectedRows()) {
                  int j = (int)tAverage.getModel().getValueAt(i,0);
                  MatrixDialogAverage maf = new MatrixDialogAverage(this, false, avgData.get(j).getPitches(), avgData.get(j).getInstrumentGroup(), avgData.get(j).getKeyGroup(),(String)tAverage.getModel().getValueAt(i,1),DB_USER, DB_PASSWORD, DB_URL);
                  maf.setVisible(true);
               }
              break;
          case 2:
              break;
      }
   }//GEN-LAST:event_btnNotesActionPerformed
   
   private void mNotesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mNotesActionPerformed
      btnNotesActionPerformed(evt);
   }//GEN-LAST:event_mNotesActionPerformed

   private void mAddActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mAddActionPerformed
      btnDatabaseAddActionPerformed(evt);
   }//GEN-LAST:event_mAddActionPerformed

   /**
    * zeigt die verwendeten Instrumente des MIDI-Files an, wenn ein Linksklick auf eine Zeile gemacht wird
    * @param evt 
    */
   private void tAnalyseMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_tAnalyseMouseClicked
      
   }//GEN-LAST:event_tAnalyseMouseClicked

   private void tAnalyseMouseExited(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_tAnalyseMouseExited

   }//GEN-LAST:event_tAnalyseMouseExited

   /**
    * oeffnet einen Dialog und zeigt die Instrumente der ausgewaehlten MIDI-Files an
    * @param evt 
    */
   private void btnInstrumentsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnInstrumentsActionPerformed
      if (tAnalyse.getSelectedRows().length == 0) {
         JOptionPane.showMessageDialog(this, "You have to select at least one row", "Error", JOptionPane.ERROR_MESSAGE);
      }
      for (int i : tAnalyse.getSelectedRows()) {
         int j = (int)tAnalyse.getModel().getValueAt(i,0);
         InstrumentDialog id = new InstrumentDialog(this, false, almfd.get(j).getInstruments(), almfd.get(j).getPercussion(),(String)tAnalyse.getModel().getValueAt(i,1));
         id.setVisible(true);
      }
   }//GEN-LAST:event_btnInstrumentsActionPerformed
   
   private void mInstrumentsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mInstrumentsActionPerformed
      btnInstrumentsActionPerformed(evt);
   }//GEN-LAST:event_mInstrumentsActionPerformed

   /**
    * oeffnet den Dialog zum Konvertieren eines MIDI-Files in ein anderes Genre
    * @param evt 
    */
   private void mConvertActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mConvertActionPerformed
      //Start ConvertDialog
      ConvertDialog cd = new ConvertDialog(this, true, DB_USER, DB_PASSWORD, DB_URL, path);
      if(loggedIn)
      {
          cd.setVisible(true);
      }
      else
      {
          DatabaseLoginDialogConverter loginDialog = new DatabaseLoginDialogConverter(this, true, cd); 
          loginDialog.setVisible(true);
      }
   }//GEN-LAST:event_mConvertActionPerformed

   /**
    * oeffnet den Dialog zum Erstellen einer neuen Komposition
    * @param evt 
    */
    private void mComposeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mComposeActionPerformed
        PolyphoneComposingDialog pcd = new PolyphoneComposingDialog(this, true, DB_USER, DB_PASSWORD, DB_URL, path);
        if(loggedIn)
        {
            pcd.setVisible(true);
        }
        else
        {
            DatabaseLoginDialogComposing loginDialog = new DatabaseLoginDialogComposing(this, true, pcd); 
            loginDialog.setVisible(true);
        }
    }//GEN-LAST:event_mComposeActionPerformed

    /**
     * aktiviert/deaktiviert den Tab mit den Logausgaben
     * @param evt 
     */
    private void mChkLogActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mChkLogActionPerformed
        tabData.setEnabledAt(3, mChkLog.isSelected());
        if (mChkLog.isSelected() == false){
            tabData.setSelectedIndex(2);
        }
    }//GEN-LAST:event_mChkLogActionPerformed

    private void mMotifViewerActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mMotifViewerActionPerformed
        MotifViewer mv = new MotifViewer(this, true, DB_USER, DB_PASSWORD, DB_URL);
        if(loggedIn)
        {
            mv.setVisible(true);
        }
        else
        {
            DatabaseLoginDialogMotifViewer loginDialog = new DatabaseLoginDialogMotifViewer(this, true, mv); 
            loginDialog.setVisible(true);
        }
    }//GEN-LAST:event_mMotifViewerActionPerformed

    /**
     * All in one Feature
     * Analysiert eine neue Kategorie (Notenhaeufigkeiten, Instrumente, Tonarten)
     * detektiert alle Motife der Lieder
     * bestimmt die Taktuebergaenge
     * und bewertet die Takte nach Tempo und Interessantheit
     * @param evt 
     */
    private void mNewCategoryActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mNewCategoryActionPerformed
        TreePath[] paths = tFiles.getSelectionPaths();       
        startDetect = false;
        goOn = false;
        
        if (paths != null)
        {
            AllInOne = true;
            aiosd = new AllInOneStatusDialog(this, false);
            aiosd.setVisible(true);
            DetectionDialogAllInOne detectionDialogAllInOne = new DetectionDialogAllInOne(this, true, username, paths[0].getLastPathComponent().toString());
            detectionDialogAllInOne.addWindowListener(new WindowAdapter() {
                @Override
                public void windowClosed(WindowEvent e) {
                    if (startDetect)
                    {
                        countFiles();
                        currentFileCount = 0;
                        proBar.setMaximum(fileCount);
                        setButtons(false);
                        setCursor(new Cursor(Cursor.WAIT_CURSOR));

                        //Tabelle der Analyse leeren
                        clearTableView(tAnalyse);
                        almfd = new ArrayList<>();
                        clearTableView(tAverage);
                        avgData = new ArrayList<>();
                        
                        //start Analyse der Kategorie (Notenhaeufigkeiten, Instrumente, Tonarten)
                        thread = new Thread(new Runnable() {
                            @Override
                            public void run() {
                                analyseFiles();
                            }
                        });
                        thread.start();
                        
                        while (!goOn && startDetect){
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException ex) {
                                Logger.getLogger(AnalyserForm.class.getName()).log(Level.SEVERE, null, ex);
                            }
                        }
                        saveInDB();
                        aiosd.setLabelEnabled(1);
                        
                        //start Motifdetektion, Taktuebergaenge, Bewertung der Takte
                        currentFileCount = 0;
                        proBar.setString("");
                        proBar.setValue(0);
                        proBar.setMaximum(fileCount);

                        thread = new Thread(new Runnable() {
                            @Override
                            public void run() {
                                startWork();
                            }
                        });
                        thread.start();
                    } else {
                        aiosd.dispose();
                    }
                }
            });
            
            if(loggedIn)
            {
                detectionDialogAllInOne.setVisible(true);
            }
            else
            {
                DatabaseLoginDialogAllInOne loginDialog = new DatabaseLoginDialogAllInOne(this, true, detectionDialogAllInOne); 
                loginDialog.setVisible(true);
            }
        }
        else
        {
            JOptionPane.showMessageDialog(this, "You have to select at least one entry which contains a MIDI-file", "Error", JOptionPane.ERROR_MESSAGE);
        }
    }//GEN-LAST:event_mNewCategoryActionPerformed

    
    private void mnuHelpMenuSelected(javax.swing.event.MenuEvent evt) {//GEN-FIRST:event_mnuHelpMenuSelected
        
    }//GEN-LAST:event_mnuHelpMenuSelected

    private void mnuHelpMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_mnuHelpMouseClicked
        
    }//GEN-LAST:event_mnuHelpMouseClicked

    /**
     * zeigt die Hilfe an
     * @param evt 
     */
    private void mHelpActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mHelpActionPerformed
        try {
            File htmlFile = new File("Help.htm");
            Desktop.getDesktop().browse(htmlFile.toURI());
        } catch (IOException ex) {
            Logger.getLogger(AnalyserForm.class.getName()).log(Level.SEVERE, null, ex);
        }
    }//GEN-LAST:event_mHelpActionPerformed

   
    /**
     * speichert die Durchschnittsdaten in der Datenbank
     */
    private void saveInDB(){
        int j = (int)tAverage.getModel().getValueAt(0,0);
        MatrixDialogAverage maf = new MatrixDialogAverage(this, false, avgData.get(j).getPitches(), avgData.get(j).getInstrumentGroup(), avgData.get(j).getKeyGroup(),categoryName,DB_USER, DB_PASSWORD, DB_URL);
        maf.SaveInDatabase();
        maf.dispose();
    }
    
    public void setMinMotifSize(int min) {
        minMotifSize = min;
    }
    
    public void setMaxMotifSize(int max) {
        maxMotifSize = max;
    }
    
    public void setAllowedInterval(int interval) {
        allowedInterval = interval;
    }
    
    public void setMaxMotifExtention(int max) {
        maxMotifExtention = max;
    }
    
    public void setCategoryID(int id) {
        categoryId = id;
    }
    
    public void setCategoryName(String category_name) {
        categoryName = category_name;
    }
    
    public void setSubmotifs(boolean b) {
        bSubmotifs = b;
    }
    
    public void setStartDetect(boolean b) {
        startDetect = b;
    }
    
    /**
     * enabled/disabled alle Buttons und Menuepunkte
     * @param b enabled/disabled
     */
    private void setButtons(boolean b) {
        btnAnalyse.setEnabled(b);
        btnClear.setEnabled(b);
        btnDatabaseAdd.setEnabled(b);
        btnSheet.setEnabled(b);
        btnPlay.setEnabled(b);
        btnStop.setEnabled(b);
        btnNotes.setEnabled(b);
        btnInstruments.setEnabled(b);
        mFile.setEnabled(b);
        mAnalyse.setEnabled(b);
        mMotifs.setEnabled(b);
        mComposer.setEnabled(b);
        mLog.setEnabled(b);
    }
    
    /**
     * detektiert Motive und speichert sie in der DB
     */
    public void startWork() {
        setButtons(false);
        AllRep = 0; AllSeq = 0; AllKrebs = 0; AllMirror = 0; AllPolyRep = 0; AllPolySeq = 0; AllPolyKrebs = 0; AllPolyMirror = 0;
        setCursor(new Cursor(Cursor.WAIT_CURSOR));
        clearTableView(tConsole);
        //Initialisierung des Arrays fuer die Taktuebergaenge
        for (int i=0; i<beatTransitions.length; i++){
            for (int j=0; j<beatTransitions[i].length; j++){
                beatTransitions[i][j] = 0;
            }
        }
        //Anzeige des korrekten Tabs
        if (mChkLog.isSelected()){
            tabData.setSelectedIndex(3); //show Log
        } else {
            tabData.setSelectedIndex(2); //show Console
        }
        TreePath[] paths = tFiles.getSelectionPaths();
        //DB Interface
        DatabaseInterface databaseInterface = new DatabaseInterface(DB_USER, DB_PASSWORD, DB_URL);
        userId = databaseInterface.getIdForUsername(username);
        databaseInterface.closeConnection();
        startTime = Calendar.getInstance(); //aktuelle Zeit
        String pathToMidi;
        for (TreePath p : paths) {
            pathToMidi = getPathFromFile(p);
            if (pathToMidi.toLowerCase().endsWith(".mid"))
            {
                currentFileCount++;
                //Progressbar updaten
                SwingUtilities.invokeLater(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        proBar.setString("file " +currentFileCount +" of " +fileCount +" files.");
                        proBar.setValue(currentFileCount);
                    }
                });
                detectAndStoreMIDI(pathToMidi);
            }
            else //Ordner ist ausgewaehlt => verwende alle Subordner und beinhalteten MIDIs
            {
                DefaultMutableTreeNode tn = (DefaultMutableTreeNode)p.getLastPathComponent();
                getSelectedFolder(tn,false);
            }
        }
        if (aiosd != null){
            aiosd.setLabelEnabled(2);
        }
        
        //Taktubergaenge auswerten und Wahrscheinlichkeiten in DB speichern
        int[] Sum = {0,0,0,0,0,0,0,0,0,0,0,0};
        for (int i=0; i<beatTransitions.length; i++){
            for (int j=0; j<beatTransitions[i].length; j++){
                Sum[i] += beatTransitions[i][j];
            }
        }
        ArrayList<BeatTransition> bt = new ArrayList<>();
        for (int i=0; i<beatTransitions.length; i++){
            for (int j=0; j<beatTransitions[i].length; j++){
                double x = 100.0/Sum[i] * beatTransitions[i][j];
                int x2 = (int) Math.round(x);
                bt.add(new BeatTransition(getNoteForInt(i), getNoteForInt(j), x2));
            }
        }
        DatabaseInterface dbI = new DatabaseInterface(DB_USER, DB_PASSWORD, DB_URL);
        dbI.insertBeatTransition(bt, categoryId);
        dbI.closeConnection();
        if (aiosd != null){
            aiosd.setLabelEnabled(3);
        }
        
        //Tempo und Stimmung in DB berechnen und Werte aktualisieren
        dbI = new DatabaseInterface(DB_USER, DB_PASSWORD, DB_URL);
        dbI.setBeatTempo(categoryId);
        dbI.closeConnection();
        if (aiosd != null){
            aiosd.setLabelEnabled(4);
        }
        //Takte anhand der Notenhaeufigkeiten bewerten
        dbI = new DatabaseInterface(DB_USER, DB_PASSWORD, DB_URL);
        dbI.setBeatInterestingMeasures(categoryId);
        dbI.closeConnection();
        if (aiosd != null){
            aiosd.setLabelEnabled(5);
            aiosd.dispose();
        }
        
        startDetect = false;
        btnAnalyse.setEnabled(true);
        btnClear.setEnabled(true);
        btnDatabaseAdd.setEnabled(true);
        btnSheet.setEnabled(false);
        btnPlay.setEnabled(true);
        btnStop.setEnabled(true);
        btnNotes.setEnabled(false);
        btnInstruments.setEnabled(false);
        mFile.setEnabled(true);
        mAnalyse.setEnabled(true);
        mMotifs.setEnabled(true);
        mComposer.setEnabled(true);
        mLog.setEnabled(true);
        setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
        if (!AllInOne){
            JOptionPane.showMessageDialog(this, "Total detected:\n\nmonophon | polyphon\nRepetition: " +AllRep +" | " +AllPolyRep
                +"\nSequence: " +AllSeq  +" | " +AllPolySeq +"\nReversion: " +AllKrebs  +" | " +AllPolyKrebs 
                +"\nInversion: " +AllMirror  +" | " +AllPolyMirror, "Information", JOptionPane.INFORMATION_MESSAGE);
        }
        AllInOne = false;
    }
    
    /**
     * Detektiert die Motive der MIDI-Datei und speichert sie in der Datenbank
     * @param mPath Pfad zur MIDI-Datei
     */
    private void detectAndStoreMIDI(String mPath) {
        midiAnalyser = null;
        midiAnalyser = new MIDIAnalyser(new File(mPath));
        iScore = midiAnalyser.analyse();
        tableModel = (DefaultTableModel)tConsole.getModel();
        long timediff = 0;
        
        Score score = new Score();
        score.setTitle(mPath.substring(mPath.lastIndexOf("\\")+1));
        Read.midi(score, mPath);
        
        //MIDI-Datei quantisieren
        MidiQuantizer mq;
        if (mChkLog.isSelected()){
            mq = new MidiQuantizer(txtLog);
        } else {
            mq = new MidiQuantizer();
        }
        ArrayList<ArrayList<Beat>> alb = mq.getQuantizedScoreInBeats(score);
        mq.getNumerator();
        mq.getDenominator();
        mq.getKey();
        
        //Taktuebergaenge bestimmen
        String lastNote = "";
        for (ArrayList<Beat> al : alb){
            boolean FirstBeat = true;
            boolean isRest = false;
            for (Beat b : al){
                if (FirstBeat){
                    if (b.getNoteCount() > 0){
                        lastNote = b.getNote(b.getNoteCount()-1).getNote(); //letzte Note des Takts
                        if (b.getNote(b.getNoteCount()-1).getPitch() == Integer.MIN_VALUE){
                            isRest = true;
                        } else {
                            isRest = false;
                        }
                        FirstBeat = false;
                    }
                } else {
                    //Taktuebergang ins Array eintragen
                    if (b.getNoteCount() > 0){
                        if (!isRest && b.getNote(0).getPitch() != Integer.MIN_VALUE){ //wenn letzte und erste Note eine Pause => ignorieren
                            beatTransitions[getIntForNote(lastNote)][getIntForNote(b.getNote(0).getNote())]++;
                        }
                        lastNote = b.getNote(b.getNoteCount()-1).getNote(); //letzte Note des Takts
                        if (b.getNote(b.getNoteCount()-1).getPitch() == Integer.MIN_VALUE){
                            isRest = true;
                        } else {
                            isRest = false;
                        }
                    } else { //keine letzte Note vorhanden, daher als ersten Takt behandeln
                        FirstBeat = true;
                    }
                }
            }
        }
        
        //Motife detektieren
        PolyphonMotifDetection pmd;
        if (mChkLog.isSelected()){
            pmd = new PolyphonMotifDetection(mq.getNumerator(), mq.getDenominator(), mq.getKey(), txtLog);
        } else {
            pmd = new PolyphonMotifDetection(mq.getNumerator(), mq.getDenominator(), mq.getKey());
        }
        pmd.detectMotifs(alb);
        ArrayList<ArrayList<ArrayList<MotifData>>> mpm = pmd.getMonophonMotifs();
        ArrayList<ArrayList<ArrayList<MultiMotifData>>> ppm = pmd.getPolyphonMotifs();

        //monophone Motife in Datenbank speichern
        int Rep = 0, Seq = 0, Krebs = 0, Mirror = 0, polyRep = 0, polySeq = 0, polyKrebs = 0, polyMirror = 0;
        for (ArrayList<ArrayList<MotifData>> alalmd : mpm){
            for (ArrayList<MotifData> almd : alalmd){
                for (MotifData md : almd){
                    switch (md.getType()){
                        case "Rep":
                            Rep++;
                            break;
                        case "Seq":
                            Seq++;
                            break;
                        case "Krebs":
                            Krebs++;
                            break;
                        case "Spiegel":
                            Mirror++;
                            break;
                    }
                    storeMonophonMotifs(md);
                }
            }
        }
        
        //polyphone Motife in Datenbank speichern
        for (ArrayList<ArrayList<MultiMotifData>> alalmd : ppm){
            for (ArrayList<MultiMotifData> almd : alalmd){
                for (MultiMotifData mmd : almd){
                    switch (mmd.getType()){
                        case "Rep":
                            polyRep++;
                            break;
                        case "Seq":
                            polySeq++;
                            break;
                        case "Krebs":
                            polyKrebs++;
                            break;
                        case "Spiegel":
                            polyMirror++;
                            break;
                    }
                    storePolyphonMotifs(mmd);
                }
            }
        }
        
        //Dauer des Vorgangs berechnen
        curTime = Calendar.getInstance(); //aktuelle Zeit
        if (tableModel.getRowCount()>0)
        {
          String time = (String)tableModel.getValueAt(0, 5);
          try {
            Calendar prevTime = Calendar.getInstance();
            prevTime.setTime(sdf.parse(time));
            timediff = curTime.getTimeInMillis() - prevTime.getTimeInMillis();
          }catch (ParseException none){}
        } else {
            timediff = curTime.getTimeInMillis() - startTime.getTimeInMillis();
        }
        
        //Console updaten
        tableModel.insertRow(0,new Object[]{mPath,
            Rep+polyRep,
            Seq+polySeq,
            Krebs+polyKrebs,
            Mirror+polyMirror,
            sdf.format(curTime.getTimeInMillis()),
            sdf2.format(new Date(timediff))
        });
        
        //Summe pro Typ updaten
        AllRep += Rep;
        AllSeq += Seq;
        AllKrebs += Krebs;
        AllMirror += Mirror;
        AllPolyRep += polyRep;
        AllPolySeq += polySeq;
        AllPolyKrebs += polyKrebs;
        AllPolyMirror += polyMirror;
    }
    
    /**
     * speichert ein monphones Motiv in der Datenbank
     * @param md monophones Motiv
     */
    private void storeMonophonMotifs(MotifData md)
    {           
        if(md != null)
        {
            DatabaseInterface dbI = new DatabaseInterface(DB_USER, DB_PASSWORD, DB_URL);
            dbI.insertMonophonMotif(md, categoryId, bSubmotifs);
            dbI.closeConnection();
        }
    }
    
    /**
     * speichert ein polyphones Motiv in der Datenbank
     * @param mmd polyphones Motiv
     */
    private void storePolyphonMotifs(MultiMotifData mmd)
    {           
        if(mmd != null)
        {
            DatabaseInterface dbI = new DatabaseInterface(DB_USER, DB_PASSWORD, DB_URL);
            dbI.insertPolyphonMotif(mmd, categoryId, bSubmotifs);
            dbI.closeConnection();
        }
    }
    
    /**
     * liefert den Index einer Note im Array der Taktuebergaenge
     * @param note Note als String (C, C#, D, Eb, ...)
     * @return ID im Array
     */
    private int getIntForNote(String note){
        switch (note){
            case "C":
                return 0;
            case "C#":
                return 1;
            case "D":
                return 2;
            case "Eb":
                return 3;
            case "E":
                return 4;
            case "F":
                return 5;
            case "F#":
                return 6;
            case "G":
                return 7;
            case "Ab":
                return 8;
            case "A":
                return 9;
            case "Bb":
                return 10;
            case "B":
                return 11;
            default:
                return -1;
        }
    }
    
    /**
     * liefert zu einem Index im Array der Taktuebergaenge eine Note
     * @param id der Index der Note
     * @return die Note als String
     */
    private String getNoteForInt(int id){
        switch (id){
            case 0:
                return "C";
            case 1:
                return "C#";
            case 2:
                return "D";
            case 3:
                return "Eb";
            case 4:
                return "E";
            case 5:
                return "F";
            case 6:
                return "F#";
            case 7:
                return "G";
            case 8:
                return "Ab";
            case 9:
                return "A";
            case 10:
                return "Bb";
            case 11:
                return "B";
            default:
                return "";
        }
    }
    
    /**
     * initialisiert die Werte der MIDI-Auswertung
     */
    private void initMIDIData() {
        highNote = 0;
        lowNote = 127;
        longestNote = 0.0;
        shortestNote  = 100.0;
        ascending = 0;
        descending = 0;
    }
    
    /**
     * Add nodes from under "curTop" into a TreeNode. Highly recursive.
     * Quelle: http://www.java2s.com/Code/Java/File-Input-Output/DisplayafilesysteminaJTreeview.htm
     * @param curTop
     * @param dir
     * @return a TreeNode with all Subfolders and MIDIs
     */
    private DefaultMutableTreeNode addNodes(DefaultMutableTreeNode curTop, File dir) {
      String curPath = dir.getPath();
      String help = curPath.substring(curPath.lastIndexOf("\\")+1);
      DefaultMutableTreeNode curDir = new DefaultMutableTreeNode(help);
      if (curTop != null) { // should only be null at root
        curTop.add(curDir);
      }
      Vector ol = new Vector();
      String[] tmp = dir.list();
      for (int i = 0; i < tmp.length; i++)
      {
        ol.addElement(tmp[i]);
      }
      Collections.sort(ol, String.CASE_INSENSITIVE_ORDER);
      File f;
      Vector files = new Vector();
      // Make two passes, one for Dirs and one for Files. This is #1.
      for (int i = 0; i < ol.size(); i++) {
        String thisObject = (String) ol.elementAt(i);
        String newPath;
        if (curPath.equals("."))
        {
          newPath = thisObject;
        }
        else
        {
          newPath = curPath + File.separator + thisObject;
        }
        if ((f = new File(newPath)).isDirectory())
        {
          addNodes(curDir, f);
        }
        else if (thisObject.toLowerCase().endsWith(".mid"))
        {
          files.addElement(thisObject);
        }
      }
      // Pass two: for files.
      for (int fnum = 0; fnum < files.size(); fnum++)
      {
        curDir.add(new DefaultMutableTreeNode(files.elementAt(fnum)));
      }
      return curDir;
    }
    
    /**
     * loescht alle Zeilen der Tabelle
     */
    private void clearTableView(JTable t)
    {
        tableModel = (DefaultTableModel)t.getModel();
        int rowCount = tableModel.getRowCount();
        
        for(int i = 0; i < rowCount; i++)
        {
            tableModel.removeRow(0);
        }
    }
    
    /**
     * schreibt die Analyseergebnisse der MIDI-Dateien in die Tabelle
     * pro Zeile 1 MIDI-File
     */
    private void updateTable(String _name) {
        clearTableView(tAnalyse);
        tableModel = (DefaultTableModel)tAnalyse.getModel();
        for (MIDIFileData mfd : almfd)
        {
            tableModel.insertRow(tableModel.getRowCount(), new Object[]{mfd.getId(),mfd.getTitle(),mfd.getKey(),mfd.getOther(),
                mfd.getLowNote(),mfd.getHighNote(),mfd.getShortestNote(),mfd.getLongestNote(),mfd.getAscending(),mfd.getDescending()});
        }
        
        //calculate average Data
        calculateAvgData(_name);
        //update average Data in table tAverage
        clearTableView(tAverage);
        tableModel = (DefaultTableModel)tAverage.getModel();
        for (MIDIFileData mfd : avgData)
        {
            tableModel.insertRow(tableModel.getRowCount(), new Object[]{mfd.getId(),mfd.getTitle(), mfd.getOther(),
                mfd.getLowNote(),mfd.getHighNote(),mfd.getShortestNote(),mfd.getLongestNote(),mfd.getAscending(),mfd.getDescending()});
        }
    }
    
    /**
     * berechnet die Durchschnitte der Elemente des Typs MIDIFileData, welche sich in der Tabelle tAnalyse befinden
     */
    private void calculateAvgData(String _name) {
        int t = 1;
        int count;
        AverageMIDIFileData m = new AverageMIDIFileData();
        
        m.setId(avgData.size());
        m.setTitle(_name);
        for (MIDIFileData mf : almfd)
        {
            m.setOther(m.getOther() + ((mf.getOther() - m.getOther()) / t));
            m.setHighNote(m.getHighNote() + ((mf.getHighNote() - m.getHighNote()) / t));
            m.setLowNote(m.getLowNote() + ((mf.getLowNote() - m.getLowNote()) / t));
            m.setLongestNote(m.getLongestNote() + ((mf.getLongestNote() - m.getLongestNote()) / t));
            m.setShortestNote(m.getShortestNote() + ((mf.getShortestNote() - m.getShortestNote()) / t));
            m.setAscending(m.getAscending() + ((mf.getAscending() - m.getAscending()) / t));
            m.setDescending(m.getDescending() + ((mf.getDescending() - m.getDescending()) / t));
            //Prozentanteil des Pitches berechnen und speichern
            count = getNoteCount(mf);
            for (int i=0; i<mf.getPitches().length; i++) {
               int x = Math.round((100.0F * mf.getPitch(i)) / count); //Prozentanteil der Note im MIDI-File
               m.setPitch(i,m.getPitch(i) + ((x - m.getPitch(i)) / t));
            }
            for (int i : mf.getInstruments()) {
               m.incInstrument(i);
            }
            for (int i : mf.getPercussion()) {
               m.incInstrument(200); //muss nur hoeher als 128 sein, um als Percussion zu zaehlen
            }
            if (mf.getKey() != null) {
               m.incKey(mf.getKey());
            }
            ++t;
        }
        avgData.add(m);
    }
    
    /**
     * gibt die Anzahl der Noten des MIDIFiles zurueck
     * @param mf da MIDIFile bzw die Daten des MIDI-Files
     * @return Anzahl der Noten
     */
    private int getNoteCount(MIDIFileData mf) {
       int Count = 0;
       for (int i=0; i<mf.getPitches().length; i++) {
          Count += mf.getPitch(i);
       }
       return Count;
    }
    
    /**
     * Durchsucht rekursiv den aktuellen Knoten und verwendet alle Subknoten und die darin befindlichen MIDI-Dateien
     * @param tn Der aktuelle TreeNode
     */
    private void getSelectedFolder(DefaultMutableTreeNode tn, boolean analyse) {
        for (int i=0; i<tn.getChildCount(); i++)
        {
            DefaultMutableTreeNode t = (DefaultMutableTreeNode)tn.getChildAt(i);
            TreeNode[] tno = t.getPath();
            if (tno[tno.length-1].toString().toLowerCase().endsWith(".mid"))
            {
                String pathToMidi = getPathFromFile(tno);
                
                currentFileCount++;
                //Progressbar updaten
                SwingUtilities.invokeLater(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        proBar.setString("file " +currentFileCount +" of " +fileCount +" files.");
                        proBar.setValue(currentFileCount);
                    }
                });
                if (analyse)
                {
                    analyseMIDI(pathToMidi);
                }
                else
                {
                    detectAndStoreMIDI(pathToMidi);
                }
            }
            else
            {
                getSelectedFolder(t,analyse);
            }
        }
    }
    
    /**
     * spielt das MIDI ab, welches zuerst in der Liste markiert wurde
     */
    private void play() {
        if(tFiles.getSelectionCount()>0)
        {
            String pathToMidi = getPathFromFile(tFiles.getSelectionPath());
            if (pathToMidi.toLowerCase().endsWith(".mid"))
            {
                midiAnalyser = new MIDIAnalyser(new File(pathToMidi));
                iScore = midiAnalyser.analyse();
                if(iScore != null)
                {
                    MIDIPlayer.play(iScore);
                }
            }
            else
            {
                JOptionPane.showMessageDialog(this, "You have to select a MIDI", "Error", JOptionPane.ERROR_MESSAGE);
            }
        }
    }
    
    /**
     * Erzeugt einen vollstaendigen Dateipfad des uebergebenen TreePaths
     * @param tpath
     * @return Der vollstaendige Pfad der Datei
     */
    private String getPathFromFile(TreePath tpath) {
        String s = "";
        for (int i=2; i<tpath.getPathCount(); i++)
        {
            s += tpath.getPathComponent(i) +"\\";
        }
        if (s.equals(""))
        {
            return s;
        }
        else
        {
            return path + "\\" +s.substring(0, s.length()-1);
        }
    }
    
    /**
     * Erzeugt einen vollstaendigen Dateipfad des uebergebenen TreeNodesArrays
     * @param tpath
     * @return absolute path
     */
    private String getPathFromFile(TreeNode[] tpath) {
        String s = "";
        for (int i=2; i<tpath.length; i++)
        {
            s += tpath[i].toString() +"\\";
        }
        return path + "\\" +s.substring(0, s.length()-1);
    }
    
    /**
     * stoppt das Abspielen des MIDIs
     */
    private void stop() {
       try {
            MIDIPlayer.stop();
        }
        catch (NullPointerException npe) //Player wurde nie gespielt
        {
            //do nothing
        }
    }
    
    /**
     * simple Analyse von MIDI-Dateien
     * Quelle: http://explodingart.com/jmusic/jmtutorial/SimpleAnalysis.html
     * @param mPath 
     */
    private void analyseMIDI(String mPath) {
        boolean inDrums;
        double duration;
        DecimalFormat roundDuration = new DecimalFormat("#.##");
        
        scr.setTitle(mPath.substring(mPath.lastIndexOf("\\")+1));
        try {
            Read.midi(scr,mPath);       
            MIDIFileData mf = new MIDIFileData();
   
            Enumeration e = scr.getPartList().elements();
            while(e.hasMoreElements()){
                    Part nextPt = (Part)e.nextElement();
                    if (nextPt.getChannel() == 9 || nextPt.getChannel() == 10) { //drums
                       inDrums = true;
                    }
                    else //Instrumente speichern
                    {
                       inDrums = false;
                       if (!mf.getInstruments().contains(nextPt.getInstrument())) {
                          mf.addInstrument(nextPt.getInstrument());
                       }
                    }
                    Enumeration enum2 = nextPt.getPhraseList().elements();
                    while(enum2.hasMoreElements()){
                        Phrase nextPhr = (Phrase)enum2.nextElement();
                        Enumeration enum3 = nextPhr.getNoteList().elements();
                        while(enum3.hasMoreElements()){
                           Note nextNote = (Note)enum3.nextElement();
                           //speichern der verwendeten Percussion-Instrumente
                           //andere Eigenschaften dieses Channels (9 und 10) werden ignoriert,
                           //da reservierte Spur fuer Percussions (MIDI-Standard)
                           if (inDrums) {
                              if (nextNote.getPitch() != Integer.MIN_VALUE) {
                                 if (!mf.getPercussion().contains(nextNote.getPitch())) {
                                    mf.addPercussion(nextNote.getPitch());
                                 }
                              }
                           }
                           else
                           {
                              //nur gueltige Pitches setzen - keine Pausen
                              if (nextNote.getPitch() != Integer.MIN_VALUE) {
                                 mf.increasePitch(nextNote.getPitch());
                                 //check range                
                                 pitchRange(nextNote.getPitch());
                                 //check direction                
                                 upOrDown(nextNote.getPitch());
                                 //1.0 = Viertel, 0.5 = Achtel, ....
                                 duration = nextNote.getRhythmValue()/scr.getPPQN(); 
                                 duration = Double.valueOf(roundDuration.format(duration).replace(",", "."));
                                 noteLength(duration);
                                 switch (nextNote.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;
                                 }
                              }
                              else
                              {
                                 other++;
                              }
                           }
                     }
                }
            }
            mf.setTitle(scr.getTitle());
            mf.setOther(other);
            mf.setId(almfd.size());
            mf.setFilepath(mPath);
            mf.setLowNote(lowNote);
            mf.setHighNote(highNote);
            mf.setShortestNote(shortestNote);
            mf.setLongestNote(longestNote);
            mf.setAscending(ascending);
            mf.setDescending(descending);
            //krumhansl key finding algorithm
            KeyFinder kf = new KeyFinder(noteDurations);
            mf.setKey(kf.getKey());
            almfd.add(mf);
            for (int i=0; i<noteDurations.length; i++) {
               noteDurations[i] = 0;
            }
            other = 0;
            initMIDIData();
        }
        catch (Exception e)
        {
            System.err.println(mPath);
        }
    }

    /**
     * zugehoerige Funktionen fuer analyseMIDI(String mPath)
     * Quelle: http://explodingart.com/jmusic/jmtutorial/SimpleAnalysis.html
     * @param pitch
     */
    private void pitchRange(int pitch) {
            if(pitch < lowNote) { lowNote = pitch; }
            if(pitch > highNote) { highNote = pitch; }
    }	

    /**
     * zugehoerige Funktionen fuer analyseMIDI(String mPath)
     * Quelle: http://explodingart.com/jmusic/jmtutorial/SimpleAnalysis.html
     * @param duration duration of a note
     */
    private void noteLength(double duration) {
            if(duration < shortestNote) { shortestNote = duration; }
            if(duration > longestNote) { longestNote = duration; }
    }	

    /**
     * zugehoerige Funktionen fuer analyseMIDI(String mPath)
     * Quelle: http://explodingart.com/jmusic/jmtutorial/SimpleAnalysis.html
     * @param pitch
     */
    private void upOrDown(int pitch) {
            if(pitch < prev) { descending++; }
            if(pitch > prev) { ascending++; }
            prev = pitch;
    }
    
    /**
     * leert den Treeview der MIDIs
     */
    private void clearFileTreeView() {
        DefaultMutableTreeNode root = (DefaultMutableTreeNode)tFiles.getModel().getRoot();
        root.removeAllChildren();
    }
    
    /**
     * @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(AnalyserForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(AnalyserForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(AnalyserForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(AnalyserForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
           @Override
            public void run() {
                new AnalyserForm().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton btnAnalyse;
    private javax.swing.JButton btnClear;
    private javax.swing.JButton btnDatabaseAdd;
    private javax.swing.JButton btnInstruments;
    private javax.swing.JButton btnNotes;
    private javax.swing.JButton btnPlay;
    private javax.swing.JButton btnSheet;
    private javax.swing.JButton btnStop;
    private javax.swing.JMenuItem clear;
    private javax.swing.JMenuBar jMenuBar1;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JScrollPane jScrollPane3;
    private javax.swing.JScrollPane jScrollPane4;
    private javax.swing.JScrollPane jScrollPane5;
    private javax.swing.JPopupMenu.Separator jSeparator1;
    private javax.swing.JToolBar.Separator jSeparator5;
    private javax.swing.JToolBar.Separator jSeparator6;
    private javax.swing.JToolBar jToolBar1;
    private javax.swing.JMenuItem mAdd;
    private javax.swing.JMenu mAnalyse;
    private javax.swing.JCheckBoxMenuItem mChkLog;
    private javax.swing.JMenuItem mChoose;
    private javax.swing.JMenuItem mClose;
    private javax.swing.JMenuItem mCompose;
    private javax.swing.JMenu mComposer;
    private javax.swing.JMenuItem mConvert;
    private javax.swing.JMenu mFile;
    private javax.swing.JMenuItem mHelp;
    private javax.swing.JMenuItem mInstruments;
    private javax.swing.JMenu mLog;
    private javax.swing.JMenuItem mMotifViewer;
    private javax.swing.JMenu mMotifs;
    private javax.swing.JMenuItem mNewCategory;
    private javax.swing.JMenuItem mNoteSheet;
    private javax.swing.JMenuItem mNotes;
    private javax.swing.JMenu mnuHelp;
    private javax.swing.JPanel panData;
    private javax.swing.JProgressBar proBar;
    private javax.swing.JMenuItem start;
    private javax.swing.JTable tAnalyse;
    private javax.swing.JTable tAverage;
    private javax.swing.JTable tConsole;
    private javax.swing.JTree tFiles;
    private javax.swing.JTabbedPane tabData;
    private javax.swing.JTextPane txtLog;
    // End of variables declaration//GEN-END:variables
}
