OSDN Git Service

・再生スピード調整をよりわかりやすくした
[midichordhelper/MIDIChordHelper.git] / src / camidion / chordhelper / ChordHelperApplet.java
index 8f7e09a..0b7c3b6 100644 (file)
@@ -22,13 +22,14 @@ import java.net.URISyntaxException;
 import java.net.URL;
 import java.security.AccessControlException;
 import java.util.Arrays;
-import java.util.Vector;
 
 import javax.sound.midi.InvalidMidiDataException;
 import javax.sound.midi.MetaEventListener;
 import javax.sound.midi.MetaMessage;
 import javax.sound.midi.Sequence;
 import javax.sound.midi.Sequencer;
+import javax.swing.AbstractAction;
+import javax.swing.Action;
 import javax.swing.Box;
 import javax.swing.BoxLayout;
 import javax.swing.ImageIcon;
@@ -51,17 +52,22 @@ import javax.swing.event.HyperlinkEvent;
 import javax.swing.event.HyperlinkListener;
 
 import camidion.chordhelper.anogakki.AnoGakkiPane;
+import camidion.chordhelper.chorddiagram.CapoComboBoxModel;
 import camidion.chordhelper.chorddiagram.ChordDiagram;
 import camidion.chordhelper.chordmatrix.ChordButtonLabel;
 import camidion.chordhelper.chordmatrix.ChordMatrix;
 import camidion.chordhelper.chordmatrix.ChordMatrixListener;
 import camidion.chordhelper.mididevice.MidiDeviceDialog;
-import camidion.chordhelper.mididevice.MidiDeviceModelList;
+import camidion.chordhelper.mididevice.MidiSequencerModel;
+import camidion.chordhelper.mididevice.MidiTransceiverListModelList;
 import camidion.chordhelper.mididevice.SequencerMeasureView;
 import camidion.chordhelper.mididevice.SequencerTimeView;
 import camidion.chordhelper.mididevice.VirtualMidiDevice;
 import camidion.chordhelper.midieditor.Base64Dialog;
 import camidion.chordhelper.midieditor.KeySignatureLabel;
+import camidion.chordhelper.midieditor.MidiSequenceEditor;
+import camidion.chordhelper.midieditor.NewSequenceDialog;
+import camidion.chordhelper.midieditor.PlaylistTableModel;
 import camidion.chordhelper.midieditor.SequenceTickIndex;
 import camidion.chordhelper.midieditor.SequenceTrackListTableModel;
 import camidion.chordhelper.midieditor.TempoSelecter;
@@ -77,7 +83,7 @@ import camidion.chordhelper.pianokeyboard.PianoKeyboardAdapter;
  * (アプレットクラス)
  *
  *     @auther
