OSDN Git Service

Swing の invokeLater() を使って Sequencer EDT などから Swing EDTへ振り直すようにした
authorAkiyoshi Kamide <kamide@yk.rim.or.jp>
Wed, 27 Nov 2013 17:16:17 +0000 (17:16 +0000)
committerAkiyoshi Kamide <kamide@yk.rim.or.jp>
Wed, 27 Nov 2013 17:16:17 +0000 (17:16 +0000)
git-svn-id: https://svn.sourceforge.jp/svnroot/midichordhelper/MIDIChordHelper@18 302f1594-2db2-43b1-aaa4-6307b5a2a2de

src/ChordHelperApplet.java
src/MIDIEditor.java
src/MIDIMsgForm.java
src/MIDISequencer.java
src/MidiChordHelper.java
src/PianoKeyboard.java

index f1d6d4f..7c76db0 100644 (file)
@@ -44,6 +44,7 @@ import javax.swing.JSlider;
 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
@@ -251,7 +252,7 @@ public class ChordHelperApplet extends JApplet {
         */\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
@@ -351,21 +352,9 @@ public class ChordHelperApplet extends JApplet {
        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
@@ -445,15 +434,20 @@ public class ChordHelperApplet extends JApplet {
                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
@@ -461,35 +455,54 @@ public class ChordHelperApplet extends JApplet {
                //\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
@@ -1023,7 +1036,7 @@ class InversionAndOmissionLabel extends JLabel
        }\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
@@ -1040,15 +1053,40 @@ class ChordTextField extends JTextField {
                        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
@@ -1067,15 +1105,29 @@ class ChordTextField extends JTextField {
                ) {\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
index 5803b80..188cb15 100644 (file)
@@ -310,39 +310,11 @@ class MidiEditor extends JDialog implements DropTargetListener {
                }\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
@@ -369,7 +341,9 @@ class MidiEditor extends JDialog implements DropTargetListener {
                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
@@ -424,12 +398,7 @@ class MidiEditor extends JDialog implements DropTargetListener {
        /**\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
@@ -448,7 +417,7 @@ class MidiEditor extends JDialog implements DropTargetListener {
        /**\r
         * スクロール可能なMIDIイベントテーブルビュー\r
         */\r
-       private JScrollPane scrollableEventTableView = new JScrollPane(trackEventListTableView);\r
+       private JScrollPane scrollableEventTableView;\r
        /**\r
         * 指定の MIDI tick のイベントへスクロールします。\r
         * @param tick MIDI tick\r
@@ -768,6 +737,7 @@ class MidiEditor extends JDialog implements DropTargetListener {
         */\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
@@ -973,8 +943,36 @@ class MidiEditor extends JDialog implements DropTargetListener {
                        });\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
@@ -1003,8 +1001,16 @@ class MidiEditor extends JDialog implements DropTargetListener {
                        }});\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
@@ -1041,20 +1047,17 @@ class MidiEditor extends JDialog implements DropTargetListener {
         * ボタン状態の更新\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
index bdbe5fe..7be1668 100644 (file)
@@ -15,6 +15,7 @@ import java.awt.event.MouseListener;
 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
@@ -39,6 +40,7 @@ import javax.swing.JSpinner;
 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
@@ -1144,7 +1146,7 @@ class DurationForm extends JPanel implements ActionListener, ChangeListener {
 /**\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
@@ -1168,6 +1170,24 @@ class TempoSelecter extends JPanel implements MouseListener {
                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
@@ -1250,7 +1270,7 @@ class TempoSelecter extends JPanel implements MouseListener {
 /**\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
@@ -1266,6 +1286,24 @@ class TimeSignatureSelecter extends JPanel {
                        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
index 6b00441..a05ec41 100644 (file)
@@ -19,6 +19,7 @@ import javax.swing.DefaultBoundedRangeModel;
 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
@@ -171,7 +172,9 @@ class MeasureIndicator extends JPanel implements ChangeListener {
 /**\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
@@ -185,38 +188,51 @@ class MidiSequencerModel extends MidiConnecterListModel implements BoundedRangeM
        ) {\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
index d63991b..cc2b87f 100644 (file)
@@ -16,10 +16,12 @@ import java.io.InputStream;
 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
@@ -30,29 +32,39 @@ import javax.swing.event.TableModelListener;
  * 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
index 8e08516..306770e 100644 (file)
@@ -113,21 +113,18 @@ public class PianoKeyboard extends JComponent {
        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
@@ -226,8 +223,8 @@ public class PianoKeyboard extends JComponent {
                }\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
@@ -236,16 +233,16 @@ public class PianoKeyboard extends JComponent {
        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
@@ -389,23 +386,25 @@ public class PianoKeyboard extends JComponent {
                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
@@ -668,8 +667,8 @@ public class PianoKeyboard extends JComponent {
                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
@@ -679,7 +678,6 @@ public class PianoKeyboard extends JComponent {
        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
@@ -688,19 +686,15 @@ public class PianoKeyboard extends JComponent {
                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
@@ -777,43 +771,42 @@ public class PianoKeyboard extends JComponent {
        }\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
@@ -901,8 +894,6 @@ class MidiKeyboardPanel extends JPanel {
                add( keyboardSouthPanel );\r
        }\r
 \r
-       // Methods\r
-       //\r
        public void setDarkMode(boolean isDark) {\r
                Color col = isDark ? Color.black : null;\r
                setBackground(col);\r