1838 lines
46 KiB
Java
1838 lines
46 KiB
Java
/*
|
|
----
|
|
This file is part of SECONDO.
|
|
|
|
Copyright (C) 2004, University in Hagen, Department of Computer Science,
|
|
Database Systems for New Applications.
|
|
|
|
SECONDO is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
SECONDO is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with SECONDO; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
----
|
|
|
|
//paragraph [1] Title: [{\Large \bf \begin {center}] [\end {center}}]
|
|
//paragraph [21] table1column: [\begin{quote}\begin{tabular}{l}] [\end{tabular}\end{quote}]
|
|
//paragraph [22] table2columns: [\begin{quote}\begin{tabular}{ll}] [\end{tabular}\end{quote}]
|
|
//paragraph [23] table3columns: [\begin{quote}\begin{tabular}{lll}] [\end{tabular}\end{quote}]
|
|
//paragraph [24] table4columns: [\begin{quote}\begin{tabular}{llll}] [\end{tabular}\end{quote}]
|
|
//[--------] [\hline]
|
|
//characters [1] verbatim: [$] [$]
|
|
//characters [2] formula: [$] [$]
|
|
//characters [3] capital: [\textsc{] [}]
|
|
//characters [4] teletype: [\texttt{] [}]
|
|
//[ss] [{\ss}]
|
|
//[<=] [\leq]
|
|
//[#] [\neq]
|
|
//[tilde] [\verb|~|]
|
|
//[->] [$\rightarrow $]
|
|
|
|
\pagebreak
|
|
|
|
1 Implementation of the JavaGUI
|
|
|
|
This class provides the SECODNO viewer for Midi type objects and relations
|
|
containing midis. Therefore this viewer consists of two single
|
|
viewers combined to a whole one, being capable of displaying both.
|
|
|
|
2 Imports
|
|
|
|
*/
|
|
package viewer;
|
|
|
|
import javax.swing.*;
|
|
import javax.swing.text.*;
|
|
import javax.swing.event.*;
|
|
import javax.swing.border.*;
|
|
import javax.swing.table.*;
|
|
import java.util.Vector;
|
|
import java.util.*;
|
|
import java.awt.*;
|
|
import java.lang.Long;
|
|
import java.awt.event.*;
|
|
import gui.SecondoObject;
|
|
import gui.*;
|
|
import sj.lang.*;
|
|
import tools.*;
|
|
import viewer.midi.*;
|
|
import javax.swing.Timer;
|
|
import java.io.File;
|
|
import java.io.InputStream;
|
|
import java.io.IOException;
|
|
import java.io.*;
|
|
import javax.sound.midi.*;
|
|
import tools.Reporter;
|
|
|
|
|
|
/*
|
|
3 Class ~MidiViewer~
|
|
|
|
This class extends the class SecondoViewer and must therefore implement the
|
|
abstract methods inherited. Additionally it implements three
|
|
listener interfaces to act itself as ActionListener.
|
|
|
|
*/
|
|
|
|
|
|
public class MidiViewer extends SecondoViewer implements ChangeListener,
|
|
ItemListener,ListSelectionListener
|
|
{
|
|
/*
|
|
3.1 private components
|
|
|
|
The name of the viewer including its version
|
|
|
|
*/
|
|
private static final String name = "MidiViewer 1.22";
|
|
|
|
/*
|
|
*global swing-components*
|
|
|
|
A JTabbedPane with three tabs as the primary organising component
|
|
|
|
*/
|
|
private JTabbedPane jtp = new JTabbedPane(JTabbedPane.TOP);
|
|
|
|
/*
|
|
The first tab is the playPanel, containing the Midi player
|
|
|
|
*/
|
|
private JPanel playPanel = new JPanel();
|
|
private JPanel PlayerPanel = new JPanel();
|
|
private JPanel InfoPanel = new JPanel();
|
|
private JButton play = new JButton("play");
|
|
private JButton halt = new JButton("pause");
|
|
private JButton stop = new JButton("stop");
|
|
private JButton ff = new JButton("ff");
|
|
private JButton rew = new JButton("rew");
|
|
private JButton export = new JButton("export");
|
|
private JButton skipP = new JButton("skip +");
|
|
private JButton skipM = new JButton("skip -");
|
|
private JButton setTickPosition = new JButton("setTickPosition");
|
|
private JList list;
|
|
private JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 100000, 0);
|
|
private JLabel time = new JLabel("0:00 / 0:00");
|
|
private JPanel TrackPanel = new JPanel();
|
|
private JLabel infoNameLabel = new JLabel(("----- Welcome to " + name +" -----"), JLabel.CENTER);
|
|
private JLabel infoAssumedNameLabel = new JLabel();
|
|
private JLabel infoTempoLabel = new JLabel();
|
|
private JLabel infoLengthLabel = new JLabel();
|
|
private JLabel infoTickLengthLabel = new JLabel();
|
|
private JLabel infoTrackLabel = new JLabel();
|
|
private JLabel infoResLabel = new JLabel();
|
|
/*
|
|
The second tab is used to display the relations
|
|
|
|
*/
|
|
private JPanel relPanel = new JPanel();
|
|
private JComboBox ComboBox;
|
|
private JScrollPane ScrollPane;
|
|
private JTable CurrentTable;
|
|
private JPanel dummy;
|
|
/*
|
|
The third tab displays the meta informations of the currently playing
|
|
Midi
|
|
|
|
*/
|
|
private JPanel metaPanel = new JPanel();
|
|
/*
|
|
*global components of the adapted relation viewer*
|
|
|
|
*/
|
|
private Vector Tables;
|
|
private Vector midiTables = new Vector();
|
|
private boolean listSelectionAllowed = true;
|
|
/*
|
|
*global components of the Midi player*
|
|
|
|
All midis of the player are stored in a vector, representing the
|
|
~playlist~, as there should be no limitation of the maximum of queried
|
|
midis
|
|
|
|
*/
|
|
private Vector midiVector;
|
|
/*
|
|
The first approach to the MidiViewer project was to be as object
|
|
orientated as possible. Therefore every MidiFile had its own sequencer.
|
|
After some performance and threading problems we decided that there
|
|
should be only one sequencer managed by the viewer itself.
|
|
|
|
*/
|
|
private static Sequencer sequencer = null;
|
|
/*
|
|
As it is not guaranteed that a sequencer provided by the system is an
|
|
instance of ~Synthesizer~ there must be a way to call a synthsizer, a
|
|
receiver and a transmitter
|
|
|
|
*/
|
|
private Synthesizer synthesizer = null;
|
|
private Receiver receiver = null;
|
|
private Transmitter transmitter = null;
|
|
|
|
/*
|
|
The sequencer loads only the sequence from a MidiFile. That is why it is
|
|
necessary to have a reference to the currently loaded MidiFile.
|
|
|
|
*/
|
|
private MidiFile thisMidiFile = null;
|
|
|
|
/*
|
|
The slider should capable to change its value as the Midi is playing,
|
|
so that its position must be updated continuesly. On the other hand,
|
|
every change made by the user should set the current position.
|
|
The problem was, that every change of the slider position
|
|
made by the player, invoked the ActionListner waiting for
|
|
user commands. This caused unfortunately some bad behavior of the
|
|
sequencer. Therefore a flag is used to indicate whether the change was
|
|
made by the user or the system.
|
|
|
|
*/
|
|
private boolean mouseSliding = false;
|
|
/*
|
|
An array of the current tracks
|
|
|
|
*/
|
|
private MidiTrack[] currentTracks;
|
|
/*
|
|
As a sequencer does not implement a pause function, it is neccassary to
|
|
know the position to relaunch after stopping the sequencer for pausing
|
|
|
|
*/
|
|
private long startAt = 0;
|
|
|
|
/*
|
|
By using the skip buttons, the position shall be changed by ten
|
|
percent of the whole length
|
|
|
|
*/
|
|
private long skippy = 0;
|
|
/*
|
|
Length of the sequence is handled as ~String~
|
|
|
|
*/
|
|
private String length = "0:00 / 0:00";
|
|
private String lengthOfCurrentMidi = "0:00";
|
|
/*
|
|
During a Midi sequence the tempo might change. To display always the
|
|
correct tempo it's necessary to save the current displayed tempo to not
|
|
always update the displayed tempo when not needed
|
|
|
|
*/
|
|
private float displayedTempo = 0.0f;
|
|
|
|
/*
|
|
3.1 constructor
|
|
|
|
The constructor initializes the whole GUI in a couple of steps.
|
|
|
|
*/
|
|
public MidiViewer()
|
|
{
|
|
/*
|
|
Creates the GUI by calling the three methods to create and show the
|
|
three tabs of the viewer
|
|
|
|
*/
|
|
createAndShowPlayerGUI();
|
|
createAndShowRelationGUI();
|
|
createAndShowMetaPanel();
|
|
|
|
/*
|
|
As swing is not threadsafe the two update methods ~refreshTime~
|
|
and ~refreshSlider~ are called from a timer
|
|
|
|
*/
|
|
int delay = 50;
|
|
ActionListener taskPerformer = new ActionListener() {
|
|
public void actionPerformed(ActionEvent evt)
|
|
{
|
|
refreshTime();
|
|
refreshSlider();
|
|
}
|
|
};
|
|
new Timer(delay, taskPerformer).start();
|
|
|
|
setLayout(new BorderLayout());
|
|
|
|
/*
|
|
As the viewer manages the one and only sequencer of the whole
|
|
implementation, the ~MidiSystem~ and expecially the sequencer needs to be
|
|
initialized.
|
|
|
|
*/
|
|
try { sequencer = MidiSystem.getSequencer(); }
|
|
catch (MidiUnavailableException e)
|
|
{
|
|
Reporter.writeError("MidiFile 3: Midi unavailable");
|
|
}
|
|
|
|
if (sequencer == null)
|
|
{
|
|
Reporter.writeError("MidiFile 4: can't get a sequencer");
|
|
}
|
|
|
|
if (! (sequencer instanceof Synthesizer))
|
|
{
|
|
try
|
|
{
|
|
synthesizer = MidiSystem.getSynthesizer();
|
|
synthesizer.open();
|
|
receiver = synthesizer.getReceiver();
|
|
transmitter = sequencer.getTransmitter();
|
|
transmitter.setReceiver(receiver);
|
|
}
|
|
catch (MidiUnavailableException e)
|
|
{
|
|
Reporter.writeError("MidiFile 7: can't get a synthesizer");
|
|
}
|
|
}
|
|
|
|
/*
|
|
Getting the GUI together
|
|
|
|
*/
|
|
jtp.addTab("Midi-Player", playPanel);
|
|
jtp.add("Relation", relPanel);
|
|
jtp.add("Meta-Informations", (new JScrollPane(metaPanel)));
|
|
add(jtp, BorderLayout.CENTER);
|
|
}
|
|
|
|
/*
|
|
3.2 inherited methods of SecondoViewer
|
|
|
|
3.3.3 getName
|
|
|
|
Returns the name of the viewer
|
|
|
|
*/
|
|
public String getName()
|
|
{
|
|
return name;
|
|
}
|
|
|
|
/*
|
|
3.2.3 addObject
|
|
|
|
Checks whether the given SecondoObject is a Midi or relation containing
|
|
midis. If it is a Midi, a MidiFile is created, automatically started and added to the
|
|
playlist. Otherwise a table is created containing the relation. From this table
|
|
it is possible to load the containing midis
|
|
|
|
*/
|
|
public boolean addObject(SecondoObject o)
|
|
{
|
|
ListExpr LE = o.toListExpr();
|
|
|
|
if (LE.first().isAtom() && LE.first().symbolValue().equals("midi"))
|
|
{
|
|
unload();
|
|
|
|
if (isDisplayed(o))
|
|
{
|
|
for (int i = 0; i < midiVector.size(); i++)
|
|
{
|
|
if(((MidiFile)midiVector.elementAt(i)).getID().equals(
|
|
o.getID().toString()))
|
|
{
|
|
thisMidiFile = (MidiFile)midiVector.elementAt(i);
|
|
load();
|
|
play();
|
|
return true;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
Base64Decoder b64;
|
|
StringReader sr = new StringReader(LE.second().first().textValue());
|
|
b64 = new Base64Decoder(sr);
|
|
MidiFile midi = new MidiFile(b64.getInputStream(), o.getName(),
|
|
o.getID().toString());
|
|
midiVector.addElement(midi);
|
|
list.setListData(midiVector);
|
|
thisMidiFile = midi;
|
|
load();
|
|
long l = getLengthInTicks();
|
|
length = Long.toString(l);
|
|
play();
|
|
slider.setMaximum((int) getLengthInTicks());
|
|
currentTracks = thisMidiFile.getTrackArray();
|
|
setTrackPanel();
|
|
setInfoPanel();
|
|
jtp.setSelectedIndex(0);
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (isDisplayed(o))
|
|
{
|
|
selectObject(o);
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
JTable NTable = createTableFrom(o.toListExpr(), o.getName());
|
|
if(NTable==null)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
Tables.add(NTable);
|
|
ComboBox.addItem(o.getName());
|
|
selectObject(o);
|
|
ScrollPane.setViewportView(NTable);
|
|
jtp.setSelectedIndex(1);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
3.3.3 removeObject
|
|
|
|
Removes the targeted object from the viewer. If a relation has to be
|
|
removed, so all objects are loaded from the relation into the player.
|
|
|
|
*/
|
|
|
|
public void removeObject(SecondoObject o)
|
|
{
|
|
ListExpr LE = o.toListExpr();
|
|
|
|
if (LE.first().isAtom() && LE.first().symbolValue().equals("midi"))
|
|
{
|
|
if (thisMidiFile.getID().equals(o.getID().toString()))
|
|
{
|
|
midiVector.remove(thisMidiFile);
|
|
unload();
|
|
setInfoPanel();
|
|
setTrackPanel();
|
|
setMetaPanel();
|
|
}
|
|
else
|
|
{
|
|
int finder = -1;
|
|
// searches the right midi
|
|
for (int i = 0; i < midiVector.size(); i++)
|
|
{
|
|
if (((MidiFile)midiVector.elementAt(i)).getID().equals(
|
|
o.getID().toString()))
|
|
{
|
|
finder = i;
|
|
}
|
|
}
|
|
if (finder != -1)
|
|
{
|
|
midiVector.remove(finder);
|
|
}
|
|
}
|
|
list.setListData(midiVector);
|
|
}
|
|
else
|
|
{
|
|
int index = getIndexOf(o.getName());
|
|
|
|
if( index >= 0 )
|
|
{
|
|
kickThem(o.getName());
|
|
setInfoPanel();
|
|
setTrackPanel();
|
|
setMetaPanel();
|
|
ComboBox.removeItemAt(index);
|
|
Tables.remove(index);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
3.3.3 removeAll
|
|
|
|
Simple and selfexplaining
|
|
|
|
*/
|
|
public void removeAll()
|
|
{
|
|
|
|
unload();
|
|
midiVector.removeAllElements();
|
|
list.setListData(midiVector);
|
|
ComboBox.removeAllItems();
|
|
Tables.clear();
|
|
jtp.setSelectedIndex(0);
|
|
setTrackPanel();
|
|
setMetaPanel();
|
|
}
|
|
|
|
/*
|
|
3.3.3 canDisplay
|
|
|
|
Checks if SecondoObject can be displayed. There are two possible types
|
|
which can be displayed. On the one hand a Midi can be displayed,
|
|
on the other hand relations can be displayed. If the type is rel, it is
|
|
checked if the relation contains midis at all.
|
|
|
|
*/
|
|
public boolean canDisplay(SecondoObject o)
|
|
{
|
|
ListExpr LE = o.toListExpr();
|
|
|
|
if(LE.listLength()!=2) return false;
|
|
|
|
if(LE.first().atomType()==ListExpr.SYMBOL_ATOM &&
|
|
LE.first().symbolValue().equals("midi"))
|
|
{
|
|
if (LE.second().first().atomType() == ListExpr.TEXT_ATOM)
|
|
return true;
|
|
else return false;
|
|
}
|
|
LE = LE.first();
|
|
|
|
if(LE.isAtom()) return false;
|
|
else
|
|
{
|
|
LE = LE.first();
|
|
if(LE.isAtom() && LE.atomType()==ListExpr.SYMBOL_ATOM &&
|
|
(LE.symbolValue().equals("rel") |
|
|
LE.symbolValue().equals("mrel")) )
|
|
{
|
|
if (containsMidiObject(o)) return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
4.4.4 isDisplayed
|
|
|
|
Checks whether an object is already displayed or not.
|
|
|
|
*/
|
|
public boolean isDisplayed(SecondoObject o)
|
|
{
|
|
ListExpr LE = o.toListExpr();
|
|
if (LE.first().isAtom() && LE.first().symbolValue().equals("midi"))
|
|
{
|
|
for (int i = 0; i < midiVector.size(); i++)
|
|
{
|
|
if(((MidiFile)midiVector.elementAt(i)).getID().equals(
|
|
o.getID().toString()))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return getIndexOf(o.getName())>=0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
3.3.3 selectObject
|
|
|
|
Selected Object is displayed
|
|
|
|
*/
|
|
public boolean selectObject(SecondoObject o)
|
|
{
|
|
ListExpr LE = o.toListExpr();
|
|
if (LE.first().isAtom() && LE.first().symbolValue().equals("midi"))
|
|
{
|
|
unload();
|
|
for (int i = 0; i < midiVector.size(); i++)
|
|
{
|
|
if(((MidiFile)midiVector.elementAt(i)).getID().equals(
|
|
o.getID().toString()))
|
|
{
|
|
thisMidiFile = (MidiFile)midiVector.elementAt(i);
|
|
load();
|
|
play();
|
|
setInfoPanel();
|
|
setTrackPanel();
|
|
jtp.setSelectedIndex(0);
|
|
return true;
|
|
}
|
|
}
|
|
setTrackPanel();
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
int index = getIndexOf(o.getName());
|
|
if ( index < 0 )
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
ComboBox.setSelectedIndex(index);
|
|
showSelectedObject();
|
|
jtp.setSelectedIndex(1);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
2.2.2 getMenuVector
|
|
|
|
As there is no need for an extra menu on the GUI, null is returned
|
|
|
|
*/
|
|
public MenuVector getMenuVector()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
|
|
/*
|
|
2.3.3 getDisplayQuality
|
|
|
|
If the SecondoObject is a Midi or a relation containing midis this
|
|
viewer shall be used to display it
|
|
|
|
*/
|
|
public double getDisplayQuality(SecondoObject o)
|
|
{
|
|
if (canDisplay(o))
|
|
{
|
|
return 0.9;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
3.2 the player
|
|
|
|
3.3.3 setTrackPanel
|
|
|
|
The track panel is located in the lower left corner of the Midi player.
|
|
It shall display all tracks and their names. Also it must be possible
|
|
to mute and to solo a single track.
|
|
|
|
*/
|
|
void setTrackPanel()
|
|
{
|
|
TrackPanel.removeAll();
|
|
|
|
if (sequencer == null || !sequencer.isOpen())
|
|
{
|
|
TrackPanel.add(new JLabel("Nothing to display"));
|
|
TrackPanel.repaint();
|
|
}
|
|
else
|
|
{
|
|
int i;
|
|
JPanel headline = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
|
headline.add(new JLabel(" Current Tracks - "));
|
|
headline.add(new JLabel("monitoring states"));
|
|
JButton playAll = new JButton("play all");
|
|
playAll.addMouseListener(new MouseAdapter()
|
|
{
|
|
public void mouseClicked(MouseEvent e)
|
|
{
|
|
unMuteAllTracks();
|
|
}
|
|
});
|
|
TrackPanel.add(headline);
|
|
JPanel playAllPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
|
playAllPanel.add(playAll);
|
|
TrackPanel.add(playAllPanel);
|
|
|
|
for (i = 0; i < currentTracks.length; i++)
|
|
{
|
|
boolean checked = !sequencer.getTrackMute(i);
|
|
JPanel jp = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
|
JCheckBox cb = new JCheckBox(currentTracks[i].getName(),
|
|
checked);
|
|
|
|
JButton jb = new JButton("Solo");
|
|
jb.setName(i + " ");
|
|
cb.addItemListener(this);
|
|
jb.addMouseListener(new MouseAdapter()
|
|
{
|
|
public void mouseClicked(MouseEvent e)
|
|
{
|
|
int i = getIndexNo(((JButton)
|
|
e.getSource()).getName());
|
|
soloTrack(i);
|
|
}
|
|
});
|
|
jp.add(jb);
|
|
jp.add(cb);
|
|
TrackPanel.add(jp);
|
|
}
|
|
TrackPanel.setLayout(new GridLayout(i+2, 1));
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
2.3.2 soloTrack
|
|
|
|
If a solo button is hit, every track but one needs to be set to the mute state.
|
|
|
|
(As said in the Java documentation it is possible that not every system
|
|
supports the solo functionality provided by Java. Therefore we are just
|
|
using mute functions.)
|
|
|
|
*/
|
|
void soloTrack(int trackSolo)
|
|
{
|
|
for (int i = 0; i < currentTracks.length; i++)
|
|
{
|
|
if (i != trackSolo)
|
|
{
|
|
sequencer.setTrackMute(i, true);
|
|
}
|
|
else
|
|
{
|
|
sequencer.setTrackMute(i, false);
|
|
}
|
|
}
|
|
setTrackPanel();
|
|
}
|
|
|
|
/*
|
|
3.3.3 unMuteAllTracks
|
|
|
|
Reverses the effect of soloTrack and unsets all mute states
|
|
|
|
*/
|
|
void unMuteAllTracks()
|
|
{
|
|
for(int i = 0; i < currentTracks.length; i++)
|
|
{
|
|
sequencer.setTrackMute(i, false);
|
|
}
|
|
setTrackPanel();
|
|
}
|
|
|
|
/*
|
|
The info panel is located right under the player controls in the lower
|
|
right corner. It is meant to give a quick overview of the current
|
|
playing Midi.
|
|
|
|
*/
|
|
void setInfoPanel()
|
|
{
|
|
if (sequencer == null || !sequencer.isOpen())
|
|
{
|
|
infoAssumedNameLabel.setText("");
|
|
infoTempoLabel.setText("");
|
|
infoLengthLabel.setText("");
|
|
infoTickLengthLabel.setText("");
|
|
infoTrackLabel.setText("");
|
|
infoResLabel.setText("");
|
|
}
|
|
else
|
|
{
|
|
infoAssumedNameLabel.setText("Assumed Name: "+thisMidiFile.getName());
|
|
displayedTempo = getTempo();
|
|
infoTempoLabel.setText("Tempo: " +
|
|
getTempo() / sequencer.getTempoFactor() + " bpm x " +
|
|
getTempoFactor() + " = " + getTempo()+ " bpm");
|
|
infoLengthLabel.setText("Length: "+ lengthOfCurrentMidi);
|
|
infoTickLengthLabel.setText("Length in Ticks: " + sequencer.getTickLength());
|
|
infoTrackLabel.setText("Tracks: "+ thisMidiFile.getNrTracks());
|
|
if (thisMidiFile.getDivisionType() == Sequence.PPQ)
|
|
{
|
|
infoResLabel.setText("Resolution in ticks per beat: "
|
|
+ thisMidiFile.getRes());
|
|
}
|
|
else
|
|
{
|
|
infoResLabel.setText("Resolution in ticks per frame: "
|
|
+ thisMidiFile.getRes());
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
3.2.2 set MetaPanel
|
|
|
|
Calls the meta informations belonging to a MidiFile and displays them at
|
|
the third tab
|
|
|
|
*/
|
|
void setMetaPanel()
|
|
{
|
|
int zaehler = 0;
|
|
metaPanel.removeAll();
|
|
|
|
if (thisMidiFile == null)
|
|
{
|
|
metaPanel.add(new JLabel("nothing to display..."));
|
|
}
|
|
else
|
|
{
|
|
Vector v;
|
|
|
|
for (int i = 0; i < thisMidiFile.getNrTracks(); i++)
|
|
{
|
|
v = thisMidiFile.getMetaInfos(i);
|
|
|
|
for (int j = 0; j < v.size(); j++)
|
|
{
|
|
String s = (String) v.get(j);
|
|
JLabel jl = new JLabel(s);
|
|
metaPanel.add(jl);
|
|
zaehler++;
|
|
}
|
|
}
|
|
metaPanel.setLayout(new GridLayout(zaehler,1));
|
|
}
|
|
}
|
|
|
|
/*
|
|
3.4.3 createAndShowMetaPanel
|
|
|
|
Some kind of ~constructor~ for the meta panel. When it is called there
|
|
can be no Midi file loaded. So there is nothing to do.
|
|
|
|
*/
|
|
public void createAndShowMetaPanel()
|
|
{
|
|
metaPanel.add(new JLabel("nothing to display"));
|
|
}
|
|
|
|
|
|
/*
|
|
2.2.2 createAndShowPlayerGUI
|
|
|
|
In contrast to ~createAndShowMetaPanel~ this ~constructor~ of the player
|
|
is much more sophisticated as every button. ActionListener and all other
|
|
components needs to be initialized.
|
|
|
|
*/
|
|
public void createAndShowPlayerGUI()
|
|
{
|
|
midiVector = new Vector();
|
|
/*
|
|
the buttons and their listener are linked
|
|
|
|
*/
|
|
play.addMouseListener(new MouseAdapter()
|
|
{
|
|
public void mouseClicked (MouseEvent e)
|
|
{
|
|
if (sequencer.isOpen())
|
|
{
|
|
play();
|
|
setInfoPanel();
|
|
}
|
|
}
|
|
});
|
|
|
|
halt.addMouseListener(new MouseAdapter()
|
|
{
|
|
public void mouseClicked(MouseEvent e)
|
|
{
|
|
if (sequencer.isOpen())
|
|
{
|
|
halt();
|
|
//setInfoPanel();
|
|
}
|
|
}
|
|
});
|
|
|
|
ff.addMouseListener(new MouseAdapter()
|
|
{
|
|
public void mouseClicked(MouseEvent e)
|
|
{
|
|
if (sequencer.isOpen())
|
|
{
|
|
fastforward();
|
|
}
|
|
}
|
|
});
|
|
|
|
rew.addMouseListener(new MouseAdapter()
|
|
{
|
|
public void mouseClicked(MouseEvent e)
|
|
{
|
|
if (sequencer.isOpen())
|
|
{
|
|
rewind();
|
|
}
|
|
}
|
|
});
|
|
|
|
stop.addMouseListener(new MouseAdapter()
|
|
{
|
|
public void mouseClicked(MouseEvent e)
|
|
{
|
|
if (sequencer.isOpen())
|
|
{
|
|
stop();
|
|
}
|
|
}
|
|
});
|
|
|
|
export.addMouseListener(new MouseAdapter()
|
|
{
|
|
public void mouseClicked(MouseEvent e)
|
|
{
|
|
if(sequencer.isOpen())
|
|
{
|
|
thisMidiFile.export();
|
|
}
|
|
}
|
|
});
|
|
|
|
skipM.addMouseListener(new MouseAdapter()
|
|
{
|
|
public void mouseClicked(MouseEvent e)
|
|
{
|
|
if (sequencer.isOpen())
|
|
{
|
|
skipM();
|
|
}
|
|
}
|
|
});
|
|
|
|
skipP.addMouseListener(new MouseAdapter()
|
|
{
|
|
public void mouseClicked(MouseEvent e)
|
|
{
|
|
if(sequencer.isOpen())
|
|
{
|
|
skipP();
|
|
}
|
|
}
|
|
});
|
|
setTickPosition.addMouseListener(new MouseAdapter()
|
|
{
|
|
public void mouseClicked(MouseEvent e)
|
|
{
|
|
if (sequencer.isOpen())
|
|
{
|
|
try
|
|
{
|
|
String s = (String)JOptionPane.showInputDialog(
|
|
null, "Set the Tick Position","Enter the new Tick Position" ,
|
|
JOptionPane.QUESTION_MESSAGE);
|
|
long l = (Long.decode(s)).longValue();
|
|
|
|
if (sequencer.getTickLength() >= l)
|
|
{
|
|
sequencer.setTickPosition(l);
|
|
}
|
|
}
|
|
catch (Exception ede) {Reporter.writeError("wrong Input");}
|
|
}
|
|
refreshSlider();
|
|
}
|
|
});
|
|
|
|
slider.addChangeListener(this);
|
|
/*
|
|
The borders of the buttons are set
|
|
|
|
*/
|
|
Border bd1 = BorderFactory.createEtchedBorder();
|
|
play.setBorder(bd1);
|
|
play.setForeground(new Color(0,0,255));
|
|
halt.setBorder(bd1);
|
|
ff.setBorder(bd1);
|
|
rew.setBorder(bd1);
|
|
stop.setBorder(bd1);
|
|
export.setBorder(bd1);
|
|
slider.setBorder(bd1);
|
|
skipM.setBorder(bd1);
|
|
skipP.setBorder(bd1);
|
|
/*
|
|
As layout manager GridBagLayout is used. All Components get their
|
|
constraints.
|
|
|
|
*/
|
|
InfoPanel.removeAll();
|
|
InfoPanel.add(infoNameLabel);
|
|
InfoPanel.add(infoAssumedNameLabel);
|
|
InfoPanel.add(infoTempoLabel);
|
|
InfoPanel.add(infoLengthLabel);
|
|
InfoPanel.add(infoTickLengthLabel);
|
|
InfoPanel.add(infoTrackLabel);
|
|
InfoPanel.add(infoResLabel);
|
|
InfoPanel.add(setTickPosition);
|
|
|
|
// The playlist is created
|
|
list = new JList(midiVector);
|
|
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
|
|
|
list.addMouseListener(new MouseAdapter()
|
|
{
|
|
public void mouseClicked(MouseEvent e)
|
|
{
|
|
if (e.getClickCount() == 2 && listSelectionAllowed)
|
|
{
|
|
listSelectionAllowed = false;
|
|
unload();
|
|
thisMidiFile = ((MidiFile) list.getSelectedValue());
|
|
load();
|
|
play();
|
|
setInfoPanel();
|
|
setTrackPanel();
|
|
listSelectionAllowed = true;
|
|
}
|
|
}
|
|
});
|
|
|
|
JScrollPane listScrollPane = new JScrollPane(list);
|
|
|
|
// Trackpanel is generated
|
|
TrackPanel.setLayout(new GridLayout(1,1));
|
|
JScrollPane trackScrollPane = new JScrollPane(TrackPanel);
|
|
|
|
// playercontrols are generated
|
|
GridBagLayout playLayout = new GridBagLayout();
|
|
GridBagConstraints playConstraints = new GridBagConstraints();
|
|
PlayerPanel.setLayout(playLayout);
|
|
playConstraints.gridx = 0;
|
|
playConstraints.gridy = 0;
|
|
playConstraints.gridwidth = 2;
|
|
playConstraints.gridheight = 1;
|
|
playConstraints.weightx = 100;
|
|
playConstraints.weighty = 100;
|
|
playConstraints.fill = GridBagConstraints.BOTH;
|
|
|
|
playLayout.setConstraints(slider, playConstraints);
|
|
PlayerPanel.add(slider);
|
|
playConstraints.gridx = 2;
|
|
playLayout.setConstraints(time, playConstraints);
|
|
PlayerPanel.add(time);
|
|
playConstraints.gridwidth = 1;
|
|
playConstraints.gridx = 0;
|
|
playConstraints.gridy = 1;
|
|
playLayout.setConstraints(play, playConstraints);
|
|
PlayerPanel.add(play);
|
|
playConstraints.gridx = 1;
|
|
playLayout.setConstraints(halt, playConstraints);
|
|
PlayerPanel.add(halt);
|
|
playConstraints.gridx = 2;
|
|
playLayout.setConstraints(stop, playConstraints);
|
|
PlayerPanel.add(stop);
|
|
playConstraints.gridx = 3;
|
|
playLayout.setConstraints(export, playConstraints);
|
|
PlayerPanel.add(export);
|
|
playConstraints.gridx = 0;
|
|
playConstraints.gridy = 2;
|
|
playLayout.setConstraints(rew, playConstraints);
|
|
PlayerPanel.add(rew);
|
|
playConstraints.gridx = 1;
|
|
playLayout.setConstraints(skipM, playConstraints);
|
|
PlayerPanel.add(skipM);
|
|
playConstraints.gridx = 2;
|
|
playLayout.setConstraints(skipP, playConstraints);
|
|
PlayerPanel.add(skipP);
|
|
playConstraints.gridx = 3;
|
|
playLayout.setConstraints(ff, playConstraints);
|
|
PlayerPanel.add(ff);
|
|
|
|
// InfoPanel is generated
|
|
InfoPanel.setLayout(new GridLayout(8,1));
|
|
InfoPanel.setBorder(bd1);
|
|
|
|
playPanel.setLayout(new GridLayout(1,1));
|
|
|
|
GridBagLayout layoutLeftTop = new GridBagLayout();
|
|
GridBagConstraints constraintsLeftTop = new GridBagConstraints();
|
|
GridBagLayout layoutLeftButtom = new GridBagLayout();
|
|
GridBagConstraints constraintsLeftButtom = new GridBagConstraints();
|
|
GridBagLayout layoutRight = new GridBagLayout();
|
|
GridBagConstraints constraintsRight = new GridBagConstraints();
|
|
|
|
JPanel left = new JPanel(new GridLayout(1,1));
|
|
JPanel right = new JPanel(layoutRight);
|
|
JPanel top = new JPanel(layoutLeftTop);
|
|
JPanel buttom = new JPanel(layoutLeftButtom);
|
|
|
|
// adding the player controls
|
|
constraintsRight.gridx = 0;
|
|
constraintsRight.gridy = 0;
|
|
constraintsRight.gridwidth = 1;
|
|
constraintsRight.gridheight = 1;
|
|
constraintsRight.weightx = 100;
|
|
constraintsRight.weighty = 100;
|
|
constraintsRight.fill = GridBagConstraints.BOTH;
|
|
layoutRight.setConstraints(PlayerPanel, constraintsRight);
|
|
right.add(PlayerPanel);
|
|
|
|
// adding the InfoPanel
|
|
constraintsRight.gridy = 1;
|
|
layoutRight.setConstraints(InfoPanel, constraintsRight);
|
|
right.add(InfoPanel);
|
|
|
|
// adding the trackpanel
|
|
constraintsLeftButtom.gridx = 0;
|
|
constraintsLeftButtom.gridy = 0;
|
|
constraintsLeftButtom.gridwidth = 1;
|
|
constraintsLeftButtom.gridheight = 1;
|
|
constraintsLeftButtom.weightx = 100;
|
|
constraintsLeftButtom.weighty = 100;
|
|
constraintsLeftButtom.fill = GridBagConstraints.BOTH;
|
|
layoutLeftButtom.setConstraints(trackScrollPane, constraintsLeftButtom);
|
|
buttom.add(trackScrollPane);
|
|
|
|
// adding the list
|
|
constraintsLeftTop.gridx = 0;
|
|
constraintsLeftTop.gridy = 0;
|
|
constraintsLeftTop.gridwidth = 1;
|
|
constraintsLeftTop.gridheight = 1;
|
|
constraintsLeftTop.weightx = 100;
|
|
constraintsLeftTop.weighty = 100;
|
|
constraintsLeftTop.fill = GridBagConstraints.BOTH;
|
|
layoutLeftTop.setConstraints(listScrollPane, constraintsLeftTop);
|
|
top.add(listScrollPane);
|
|
|
|
JSplitPane splitPaneLeftVertical = new JSplitPane(JSplitPane.VERTICAL_SPLIT, top, buttom);
|
|
splitPaneLeftVertical.setContinuousLayout(true);
|
|
left.add(splitPaneLeftVertical);
|
|
|
|
JSplitPane splitPaneHorizontal = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, left, right);
|
|
splitPaneHorizontal.setContinuousLayout(true);
|
|
splitPaneHorizontal.setDividerLocation(400);
|
|
|
|
playPanel.add(splitPaneHorizontal);
|
|
|
|
setInfoPanel();
|
|
setTrackPanel();
|
|
}
|
|
|
|
/*
|
|
2.2.2 stateChanged
|
|
|
|
Implementation of the ChangeListener belonging to the slider
|
|
|
|
*/
|
|
public void stateChanged(ChangeEvent e)
|
|
{
|
|
if(slider.getValueIsAdjusting())
|
|
{
|
|
mouseSliding = true;
|
|
}
|
|
if(!slider.getValueIsAdjusting())
|
|
{
|
|
if(sequencer.isOpen() && mouseSliding)
|
|
{
|
|
setPosition(slider.getValue());
|
|
mouseSliding = false;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
2.3.2 refreshSlider
|
|
|
|
Refreshes the slider
|
|
|
|
*/
|
|
private void refreshSlider()
|
|
{
|
|
if (sequencer.isOpen())
|
|
{
|
|
if (!mouseSliding)
|
|
{
|
|
slider.setValue((int) getPosition());
|
|
if (displayedTempo != getTempo())
|
|
{
|
|
setInfoPanel();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
2.2.2 refreshTime
|
|
|
|
Refreshes the time display
|
|
|
|
*/
|
|
private void refreshTime()
|
|
{
|
|
if (sequencer.isOpen())
|
|
{
|
|
time.setText(getCurrentTime() + " / " + lengthOfCurrentMidi);
|
|
}
|
|
}
|
|
/*
|
|
3.3.3 getIndexNo
|
|
|
|
Extracts the first three digits of a given ~String~ and returns them as
|
|
an ~Integer~ value. This function is used to get the track number when a
|
|
mute check box has changed its value by using only the check box name.
|
|
|
|
*/
|
|
private int getIndexNo(String s)
|
|
{
|
|
// by constructing the strings it is guarenteed that there are at
|
|
// least 3 Characters
|
|
char c1 = s.charAt(0);
|
|
char c2 = s.charAt(1);
|
|
char c3 = s.charAt(2);
|
|
int result = 0;
|
|
int firstDigit = (int) '0';
|
|
boolean b = Character.isDigit(c3);
|
|
|
|
// indicates which of the three characters contains is relevant
|
|
if (b)
|
|
{
|
|
result += (((int) c3) - firstDigit);
|
|
result += (((int) c2) - firstDigit)*10;
|
|
result += (((int) c1) - firstDigit)*100;
|
|
return result;
|
|
}
|
|
|
|
b = Character.isDigit(c2);
|
|
|
|
if (b)
|
|
{
|
|
result += (((int) c2) - firstDigit);
|
|
result += (((int) c1) - firstDigit)*10;
|
|
return result;
|
|
}
|
|
else
|
|
{
|
|
result += (((int) c1) - firstDigit);
|
|
}
|
|
return result;
|
|
}
|
|
/*
|
|
2.2.2 itemStateChanged
|
|
|
|
Implementation of the ItemListener belonging to the checkboxes of the
|
|
track panel
|
|
|
|
*/
|
|
public void itemStateChanged(ItemEvent e)
|
|
{
|
|
JCheckBox cb = (JCheckBox)e.getSource();
|
|
int index = getIndexNo(cb.getText());
|
|
|
|
if (e.getStateChange() == ItemEvent.SELECTED)
|
|
{
|
|
changeMute(index, false);
|
|
}
|
|
else
|
|
{
|
|
changeMute(index, true);
|
|
}
|
|
}
|
|
|
|
/*
|
|
5.5.5 load
|
|
|
|
Loads the sequence of the current MidiFile into the sequencer and
|
|
starts playing
|
|
|
|
*/
|
|
public boolean load()
|
|
{
|
|
if (sequencer.isOpen()) unload();
|
|
|
|
try { sequencer.open(); }
|
|
catch (MidiUnavailableException e)
|
|
{
|
|
Reporter.writeError("MidFile 5: Midi unavailable");
|
|
return false;
|
|
}
|
|
try { sequencer.setSequence(thisMidiFile.getSequence());}
|
|
catch (InvalidMidiDataException e)
|
|
{
|
|
Reporter.writeError("MidiFile 6: Invalid Mididfile");
|
|
return false;
|
|
}
|
|
/*
|
|
A fresh Midi starts at the beginning
|
|
|
|
*/
|
|
startAt = 0;
|
|
/*
|
|
The skipping amount is calculated
|
|
|
|
*/
|
|
skippy = sequencer.getTickLength() / 20;
|
|
/*
|
|
And finally the length of the Sequence is expressed as MM:SS
|
|
|
|
*/
|
|
long ticklength = sequencer.getMicrosecondLength();
|
|
ticklength = ticklength / 1000000;
|
|
long minutes = ticklength / 60;
|
|
ticklength = ticklength - minutes*60;
|
|
// ticklength -> now the remaining seconds
|
|
lengthOfCurrentMidi = Long.toString(minutes);
|
|
lengthOfCurrentMidi += ":";
|
|
if (ticklength < 10) { lengthOfCurrentMidi += "0"; }
|
|
lengthOfCurrentMidi += Long.toString(ticklength);
|
|
slider.setMaximum((int) getLengthInTicks());
|
|
setInfoPanel();
|
|
setMetaPanel();
|
|
return true;
|
|
}
|
|
/*
|
|
2.2.2 unload
|
|
|
|
Closes the sequencer and releases the captured resources
|
|
|
|
*/
|
|
public void unload()
|
|
{
|
|
if(sequencer.isOpen())
|
|
{
|
|
sequencer.stop();
|
|
sequencer.close();
|
|
}
|
|
startAt = 0;
|
|
thisMidiFile = null;
|
|
}
|
|
/*
|
|
2.2.2 play
|
|
|
|
Starts the sequencer. If it is already running the speed is set to
|
|
~normal~.
|
|
|
|
*/
|
|
public void play()
|
|
{
|
|
if (!sequencer.isRunning())
|
|
{
|
|
sequencer.setTickPosition(startAt);
|
|
sequencer.start();
|
|
}
|
|
else
|
|
{
|
|
sequencer.setTempoFactor(1.0f);
|
|
}
|
|
}
|
|
/*
|
|
2.2.2 stop
|
|
|
|
Stops the sequencer
|
|
|
|
*/
|
|
public void stop()
|
|
{
|
|
sequencer.stop();
|
|
sequencer.setTickPosition(0);
|
|
startAt = 0;
|
|
}
|
|
/*
|
|
3.3.3 halt
|
|
|
|
Stops the sequencer, but the current Position remains - pause function
|
|
|
|
*/
|
|
public void halt()
|
|
{
|
|
startAt = sequencer.getTickPosition();
|
|
sequencer.stop();
|
|
}
|
|
/*
|
|
2.2.2 fastforward
|
|
|
|
Increases the speed by factor 2
|
|
|
|
*/
|
|
public void fastforward()
|
|
{
|
|
if (sequencer.getTempoFactor() < 8)
|
|
{
|
|
sequencer.setTempoFactor(sequencer.getTempoFactor() * 2.0f);
|
|
}
|
|
}
|
|
/*
|
|
9.9.9 rewind
|
|
|
|
Decreases the speedfactor by 2
|
|
|
|
~Not really a~ rewind ~function but the opposite of fastforward.~
|
|
|
|
*/
|
|
public void rewind ()
|
|
{
|
|
if (sequencer.getTempoFactor() > 0.25)
|
|
{
|
|
sequencer.setTempoFactor(sequencer.getTempoFactor() * 0.5f);
|
|
}
|
|
}
|
|
/*
|
|
2.2.2 skipP
|
|
|
|
Increases the actual position by 1/10 of the whole
|
|
|
|
*/
|
|
public void skipP()
|
|
{
|
|
if((skippy+sequencer.getTickPosition())<getLengthInTicks())
|
|
{
|
|
sequencer.setTickPosition(sequencer.getTickPosition() + skippy);
|
|
}
|
|
}
|
|
|
|
/*
|
|
4.4.4 skipM
|
|
|
|
Decreases the actual position by 1/10 of the whole
|
|
|
|
*/
|
|
public void skipM()
|
|
{
|
|
if (skippy < sequencer.getTickPosition())
|
|
{
|
|
sequencer.setTickPosition(sequencer.getTickPosition() - skippy);
|
|
}
|
|
else
|
|
{
|
|
sequencer.setTickPosition(0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
6.6.6 setPosition
|
|
|
|
Sets the current position, necceassary for the slider
|
|
|
|
*/
|
|
|
|
public void setPosition(long tick)
|
|
{
|
|
sequencer.setTickPosition(tick);
|
|
}
|
|
|
|
/*
|
|
3.3.3 getPosition
|
|
|
|
Returns the current Position
|
|
|
|
*/
|
|
public long getPosition()
|
|
{
|
|
try
|
|
{
|
|
return sequencer.getTickPosition();
|
|
}
|
|
catch (NullPointerException e)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
/*
|
|
5.4.4 getLengthInTicks
|
|
|
|
Returns the length of the sequence in ticks
|
|
|
|
*/
|
|
public long getLengthInTicks()
|
|
{
|
|
return sequencer.getTickLength();
|
|
}
|
|
|
|
/*
|
|
2.2.2 getCurrentTime
|
|
|
|
Returns the current Position expressed as a ~String~ (MM:SS)
|
|
|
|
*/
|
|
public String getCurrentTime()
|
|
{
|
|
try
|
|
{
|
|
String mtime;
|
|
long ticklength = sequencer.getMicrosecondPosition();
|
|
ticklength = ticklength / 1000000;
|
|
long minutes = ticklength / 60;
|
|
ticklength = ticklength - minutes*60;
|
|
// length -> remaining seconds
|
|
mtime = Long.toString(minutes);
|
|
mtime += ":";
|
|
if (ticklength < 10) {mtime += "0";}
|
|
mtime += Long.toString(ticklength);
|
|
return mtime;
|
|
}
|
|
catch (NullPointerException e)
|
|
{
|
|
return "0:00";
|
|
}
|
|
}
|
|
|
|
/*
|
|
3.3.3 getTempoFactor
|
|
|
|
Returns the current tempo factor
|
|
|
|
*/
|
|
public String getTempoFactor()
|
|
{
|
|
try
|
|
{
|
|
return Float.toString(sequencer.getTempoFactor());
|
|
}
|
|
catch (NullPointerException e)
|
|
{
|
|
return "1.0";
|
|
}
|
|
}
|
|
|
|
/*
|
|
3.3.3 getTempo
|
|
|
|
Returns the tempo in BPM
|
|
|
|
*/
|
|
public float getTempo()
|
|
{
|
|
return (sequencer.getTempoInBPM() * sequencer.getTempoFactor());
|
|
}
|
|
|
|
/*
|
|
2.2.2 changeMute
|
|
|
|
Changes the mute state of target track
|
|
|
|
*/
|
|
|
|
public void changeMute(int track, boolean newMuteState)
|
|
{
|
|
if (track <= thisMidiFile.getNrTracks())
|
|
sequencer.setTrackMute(track-1, newMuteState);
|
|
}
|
|
|
|
/*
|
|
5.5.5 kickThem
|
|
|
|
Removes all objects with target ID from the playlist. Expecially needed
|
|
to remove all midis loaded from a relation, when the relation is removed.
|
|
|
|
*/
|
|
public void kickThem (String id)
|
|
{
|
|
if (thisMidiFile == null || thisMidiFile.getID().equals(id))
|
|
{
|
|
unload();
|
|
}
|
|
try
|
|
{
|
|
int midiVectorSize = midiVector.size();
|
|
// by removing elements, the vectors size is decreased
|
|
// to avoid NullPointerExceptions, the vector size must be updated
|
|
for (int i = 0; i < midiVectorSize; i++)
|
|
{
|
|
if (((MidiFile) midiVector.get(i)).getID().equals(id))
|
|
{
|
|
midiVector.removeElementAt(i);
|
|
i--;
|
|
midiVectorSize--;
|
|
}
|
|
}
|
|
list.setListData(midiVector);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Reporter.writeError("MidiViewer: incorrect Vectorhandling");
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
3.3 The Relation Viewer
|
|
|
|
3.3.3 createAndShowRelationGUI
|
|
|
|
The ~construcor~ of the relation tab. Adapted from the standard
|
|
relation viewer.
|
|
|
|
*/
|
|
public void createAndShowRelationGUI()
|
|
{
|
|
ComboBox = new JComboBox();
|
|
ScrollPane = new JScrollPane();
|
|
dummy = new JPanel();
|
|
CurrentTable = null;
|
|
Tables = new Vector();
|
|
relPanel.setLayout(new BorderLayout());
|
|
relPanel.add(ComboBox,BorderLayout.NORTH);
|
|
relPanel.add(ScrollPane,BorderLayout.CENTER);
|
|
ComboBox.addActionListener(new ActionListener()
|
|
{
|
|
public void actionPerformed(ActionEvent evt)
|
|
{
|
|
showSelectedObject();
|
|
}
|
|
});
|
|
}
|
|
|
|
/*
|
|
4.4.4 showSelectedObject
|
|
|
|
Shows the table of the selected relation
|
|
|
|
*/
|
|
private void showSelectedObject()
|
|
{
|
|
int index = ComboBox.getSelectedIndex();
|
|
|
|
if( index >= 0 )
|
|
{
|
|
CurrentTable = (JTable)Tables.get(index);
|
|
ScrollPane.setViewportView(CurrentTable);
|
|
}
|
|
else
|
|
ScrollPane.setViewportView(dummy);
|
|
}
|
|
|
|
/*
|
|
2.2.2 createTableFrom
|
|
|
|
Creates the table of a relation
|
|
|
|
*/
|
|
|
|
private JTable createTableFrom(ListExpr LE, String secondoName)
|
|
{
|
|
int midiPosition = -1;
|
|
boolean result = true;
|
|
JTable NTable = null;
|
|
Vector relMidis = new Vector();
|
|
|
|
if (LE.listLength()!=2)
|
|
return null;
|
|
else
|
|
{
|
|
ListExpr type = LE.first();
|
|
ListExpr value = LE.second();
|
|
ListExpr maintype = type.first();
|
|
// analyse type
|
|
if (type.listLength()!=2 || !maintype.isAtom() ||
|
|
maintype.atomType()!=ListExpr.SYMBOL_ATOM ||
|
|
!(maintype.symbolValue().equals("rel") |
|
|
maintype.symbolValue().equals("mrel"))) return null;
|
|
|
|
ListExpr tupletype = type.second();
|
|
ListExpr TupleFirst=tupletype.first();
|
|
|
|
if (tupletype.listLength()!=2 || !TupleFirst.isAtom() ||
|
|
TupleFirst.atomType()!=ListExpr.SYMBOL_ATOM ||
|
|
!(TupleFirst.symbolValue().equals("tuple") |
|
|
TupleFirst.symbolValue().equals("mtuple"))) return null;
|
|
|
|
ListExpr TupleTypeValue = tupletype.second();
|
|
String[] head=new String[TupleTypeValue.listLength()];
|
|
|
|
for(int i=0;!TupleTypeValue.isEmpty()&&result;i++)
|
|
{
|
|
ListExpr TupleSubType = TupleTypeValue.first();
|
|
|
|
if (TupleSubType.listLength()!=2) result = false;
|
|
else
|
|
{
|
|
head[i] = TupleSubType.first().writeListExprToString();
|
|
head[i] += " "+
|
|
TupleSubType.second().writeListExprToString();
|
|
|
|
if (TupleSubType.second().writeListExprToString().endsWith("midi"))
|
|
{
|
|
midiPosition = i;
|
|
}
|
|
}
|
|
TupleTypeValue = TupleTypeValue.rest();
|
|
}
|
|
|
|
if (result)
|
|
{
|
|
ListExpr TupleValue;
|
|
Vector V = new Vector();
|
|
String[] row;
|
|
int pos;
|
|
ListExpr Elem;
|
|
|
|
while (!value.isEmpty())
|
|
{
|
|
TupleValue = value.first();
|
|
row = new String[head.length];
|
|
pos = 0;
|
|
|
|
while(pos<head.length & !TupleValue.isEmpty())
|
|
{
|
|
Elem = TupleValue.first();
|
|
|
|
if (Elem.isAtom() && Elem.atomType()==ListExpr.STRING_ATOM)
|
|
{
|
|
row[pos] = Elem.stringValue();
|
|
}
|
|
else
|
|
if(Elem.isAtom() && Elem.atomType()==ListExpr.TEXT_ATOM)
|
|
{
|
|
if(Elem.textLength()<40)
|
|
{
|
|
row[pos] = Elem.textValue();
|
|
}
|
|
else
|
|
{
|
|
row[pos] = "a long text";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
row[pos] = TupleValue.first().writeListExprToString();
|
|
|
|
if (pos == midiPosition)
|
|
{
|
|
Base64Decoder b64;
|
|
StringReader sr = new StringReader(
|
|
TupleValue.first().first().textValue());
|
|
b64 = new Base64Decoder(sr);
|
|
MidiFile midi = new MidiFile(b64.getInputStream(),
|
|
( "Midi " + V.size() + " of Rel " +
|
|
secondoName), secondoName);
|
|
relMidis.addElement(midi);
|
|
row[pos] = midi.getName();
|
|
}
|
|
}
|
|
pos++;
|
|
TupleValue = TupleValue.rest();
|
|
}
|
|
V.add(row);
|
|
value = value.rest();
|
|
}
|
|
|
|
String[][] TableDatas = new String[V.size()][head.length];
|
|
for(int i=0;i<V.size();i++)
|
|
{
|
|
TableDatas[i]=(String[]) V.get(i);
|
|
}
|
|
midiTables.add(relMidis);
|
|
NTable = new JTable(TableDatas,head);
|
|
NTable.getSelectionModel().addListSelectionListener(this);
|
|
}
|
|
}
|
|
if(result)
|
|
return NTable;
|
|
else
|
|
return null;
|
|
}
|
|
|
|
/*
|
|
3.3.3 valueChanged
|
|
|
|
Implementation of the ListSelectionListener belonging to the relation
|
|
tables.
|
|
|
|
*/
|
|
public void valueChanged(ListSelectionEvent e)
|
|
{
|
|
if (!e.getValueIsAdjusting())
|
|
{
|
|
int last = CurrentTable.getSelectedRow();
|
|
MidiFile midi = (MidiFile)
|
|
((Vector)midiTables.get(ComboBox.getSelectedIndex())).get(last);
|
|
unload();
|
|
midiVector.addElement(midi);
|
|
list.setListData(midiVector);
|
|
thisMidiFile = midi;
|
|
load();
|
|
long l = getLengthInTicks();
|
|
length = Long.toString(l);
|
|
play();
|
|
slider.setMaximum((int) getLengthInTicks());
|
|
currentTracks = thisMidiFile.getTrackArray();
|
|
setTrackPanel();
|
|
setInfoPanel();
|
|
jtp.setSelectedIndex(0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
5.5.5 containsMidiObject
|
|
|
|
Checks wheter a relation contains a Midi or not
|
|
|
|
*/
|
|
|
|
public boolean containsMidiObject(SecondoObject o)
|
|
{
|
|
ListExpr LE = o.toListExpr();
|
|
boolean result = true;
|
|
if (LE.listLength()!=2)
|
|
return false;
|
|
else
|
|
{
|
|
ListExpr type = LE.first();
|
|
ListExpr value = LE.second();
|
|
ListExpr maintype = type.first();
|
|
|
|
if (type.listLength()!=2 || !maintype.isAtom() ||
|
|
maintype.atomType()!=ListExpr.SYMBOL_ATOM ||
|
|
!(maintype.symbolValue().equals("rel") |
|
|
maintype.symbolValue().equals("mrel"))) return false;
|
|
|
|
ListExpr tupletype = type.second();
|
|
ListExpr TupleFirst=tupletype.first();
|
|
|
|
if (tupletype.listLength()!=2 || !TupleFirst.isAtom() ||
|
|
TupleFirst.atomType()!=ListExpr.SYMBOL_ATOM ||
|
|
!(TupleFirst.symbolValue().equals("tuple") |
|
|
TupleFirst.symbolValue().equals("mtuple"))) return false;
|
|
|
|
ListExpr TupleTypeValue = tupletype.second();
|
|
String[] head=new String[TupleTypeValue.listLength()];
|
|
|
|
for(int i=0;!TupleTypeValue.isEmpty()&&result;i++)
|
|
{
|
|
ListExpr TupleSubType = TupleTypeValue.first();
|
|
|
|
if (TupleSubType.listLength()!=2) result = false;
|
|
else
|
|
{
|
|
head[i] = TupleSubType.first().writeListExprToString();
|
|
head[i] += " "+
|
|
TupleSubType.second().writeListExprToString();
|
|
|
|
if (TupleSubType.second().writeListExprToString().endsWith("midi"))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
TupleTypeValue = TupleTypeValue.rest();
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
2.2.2 getIndexOf
|
|
|
|
Returns the ComboBox index of selected relation
|
|
|
|
*/
|
|
private int getIndexOf(String S)
|
|
{
|
|
int count = ComboBox.getItemCount();
|
|
int pos = -1;
|
|
|
|
for( int i = 0; i < count; i++)
|
|
{
|
|
if( ((String)ComboBox.getItemAt(i)).equals(S))
|
|
{
|
|
pos = i;
|
|
}
|
|
}
|
|
return pos;
|
|
}
|
|
}
|