- *             Copyright (C) 2004-2014 @きよし - Akiyoshi Kamide
+ *             Copyright (C) 2004-2016 @きよし - Akiyoshi Kamide
  *             http://www.yk.rim.or.jp/~kamide/music/chordhelper/
  */
 public class ChordHelperApplet extends JApplet {
@@ -90,18 +96,17 @@ public class ChordHelperApplet extends JApplet {
         * 未保存の修正済み MIDI ファイルがあるかどうか調べます。
         * @return 未保存の修正済み MIDI ファイルがあれば true
         */
-       public boolean isModified() {
-               return deviceModelList.editorDialog.sequenceListTable.getModel().isModified();
-       }
+       public boolean isModified() { return playlistModel.isModified(); }
        /**
         * 指定された小節数の曲を、乱数で自動作曲してプレイリストへ追加します。
         * @param measureLength 小節数
         * @return 追加先のインデックス値(0から始まる)。追加できなかったときは -1
+        * @throws InvalidMidiDataException {@link Sequencer#setSequence(Sequence)} を参照
         */
-       public int addRandomSongToPlaylist(int measureLength) {
-               deviceModelList.editorDialog.newSequenceDialog.setRandomChordProgression(measureLength);
-               Sequence sequence = deviceModelList.editorDialog.newSequenceDialog.getMidiSequence();
-               return deviceModelList.editorDialog.sequenceListTable.getModel().addSequenceAndPlay(sequence);
+       public int addRandomSongToPlaylist(int measureLength) throws InvalidMidiDataException {
+               NewSequenceDialog d = midiEditor.newSequenceDialog;
+               d.setRandomChordProgression(measureLength);
+               return playlistModel.addSequenceAndPlay(d.getMidiSequence());
        }
        /**
         * URLで指定されたMIDIファイルをプレイリストへ追加します。
@@ -114,12 +119,12 @@ public class ChordHelperApplet extends JApplet {
         */
        public int addToPlaylist(String midiFileUrl) {
                try {
-                       return deviceModelList.editorDialog.sequenceListTable.getModel().addSequenceFromURL(midiFileUrl);
+                       return playlistModel.addSequenceFromURL(midiFileUrl);
                } catch( URISyntaxException|IOException|InvalidMidiDataException e ) {
-                       deviceModelList.editorDialog.showWarning(e.getMessage());
+                       midiEditor.showWarning(e.getMessage());
                } catch( AccessControlException e ) {
                        e.printStackTrace();
-                       deviceModelList.editorDialog.showError(e.getMessage());
+                       midiEditor.showError(e.getMessage());
                }
                return -1;
        }
@@ -141,31 +146,30 @@ public class ChordHelperApplet extends JApplet {
         * @return 追加先のインデックス値(0から始まる)。追加できなかったときは -1
         */
        public int addToPlaylistBase64(String base64EncodedText, String filename) {
-               Base64Dialog d = deviceModelList.editorDialog.base64Dialog;
+               Base64Dialog d = midiEditor.base64Dialog;
                d.setBase64Data(base64EncodedText);
                try {
-                       return deviceModelList.editorDialog.sequenceListTable.getModel().addSequence(d.getMIDIData(), filename);
+                       return playlistModel.addSequence(d.getMIDIData(), filename);
                } catch (IOException | InvalidMidiDataException e) {
                        e.printStackTrace();
-                       deviceModelList.editorDialog.showWarning(e.getMessage());
+                       midiEditor.showWarning(e.getMessage());
                        return -1;
                }
        }
        /**
         * プレイリスト上で現在選択されているMIDIシーケンスを、
         * シーケンサへロードして再生します。
+        * @throws InvalidMidiDataException {@link Sequencer#setSequence(Sequence)} を参照
         */
-       public void play() {
-               play(deviceModelList.editorDialog.sequenceListTable.getModel().sequenceListSelectionModel.getMinSelectionIndex());
-       }
+       public void play() throws InvalidMidiDataException { play(playlistModel.sequenceListSelectionModel.getMinSelectionIndex()); }
        /**
         * 指定されたインデックス値が示すプレイリスト上のMIDIシーケンスを、
         * シーケンサへロードして再生します。
         * @param index インデックス値(0から始まる)
+        * @throws InvalidMidiDataException {@link Sequencer#setSequence(Sequence)} を参照
         */
-       public void play(int index) {
-               deviceModelList.editorDialog.sequenceListTable.getModel().loadToSequencer(index);
-               deviceModelList.getSequencerModel().start();
+       public void play(int index) throws InvalidMidiDataException {
+               playlistModel.loadToSequencer(index); sequencerModel.start();
        }
        /**
         * シーケンサが実行中かどうかを返します。
@@ -173,9 +177,7 @@ public class ChordHelperApplet extends JApplet {
         *
         * @return 実行中のときtrue
         */
-       public boolean isRunning() {
-               return deviceModelList.getSequencerModel().getSequencer().isRunning();
-       }
+       public boolean isRunning() { return sequencerModel.getSequencer().isRunning(); }
        /**
         * シーケンサが再生中かどうかを返します。
         * @return 再生中のときtrue
@@ -187,17 +189,16 @@ public class ChordHelperApplet extends JApplet {
         * @return MIDIデータをBase64テキストに変換した結果
         */
        public String getMidiDataBase64() {
-               SequenceTrackListTableModel sequenceModel =
-                       deviceModelList.editorDialog.sequenceListTable.getModel().sequencerModel.getSequenceTrackListTableModel();
-               deviceModelList.editorDialog.base64Dialog.setMIDIData(sequenceModel.getMIDIdata());
-               return deviceModelList.editorDialog.base64Dialog.getBase64Data();
+               SequenceTrackListTableModel sequenceModel = sequencerModel.getSequenceTrackListTableModel();
+               midiEditor.base64Dialog.setMIDIData(sequenceModel.getMIDIdata());
+               return midiEditor.base64Dialog.getBase64Data();
        }
        /**
         * 現在シーケンサにロードされているMIDIファイルのファイル名を返します。
         * @return MIDIファイル名(設定されていないときは空文字列)
         */
        public String getMidiFilename() {
-               SequenceTrackListTableModel seq_model = deviceModelList.getSequencerModel().getSequenceTrackListTableModel();
+               SequenceTrackListTableModel seq_model = sequencerModel.getSequenceTrackListTableModel();
                if( seq_model == null ) return null;
                String fn = seq_model.getFilename();
                return fn == null ? "" : fn ;
@@ -284,7 +285,7 @@ public class ChordHelperApplet extends JApplet {
         */
        public static class VersionInfo {
                public static final String      NAME = "MIDI Chord Helper";
-               public static final String      VERSION = "Ver.20160501.1";
+               public static final String      VERSION = "Ver.20160605.1";
                public static final String      COPYRIGHT = "Copyright (C) 2004-2016";
                public static final String      AUTHER = "@きよし - Akiyoshi Kamide";
                public static final String      URL = "http://www.yk.rim.or.jp/~kamide/music/chordhelper/";
@@ -298,7 +299,7 @@ public class ChordHelperApplet extends JApplet {
        }
        @Override
        public String getAppletInfo() { return VersionInfo.getInfo(); }
-       private class AboutMessagePane extends JEditorPane implements ActionListener {
+       private class AboutMessagePane extends JEditorPane {
                URI uri = null;
                public AboutMessagePane() { this(true); }
                public AboutMessagePane(boolean link_enabled) {
@@ -346,36 +347,42 @@ public class ChordHelperApplet extends JApplet {
                                }
                        });
                }
-               @Override
-               public void actionPerformed(ActionEvent e) {
-                       JOptionPane.showMessageDialog(
-                               null, this, "Version info",
-                               JOptionPane.INFORMATION_MESSAGE, imageIcon
-                       );
-               }
-       }
-       // 終了してよいか確認する
-       public boolean isConfirmedToExit() {
-               return ! isModified() || JOptionPane.showConfirmDialog(
-                       this,
-                       "MIDI file not saved, exit anyway ?\n保存されていないMIDIファイルがありますが、終了してよろしいですか?",
-                       VersionInfo.NAME,
-                       JOptionPane.YES_NO_OPTION,
-                       JOptionPane.WARNING_MESSAGE
-               ) == JOptionPane.YES_OPTION ;
+               /**
+                * バージョン情報を開くアクション
+                */
+               public Action openAction = new AbstractAction() {
+                       {
+                               putValue(NAME, "Version info");
+                               putValue(SHORT_DESCRIPTION, VersionInfo.NAME + " " + VersionInfo.VERSION);
+                       }
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               JOptionPane.showMessageDialog(
+                                       null, AboutMessagePane.this, getValue(NAME).toString(),
+                                       JOptionPane.INFORMATION_MESSAGE, imageIcon
+                               );
+                       }
+               };
        }
        /**
-        * ã\82¢ã\83\97ã\83ªã\82±ã\83¼ã\82·ã\83§ã\83³ã\81®ã\82¢ã\82¤ã\82³ã\83³ã\82¤ã\83¡ã\83¼ã\82¸
+        * ã\82¢ã\83\97ã\83ªã\82±ã\83¼ã\82·ã\83§ã\83³ã\81®ã\82¤ã\83¡ã\83¼ã\82¸ã\82¢ã\82¤ã\82³ã\83³
         */
        public ImageIcon imageIcon;
        /**
+        * アプリケーションのアイコンイメージ
+        */
+       public Image iconImage;
+       /**
         * ボタンの余白を詰めたいときに setMargin() の引数に指定するインセット
         */
        public static final Insets ZERO_INSETS = new Insets(0,0,0,0);
        //
-       public ChordMatrix chordMatrix;
-       MidiDeviceModelList     deviceModelList;
+       private static final String IMAGE_ICON_PATH = "midichordhelper.png";
        //
+       MidiSequenceEditor midiEditor;
+       PlaylistTableModel playlistModel;
+       MidiSequencerModel sequencerModel;
+       public ChordMatrix chordMatrix;
        private JPanel keyboardSequencerPanel;
        private JPanel chordGuide;
        private Color rootPaneDefaultBgcolor;
@@ -388,28 +395,33 @@ public class ChordHelperApplet extends JApplet {
        private MidiKeyboardPanel keyboardPanel;
        private InversionAndOmissionLabel inversionOmissionButton;
        private JToggleButton darkModeToggleButton;
-       private MidiDeviceDialog midiConnectionDialog;
+       private MidiDeviceDialog midiDeviceDialog;
        private ChordDiagram chordDiagram;
        private TempoSelecter tempoSelecter;
        private TimeSignatureSelecter timesigSelecter;
        private KeySignatureLabel keysigLabel;
-       private JLabel songTitleLabel;
+       private JLabel songTitleLabel = new JLabel();
        private AnoGakkiPane anoGakkiPane;
        private JToggleButton anoGakkiToggleButton;
+       private MidiTransceiverListModelList deviceModelList;
 
        public void init() {
-               String imageIconPath = "midichordhelper.png";
-               URL imageIconUrl = getClass().getResource(imageIconPath);
+               //
+               // アイコンイメージの取得
+               URL imageIconUrl = getClass().getResource(IMAGE_ICON_PATH);
                if( imageIconUrl == null ) {
-                       System.out.println("Icon image "+imageIconPath+" not found");
-                       imageIcon = null;
+                       System.out.println("Icon image "+IMAGE_ICON_PATH+" not found");
                }
                else {
-                       imageIcon = new ImageIcon(imageIconUrl);
+                       iconImage = (imageIcon = new ImageIcon(imageIconUrl)).getImage();
                }
-               Image iconImage = (imageIcon == null) ? null : imageIcon.getImage();
+               // 背景色の取得
                rootPaneDefaultBgcolor = getContentPane().getBackground();
-               chordMatrix = new ChordMatrix() {{
+               //
+               // コードダイアグラム、コードボタン、ピアノ鍵盤のセットアップ
+               CapoComboBoxModel capoValueModel = new CapoComboBoxModel();
+               chordDiagram = new ChordDiagram(capoValueModel);
+               chordMatrix = new ChordMatrix(capoValueModel) {{
                        addChordMatrixListener(new ChordMatrixListener(){
                                public void keySignatureChanged() {
                                        Key capoKey = getKeySignatureCapo();
@@ -418,59 +430,61 @@ public class ChordHelperApplet extends JApplet {
                                }
                                public void chordChanged() { chordOn(); }
                        });
-               }};
-               chordMatrix.capoSelecter.checkbox.addItemListener(
-                       new ItemListener() {
+                       capoSelecter.checkbox.addItemListener(new ItemListener() {
                                public void itemStateChanged(ItemEvent e) {
                                        chordOn();
                                        keyboardPanel.keyboardCenterPanel.keyboard.chordDisplay.clear();
                                        chordDiagram.clear();
                                }
-                       }
-               );
-               chordMatrix.capoSelecter.valueSelecter.addActionListener(
-                       new ActionListener() {
+                       });
+                       capoSelecter.valueSelecter.addActionListener(new ActionListener() {
                                public void actionPerformed(ActionEvent e) {
                                        chordOn();
                                        keyboardPanel.keyboardCenterPanel.keyboard.chordDisplay.clear();
                                        chordDiagram.clear();
                                }
-                       }
-               );
+                       });
+               }};
+               keysigLabel = new KeySignatureLabel() {{
+                       addMouseListener(new MouseAdapter() {
+                               public void mousePressed(MouseEvent e) { chordMatrix.setKeySignature(getKey()); }
+                       });
+               }};
                keyboardPanel = new MidiKeyboardPanel(chordMatrix) {{
-                       keyboardCenterPanel.keyboard.addPianoKeyboardListener(
-                               new PianoKeyboardAdapter() {
-                                       @Override
-                                       public void pianoKeyPressed(int n, InputEvent e) {
-                                               chordDiagram.clear();
-                                       }
-                               }
-                       );
-                       keySelecter.keysigCombobox.addActionListener(
-                               new ActionListener() {
-                                       @Override
-                                       public void actionPerformed(ActionEvent e) {
-                                               Key key = keySelecter.getKey();
-                                               key.transpose( - chordMatrix.capoSelecter.getCapo() );
-                                               chordMatrix.setKeySignature(key);
-                                       }
+                       keyboardCenterPanel.keyboard.addPianoKeyboardListener(new PianoKeyboardAdapter() {
+                               @Override
+                               public void pianoKeyPressed(int n, InputEvent e) { chordDiagram.clear(); }
+                       });
+                       keySelecter.keysigCombobox.addActionListener(new ActionListener() {
+                               @Override
+                               public void actionPerformed(ActionEvent e) {
+                                       Key key = keySelecter.getKey();
+                                       key.transpose( - chordMatrix.capoSelecter.getCapo() );
+                                       chordMatrix.setKeySignature(key);
                                }
-                       );
+                       });
                        keyboardCenterPanel.keyboard.setPreferredSize(new Dimension(571, 80));
                }};
-               deviceModelList = new MidiDeviceModelList(
-                       new Vector<VirtualMidiDevice>() {
-                               {
-                                       add(keyboardPanel.keyboardCenterPanel.keyboard.midiDevice);
-                               }
-                       }
-               );
-               deviceModelList.editorDialog.setIconImage(iconImage);
-               new DropTarget(this, DnDConstants.ACTION_COPY_OR_MOVE, deviceModelList.editorDialog, true);
-               keyboardPanel.setEventDialog(deviceModelList.editorDialog.eventDialog);
-               midiConnectionDialog = new MidiDeviceDialog(deviceModelList);
-               midiConnectionDialog.setIconImage(iconImage);
-               lyricDisplay = new ChordTextField(deviceModelList.getSequencerModel()) {{
+               VirtualMidiDevice guiMidiDevice = keyboardPanel.keyboardCenterPanel.keyboard.midiDevice;
+               //
+               // MIDIデバイス一覧を構築
+               deviceModelList = new MidiTransceiverListModelList(Arrays.asList(guiMidiDevice));
+               (midiDeviceDialog = new MidiDeviceDialog(deviceModelList)).setIconImage(iconImage);
+               //
+               // MIDIデバイス一覧のシーケンサと連携するプレイリストを構築
+               playlistModel = new PlaylistTableModel(sequencerModel = deviceModelList.getSequencerModel());
+               //
+               // MIDIエディタダイアログの構築
+               (midiEditor = new MidiSequenceEditor(playlistModel, guiMidiDevice)).setIconImage(iconImage);
+               //
+               // メイン画面へのMIDIファイルのドラッグ&ドロップ受付開始
+               new DropTarget(this, DnDConstants.ACTION_COPY_OR_MOVE, midiEditor.dropTargetListener, true);
+               //
+               // MIDIエディタのイベントダイアログを、ピアノ鍵盤のイベント送出ダイアログと共用
+               keyboardPanel.setEventDialog(midiEditor.eventDialog);
+               //
+               // 歌詞表示
+               lyricDisplay = new ChordTextField(sequencerModel) {{
                        addActionListener(new ActionListener() {
                                @Override
                                public void actionPerformed(ActionEvent event) {
@@ -481,59 +495,42 @@ public class ChordHelperApplet extends JApplet {
                }};
                lyricDisplayDefaultBorder = lyricDisplay.getBorder();
                lyricDisplayDefaultBgcolor = lyricDisplay.getBackground();
-               chordDiagram = new ChordDiagram(this);
-               tempoSelecter = new TempoSelecter() {{
-                       setEditable(false);
-                       deviceModelList.getSequencerModel().getSequencer().addMetaEventListener(this);
-               }};
-               timesigSelecter = new TimeSignatureSelecter() {{
-                       setEditable(false);
-                       deviceModelList.getSequencerModel().getSequencer().addMetaEventListener(this);
-               }};
-               keysigLabel = new KeySignatureLabel() {{
-                       addMouseListener(new MouseAdapter() {
-                               public void mousePressed(MouseEvent e) {
-                                       chordMatrix.setKeySignature(getKey());
-                               }
-                       });
-               }};
-               deviceModelList.getSequencerModel().getSequencer().addMetaEventListener(
-                       new MetaEventListener() {
-                               class SetKeySignatureRunnable implements Runnable {
-                                       Key key;
-                                       public SetKeySignatureRunnable(Key key) {
-                                               this.key = key;
-                                       }
-                                       @Override
-                                       public void run() { setKeySignature(key); }
-                               }
-                               @Override
-                               public void meta(MetaMessage msg) {
-                                       switch(msg.getType()) {
-                                       case 0x59: // Key signature (2 bytes) : 調号
-                                               Key key = new Key(msg.getData());
-                                               if( ! SwingUtilities.isEventDispatchThread() ) {
-                                                       SwingUtilities.invokeLater(
-                                                               new SetKeySignatureRunnable(key)
-                                                       );
-                                               }
-                                               setKeySignature(key);
-                                               break;
+               //
+               // メタイベント(テンポ・拍子・調号)を受信して表示するリスナーを登録
+               Sequencer sequencer = sequencerModel.getSequencer();
+               sequencer.addMetaEventListener(tempoSelecter = new TempoSelecter() {{ setEditable(false); }});
+               sequencer.addMetaEventListener(timesigSelecter = new TimeSignatureSelecter() {{ setEditable(false); }});
+               sequencer.addMetaEventListener(new MetaEventListener() {
+                       private Key key;
+                       @Override
+                       public void meta(MetaMessage msg) {
+                               switch(msg.getType()) {
+                               case 0x59: // Key signature (2 bytes) : 調号
+                                       key = new Key(msg.getData());
+                                       if( SwingUtilities.isEventDispatchThread() ) {
+                                               keysigLabel.setKeySignature(key);
+                                               chordMatrix.setKeySignature(key);
+                                       } else {
+                                               // MIDIシーケンサのスレッドから呼ばれた場合、GUI更新は自分で行わず、
+                                               // AWTイベントディスパッチスレッドに依頼する。
+                                               SwingUtilities.invokeLater(new Runnable() {
+                                                       @Override
+                                                       public void run() {
+                                                               keysigLabel.setKeySignature(key);
+                                                               chordMatrix.setKeySignature(key);
+                                                       }
+                                               });
                                        }
-                               }
-                               private void setKeySignature(Key key) {
-                                       keysigLabel.setKeySignature(key);
-                                       chordMatrix.setKeySignature(key);
+                                       break;
                                }
                        }
-               );
-               songTitleLabel = new JLabel();
+               });
                //シーケンサーの時間スライダーの値が変わったときのリスナーを登録
-               deviceModelList.getSequencerModel().addChangeListener(new ChangeListener() {
+               sequencerModel.addChangeListener(new ChangeListener() {
                        @Override
                        public void stateChanged(ChangeEvent e) {
-                               SequenceTrackListTableModel sequenceTableModel = deviceModelList.getSequencerModel().getSequenceTrackListTableModel();
-                               int loadedSequenceIndex = deviceModelList.editorDialog.sequenceListTable.getModel().indexOfSequenceOnSequencer();
+                               SequenceTrackListTableModel sequenceTableModel = sequencerModel.getSequenceTrackListTableModel();
+                               int loadedSequenceIndex = playlistModel.indexOfSequenceOnSequencer();
                                songTitleLabel.setText(
                                        "<html>"+(
                                                loadedSequenceIndex < 0 ? "[No MIDI file loaded]" :
@@ -546,17 +543,14 @@ public class ChordHelperApplet extends JApplet {
                                                )
                                        )+"</html>"
                                );
-                               Sequencer sequencer = deviceModelList.getSequencerModel().getSequencer();
+                               Sequencer sequencer = sequencerModel.getSequencer();
                                chordMatrix.setPlaying(sequencer.isRunning());
                                if( sequenceTableModel != null ) {
                                        SequenceTickIndex tickIndex = sequenceTableModel.getSequenceTickIndex();
                                        long tickPos = sequencer.getTickPosition();
                                        tickIndex.tickToMeasure(tickPos);
                                        chordMatrix.setBeat(tickIndex);
-                                       if(
-                                               deviceModelList.getSequencerModel().getValueIsAdjusting() ||
-                                               ! (sequencer.isRunning() || sequencer.isRecording())
-                                       ) {
+                                       if( sequencerModel.getValueIsAdjusting() || ! (sequencer.isRunning() || sequencer.isRecording()) ) {
                                                MetaMessage msg;
                                                msg = tickIndex.lastMetaMessageAt(
                                                        SequenceTickIndex.MetaMessageType.TIME_SIGNATURE, tickPos
@@ -581,7 +575,7 @@ public class ChordHelperApplet extends JApplet {
                                }
                        }
                });
-               deviceModelList.getSequencerModel().fireStateChanged();
+               sequencerModel.fireStateChanged();
                chordGuide = new JPanel() {
                        {
                                setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
@@ -615,21 +609,17 @@ public class ChordHelperApplet extends JApplet {
                                        setBorder(null);
                                }});
                                add( Box.createHorizontalStrut(5) );
-                               add( anoGakkiToggleButton = new JToggleButton(
-                                       new ButtonIcon(ButtonIcon.ANO_GAKKI_ICON)
-                               ) {{
+                               add( anoGakkiToggleButton = new JToggleButton(new ButtonIcon(ButtonIcon.ANO_GAKKI_ICON)) {{
                                        setOpaque(false);
                                        setMargin(ZERO_INSETS);
                                        setBorder( null );
                                        setToolTipText("あの楽器");
-                                       addItemListener(
-                                               new ItemListener() {
-                                                       public void itemStateChanged(ItemEvent e) {
-                                                               keyboardPanel.keyboardCenterPanel.keyboard.anoGakkiPane
-                                                               = anoGakkiToggleButton.isSelected() ? anoGakkiPane : null ;
-                                                       }
+                                       addItemListener(new ItemListener() {
+                                               public void itemStateChanged(ItemEvent e) {
+                                                       keyboardPanel.keyboardCenterPanel.keyboard.anoGakkiPane
+                                                       = anoGakkiToggleButton.isSelected() ? anoGakkiPane : null ;
                                                }
-                                       );
+                                       });
                                }} );
                                add( Box.createHorizontalStrut(5) );
                                add( inversionOmissionButton = new InversionAndOmissionLabel() );
@@ -642,9 +632,7 @@ public class ChordHelperApplet extends JApplet {
                        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
                        add(chordGuide);
                        add(Box.createVerticalStrut(5));
-                       add(keyboardSplitPane = new JSplitPane(
-                               JSplitPane.HORIZONTAL_SPLIT, keyboardPanel, chordDiagram
-                       ) {{
+                       add(keyboardSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, keyboardPanel, chordDiagram) {{
                                setOneTouchExpandable(true);
                                setResizeWeight(1.0);
                                setAlignmentX((float)0.5);
@@ -661,47 +649,29 @@ public class ChordHelperApplet extends JApplet {
                                        add( Box.createHorizontalStrut(12) );
                                        add( tempoSelecter );
                                        add( Box.createHorizontalStrut(12) );
-                                       add( new SequencerMeasureView(deviceModelList.getSequencerModel()) );
+                                       add( new SequencerMeasureView(sequencerModel) );
                                        add( Box.createHorizontalStrut(12) );
                                        add( songTitleLabel );
                                        add( Box.createHorizontalStrut(12) );
-                                       add( new JButton(deviceModelList.editorDialog.openAction) {{ setMargin(ZERO_INSETS); }});
+                                       add( new JButton(midiEditor.openAction) {{ setMargin(ZERO_INSETS); }});
                                }});
                                add(new JPanel() {{
                                        setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
-                                       add( Box.createHorizontalStrut(10) );
-                                       add( new JSlider(deviceModelList.getSequencerModel()) );
-                                       add( new SequencerTimeView(deviceModelList.getSequencerModel()) );
-                                       add( Box.createHorizontalStrut(5) );
-                                       add( new JButton(deviceModelList.editorDialog.sequenceListTable.getModel().moveToTopAction) {{
-                                               setMargin(ZERO_INSETS);
-                                       }});
-                                       add(new JButton(deviceModelList.getSequencerModel().moveBackwardAction) {{
-                                               setMargin(ZERO_INSETS);
-                                       }});
-                                       add(new JToggleButton(deviceModelList.getSequencerModel().startStopAction));
-                                       add(new JButton(deviceModelList.getSequencerModel().moveForwardAction) {{
-                                               setMargin(ZERO_INSETS);
-                                       }});
-                                       add(new JButton(deviceModelList.editorDialog.sequenceListTable.getModel().moveToBottomAction) {{
-                                               setMargin(ZERO_INSETS);
-                                       }});
-                                       add(new JToggleButton(deviceModelList.editorDialog.sequenceListTable.getModel().toggleRepeatAction) {{
-                                               setMargin(ZERO_INSETS);
-                                       }});
+                                       add(Box.createHorizontalStrut(10));
+                                       add(new JSlider(sequencerModel));
+                                       add(new SequencerTimeView(sequencerModel));
+                                       add(Box.createHorizontalStrut(5));
+                                       add(new JButton(playlistModel.moveToTopAction) {{ setMargin(ZERO_INSETS); }});
+                                       add(new JButton(sequencerModel.moveBackwardAction) {{ setMargin(ZERO_INSETS); }});
+                                       add(new JToggleButton(sequencerModel.startStopAction));
+                                       add(new JButton(sequencerModel.moveForwardAction) {{ setMargin(ZERO_INSETS); }});
+                                       add(new JButton(playlistModel.moveToBottomAction) {{ setMargin(ZERO_INSETS); }});
+                                       add(new JToggleButton(playlistModel.toggleRepeatAction) {{ setMargin(ZERO_INSETS); }});
                                        add( Box.createHorizontalStrut(10) );
                                }});
                                add(new JPanel() {{
-                                       add(new JButton(
-                                               "MIDI device connection",
-                                               new ButtonIcon( ButtonIcon.MIDI_CONNECTOR_ICON )
-                                       ) {{
-                                               addActionListener(midiConnectionDialog);
-                                       }});
-                                       add(new JButton("Version info") {{
-                                               setToolTipText(VersionInfo.NAME + " " + VersionInfo.VERSION);
-                                               addActionListener(new AboutMessagePane());
-                                       }});
+                                       add(new JButton(midiDeviceDialog.openAction));
+                                       add(new JButton((new AboutMessagePane()).openAction));
                                }});
                        }});
                }};
@@ -710,23 +680,14 @@ public class ChordHelperApplet extends JApplet {
                                add(anoGakkiPane = new AnoGakkiPane(), JLayeredPane.PALETTE_LAYER);
                                addComponentListener(new ComponentAdapter() {
                                        @Override
-                                       public void componentResized(ComponentEvent e) {
-                                               adjustSize();
-                                       }
+                                       public void componentResized(ComponentEvent e) { adjustSize(); }
                                        @Override
-                                       public void componentShown(ComponentEvent e) {
-                                               adjustSize();
-                                       }
-                                       private void adjustSize() {
-                                               anoGakkiPane.setBounds(getBounds());
-                                       }
+                                       public void componentShown(ComponentEvent e) { adjustSize(); }
+                                       private void adjustSize() { anoGakkiPane.setBounds(getBounds()); }
                                });
                                setLayout(new BorderLayout());
                                setOpaque(true);
-                               add(mainSplitPane = new JSplitPane(
-                                       JSplitPane.VERTICAL_SPLIT,
-                                       chordMatrix, keyboardSequencerPanel
-                               ){
+                               add(mainSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, chordMatrix, keyboardSequencerPanel){
                                        {
                                                setResizeWeight(0.5);
                                                setAlignmentX((float)0.5);
@@ -738,6 +699,11 @@ public class ChordHelperApplet extends JApplet {
                setPreferredSize(new Dimension(750,470));
        }
        @Override
+       public void destroy() {
+               deviceModelList.closeAllDevices();
+               super.destroy();
+       }
+       @Override
        public void start() {
                //
                // コードボタンで設定されている現在の調を
@@ -750,19 +716,21 @@ public class ChordHelperApplet extends JApplet {
                System.gc();
                if( midi_url != null ) {
                        addToPlaylist(midi_url);
-                       play();
+                       try {
+                               play();
+                       } catch (InvalidMidiDataException ex) {
+                               ex.printStackTrace();
+                       }
                }
        }
        @Override
        public void stop() {
-               deviceModelList.getSequencerModel().stop(); // MIDI再生を強制終了
+               sequencerModel.stop(); // MIDI再生を強制終了
                System.gc();
        }
        private void innerSetDarkMode(boolean isDark) {
                Color col = isDark ? Color.black : null;
-               getContentPane().setBackground(
-                       isDark ? Color.black : rootPaneDefaultBgcolor
-               );
+               getContentPane().setBackground(isDark ? Color.black : rootPaneDefaultBgcolor);
                mainSplitPane.setBackground(col);
                keyboardSplitPane.setBackground(col);
                enterButtonLabel.setDarkMode(isDark);