import javax.swing.JSplitPane;\r
import javax.swing.JTextField;\r
import javax.swing.JToggleButton;\r
+import javax.swing.SwingUtilities;\r
import javax.swing.border.Border;\r
import javax.swing.event.ChangeEvent;\r
import javax.swing.event.ChangeListener;\r
*/\r
public static class VersionInfo {\r
public static final String NAME = "MIDI Chord Helper";\r
- public static final String VERSION = "Ver.20131127.1";\r
+ public static final String VERSION = "Ver.20131128.1";\r
public static final String COPYRIGHT = "Copyright (C) 2004-2013";\r
public static final String AUTHER = "@きよし - Akiyoshi Kamide";\r
public static final String URL = "http://www.yk.rim.or.jp/~kamide/music/chordhelper/";\r
MidiDeviceDialog midiConnectionDialog;\r
MidiEditor editorDialog;\r
ChordDiagram chordDiagram;\r
- TempoSelecter tempoSelecter = new TempoSelecter() {\r
- { setEditable(false); }\r
- };\r
- TimeSignatureSelecter timesigSelecter = new TimeSignatureSelecter() {\r
- { setEditable(false); }\r
- };\r
- KeySignatureLabel keysigLabel = new KeySignatureLabel() {\r
- {\r
- addMouseListener(new MouseAdapter() {\r
- public void mousePressed(MouseEvent e) {\r
- chordMatrix.setKeySignature(keysigLabel.getKey());\r
- }\r
- });\r
- }\r
- };\r
+ TempoSelecter tempoSelecter;\r
+ TimeSignatureSelecter timesigSelecter;\r
+ KeySignatureLabel keysigLabel;\r
JLabel songTitleLabel = new JLabel();\r
//\r
// あの楽器\r
keyboardPanel.eventDialog = editorDialog.eventCellEditor.eventDialog;\r
midiConnectionDialog = new MidiDeviceDialog(deviceModelList);\r
midiConnectionDialog.setIconImage(iconImage);\r
- lyricDisplay = new ChordTextField() {{\r
- addActionListener(new ActionListener() {\r
- public void actionPerformed(ActionEvent event) {\r
- chordMatrix.setSelectedChord(\r
- event.getActionCommand().trim().split("[ \t\r\n]")[0]\r
- );\r
- }\r
- });\r
- }};\r
+ lyricDisplay = new ChordTextField() {\r
+ {\r
+ deviceModelList.sequencerModel.getSequencer().addMetaEventListener(this);\r
+ addActionListener(\r
+ new ActionListener() {\r
+ public void actionPerformed(ActionEvent event) {\r
+ chordMatrix.setSelectedChord(\r
+ event.getActionCommand().trim().split("[ \t\r\n]")[0]\r
+ );\r
+ }\r
+ }\r
+ );\r
+ }\r
+ };\r
lyricDisplayDefaultBorder = lyricDisplay.getBorder();\r
lyricDisplayDefaultBgcolor = lyricDisplay.getBackground();\r
//\r
//\r
chordDiagram = new ChordDiagram(this);\r
//\r
- // MIDI parts\r
+ // MetaEvent listeners\r
//\r
+ tempoSelecter = new TempoSelecter() {{\r
+ setEditable(false);\r
+ deviceModelList.sequencerModel.getSequencer().addMetaEventListener(this);\r
+ }};\r
+ timesigSelecter = new TimeSignatureSelecter() {{\r
+ setEditable(false);\r
+ deviceModelList.sequencerModel.getSequencer().addMetaEventListener(this);\r
+ }};\r
+ keysigLabel = new KeySignatureLabel() {{\r
+ addMouseListener(new MouseAdapter() {\r
+ public void mousePressed(MouseEvent e) {\r
+ chordMatrix.setKeySignature(getKey());\r
+ }\r
+ });\r
+ }};\r
deviceModelList.sequencerModel.getSequencer().addMetaEventListener(\r
new MetaEventListener() {\r
+ class SetKeySignatureRunnable implements Runnable {\r
+ Music.Key key;\r
+ public SetKeySignatureRunnable(Music.Key key) {\r
+ this.key = key;\r
+ }\r
+ @Override\r
+ public void run() { setKeySignature(key); }\r
+ }\r
@Override\r
public void meta(MetaMessage msg) {\r
switch(msg.getType()) {\r
- case 0x01: // Text(任意のテキスト:コメントなど)\r
- case 0x02: // Copyright(著作権表示)\r
- case 0x05: // Lyrics(歌詞)\r
- case 0x06: // Marker\r
- case 0x03: // Sequence Name / Track Name(曲名またはトラック名)\r
- lyricDisplay.addLyric(msg.getData());\r
- break;\r
- case 0x51: // Tempo (3 bytes) - テンポ\r
- tempoSelecter.setTempo(msg.getData());\r
- break;\r
- case 0x58: // Time signature (4 bytes) - 拍子\r
- timesigSelecter.setValue(msg.getData());\r
- break;\r
case 0x59: // Key signature (2 bytes) : 調号\r
Music.Key key = new Music.Key(msg.getData());\r
- keysigLabel.setKeySignature(key);\r
- chordMatrix.setKeySignature(key);\r
+ if( ! SwingUtilities.isEventDispatchThread() ) {\r
+ SwingUtilities.invokeLater(\r
+ new SetKeySignatureRunnable(key)\r
+ );\r
+ }\r
+ setKeySignature(key);\r
break;\r
}\r
}\r
+ private void setKeySignature(Music.Key key) {\r
+ keysigLabel.setKeySignature(key);\r
+ chordMatrix.setKeySignature(key);\r
+ }\r
}\r
);\r
+ //シーケンサーの時間スライダーの値が変わったときのリスナーを登録\r
deviceModelList.sequencerModel.addChangeListener(new ChangeListener() {\r
@Override\r
public void stateChanged(ChangeEvent e) {\r
}\r
}\r
\r
-class ChordTextField extends JTextField {\r
+class ChordTextField extends JTextField implements MetaEventListener {\r
Music.Chord currentChord = null;\r
private long lyricArrivedTime = System.nanoTime();\r
public ChordTextField() {\r
java.awt.Toolkit.getDefaultToolkit().getScreenSize()\r
);\r
}\r
- public void appendChord(Music.Chord chord) {\r
- if( currentChord == null && chord == null )\r
- return;\r
- if( currentChord != null && chord != null && chord.equals(currentChord) )\r
- return;\r
- String delimiter = ""; // was "\n"\r
- setText( getText() + (chord == null ? delimiter : chord + " ") );\r
- currentChord = ( chord == null ? null : chord.clone() );\r
+ @Override\r
+ public void meta(MetaMessage msg) {\r
+ switch(msg.getType()) {\r
+ case 0x01: // Text(任意のテキスト:コメントなど)\r
+ case 0x02: // Copyright(著作権表示)\r
+ case 0x05: // Lyrics(歌詞)\r
+ case 0x06: // Marker\r
+ case 0x03: // Sequence Name / Track Name(曲名またはトラック名)\r
+ byte[] data = msg.getData();\r
+ if( ! SwingUtilities.isEventDispatchThread() ) {\r
+ // MIDIシーケンサの EDT から呼ばれた場合、\r
+ // 表示処理を Swing の EDT に振り直す。\r
+ SwingUtilities.invokeLater(new AddLyricJob(data));\r
+ break;\r
+ }\r
+ addLyric(data);\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+ }\r
+ /**\r
+ * 歌詞を追加するジョブ\r
+ */\r
+ private class AddLyricJob implements Runnable {\r
+ private byte[] data;\r
+ public AddLyricJob(byte[] data) { this.data = data; }\r
+ @Override\r
+ public void run() { addLyric(data); }\r
}\r
+ /**\r
+ * 歌詞を追加し、カーソルを末尾に移動します。\r
+ * @param data 歌詞データ\r
+ */\r
public void addLyric(byte[] data) {\r
long startTime = System.nanoTime();\r
// 歌詞を表示\r
) {\r
// 長い歌詞や空白が来たり、追加先に歌詞がなかった場合は上書きする。\r
// ただし、前回から充分に時間が経っていない場合は上書きしない。\r
- setText(additionalLyric);\r
+ lyric = additionalLyric;\r
}\r
else {\r
// 短い歌詞だった場合は、既存の歌詞に追加する\r
- setText( lyric + " " + additionalLyric );\r
+ lyric += " " + additionalLyric;\r
}\r
+ setText(lyric);\r
setCaretPosition(getText().length());\r
lyricArrivedTime = startTime;\r
}\r
+ /**\r
+ * コードを追加します。\r
+ * @param chord コード\r
+ */\r
+ public void appendChord(Music.Chord chord) {\r
+ if( currentChord == null && chord == null )\r
+ return;\r
+ if( currentChord != null && chord != null && chord.equals(currentChord) )\r
+ return;\r
+ String delimiter = ""; // was "\n"\r
+ setText( getText() + (chord == null ? delimiter : chord + " ") );\r
+ currentChord = ( chord == null ? null : chord.clone() );\r
+ }\r
}\r
\r
\r
}\r
}\r
\r
+ SequenceTrackListTableModel sequenceTrackListTableModel;\r
/**\r
* MIDIトラック選択状態\r
*/\r
- private ListSelectionModel trackSelectionModel = new DefaultListSelectionModel() {{\r
- setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);\r
- addListSelectionListener(new ListSelectionListener() {\r
- @Override\r
- public void valueChanged(ListSelectionEvent e) {\r
- if( e.getValueIsAdjusting() )\r
- return;\r
- SequenceTrackListTableModel sequenceModel =\r
- sequenceListTableModel.getSelectedSequenceModel();\r
- if( sequenceModel == null || isSelectionEmpty() ) {\r
- trackEventListTableView.setModel(new TrackEventListTableModel());\r
- }\r
- else {\r
- int selIndex = getMinSelectionIndex();\r
- TrackEventListTableModel trackModel = sequenceModel.getTrackModel(selIndex);\r
- if( trackModel == null ) {\r
- trackEventListTableView.setModel(new TrackEventListTableModel());\r
- }\r
- else {\r
- trackEventListTableView.setModel(trackModel);\r
- TableColumnModel tcm = trackEventListTableView.getColumnModel();\r
- trackModel.sizeColumnWidthToFit(tcm);\r
- TableColumn midiMessageColumn = tcm.getColumn(TrackEventListTableModel.Column.MESSAGE.ordinal());\r
- midiMessageColumn.setCellEditor(eventCellEditor);\r
- }\r
- }\r
- updateButtonStatus();\r
- }\r
- });\r
- }};\r
+ ListSelectionModel trackSelectionModel;\r
/**\r
* トラック追加アクション\r
*/\r
public void actionPerformed(ActionEvent e) {\r
if( ! confirm("Do you want to delete selected track ?\n選択したトラックを削除しますか?"))\r
return;\r
- sequenceListTableModel.getSelectedSequenceModel().deleteTracks(trackSelectionModel);\r
+ sequenceListTableModel.getSelectedSequenceModel().deleteTracks(\r
+ trackSelectionModel\r
+ );\r
}\r
};\r
/**\r
/**\r
* MIDIイベントリストテーブルビュー\r
*/\r
- private JTable trackEventListTableView = new JTable(\r
- new TrackEventListTableModel(),\r
- null,\r
- eventSelectionModel\r
- );\r
- private MidiEventsLabel midiEventsLabel = new MidiEventsLabel();\r
+ private JTable trackEventListTableView;\r
private class MidiEventsLabel extends JLabel implements ListSelectionListener {\r
private static final String TITLE = "MIDI Events";\r
public MidiEventsLabel() {\r
/**\r
* スクロール可能なMIDIイベントテーブルビュー\r
*/\r
- private JScrollPane scrollableEventTableView = new JScrollPane(trackEventListTableView);\r
+ private JScrollPane scrollableEventTableView;\r
/**\r
* 指定の MIDI tick のイベントへスクロールします。\r
* @param tick MIDI tick\r
*/\r
public MidiEditor(MidiSequencerModel sequencerModel) {\r
sequenceListTableModel = new SequenceListTableModel(sequencerModel) ;\r
+ sequenceTrackListTableModel = new SequenceTrackListTableModel(sequenceListTableModel);\r
setTitle("MIDI Editor/Playlist - MIDI Chord Helper");\r
setBounds( 150, 200, 850, 500 );\r
setLayout(new FlowLayout());\r
});\r
add(Box.createRigidArea(new Dimension(0, 5)));\r
add(new JScrollPane(trackListTableView = new JTable(\r
- new SequenceTrackListTableModel(sequenceListTableModel),\r
- null, trackSelectionModel\r
+ sequenceTrackListTableModel, null,\r
+ trackSelectionModel = new DefaultListSelectionModel() {{\r
+ setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);\r
+ addListSelectionListener(new ListSelectionListener() {\r
+ @Override\r
+ public void valueChanged(ListSelectionEvent e) {\r
+ if( e.getValueIsAdjusting() )\r
+ return;\r
+ SequenceTrackListTableModel sequenceModel =\r
+ sequenceListTableModel.getSelectedSequenceModel();\r
+ if( sequenceModel == null || isSelectionEmpty() ) {\r
+ trackEventListTableView.setModel(new TrackEventListTableModel());\r
+ }\r
+ else {\r
+ TrackEventListTableModel trackModel = sequenceModel.getTrackModel(getMinSelectionIndex());\r
+ if( trackModel == null ) {\r
+ trackEventListTableView.setModel(new TrackEventListTableModel());\r
+ }\r
+ else {\r
+ trackEventListTableView.setModel(trackModel);\r
+ TableColumnModel tcm = trackEventListTableView.getColumnModel();\r
+ trackModel.sizeColumnWidthToFit(tcm);\r
+ TableColumn midiMessageColumn = tcm.getColumn(TrackEventListTableModel.Column.MESSAGE.ordinal());\r
+ midiMessageColumn.setCellEditor(eventCellEditor);\r
+ }\r
+ }\r
+ updateButtonStatus();\r
+ }\r
+ });\r
+ }}\r
) {{\r
// 録音対象のMIDIチャンネルをコンボボックスで選択できるよう、\r
// セルエディタを差し替える。\r
}});\r
}};\r
JPanel eventListPanel = new JPanel() {{\r
- add(midiEventsLabel);\r
- add(scrollableEventTableView);\r
+ add(new MidiEventsLabel());\r
+ add(scrollableEventTableView = new JScrollPane(\r
+ trackEventListTableView = new JTable(\r
+ new TrackEventListTableModel(),\r
+ null,\r
+ eventSelectionModel\r
+ ) {{\r
+ setAutoCreateColumnsFromModel(false);\r
+ }}\r
+ ));\r
add(new JPanel() {{\r
add(pairNoteCheckbox);\r
add(new JButton(eventCellEditor.queryJumpEventAction) {{\r
* ボタン状態の更新\r
*/\r
public void updateButtonStatus() {\r
- SequenceTrackListTableModel sequenceModel =\r
- sequenceListTableModel.getSelectedSequenceModel();\r
- boolean isSequenceSelected = (sequenceModel != null);\r
- if(isSequenceSelected) {\r
- trackListTableView.setModel(sequenceModel);\r
- }\r
- else {\r
+ SequenceTrackListTableModel\r
+ sequenceModel = sequenceListTableModel.getSelectedSequenceModel();\r
+ if(sequenceModel == null)\r
trackListTableView.setModel(new SequenceTrackListTableModel(sequenceListTableModel));\r
- }\r
- addTrackAction.setEnabled(isSequenceSelected);\r
+ else\r
+ trackListTableView.setModel(sequenceModel);\r
+ addTrackAction.setEnabled(sequenceModel != null);\r
boolean isTrackSelected = (\r
! trackSelectionModel.isSelectionEmpty()\r
&&\r
- isSequenceSelected && sequenceModel.getRowCount() > 0\r
+ sequenceModel != null && sequenceModel.getRowCount() > 0\r
);\r
deleteTrackAction.setEnabled(isTrackSelected);\r
//\r
import java.util.ArrayList;\r
\r
import javax.sound.midi.InvalidMidiDataException;\r
+import javax.sound.midi.MetaEventListener;\r
import javax.sound.midi.MetaMessage;\r
import javax.sound.midi.MidiChannel;\r
import javax.sound.midi.MidiMessage;\r
import javax.swing.JTextArea;\r
import javax.swing.ListCellRenderer;\r
import javax.swing.SpinnerNumberModel;\r
+import javax.swing.SwingUtilities;\r
import javax.swing.event.ChangeEvent;\r
import javax.swing.event.ChangeListener;\r
import javax.swing.event.ListDataEvent;\r
/**\r
* テンポ選択(QPM: Quarter Per Minute)\r
*/\r
-class TempoSelecter extends JPanel implements MouseListener {\r
+class TempoSelecter extends JPanel implements MouseListener, MetaEventListener {\r
static final int DEFAULT_QPM = 120;\r
protected SpinnerNumberModel tempoSpinnerModel =\r
new SpinnerNumberModel(DEFAULT_QPM, 1, 999, 1);\r
tempoLabel.addMouseListener(this);\r
}\r
private long prevBeatMicrosecondPosition = 0;\r
+ private class SetTempoRunnable implements Runnable {\r
+ byte[] qpm;\r
+ public SetTempoRunnable(byte[] qpm) { this.qpm = qpm; }\r
+ @Override\r
+ public void run() { setTempo(qpm);}\r
+ }\r
+ @Override\r
+ public void meta(MetaMessage msg) {\r
+ switch(msg.getType()) {\r
+ case 0x51: // Tempo (3 bytes) - テンポ\r
+ if( ! SwingUtilities.isEventDispatchThread() ) {\r
+ SwingUtilities.invokeLater(new SetTempoRunnable(msg.getData()));\r
+ break;\r
+ }\r
+ setTempo(msg.getData());\r
+ break;\r
+ }\r
+ }\r
@Override\r
public void mousePressed(MouseEvent e) {\r
Component obj = e.getComponent();\r
/**\r
* 拍子選択ビュー\r
*/\r
-class TimeSignatureSelecter extends JPanel {\r
+class TimeSignatureSelecter extends JPanel implements MetaEventListener {\r
SpinnerNumberModel upperTimesigSpinnerModel = new SpinnerNumberModel(4, 1, 32, 1);\r
private JSpinner upperTimesigSpinner = new JSpinner(\r
upperTimesigSpinnerModel\r
setSelectedIndex(2);\r
}\r
};\r
+ private class SetValueRunnable implements Runnable {\r
+ byte[] qpm;\r
+ public SetValueRunnable(byte[] qpm) { this.qpm = qpm; }\r
+ @Override\r
+ public void run() { setValue(qpm);}\r
+ }\r
+ @Override\r
+ public void meta(MetaMessage msg) {\r
+ switch(msg.getType()) {\r
+ case 0x58: // Time signature (4 bytes) - 拍子\r
+ if( ! SwingUtilities.isEventDispatchThread() ) {\r
+ SwingUtilities.invokeLater(new SetValueRunnable(msg.getData()));\r
+ break;\r
+ }\r
+ setValue(msg.getData());\r
+ break;\r
+ }\r
+ }\r
private class TimeSignatureLabel extends JLabel {\r
private byte upper = -1;\r
private byte lower_index = -1;\r
import javax.swing.Icon;\r
import javax.swing.JLabel;\r
import javax.swing.JPanel;\r
+import javax.swing.SwingUtilities;\r
import javax.swing.event.ChangeEvent;\r
import javax.swing.event.ChangeListener;\r
import javax.swing.event.EventListenerList;\r
/**\r
* MIDIシーケンサモデル\r
*/\r
-class MidiSequencerModel extends MidiConnecterListModel implements BoundedRangeModel {\r
+class MidiSequencerModel extends MidiConnecterListModel\r
+ implements BoundedRangeModel, MetaEventListener\r
+{\r
/**\r
* MIDIシーケンサモデルを構築します。\r
* @param deviceModelList 親のMIDIデバイスモデルリスト\r
) {\r
super(sequencer, modelList);\r
this.deviceModelList = deviceModelList;\r
- sequencer.addMetaEventListener(new MetaEventListener() {\r
- /**\r
- * {@inheritDoc}\r
- *\r
- * この実装では EOT (End Of Track、type==0x2F) を受信したときに、\r
- * 曲の先頭に戻し、次の曲があればその曲を再生し、\r
- * なければ秒位置更新タイマーを停止します。\r
- */\r
- @Override\r
- public void meta(MetaMessage msg) {\r
- if( msg.getType() == 0x2F ) {\r
- getSequencer().setMicrosecondPosition(0);\r
- // リピートモードの場合、同じ曲をもう一度再生する。\r
- // そうでない場合、次の曲へ進んで再生する。\r
- // 次の曲がなければ、そこで終了。\r
- boolean isRepeatMode = (Boolean)toggleRepeatAction.getValue(Action.SELECTED_KEY);\r
- if( isRepeatMode || loadNext() ) {\r
- start();\r
- }\r
- else {\r
- stop();\r
- }\r
- }\r
- }\r
- });\r
+ sequencer.addMetaEventListener(this);\r
}\r
/**\r
* MIDIデバイスモデルリスト\r
*/\r
private MidiDeviceModelList deviceModelList;\r
- private boolean loadNext() {\r
- return deviceModelList.editorDialog.sequenceListTableModel.loadNext(1);\r
+ /**\r
+ * {@inheritDoc}\r
+ *\r
+ * <p>EOT (End Of Track、type==0x2F) を受信したときの処理です。\r
+ * 通常はシーケンサの EDT から起動されるため、\r
+ * Swing の EDT へ振りなおします。\r
+ * </p>\r
+ */\r
+ @Override\r
+ public void meta(MetaMessage msg) {\r
+ if( msg.getType() == 0x2F ) {\r
+ if( ! SwingUtilities.isEventDispatchThread() ) {\r
+ SwingUtilities.invokeLater(new Runnable() {\r
+ @Override\r
+ public void run() { goNext(); }\r
+ });\r
+ return;\r
+ }\r
+ goNext();\r
+ }\r
+ }\r
+ /**\r
+ * 次の曲へ進みます。\r
+ *\r
+ * <p>リピートモードの場合は同じ曲をもう一度再生、\r
+ * そうでない場合は次の曲へ進んで再生します。\r
+ * 次の曲がなければ、そこで停止します。\r
+ * いずれの場合も局の先頭へ戻ります。\r
+ * </p>\r
+ */\r
+ private void goNext() {\r
+ getSequencer().setMicrosecondPosition(0);\r
+ boolean isRepeatMode = (Boolean)toggleRepeatAction.getValue(Action.SELECTED_KEY);\r
+ if( isRepeatMode || deviceModelList.editorDialog.sequenceListTableModel.loadNext(1) ) {\r
+ start();\r
+ }\r
+ else {\r
+ stop();\r
+ }\r
}\r
/**\r
* このシーケンサーの再生スピード調整モデル\r
import java.net.URL;\r
import java.util.Enumeration;\r
import java.util.Iterator;\r
+import java.util.List;\r
import java.util.Vector;\r
\r
import javax.swing.JFrame;\r
import javax.swing.JLabel;\r
+import javax.swing.SwingUtilities;\r
import javax.swing.WindowConstants;\r
import javax.swing.event.ChangeEvent;\r
import javax.swing.event.ChangeListener;\r
* MIDI Chord Helper を Java アプリとして起動します。\r
*/\r
public class MidiChordHelper {\r
- static int count = 0;\r
- static AppletFrame frame = null;\r
/**\r
* MIDI Chord Helper を Java アプリとして起動します。\r
* @param args コマンドライン引数\r
* @throws Exception 何らかの異常が発生した場合にスローされる\r
*/\r
public static void main(String[] args) throws Exception {\r
- ChordHelperApplet applet;\r
- if( count++ > 0 && frame != null) {\r
- applet = frame.applet;\r
- int windowState = frame.getExtendedState();\r
- if( ( windowState & Frame.ICONIFIED ) == 0 ) {\r
- frame.toFront();\r
- } else {\r
- frame.setExtendedState(windowState &= ~(Frame.ICONIFIED));\r
- }\r
- } else {\r
- frame = new AppletFrame(applet = new ChordHelperApplet());\r
- }\r
+ List<File> fileList = new Vector<File>();\r
if( args.length > 0 ) {\r
- Vector<File> fileList = new Vector<File>();\r
for( String arg : args ) fileList.add(new File(arg));\r
+ }\r
+ SwingUtilities.invokeLater(new AppletRunnable(fileList));\r
+ }\r
+ static int count = 0;\r
+ static AppletFrame frame = null;\r
+ private static class AppletRunnable implements Runnable {\r
+ private List<File> fileList;\r
+ public AppletRunnable(List<File> fileList) {\r
+ this.fileList = fileList;\r
+ }\r
+ @Override\r
+ public void run() {\r
+ ChordHelperApplet applet;\r
+ if( count++ > 0 && frame != null) {\r
+ applet = frame.applet;\r
+ int windowState = frame.getExtendedState();\r
+ if( ( windowState & Frame.ICONIFIED ) == 0 ) {\r
+ frame.toFront();\r
+ } else {\r
+ frame.setExtendedState(windowState &= ~(Frame.ICONIFIED));\r
+ }\r
+ } else {\r
+ frame = new AppletFrame(applet = new ChordHelperApplet());\r
+ }\r
applet.editorDialog.loadAndPlay(fileList);\r
}\r
}\r
VirtualMidiDevice midiDevice = new AbstractVirtualMidiDevice() {\r
{\r
info = new MyInfo();\r
- setReceiver( new AbstractMidiStatus() {\r
- {\r
- for( int i=0; i<MIDISpec.MAX_CHANNELS; i++ )\r
- add(new MidiChannelStatus(i));\r
+ setReceiver(\r
+ new AbstractMidiStatus() {\r
+ {\r
+ for( int i=0; i<MIDISpec.MAX_CHANNELS; i++ )\r
+ add(new MidiChannelStatus(i));\r
+ }\r
}\r
- });\r
+ );\r
}\r
class MyInfo extends Info {\r
protected MyInfo() {\r
- super(\r
- "MIDI Keyboard",\r
- "Unknown vendor",\r
- "Software MIDI keyboard",\r
- ""\r
- );\r
+ super("MIDI Keyboard","Unknown vendor","Software MIDI keyboard","");\r
}\r
}\r
};\r
}\r
private void repaintNotes() {\r
if( midiChComboboxModel.getSelectedChannel() != channel\r
- || channelNotes[channel] == null\r
- )\r
+ || channelNotes[channel] == null\r
+ )\r
return;\r
if( channelNotes[channel].size() > 0 || selectedKeyNoteList.size() > 0 )\r
repaint();\r
public MidiChannel getSelectedChannel() {\r
return midiDevice.getChannels()[midiChComboboxModel.getSelectedChannel()];\r
}\r
- public void note(boolean is_on, int note_no) {\r
+ public void note(boolean isOn, int noteNumber) {\r
MidiChannel ch = getSelectedChannel();\r
int velocity = velocityModel.getValue();\r
- if( is_on )\r
- ch.noteOn(note_no,velocity);\r
+ if( isOn )\r
+ ch.noteOn(noteNumber,velocity);\r
else\r
- ch.noteOff(note_no,velocity);\r
+ ch.noteOff(noteNumber,velocity);\r
}\r
- public void noteOn(int note_no) { note(true,note_no); }\r
- public void noteOff(int note_no) { note(false,note_no); }\r
+ public void noteOn(int noteNumber) { note(true,noteNumber); }\r
+ public void noteOff(int noteNumber) { note(false,noteNumber); }\r
\r
class PianoKey extends Rectangle {\r
public boolean isBlack = false;\r
int octaves = getPerferredOctaves();\r
octaveSizeModel = new DefaultBoundedRangeModel(\r
octaves, 0, MIN_OCTAVES, MAX_OCTAVES\r
- );\r
- octaveSizeModel.addChangeListener( new ChangeListener() {\r
- public void stateChanged(ChangeEvent e) {\r
- fireOctaveResized(e);\r
- octaveSizeChanged();\r
- }\r
- });\r
+ ) {{\r
+ addChangeListener(new ChangeListener() {\r
+ public void stateChanged(ChangeEvent e) {\r
+ fireOctaveResized(e);\r
+ octaveSizeChanged();\r
+ }\r
+ });\r
+ }};\r
octaveRangeModel = new DefaultBoundedRangeModel(\r
(MAX_OCTAVES - octaves) / 2, octaves, 0, MAX_OCTAVES\r
- );\r
- octaveRangeModel.addChangeListener( new ChangeListener() {\r
- public void stateChanged(ChangeEvent e) {\r
- fireOctaveMoved(e);\r
- checkOutOfBounds();\r
- repaint();\r
- }\r
- });\r
+ ) {{\r
+ addChangeListener(new ChangeListener() {\r
+ public void stateChanged(ChangeEvent e) {\r
+ fireOctaveMoved(e);\r
+ checkOutOfBounds();\r
+ repaint();\r
+ }\r
+ });\r
+ }};\r
addComponentListener( new ComponentAdapter() {\r
public void componentResized(ComponentEvent e) {\r
octaveSizeModel.setValue( getPerferredOctaves() );\r
if( ch != midiChComboboxModel.getSelectedChannel() )\r
return;\r
selectedKeyNoteList.add(note_no);\r
- int max_sel = (chord == null ? maxSelectable : chord.numberOfNotes());\r
- while( selectedKeyNoteList.size() > max_sel )\r
+ int maxSel = (chord == null ? maxSelectable : chord.numberOfNotes());\r
+ while( selectedKeyNoteList.size() > maxSel )\r
selectedKeyNoteList.poll();\r
if( !autoScroll(note_no) ) {\r
// When autoScroll() returned false, stateChanged() not invoked - need repaint()\r
Integer[] getSelectedNotes() {\r
return selectedKeyNoteList.toArray(new Integer[0]);\r
}\r
- //\r
Music.Chord getChord() { return chord; }\r
void setChord(Music.Chord c) {\r
chordDisplay.setChord(chord = c);\r
keySignature = ks;\r
repaint();\r
}\r
- //\r
private int maxSelectable = 1;\r
void setMaxSelectable( int max_selectable ) {\r
this.maxSelectable = max_selectable;\r
}\r
int getMaxSelectable() { return maxSelectable; }\r
- //\r
int getChromaticOffset() {\r
- return octaveRangeModel.getValue() * 12 ;\r
- }\r
- int getOctaves() {\r
- return octaveSizeModel.getValue();\r
+ return octaveRangeModel.getValue() * Music.SEMITONES_PER_OCTAVE ;\r
}\r
+ int getOctaves() { return octaveSizeModel.getValue(); }\r
private int getPerferredOctaves() {\r
int octaves = Math.round( (float)getWidth() / widthPerOctave );\r
if( octaves > MAX_OCTAVES ) {\r
}\r
}\r
\r
-class PianoKeyboardPanel extends JPanel\r
-{\r
- PianoKeyboard keyboard = new PianoKeyboard();\r
- private JSlider octaveSizeSlider = new JSlider() {\r
- {\r
- setToolTipText("Octave size");\r
- }\r
- };\r
- private JScrollBar octaveSelecter = new JScrollBar(JScrollBar.HORIZONTAL) {\r
- {\r
- setToolTipText("Octave position");\r
- }\r
- };\r
- private JPanel octaveBar = new JPanel() {\r
- {\r
- setLayout(new BoxLayout(this, BoxLayout.X_AXIS));\r
- }\r
- };\r
+class PianoKeyboardPanel extends JPanel {\r
+ PianoKeyboard keyboard;\r
+ private JScrollBar octaveSelecter;\r
+ private JSlider octaveSizeSlider;\r
+ private JPanel octaveBar;\r
public PianoKeyboardPanel() {\r
- keyboard.addPianoKeyboardListener(\r
- new PianoKeyboardAdapter() {\r
- public void octaveResized(ChangeEvent e) {\r
- octaveSelecter.setBlockIncrement(keyboard.getOctaves());\r
- }\r
- }\r
- );\r
- octaveSelecter.setModel( keyboard.octaveRangeModel );\r
- octaveSelecter.setBlockIncrement( keyboard.getOctaves() );\r
- octaveSizeSlider.setModel( keyboard.octaveSizeModel );\r
- octaveSizeSlider.setMinimumSize( new Dimension( 100, 18 ) );\r
- octaveSizeSlider.setMaximumSize( new Dimension( 100, 18 ) );\r
- octaveSizeSlider.setPreferredSize( new Dimension( 100, 18 ) );\r
- octaveBar.add(octaveSelecter);\r
- octaveBar.add(Box.createHorizontalStrut(5));\r
- octaveBar.add(octaveSizeSlider);\r
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));\r
- add(octaveBar);\r
+ keyboard = new PianoKeyboard() {{\r
+ addPianoKeyboardListener(\r
+ new PianoKeyboardAdapter() {\r
+ public void octaveResized(ChangeEvent e) {\r
+ octaveSelecter.setBlockIncrement(getOctaves());\r
+ }\r
+ }\r
+ );\r
+ }};\r
+ add(octaveBar = new JPanel() {{\r
+ setLayout(new BoxLayout(this, BoxLayout.X_AXIS));\r
+ add(octaveSelecter = new JScrollBar(JScrollBar.HORIZONTAL) {\r
+ {\r
+ setToolTipText("Octave position");\r
+ setModel(keyboard.octaveRangeModel);\r
+ setBlockIncrement(keyboard.getOctaves());\r
+ }\r
+ });\r
+ add(Box.createHorizontalStrut(5));\r
+ add(octaveSizeSlider = new JSlider() {\r
+ {\r
+ setToolTipText("Octave size");\r
+ setModel(keyboard.octaveSizeModel);\r
+ setMinimumSize(new Dimension(100, 18));\r
+ setMaximumSize(new Dimension(100, 18));\r
+ setPreferredSize(new Dimension(100, 18));\r
+ }\r
+ });\r
+ }});\r
add(keyboard);\r
setAlignmentX((float)0.5);\r
}\r
add( keyboardSouthPanel );\r
}\r
\r
- // Methods\r
- //\r
public void setDarkMode(boolean isDark) {\r
Color col = isDark ? Color.black : null;\r
setBackground(col);\r