OSDN Git Service

リファクタリング(メソッドの統廃合など)
authorAkiyoshi Kamide <kamide@yk.rim.or.jp>
Sun, 17 Nov 2013 17:45:32 +0000 (17:45 +0000)
committerAkiyoshi Kamide <kamide@yk.rim.or.jp>
Sun, 17 Nov 2013 17:45:32 +0000 (17:45 +0000)
git-svn-id: https://svn.sourceforge.jp/svnroot/midichordhelper/MIDIChordHelper@12 302f1594-2db2-43b1-aaa4-6307b5a2a2de

src/Base64Dialog.java
src/ChordHelperApplet.java
src/MIDIEditor.java
src/MIDISequencer.java
src/MIDISpec.java
src/Music.java
src/NewSequenceDialog.java

index 9e2ae09..c43b13e 100644 (file)
@@ -83,7 +83,7 @@ public class Base64Dialog extends JDialog {
                                @Override\r
                                public void actionPerformed(ActionEvent e) {\r
                                        MidiEditor midiEditor = Base64Dialog.this.midiEditor;\r
-                                       int lastIndex = midiEditor.addSequenceFromMidiData(getMIDIData(), null);\r
+                                       int lastIndex = midiEditor.addSequence(getMIDIData(), null);\r
                                        if( lastIndex < 0 ) {\r
                                                base64TextArea.requestFocusInWindow();\r
                                                lastIndex = midiEditor.sequenceListTableModel.getRowCount() - 1;\r
index c690c9d..31f9324 100644 (file)
@@ -80,7 +80,9 @@ public class ChordHelperApplet extends JApplet {
         */\r
        public int addRandomSongToPlaylist(int measureLength) {\r
                editorDialog.newSequenceDialog.setRandomChordProgression(measureLength);\r
-               return editorDialog.addSequence(editorDialog.newSequenceDialog.getMidiSequence());\r
+               return editorDialog.addSequenceAndPlay(\r
+                       editorDialog.newSequenceDialog.getMidiSequence()\r
+               );\r
        }\r
        /**\r
         * URLで指定されたMIDIファイルをプレイリストへ追加します。\r
@@ -112,19 +114,26 @@ public class ChordHelperApplet extends JApplet {
         * @return 追加先のインデックス値(0から始まる)。追加できなかったときは -1\r
         */\r
        public int addToPlaylistBase64(String base64EncodedText, String filename) {\r
-               return editorDialog.addSequenceFromBase64Text(base64EncodedText, filename);\r
+               Base64Dialog d = editorDialog.base64Dialog;\r
+               d.setBase64Data(base64EncodedText);\r
+               return editorDialog.addSequence(d.getMIDIData(), filename);\r
        }\r
        /**\r
         * プレイリスト上で現在選択されているMIDIシーケンスを、\r
         * シーケンサへロードして再生します。\r
         */\r
-       public void play() { editorDialog.loadAndPlay(); }\r
+       public void play() {\r
+               play(editorDialog.seqSelectionModel.getMinSelectionIndex());\r
+       }\r
        /**\r
         * 指定されたインデックス値が示すプレイリスト上のMIDIシーケンスを、\r
         * シーケンサへロードして再生します。\r
         * @param index インデックス値(0から始まる)\r
         */\r
-       public void play(int index) { editorDialog.loadAndPlay(index); }\r
+       public void play(int index) {\r
+               editorDialog.load(index);\r
+               deviceModelList.sequencerModel.start();\r
+       }\r
        /**\r
         * シーケンサが実行中かどうかを返します。\r
         * {@link Sequencer#isRunning()} の戻り値をそのまま返します。\r
@@ -239,7 +248,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.20131117.2";\r
+               public static final String      VERSION = "Ver.20131118.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
index b3d078a..839a57a 100644 (file)
@@ -19,14 +19,11 @@ import java.awt.event.ItemListener;
 import java.awt.event.MouseEvent;\r
 import java.io.ByteArrayInputStream;\r
 import java.io.ByteArrayOutputStream;\r
-import java.io.EOFException;\r
 import java.io.File;\r
 import java.io.FileInputStream;\r
-import java.io.FileNotFoundException;\r
 import java.io.FileOutputStream;\r
 import java.io.IOException;\r
 import java.io.InputStream;\r
-import java.net.MalformedURLException;\r
 import java.net.URI;\r
 import java.net.URISyntaxException;\r
 import java.net.URL;\r
@@ -90,9 +87,7 @@ import javax.swing.table.TableModel;
  *     Copyright (C) 2006-2013 Akiyoshi Kamide\r
  *     http://www.yk.rim.or.jp/~kamide/music/chordhelper/\r
  */\r
-class MidiEditor extends JDialog\r
-       implements DropTargetListener, ListSelectionListener, ActionListener\r
-{\r
+class MidiEditor extends JDialog implements DropTargetListener, ActionListener {\r
        public static final Insets ZERO_INSETS = new Insets(0,0,0,0);\r
        /**\r
         * プレイリストのモデル\r
@@ -190,7 +185,7 @@ class MidiEditor extends JDialog
        /**\r
         * BASE64テキスト入力ダイアログ\r
         */\r
-       private Base64Dialog base64Dialog = new Base64Dialog(this);\r
+       Base64Dialog base64Dialog = new Base64Dialog(this);\r
        /**\r
         * BASE64エンコードボタン(ライブラリが見えている場合のみ有効)\r
         */\r
@@ -620,30 +615,93 @@ class MidiEditor extends JDialog
                //\r
                seqSelectionModel = sequenceListTableView.getSelectionModel();\r
                seqSelectionModel.setSelectionMode( ListSelectionModel.SINGLE_SELECTION );\r
-               seqSelectionModel.addListSelectionListener(\r
-                       new ListSelectionListener() {\r
-                               public void valueChanged(ListSelectionEvent e) {\r
-                                       if( e.getValueIsAdjusting() ) return;\r
-                                       sequenceSelectionChanged();\r
-                                       trackSelectionModel.setSelectionInterval(0,0);\r
-                               }\r
+               seqSelectionModel.addListSelectionListener(new ListSelectionListener() {\r
+                       @Override\r
+                       public void valueChanged(ListSelectionEvent e) {\r
+                               if( e.getValueIsAdjusting() ) return;\r
+                               sequenceSelectionChanged();\r
+                               trackSelectionModel.setSelectionInterval(0,0);\r
                        }\r
-               );\r
+               });\r
                //\r
                trackSelectionModel = trackListTableView.getSelectionModel();\r
                trackSelectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);\r
-               trackSelectionModel.addListSelectionListener(this);\r
-               //\r
+               trackSelectionModel.addListSelectionListener(new ListSelectionListener() {\r
+                       @Override\r
+                       public void valueChanged(ListSelectionEvent e) {\r
+                               if( e.getValueIsAdjusting() ) return;\r
+                               MidiSequenceTableModel sequenceModel = sequenceListTableModel.getSequenceModel(seqSelectionModel);\r
+                               if( sequenceModel == null || trackSelectionModel.isSelectionEmpty() ) {\r
+                                       midiEventsLabel.setText("MIDI Events (No track selected)");\r
+                                       eventListTableView.setModel(new MidiTrackTableModel());\r
+                               }\r
+                               else {\r
+                                       int selIndex = trackSelectionModel.getMinSelectionIndex();\r
+                                       MidiTrackTableModel trackModel = sequenceModel.getTrackModel(selIndex);\r
+                                       if( trackModel == null ) {\r
+                                               midiEventsLabel.setText("MIDI Events (No track selected)");\r
+                                               eventListTableView.setModel(new MidiTrackTableModel());\r
+                                       }\r
+                                       else {\r
+                                               midiEventsLabel.setText(\r
+                                                       String.format("MIDI Events (in track No.%d)", selIndex)\r
+                                               );\r
+                                               eventListTableView.setModel(trackModel);\r
+                                               TableColumnModel tcm = eventListTableView.getColumnModel();\r
+                                               trackModel.sizeColumnWidthToFit(tcm);\r
+                                               tcm.getColumn(MidiTrackTableModel.Column.MESSAGE.ordinal()).setCellEditor(eventCellEditor);\r
+                                       }\r
+                               }\r
+                               updateButtonStatus();\r
+                               eventSelectionModel.setSelectionInterval(0,0);\r
+                       }\r
+               });\r
                eventSelectionModel = eventListTableView.getSelectionModel();\r
                eventSelectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);\r
-               eventSelectionModel.addListSelectionListener(this);\r
-               //\r
+               eventSelectionModel.addListSelectionListener(new ListSelectionListener() {\r
+                       @Override\r
+                       public void valueChanged(ListSelectionEvent e) {\r
+                               if( e.getValueIsAdjusting() ) return;\r
+                               if( ! eventSelectionModel.isSelectionEmpty() ) {\r
+                                       MidiTrackTableModel trackModel = (MidiTrackTableModel)eventListTableView.getModel();\r
+                                       int minIndex = eventSelectionModel.getMinSelectionIndex();\r
+                                       if( trackModel.hasTrack() ) {\r
+                                               MidiEvent midiEvent = trackModel.getMidiEvent(minIndex);\r
+                                               MidiMessage msg = midiEvent.getMessage();\r
+                                               if( msg instanceof ShortMessage ) {\r
+                                                       ShortMessage sm = (ShortMessage)msg;\r
+                                                       int cmd = sm.getCommand();\r
+                                                       if( cmd == 0x80 || cmd == 0x90 || cmd == 0xA0 ) {\r
+                                                               // ノート番号を持つ場合、音を鳴らす。\r
+                                                               MidiChannel outMidiChannels[] = virtualMidiDevice.getChannels();\r
+                                                               int ch = sm.getChannel();\r
+                                                               int note = sm.getData1();\r
+                                                               int vel = sm.getData2();\r
+                                                               outMidiChannels[ch].noteOn(note, vel);\r
+                                                               outMidiChannels[ch].noteOff(note, vel);\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                                       if( pairNoteCheckbox.isSelected() ) {\r
+                                               int maxIndex = eventSelectionModel.getMaxSelectionIndex();\r
+                                               int partnerIndex;\r
+                                               for( int i=minIndex; i<=maxIndex; i++ )\r
+                                                       if(\r
+                                                               eventSelectionModel.isSelectedIndex(i) &&\r
+                                                               (partnerIndex = trackModel.getIndexOfPartnerFor(i)) >= 0 &&\r
+                                                               ! eventSelectionModel.isSelectedIndex(partnerIndex)\r
+                                                       ) eventSelectionModel.addSelectionInterval(partnerIndex, partnerIndex);\r
+                                       }\r
+                               }\r
+                               updateButtonStatus();\r
+                       }\r
+               });\r
                try {\r
-                       fileChooser = new JFileChooser();\r
-                       FileNameExtensionFilter filter = new FileNameExtensionFilter(\r
-                               "MIDI sequence (*.mid)", "mid"\r
-                       );\r
-                       fileChooser.setFileFilter(filter);\r
+                       fileChooser = new JFileChooser() {{\r
+                               setFileFilter(new FileNameExtensionFilter(\r
+                                       "MIDI sequence (*.mid)", "mid"\r
+                               ));\r
+                       }};\r
                }\r
                catch( ExceptionInInitializerError|NoClassDefFoundError|AccessControlException e ) {\r
                        // アプレットの場合、Webクライアントマシンのローカルファイルには\r
@@ -656,11 +714,9 @@ class MidiEditor extends JDialog
                                addActionListener(\r
                                        new ActionListener() {\r
                                                public void actionPerformed(ActionEvent e) {\r
-                                                       if(\r
-                                                               fileChooser == null ||\r
-                                                               fileChooser.showOpenDialog(MidiEditor.this) != JFileChooser.APPROVE_OPTION\r
-                                                       ) return;\r
-                                                       addSequenceFromMidiFile(fileChooser.getSelectedFile());\r
+                                                       int resp = fileChooser.showOpenDialog(MidiEditor.this);\r
+                                                       if( resp == JFileChooser.APPROVE_OPTION )\r
+                                                               addSequence(fileChooser.getSelectedFile());\r
                                                }\r
                                        }\r
                                );\r
@@ -670,8 +726,6 @@ class MidiEditor extends JDialog
                                addActionListener(new ActionListener() {\r
                                        @Override\r
                                        public void actionPerformed(ActionEvent e) {\r
-                                               if( fileChooser == null )\r
-                                                       return;\r
                                                MidiSequenceTableModel sequenceTableModel =\r
                                                        sequenceListTableModel.getSequenceModel(seqSelectionModel);\r
                                                String filename = sequenceTableModel.getFilename();\r
@@ -680,18 +734,20 @@ class MidiEditor extends JDialog
                                                        midiFile = new File(filename);\r
                                                        fileChooser.setSelectedFile(midiFile);\r
                                                }\r
-                                               if( fileChooser.showSaveDialog(MidiEditor.this) != JFileChooser.APPROVE_OPTION ) {\r
+                                               int resp = fileChooser.showSaveDialog(MidiEditor.this);\r
+                                               if( resp != JFileChooser.APPROVE_OPTION ) {\r
                                                        return;\r
                                                }\r
                                                midiFile = fileChooser.getSelectedFile();\r
                                                if( midiFile.exists() && ! confirm(\r
-                                                               "Overwrite " + midiFile.getName() + " ?\n"\r
-                                                                       + midiFile.getName() + " を上書きしてよろしいですか?"\r
-                                                               ) ) {\r
+                                                       "Overwrite " + midiFile.getName() + " ?\n"\r
+                                                       + midiFile.getName()\r
+                                                       + " を上書きしてよろしいですか?"\r
+                                               ) ) {\r
                                                        return;\r
                                                }\r
-                                               try ( FileOutputStream fos = new FileOutputStream(midiFile) ) {\r
-                                                       fos.write(sequenceTableModel.getMIDIdata());\r
+                                               try ( FileOutputStream out = new FileOutputStream(midiFile) ) {\r
+                                                       out.write(sequenceTableModel.getMIDIdata());\r
                                                        sequenceTableModel.setModified(false);\r
                                                }\r
                                                catch( IOException ex ) {\r
@@ -870,7 +926,7 @@ class MidiEditor extends JDialog
                        if ( (action & DnDConstants.ACTION_COPY_OR_MOVE) != 0 ) {\r
                                Transferable t = event.getTransferable();\r
                                Object data = t.getTransferData(DataFlavor.javaFileListFlavor);\r
-                               loadAndPlay((java.util.List<File>)data);\r
+                               loadAndPlay((List<File>)data);\r
                                event.dropComplete(true);\r
                                return;\r
                        }\r
@@ -881,98 +937,21 @@ class MidiEditor extends JDialog
                        event.dropComplete(false);\r
                }\r
        }\r
-       public void valueChanged(ListSelectionEvent e) {\r
-               boolean is_adjusting = e.getValueIsAdjusting();\r
-               if( is_adjusting ) return;\r
-               Object src = e.getSource();\r
-               if( src == trackSelectionModel ) {\r
-                       if(\r
-                               sequenceListTableModel.getSequenceModel(seqSelectionModel) == null\r
-                               ||\r
-                               trackSelectionModel.isSelectionEmpty()\r
-                       ) {\r
-                               midiEventsLabel.setText("MIDI Events (No track selected)");\r
-                               eventListTableView.setModel(new MidiTrackTableModel());\r
-                       }\r
-                       else {\r
-                               int sel_index = trackSelectionModel.getMinSelectionIndex();\r
-                               MidiTrackTableModel track_model\r
-                               = sequenceListTableModel.getSequenceModel(seqSelectionModel).getTrackModel(sel_index);\r
-                               if( track_model == null ) {\r
-                                       midiEventsLabel.setText("MIDI Events (No track selected)");\r
-                                       eventListTableView.setModel(new MidiTrackTableModel());\r
-                               }\r
-                               else {\r
-                                       midiEventsLabel.setText(\r
-                                               String.format("MIDI Events (in track No.%d)", sel_index)\r
-                                       );\r
-                                       eventListTableView.setModel(track_model);\r
-                                       TableColumnModel tcm = eventListTableView.getColumnModel();\r
-                                       track_model.sizeColumnWidthToFit(tcm);\r
-                                       tcm.getColumn( MidiTrackTableModel.COLUMN_MESSAGE ).setCellEditor(eventCellEditor);\r
-                               }\r
-                       }\r
-                       updateButtonStatus();\r
-                       eventSelectionModel.setSelectionInterval(0,0);\r
-               }\r
-               else if( src == eventSelectionModel ) {\r
-                       if( ! eventSelectionModel.isSelectionEmpty() ) {\r
-                               MidiTrackTableModel track_model\r
-                               = (MidiTrackTableModel)eventListTableView.getModel();\r
-                               int min_index = eventSelectionModel.getMinSelectionIndex();\r
-                               if( track_model.hasTrack() ) {\r
-                                       MidiEvent midi_event = track_model.getMidiEvent(min_index);\r
-                                       MidiMessage msg = midi_event.getMessage();\r
-                                       if( msg instanceof ShortMessage ) {\r
-                                               ShortMessage sm = (ShortMessage)msg;\r
-                                               int cmd = sm.getCommand();\r
-                                               if( cmd == 0x80 || cmd == 0x90 || cmd == 0xA0 ) {\r
-                                                       // ノート番号を持つ場合、音を鳴らす。\r
-                                                       MidiChannel out_midi_channels[] = virtualMidiDevice.getChannels();\r
-                                                       int ch = sm.getChannel();\r
-                                                       int note = sm.getData1();\r
-                                                       int vel = sm.getData2();\r
-                                                       out_midi_channels[ch].noteOn( note, vel );\r
-                                                       out_midi_channels[ch].noteOff( note, vel );\r
-                                               }\r
-                                       }\r
-                               }\r
-                               if( pairNoteCheckbox.isSelected() ) {\r
-                                       int max_index = eventSelectionModel.getMaxSelectionIndex();\r
-                                       int partner_index;\r
-                                       for( int i=min_index; i<=max_index; i++ ) {\r
-                                               if(\r
-                                                       eventSelectionModel.isSelectedIndex(i)\r
-                                                       &&\r
-                                                       (partner_index = track_model.getIndexOfPartnerFor(i)) >= 0\r
-                                                       &&\r
-                                                       ! eventSelectionModel.isSelectedIndex(partner_index)\r
-                                               ) {\r
-                                                       eventSelectionModel.addSelectionInterval(\r
-                                                               partner_index, partner_index\r
-                                                       );\r
-                                               }\r
-                                       }\r
-                               }\r
-                       }\r
-                       updateButtonStatus();\r
-               }\r
-       }\r
-       private void showError( String message ) {\r
+       private void showError(String message) {\r
                JOptionPane.showMessageDialog(\r
                        this, message,\r
                        ChordHelperApplet.VersionInfo.NAME,\r
                        JOptionPane.ERROR_MESSAGE\r
                );\r
        }\r
-       private void showWarning( String message ) {\r
+       private void showWarning(String message) {\r
                JOptionPane.showMessageDialog(\r
                        this, message,\r
                        ChordHelperApplet.VersionInfo.NAME,\r
                        JOptionPane.WARNING_MESSAGE\r
                );\r
        }\r
-       private boolean confirm( String message ) {\r
+       private boolean confirm(String message) {\r
                return JOptionPane.showConfirmDialog(\r
                        this, message,\r
                        ChordHelperApplet.VersionInfo.NAME,\r
@@ -1061,52 +1040,54 @@ class MidiEditor extends JDialog
        /**\r
         * MIDIシーケンスを追加します。\r
         * シーケンサーが停止中の場合、追加したシーケンスから再生を開始します。\r
-        * @param seq MIDIシーケンス\r
+        * @param sequence MIDIシーケンス\r
         * @return 追加先インデックス(先頭が 0)\r
         */\r
-       public int addSequence(Sequence seq) {\r
-               int lastIndex = sequenceListTableModel.addSequence(seq);\r
+       public int addSequenceAndPlay(Sequence sequence) {\r
+               int lastIndex = sequenceListTableModel.addSequence(sequence,"");\r
                if( ! sequencerModel.getSequencer().isRunning() ) {\r
-                       loadAndPlay(lastIndex);\r
+                       load(lastIndex);\r
+                       sequencerModel.start();\r
                }\r
                return lastIndex;\r
        }\r
        /**\r
-        * Base64テキストとファイル名からMIDIシーケンスを追加します。\r
-        * @param base64EncodedText Base64エンコードされたテキスト\r
-        * @param filename ファイル名\r
-        * @return 追加先インデックス(先頭が 0、失敗した場合は -1)\r
-        */\r
-       public int addSequenceFromBase64Text(String base64EncodedText, String filename) {\r
-               base64Dialog.setBase64Data(base64EncodedText);\r
-               return addSequenceFromMidiData(base64Dialog.getMIDIData(), filename);\r
-       }\r
-       /**\r
         * バイト列とファイル名からMIDIシーケンスを追加します。\r
+        * バイト列が null の場合、空のMIDIシーケンスを追加します。\r
         * @param data バイト列\r
         * @param filename ファイル名\r
         * @return 追加先インデックス(先頭が 0、失敗した場合は -1)\r
         */\r
-       public int addSequenceFromMidiData(byte[] data, String filename) {\r
+       public int addSequence(byte[] data, String filename) {\r
+               if( data == null ) {\r
+                       return sequenceListTableModel.addDefaultSequence();\r
+               }\r
                int lastIndex;\r
-               try {\r
-                       lastIndex = sequenceListTableModel.addSequence(data,filename);\r
-               } catch( InvalidMidiDataException e ) {\r
-                       showWarning("MIDI data invalid");\r
+               try (InputStream in = new ByteArrayInputStream(data)) {\r
+                       Sequence seq = MidiSystem.getSequence(in);\r
+                       lastIndex =sequenceListTableModel.addSequence(seq, filename);\r
+               } catch( IOException|InvalidMidiDataException e ) {\r
+                       showWarning(e.getMessage());\r
                        return -1;\r
                }\r
                return lastIndex;\r
        }\r
        /**\r
         * MIDIファイルから読み込んだシーケンスを追加します。\r
+        * ファイルが null の場合、空のMIDIシーケンスを追加します。\r
         * @param midiFile MIDIファイル\r
         * @return 追加先インデックス(先頭が 0、失敗した場合は -1)\r
         */\r
-       public int addSequenceFromMidiFile(File midiFile) {\r
+       public int addSequence(File midiFile) {\r
+               if( midiFile == null ) {\r
+                       return sequenceListTableModel.addDefaultSequence();\r
+               }\r
                int lastIndex;\r
-               try {\r
-                       lastIndex = sequenceListTableModel.addSequence(midiFile);\r
-               } catch( FileNotFoundException|InvalidMidiDataException e ) {\r
+               try (FileInputStream in = new FileInputStream(midiFile)) {\r
+                       Sequence seq = MidiSystem.getSequence(in);\r
+                       String filename = midiFile.getName();\r
+                       lastIndex = sequenceListTableModel.addSequence(seq, filename);\r
+               } catch( IOException|InvalidMidiDataException e ) {\r
                        showWarning(e.getMessage());\r
                        return -1;\r
                } catch( AccessControlException e ) {\r
@@ -1122,18 +1103,21 @@ class MidiEditor extends JDialog
         * @return 追加先インデックス(先頭が 0、失敗した場合は -1)\r
         */\r
        public int addSequenceFromURL(String midiFileUrl) {\r
-               int lastIndex;\r
+               Sequence seq = null;\r
+               String filename = null;\r
                try {\r
-                       lastIndex = sequenceListTableModel.addSequence(midiFileUrl);\r
-               } catch( InvalidMidiDataException e ) {\r
+                       URI uri = new URI(midiFileUrl);\r
+                       URL url = uri.toURL();\r
+                       seq = MidiSystem.getSequence(url);\r
+                       filename = url.getFile().replaceFirst("^.*/","");\r
+               } catch( URISyntaxException|IOException|InvalidMidiDataException e ) {\r
                        showWarning(e.getMessage());\r
-                       return -1;\r
                } catch( AccessControlException e ) {\r
                        showError(e.getMessage());\r
                        e.printStackTrace();\r
-                       return -1;\r
                }\r
-               return lastIndex;\r
+               if( seq == null ) return -1;\r
+               return sequenceListTableModel.addSequence(seq, filename);\r
        }\r
        /**\r
         * 指定のインデックス位置にあるMIDIシーケンスをシーケンサーにロードします。\r
@@ -1154,36 +1138,24 @@ class MidiEditor extends JDialog
                return retval;\r
        }\r
        /**\r
-        * 選択中のMIDIシーケンスをシーケンサーにロードし、再生します。\r
-        */\r
-       public void loadAndPlay() {\r
-               loadAndPlay(seqSelectionModel.getMinSelectionIndex());\r
-       }\r
-       /**\r
-        * 指定のインデックス位置にあるMIDIシーケンスをシーケンサーにロードし、\r
-        * 再生します。\r
-        * @param index MIDIシーケンスのインデックス(先頭が 0)\r
-        */\r
-       public void loadAndPlay(int index) {\r
-               load(index);\r
-               sequencerModel.start();\r
-       }\r
-       /**\r
         * 複数のMIDIファイルを読み込み、再生されていなかったら再生します。\r
+        * すでに再生されていた場合、このエディタダイアログを表示します。\r
+        *\r
         * @param fileList 読み込むMIDIファイルのリスト\r
         */\r
        public void loadAndPlay(List<File> fileList) {\r
-               int nextIndex = -1;\r
-               for( File f : fileList ) {\r
-                       int lastIndex = addSequenceFromMidiFile(f);\r
-                       if( nextIndex == -1 )\r
-                               nextIndex = lastIndex;\r
+               int firstIndex = -1;\r
+               for( File file : fileList ) {\r
+                       int lastIndex = addSequence(file);\r
+                       if( firstIndex == -1 )\r
+                               firstIndex = lastIndex;\r
                }\r
                if(sequencerModel.getSequencer().isRunning()) {\r
                        setVisible(true);\r
                }\r
-               else if( nextIndex >= 0 ) {\r
-                       loadAndPlay(nextIndex);\r
+               else if( firstIndex >= 0 ) {\r
+                       load(firstIndex);\r
+                       sequencerModel.start();\r
                }\r
        }\r
        /**\r
@@ -1271,12 +1243,6 @@ class SequenceListTableModel extends AbstractTableModel implements ChangeListene
                SEQ_NUMBER("No.", 2, Integer.class),\r
                /** 変更済みフラグ */\r
                MODIFIED("Modified", 6, Boolean.class),\r
-               /** タイミング分割形式 */\r
-               DIVISION_TYPE("DivType", 6, String.class),\r
-               /** タイミング解像度 */\r
-               RESOLUTION("Resolution", 6, Integer.class),\r
-               /** トラック数 */\r
-               TRACKS("Tracks", 6, Integer.class),\r
                /** 再生中の時間位置(分:秒) */\r
                SEQ_POSITION("Position", 6, String.class),\r
                /** シーケンスの時間長(分:秒) */\r
@@ -1284,7 +1250,13 @@ class SequenceListTableModel extends AbstractTableModel implements ChangeListene
                /** ファイル名 */\r
                FILENAME("Filename", 16, String.class),\r
                /** シーケンス名(最初のトラックの名前) */\r
-               SEQ_NAME("Sequence name", 40, String.class);\r
+               SEQ_NAME("Sequence name", 40, String.class),\r
+               /** タイミング解像度 */\r
+               RESOLUTION("Resolution", 6, Integer.class),\r
+               /** トラック数 */\r
+               TRACKS("Tracks", 6, Integer.class),\r
+               /** タイミング分割形式 */\r
+               DIVISION_TYPE("DivType", 6, String.class);\r
                private String title;\r
                private int widthRatio;\r
                private Class<?> columnClass;\r
@@ -1309,7 +1281,10 @@ class SequenceListTableModel extends AbstractTableModel implements ChangeListene
                        return total;\r
                }\r
        }\r
-       MidiSequencerModel sequencerModel;\r
+       /**\r
+        * MIDIシーケンサーモデル\r
+        */\r
+       protected MidiSequencerModel sequencerModel;\r
        /**\r
         * 新しいプレイリストのテーブルモデルを構築します。\r
         * @param deviceManager MIDIデバイスマネージャ\r
@@ -1387,10 +1362,12 @@ class SequenceListTableModel extends AbstractTableModel implements ChangeListene
                default: return "";\r
                }\r
        }\r
+       @Override\r
        public boolean isCellEditable( int row, int column ) {\r
                Column c = Column.values()[column];\r
                return c == Column.FILENAME || c == Column.SEQ_NAME ;\r
        }\r
+       @Override\r
        public void setValueAt(Object val, int row, int column) {\r
                switch(Column.values()[column]) {\r
                case FILENAME:\r
@@ -1472,17 +1449,30 @@ class SequenceListTableModel extends AbstractTableModel implements ChangeListene
                        return null;\r
                return sequenceList.get(selectedIndex);\r
        }\r
+       /**\r
+        * 指定されたシーケンスが変更されたことを通知します。\r
+        * @param sequenceTableModel MIDIシーケンスモデル\r
+        */\r
+       public void fireSequenceChanged(MidiSequenceTableModel sequenceTableModel) {\r
+               int index = sequenceList.indexOf(sequenceTableModel);\r
+               if( index < 0 ) return;\r
+               fireSequenceChanged(index,index);\r
+       }\r
+       /**\r
+        * 指定された選択範囲のシーケンスが変更されたことを通知します。\r
+        * @param selectionModel 選択状態\r
+        */\r
        public void fireSequenceChanged(ListSelectionModel selectionModel) {\r
                if( ! selectionModel.isSelectionEmpty() ) fireSequenceChanged(\r
                        selectionModel.getMinSelectionIndex(),\r
                        selectionModel.getMaxSelectionIndex()\r
                );\r
        }\r
-       public void fireSequenceChanged(MidiSequenceTableModel sequenceTableModel) {\r
-               int index = sequenceList.indexOf(sequenceTableModel);\r
-               if( index < 0 ) return;\r
-               fireSequenceChanged(index,index);\r
-       }\r
+       /**\r
+        * 指定された範囲のシーケンスが変更されたことを通知します。\r
+        * @param minIndex 範囲の最小インデックス\r
+        * @param maxIndex 範囲の最大インデックス\r
+        */\r
        public void fireSequenceChanged(int minIndex, int maxIndex) {\r
                for( int index = minIndex; index <= maxIndex; index++ ) {\r
                        MidiSequenceTableModel model = sequenceList.get(index);\r
@@ -1495,70 +1485,26 @@ class SequenceListTableModel extends AbstractTableModel implements ChangeListene
                }\r
                fireTableRowsUpdated(minIndex, maxIndex);\r
        }\r
-       public int addSequence() {\r
+       /**\r
+        * デフォルトの内容でシーケンスを作成して追加します。\r
+        * @return 追加されたシーケンスのインデックス(先頭が 0)\r
+        */\r
+       public int addDefaultSequence() {\r
                Sequence seq = (new Music.ChordProgression()).toMidiSequence();\r
                return seq == null ? -1 : addSequence(seq,null);\r
        }\r
-       public int addSequence(Sequence seq) {\r
-               return addSequence(seq, "");\r
-       }\r
+       /**\r
+        * 指定のシーケンスを追加します。\r
+        * @param seq MIDIシーケンス\r
+        * @param filename ファイル名\r
+        * @return 追加されたシーケンスのインデックス(先頭が 0)\r
+        */\r
        public int addSequence(Sequence seq, String filename) {\r
-               MidiSequenceTableModel seqModel = new MidiSequenceTableModel(this);\r
-               seqModel.setSequence(seq);\r
-               seqModel.setFilename(filename);\r
-               sequenceList.add(seqModel);\r
+               sequenceList.add(new MidiSequenceTableModel(this, seq, filename));\r
                int lastIndex = sequenceList.size() - 1;\r
                fireTableRowsInserted(lastIndex, lastIndex);\r
                return lastIndex;\r
        }\r
-       public int addSequence(byte[] midiData, String filename) throws InvalidMidiDataException {\r
-               return (midiData == null) ? addSequence() : addSequence(new ByteArrayInputStream(midiData), filename);\r
-       }\r
-       public int addSequence(File midiFile) throws InvalidMidiDataException, FileNotFoundException {\r
-               FileInputStream fis = new FileInputStream(midiFile);\r
-               int retval = addSequence(fis, midiFile.getName());\r
-               try {\r
-                       fis.close();\r
-               } catch( IOException ex ) {\r
-                       ex.printStackTrace();\r
-               }\r
-               return retval;\r
-       }\r
-       public int addSequence(InputStream in, String filename) throws InvalidMidiDataException {\r
-               if(in == null) return addSequence();\r
-               Sequence seq;\r
-               try {\r
-                       seq = MidiSystem.getSequence(in);\r
-               } catch ( InvalidMidiDataException e ) {\r
-                       throw e;\r
-               } catch ( EOFException e ) {\r
-                       // No MIDI data\r
-                       return -1;\r
-               } catch ( IOException e ) {\r
-                       e.printStackTrace();\r
-                       return -1;\r
-               }\r
-               return addSequence( seq, filename );\r
-       }\r
-       public int addSequence(String midiFileUrl) throws InvalidMidiDataException, AccessControlException {\r
-               URL url = toURL(midiFileUrl);\r
-               if( url == null ) {\r
-                       return -1;\r
-               }\r
-               Sequence seq;\r
-               try {\r
-                       seq = MidiSystem.getSequence(url);\r
-               } catch ( InvalidMidiDataException e ) {\r
-                       throw e;\r
-               } catch( EOFException e ) {\r
-                       // No MIDI data\r
-                       return -1;\r
-               } catch( IOException e ) {\r
-                       e.printStackTrace();\r
-                       return -1;\r
-               }\r
-               return addSequence(seq, url.getFile().replaceFirst("^.*/",""));\r
-       }\r
        /**\r
         * 選択したシーケンスを除去します。\r
         * @param listSelectionModel 選択状態\r
@@ -1610,27 +1556,6 @@ class SequenceListTableModel extends AbstractTableModel implements ChangeListene
                loadToSequencer( index );\r
                return true;\r
        }\r
-       /**\r
-        * 文字列をURLオブジェクトに変換\r
-        * @param urlString URL文字列\r
-        * @return URLオブジェクト\r
-        */\r
-       private URL toURL(String urlString) {\r
-               if( urlString == null || urlString.isEmpty() ) {\r
-                       return null;\r
-               }\r
-               URI uri = null;\r
-               URL url = null;\r
-               try {\r
-                       uri = new URI(urlString);\r
-                       url = uri.toURL();\r
-               } catch( URISyntaxException e ) {\r
-                       e.printStackTrace();\r
-               } catch( MalformedURLException e ) {\r
-                       e.printStackTrace();\r
-               }\r
-               return url;\r
-       }\r
 }\r
 \r
 /**\r
@@ -1679,10 +1604,18 @@ class MidiSequenceTableModel extends AbstractTableModel {
                        return total;\r
                }\r
        }\r
-       private SequenceListTableModel sequenceListTableModel;\r
+       /**\r
+        * ラップされたMIDIシーケンス\r
+        */\r
        private Sequence sequence;\r
        private SequenceTickIndex sequenceTickIndex;\r
+       /**\r
+        * MIDIファイル名\r
+        */\r
        private String filename = "";\r
+       /**\r
+        * トラックリスト\r
+        */\r
        private List<MidiTrackTableModel> trackModelList = new ArrayList<>();\r
        /**\r
         * 記録するMIDIチャンネル\r
@@ -1702,12 +1635,31 @@ class MidiSequenceTableModel extends AbstractTableModel {
                }\r
        }\r
        /**\r
-        * 新しい {@link MidiSequenceTableModel} を構築します。\r
-        * @param sequenceListTableModel プレイリスト\r
+        * 親のプレイリスト\r
+        */\r
+       private SequenceListTableModel sequenceListTableModel;\r
+       /**\r
+        * 空の {@link MidiSequenceTableModel} を構築します。\r
+        * @param sequenceListTableModel 親のプレイリスト\r
         */\r
        public MidiSequenceTableModel(SequenceListTableModel sequenceListTableModel) {\r
                this.sequenceListTableModel = sequenceListTableModel;\r
        }\r
+       /**\r
+        * MIDIシーケンスとファイル名から {@link MidiSequenceTableModel} を構築します。\r
+        * @param sequenceListTableModel 親のプレイリスト\r
+        * @param sequence MIDIシーケンス\r
+        * @param filename ファイル名\r
+        */\r
+       public MidiSequenceTableModel(\r
+               SequenceListTableModel sequenceListTableModel,\r
+               Sequence sequence,\r
+               String filename\r
+       ) {\r
+               this(sequenceListTableModel);\r
+               setSequence(sequence);\r
+               setFilename(filename);\r
+       }\r
        @Override\r
        public int getRowCount() {\r
                return sequence == null ? 0 : sequence.getTracks().length;\r
@@ -1731,12 +1683,9 @@ class MidiSequenceTableModel extends AbstractTableModel {
                Column c = Column.values()[column];\r
                switch(c) {\r
                case MUTE:\r
-               case SOLO:\r
-                       if( sequence != getSequencer().getSequence() )\r
-                               return String.class;\r
+               case SOLO: if( ! isOnSequencer() ) return String.class;\r
                        // FALLTHROUGH\r
-               default:\r
-                       return c.columnClass;\r
+               default: return c.columnClass;\r
                }\r
        }\r
        @Override\r
@@ -1746,14 +1695,11 @@ class MidiSequenceTableModel extends AbstractTableModel {
                case TRACK_NUMBER: return row;\r
                case EVENTS: return sequence.getTracks()[row].size();\r
                case MUTE:\r
-                       return (sequence == getSequencer().getSequence()) ?\r
-                               getSequencer().getTrackMute(row) : "";\r
+                       return isOnSequencer() ? getSequencer().getTrackMute(row) : "";\r
                case SOLO:\r
-                       return (sequence == getSequencer().getSequence()) ?\r
-                               getSequencer().getTrackSolo(row) : "";\r
+                       return isOnSequencer() ? getSequencer().getTrackSolo(row) : "";\r
                case RECORD_CHANNEL:\r
-                       return (sequence == getSequencer().getSequence()) ?\r
-                               trackModelList.get(row).getRecordingChannel() : "";\r
+                       return isOnSequencer() ? trackModelList.get(row).getRecordingChannel() : "";\r
                case CHANNEL: {\r
                        int ch = trackModelList.get(row).getChannel();\r
                        return ch < 0 ? "" : ch + 1 ;\r
@@ -1771,7 +1717,7 @@ class MidiSequenceTableModel extends AbstractTableModel {
                switch(c) {\r
                case MUTE:\r
                case SOLO:\r
-               case RECORD_CHANNEL: return sequence == getSequencer().getSequence();\r
+               case RECORD_CHANNEL: return isOnSequencer();\r
                case CHANNEL:\r
                case TRACK_NAME: return true;\r
                default: return false;\r
@@ -1847,7 +1793,7 @@ class MidiSequenceTableModel extends AbstractTableModel {
         * MIDIシーケンスを設定します。\r
         * @param sequence MIDIシーケンス\r
         */\r
-       public void setSequence(Sequence sequence) {\r
+       private void setSequence(Sequence sequence) {\r
                getSequencer().recordDisable(null); // The "null" means all tracks\r
                this.sequence = sequence;\r
                int oldSize = trackModelList.size();\r
@@ -1861,8 +1807,9 @@ class MidiSequenceTableModel extends AbstractTableModel {
                else {\r
                        fireTimeSignatureChanged();\r
                        Track tracks[] = sequence.getTracks();\r
-                       for( Track track : tracks )\r
+                       for(Track track : tracks) {\r
                                trackModelList.add(new MidiTrackTableModel(track, this));\r
+                       }\r
                        fireTableRowsInserted(0, tracks.length-1);\r
                }\r
        }\r
@@ -1992,7 +1939,7 @@ class MidiSequenceTableModel extends AbstractTableModel {
                        sequence.deleteTrack(tracks[i]);\r
                        trackModelList.remove(i);\r
                }\r
-               fireTableRowsDeleted( minIndex, maxIndex );\r
+               fireTableRowsDeleted(minIndex, maxIndex);\r
        }\r
        /**\r
         * MIDIシーケンサを返します。\r
@@ -2002,17 +1949,27 @@ class MidiSequenceTableModel extends AbstractTableModel {
                return sequenceListTableModel.sequencerModel.getSequencer();\r
        }\r
        /**\r
+        * このシーケンスモデルのシーケンスをシーケンサーが操作しているか調べます。\r
+        * @return シーケンサーが操作していたらtrue\r
+        */\r
+       public boolean isOnSequencer() {\r
+               return sequence == getSequencer().getSequence();\r
+       }\r
+       /**\r
         * 録音可能かどうかを返します。\r
+        *\r
+        * <p>シーケンサーにロード済みで、\r
+        * かつ録音しようとしているチャンネルの設定されたトラックが一つでもあれば、\r
+        * 録音可能です。\r
+        * </p>\r
         * @return 録音可能であればtrue\r
         */\r
        public boolean isRecordable() {\r
-               if( sequence != getSequencer().getSequence() )\r
-                       return false;\r
-               int rowCount = getRowCount();\r
-               int col = Column.RECORD_CHANNEL.ordinal();\r
-               for( int row=0; row < rowCount; row++ ) {\r
-                       if("OFF".equals(getValueAt(row, col))) continue;\r
-                       return true;\r
+               if( isOnSequencer() ) {\r
+                       int rowCount = getRowCount();\r
+                       int col = Column.RECORD_CHANNEL.ordinal();\r
+                       for( int row=0; row < rowCount; row++ )\r
+                               if( ! "OFF".equals(getValueAt(row, col)) ) return true;\r
                }\r
                return false;\r
        }\r
@@ -2021,130 +1978,185 @@ class MidiSequenceTableModel extends AbstractTableModel {
 /**\r
  * MIDIトラック(MIDIイベントリスト)テーブルモデル\r
  */\r
-class MidiTrackTableModel extends AbstractTableModel\r
-{\r
-       public static final int COLUMN_EVENT_NUMBER     = 0;\r
-       public static final int COLUMN_TICK_POSITION    = 1;\r
-       public static final int COLUMN_MEASURE_POSITION = 2;\r
-       public static final int COLUMN_BEAT_POSITION            = 3;\r
-       public static final int COLUMN_EXTRA_TICK_POSITION      = 4;\r
-       public static final int COLUMN_MESSAGE  = 5;\r
-       public static final String COLUMN_TITLES[] = {\r
-               "No.", "TickPos.", "Measure", "Beat", "ExTick", "MIDI Message",\r
-       };\r
-       public static final int column_width_ratios[] = {\r
-               30, 40, 20,20,20, 280,\r
-       };\r
+class MidiTrackTableModel extends AbstractTableModel {\r
+       /**\r
+        * 列\r
+        */\r
+       public enum Column {\r
+               /** MIDIイベント番号 */\r
+               EVENT_NUMBER("No.", 30, Integer.class),\r
+               /** tick位置 */\r
+               TICK_POSITION("TickPos.", 40, Long.class),\r
+               /** tick位置に対応する小節 */\r
+               MEASURE_POSITION("Measure", 20, Integer.class),\r
+               /** tick位置に対応する拍 */\r
+               BEAT_POSITION("Beat", 20, Integer.class),\r
+               /** tick位置に対応する余剰tick(拍に収まらずに余ったtick数) */\r
+               EXTRA_TICK_POSITION("ExTick", 20, Integer.class),\r
+               /** MIDIメッセージ */\r
+               MESSAGE("MIDI Message", 280, String.class);\r
+               private String title;\r
+               private int widthRatio;\r
+               private Class<?> columnClass;\r
+               /**\r
+                * 列の識別子を構築します。\r
+                * @param title 列のタイトル\r
+                * @param widthRatio 幅の割合\r
+                * @param columnClass 列のクラス\r
+                */\r
+               private Column(String title, int widthRatio, Class<?> columnClass) {\r
+                       this.title = title;\r
+                       this.widthRatio = widthRatio;\r
+                       this.columnClass = columnClass;\r
+               }\r
+               /**\r
+                * 幅の割合の合計を返します。\r
+                * @return 幅の割合の合計\r
+                */\r
+               public static int totalWidthRatio() {\r
+                       int total = 0;\r
+                       for( Column c : values() ) total += c.widthRatio;\r
+                       return total;\r
+               }\r
+       }\r
+       /**\r
+        * ラップされているMIDIトラック\r
+        */\r
        private Track track;\r
+       /**\r
+        * 親のシーケンスモデル\r
+        */\r
        private MidiSequenceTableModel parent;\r
-       public MidiTrackTableModel() { } // To create empty model\r
+       /**\r
+        * 空のMIDIトラックモデルを構築します。\r
+        */\r
+       public MidiTrackTableModel() { }\r
+       /**\r
+        * シーケンスに連動する空のMIDIトラックモデルを構築します。\r
+        * @parent 親のシーケンステーブルモデル\r
+        */\r
        public MidiTrackTableModel(MidiSequenceTableModel parent) {\r
                this.parent = parent;\r
        }\r
+       /**\r
+        * シーケンスを親にして、その特定のトラックに連動する\r
+        * MIDIトラックモデルを構築します。\r
+        *\r
+        * @param track ラップするMIDIトラック\r
+        * @param parent 親のシーケンスモデル\r
+        */\r
        public MidiTrackTableModel(Track track, MidiSequenceTableModel parent) {\r
                this.track = track;\r
                this.parent = parent;\r
        }\r
-       public int getRowCount() { return track == null ? 0 : track.size(); }\r
-       public int getColumnCount() { return COLUMN_TITLES.length; }\r
-       public String getColumnName(int column) { return COLUMN_TITLES[column]; }\r
+       @Override\r
+       public int getRowCount() {\r
+               return track == null ? 0 : track.size();\r
+       }\r
+       @Override\r
+       public int getColumnCount() {\r
+               return Column.values().length;\r
+       }\r
+       /**\r
+        * 列名を返します。\r
+        */\r
+       @Override\r
+       public String getColumnName(int column) {\r
+               return Column.values()[column].title;\r
+       }\r
+       /**\r
+        * 列のクラスを返します。\r
+        */\r
+       @Override\r
        public Class<?> getColumnClass(int column) {\r
-               switch(column) {\r
-               case COLUMN_EVENT_NUMBER:\r
-                       return Integer.class;\r
-               case COLUMN_TICK_POSITION:\r
-                       return Long.class;\r
-               case COLUMN_MEASURE_POSITION:\r
-               case COLUMN_BEAT_POSITION:\r
-               case COLUMN_EXTRA_TICK_POSITION:\r
-                       return Integer.class;\r
-                       // case COLUMN_MESSAGE:\r
-                       default:\r
-                               return String.class;\r
-               }\r
-               // return getValueAt(0,column).getClass();\r
+               return Column.values()[column].columnClass;\r
        }\r
+       @Override\r
        public Object getValueAt(int row, int column) {\r
-               switch(column) {\r
-               case COLUMN_EVENT_NUMBER:\r
-                       return row;\r
-\r
-               case COLUMN_TICK_POSITION:\r
-                       return track.get(row).getTick();\r
-\r
-               case COLUMN_MEASURE_POSITION:\r
+               switch(Column.values()[column]) {\r
+               case EVENT_NUMBER: return row;\r
+               case TICK_POSITION: return track.get(row).getTick();\r
+               case MEASURE_POSITION:\r
                        return parent.getSequenceTickIndex().tickToMeasure(track.get(row).getTick()) + 1;\r
-\r
-               case COLUMN_BEAT_POSITION:\r
+               case BEAT_POSITION:\r
                        parent.getSequenceTickIndex().tickToMeasure(track.get(row).getTick());\r
                        return parent.getSequenceTickIndex().lastBeat + 1;\r
-\r
-               case COLUMN_EXTRA_TICK_POSITION:\r
+               case EXTRA_TICK_POSITION:\r
                        parent.getSequenceTickIndex().tickToMeasure(track.get(row).getTick());\r
                        return parent.getSequenceTickIndex().lastExtraTick;\r
-\r
-               case COLUMN_MESSAGE:\r
-                       return msgToString(track.get(row).getMessage());\r
-\r
+               case MESSAGE: return msgToString(track.get(row).getMessage());\r
                default: return "";\r
                }\r
        }\r
+       /**\r
+        * セルを編集できるときtrue、編集できないときfalseを返します。\r
+        */\r
+       @Override\r
        public boolean isCellEditable(int row, int column) {\r
-               switch(column) {\r
-               // case COLUMN_EVENT_NUMBER:\r
-               case COLUMN_TICK_POSITION:\r
-               case COLUMN_MEASURE_POSITION:\r
-               case COLUMN_BEAT_POSITION:\r
-               case COLUMN_EXTRA_TICK_POSITION:\r
-               case COLUMN_MESSAGE:\r
-                       return true;\r
+               switch(Column.values()[column]) {\r
+               case TICK_POSITION:\r
+               case MEASURE_POSITION:\r
+               case BEAT_POSITION:\r
+               case EXTRA_TICK_POSITION:\r
+               case MESSAGE: return true;\r
                default: return false;\r
                }\r
        }\r
+       /**\r
+        * セルの値を変更します。\r
+        */\r
+       @Override\r
        public void setValueAt(Object value, int row, int column) {\r
-               long tick;\r
-               switch(column) {\r
-               // case COLUMN_EVENT_NUMBER:\r
-               case COLUMN_TICK_POSITION:\r
-                       tick = (Long)value;\r
-                       break;\r
-               case COLUMN_MEASURE_POSITION:\r
-                       tick = parent.getSequenceTickIndex().measureToTick(\r
+               long newTick;\r
+               switch(Column.values()[column]) {\r
+               case TICK_POSITION: newTick = (Long)value; break;\r
+               case MEASURE_POSITION:\r
+                       newTick = parent.getSequenceTickIndex().measureToTick(\r
                                (Integer)value - 1,\r
-                               (Integer)getValueAt( row, COLUMN_BEAT_POSITION ) - 1,\r
-                               (Integer)getValueAt( row, COLUMN_EXTRA_TICK_POSITION )\r
+                               (Integer)getValueAt( row, Column.BEAT_POSITION.ordinal() ) - 1,\r
+                               (Integer)getValueAt( row, Column.TICK_POSITION.ordinal() )\r
                        );\r
                        break;\r
-               case COLUMN_BEAT_POSITION:\r
-                       tick = parent.getSequenceTickIndex().measureToTick(\r
-                               (Integer)getValueAt( row, COLUMN_MEASURE_POSITION ) - 1,\r
+               case BEAT_POSITION:\r
+                       newTick = parent.getSequenceTickIndex().measureToTick(\r
+                               (Integer)getValueAt( row, Column.MEASURE_POSITION.ordinal() ) - 1,\r
                                (Integer)value - 1,\r
-                               (Integer)getValueAt( row, COLUMN_EXTRA_TICK_POSITION )\r
+                               (Integer)getValueAt( row, Column.EXTRA_TICK_POSITION.ordinal() )\r
                        );\r
                        break;\r
-               case COLUMN_EXTRA_TICK_POSITION:\r
-                       tick = parent.getSequenceTickIndex().measureToTick(\r
-                               (Integer)getValueAt( row, COLUMN_MEASURE_POSITION ) - 1,\r
-                               (Integer)getValueAt( row, COLUMN_BEAT_POSITION ) - 1,\r
+               case EXTRA_TICK_POSITION:\r
+                       newTick = parent.getSequenceTickIndex().measureToTick(\r
+                               (Integer)getValueAt( row, Column.MEASURE_POSITION.ordinal() ) - 1,\r
+                               (Integer)getValueAt( row, Column.BEAT_POSITION.ordinal() ) - 1,\r
                                (Integer)value\r
                        );\r
                        break;\r
-               case COLUMN_MESSAGE:\r
-                       return;\r
                default: return;\r
                }\r
-               changeEventTick(row,tick);\r
-       }\r
-       public void sizeColumnWidthToFit( TableColumnModel column_model ) {\r
-               int total_width = column_model.getTotalColumnWidth();\r
-               int i, total_width_ratio = 0;\r
-               for( i=0; i<column_width_ratios.length; i++ ) {\r
-                       total_width_ratio += column_width_ratios[i];\r
+               MidiEvent oldMidiEvent = track.get(row);\r
+               if( oldMidiEvent.getTick() == newTick ) {\r
+                       return;\r
                }\r
-               for( i=0; i<column_width_ratios.length; i++ ) {\r
-                       column_model.getColumn(i).setPreferredWidth(\r
-                               total_width * column_width_ratios[i] / total_width_ratio\r
-                       );\r
+               MidiMessage msg = oldMidiEvent.getMessage();\r
+               MidiEvent newMidiEvent = new MidiEvent(msg,newTick);\r
+               track.remove(oldMidiEvent);\r
+               track.add(newMidiEvent);\r
+               fireTableDataChanged();\r
+               if( MIDISpec.isEOT(msg) ) {\r
+                       // EOTの場所が変わると曲の長さが変わるので、親モデルへ通知する。\r
+                       parent.fireSequenceChanged();\r
+               }\r
+       }\r
+       /**\r
+        * 列に合わせて幅を調整します。\r
+        * @param columnModel テーブル列モデル\r
+        */\r
+       public void sizeColumnWidthToFit(TableColumnModel columnModel) {\r
+               int totalWidth = columnModel.getTotalColumnWidth();\r
+               int totalWidthRatio = Column.totalWidthRatio();\r
+               for( Column c : Column.values() ) {\r
+                       int w = totalWidth * c.widthRatio / totalWidthRatio;\r
+                       columnModel.getColumn(c.ordinal()).setPreferredWidth(w);\r
                }\r
        }\r
        /**\r
@@ -2153,7 +2165,7 @@ class MidiTrackTableModel extends AbstractTableModel
         */\r
        public Track getTrack() { return track; }\r
        /**\r
-        * 文字列としてトラック名を返します。\r
+        * トラック名を返します。\r
         */\r
        @Override\r
        public String toString() { return MIDISpec.getNameOf(track); }\r
@@ -2170,62 +2182,68 @@ class MidiTrackTableModel extends AbstractTableModel
                fireTableDataChanged();\r
                return true;\r
        }\r
-       private String rec_ch = "OFF";\r
+       private String recordingChannel = "OFF";\r
        /**\r
         * 録音中のMIDIチャンネルを返します。\r
         * @return 録音中のMIDIチャンネル\r
         */\r
-       public String getRecordingChannel() { return rec_ch; }\r
+       public String getRecordingChannel() { return recordingChannel; }\r
        /**\r
         * 録音中のMIDIチャンネルを設定します。\r
-        * @param ch_str 録音中のMIDIチャンネル\r
+        * @param recordingChannel 録音中のMIDIチャンネル\r
         */\r
-       public void setRecordingChannel(String ch_str) {\r
+       public void setRecordingChannel(String recordingChannel) {\r
                Sequencer sequencer = parent.getSequencer();\r
-               if( ch_str.equals("OFF") ) {\r
+               if( recordingChannel.equals("OFF") ) {\r
                        sequencer.recordDisable( track );\r
                }\r
-               else if( ch_str.equals("ALL") ) {\r
+               else if( recordingChannel.equals("ALL") ) {\r
                        sequencer.recordEnable( track, -1 );\r
                }\r
                else {\r
                        try {\r
-                               int ch = Integer.decode(ch_str).intValue() - 1;\r
+                               int ch = Integer.decode(recordingChannel).intValue() - 1;\r
                                sequencer.recordEnable( track, ch );\r
                        } catch( NumberFormatException nfe ) {\r
                                sequencer.recordDisable( track );\r
-                               rec_ch = "OFF";\r
+                               this.recordingChannel = "OFF";\r
                                return;\r
                        }\r
                }\r
-               rec_ch = ch_str;\r
+               this.recordingChannel = recordingChannel;\r
        }\r
-       //\r
-       // 対象MIDIチャンネル\r
+       /**\r
+        * このトラックの対象MIDIチャンネルを返します。\r
+        * <p>全てのチャンネルメッセージが同じMIDIチャンネルの場合、\r
+        * そのMIDIチャンネルを返します。\r
+        * MIDIチャンネルの異なるチャンネルメッセージが一つでも含まれていた場合、\r
+        * -1 を返します。\r
+        * </p>\r
+        * @return 対象MIDIチャンネル(不統一の場合 -1)\r
+        */\r
        public int getChannel() {\r
-               MidiMessage msg;\r
-               ShortMessage smsg;\r
-               int index, ch, prev_ch = -1, track_size = track.size();\r
-               for( index=0; index < track_size; index++ ) {\r
-                       msg = track.get(index).getMessage();\r
+               int prevCh = -1;\r
+               int trackSize = track.size();\r
+               for( int index=0; index < trackSize; index++ ) {\r
+                       MidiMessage msg = track.get(index).getMessage();\r
                        if( ! (msg instanceof ShortMessage) )\r
                                continue;\r
-                       smsg = (ShortMessage)msg;\r
+                       ShortMessage smsg = (ShortMessage)msg;\r
                        if( ! MIDISpec.isChannelMessage(smsg) )\r
                                continue;\r
-                       ch = smsg.getChannel();\r
-                       if( prev_ch >= 0 && prev_ch != ch ) {\r
-                               // MIDIチャンネルが統一されていない場合\r
+                       int ch = smsg.getChannel();\r
+                       if( prevCh >= 0 && prevCh != ch ) {\r
                                return -1;\r
                        }\r
-                       prev_ch = ch;\r
+                       prevCh = ch;\r
                }\r
-               // すべてのMIDIチャンネルが同じならそれを返す\r
-               return prev_ch;\r
+               return prevCh;\r
        }\r
-       public void setChannel(int ch) {\r
-               // すべてのチャンネルメッセージに対して\r
-               // 同一のMIDIチャンネルをセットする\r
+       /**\r
+        * 指定されたMIDIチャンネルをすべてのチャンネルメッセージに対して設定します。\r
+        * @param channel MIDIチャンネル\r
+        */\r
+       public void setChannel(int channel) {\r
                int track_size = track.size();\r
                for( int index=0; index < track_size; index++ ) {\r
                        MidiMessage msg = track.get(index).getMessage();\r
@@ -2234,11 +2252,11 @@ class MidiTrackTableModel extends AbstractTableModel
                        ShortMessage smsg = (ShortMessage)msg;\r
                        if( ! MIDISpec.isChannelMessage(smsg) )\r
                                continue;\r
-                       if( smsg.getChannel() == ch )\r
+                       if( smsg.getChannel() == channel )\r
                                continue;\r
                        try {\r
                                smsg.setMessage(\r
-                                       smsg.getCommand(), ch,\r
+                                       smsg.getCommand(), channel,\r
                                        smsg.getData1(), smsg.getData2()\r
                                );\r
                        }\r
@@ -2247,57 +2265,34 @@ class MidiTrackTableModel extends AbstractTableModel
                        }\r
                        parent.setModified(true);\r
                }\r
-               parent.fireTrackChanged( track );\r
-               parent.fireSequenceChanged();\r
+               parent.fireTrackChanged(track);\r
                fireTableDataChanged();\r
        }\r
        /**\r
-        * MIDIイベントのtick位置を変更します。\r
-        * @param row 行インデックス\r
-        * @param new_tick 新しいtick位置\r
-        */\r
-       public void changeEventTick(int row, long new_tick) {\r
-               MidiEvent old_midi_event = track.get(row);\r
-               if( old_midi_event.getTick() == new_tick ) {\r
-                       return;\r
-               }\r
-               MidiMessage msg = old_midi_event.getMessage();\r
-               MidiEvent new_midi_event = new MidiEvent(msg,new_tick);\r
-               track.remove(old_midi_event);\r
-               track.add(new_midi_event);\r
-               fireTableDataChanged();\r
-               if( MIDISpec.isEOT(msg) ) {\r
-                       // EOTの場所が変わると曲の長さが変わるので、親モデルへ通知する。\r
-                       parent.fireSequenceChanged();\r
-               }\r
-       }\r
-       /**\r
-        * MIDI tick を行インデックスに変換します。\r
-        * 検索はバイナリーサーチで行われます。\r
+        * 指定の MIDI tick 位置にあるイベントを二分探索し、\r
+        * そのイベントの行インデックスを返します。\r
         * @param tick MIDI tick\r
         * @return 行インデックス\r
         */\r
-       public int tickToIndex( long tick ) {\r
+       public int tickToIndex(long tick) {\r
                if( track == null )\r
                        return 0;\r
-               int min_index = 0;\r
-               int max_index = track.size() - 1;\r
-               long current_tick;\r
-               int current_index;\r
-               while( min_index < max_index ) {\r
-                       current_index = (min_index + max_index) / 2 ;\r
-                       current_tick = track.get(current_index).getTick();\r
-                       if( tick > current_tick ) {\r
-                               min_index = current_index + 1;\r
+               int minIndex = 0;\r
+               int maxIndex = track.size() - 1;\r
+               while( minIndex < maxIndex ) {\r
+                       int currentIndex = (minIndex + maxIndex) / 2 ;\r
+                       long currentTick = track.get(currentIndex).getTick();\r
+                       if( tick > currentTick ) {\r
+                               minIndex = currentIndex + 1;\r
                        }\r
-                       else if( tick < current_tick ) {\r
-                               max_index = current_index - 1;\r
+                       else if( tick < currentTick ) {\r
+                               maxIndex = currentIndex - 1;\r
                        }\r
                        else {\r
-                               return current_index;\r
+                               return currentIndex;\r
                        }\r
                }\r
-               return (min_index + max_index) / 2;\r
+               return (minIndex + maxIndex) / 2;\r
        }\r
        /**\r
         * NoteOn/NoteOff ペアの一方の行インデックスから、\r
@@ -2305,7 +2300,7 @@ class MidiTrackTableModel extends AbstractTableModel
         * @param index 行インデックス\r
         * @return ペアを構成する相手の行インデックス(ない場合は -1)\r
         */\r
-       public int getIndexOfPartnerFor( int index ) {\r
+       public int getIndexOfPartnerFor(int index) {\r
                if( track == null || index >= track.size() )\r
                        return -1;\r
                MidiMessage msg = track.get(index).getMessage();\r
@@ -2366,14 +2361,6 @@ class MidiTrackTableModel extends AbstractTableModel
                return -1;\r
        }\r
        /**\r
-        * 指定のMIDIメッセージが拍子記号かどうか調べます。\r
-        * @param msg MIDIメッセージ\r
-        * @return 拍子記号のときtrue\r
-        */\r
-       public boolean isTimeSignature( MidiMessage msg ) {\r
-               return (msg instanceof MetaMessage) && ((MetaMessage)msg).getType() == 0x58;\r
-       }\r
-       /**\r
         * ノートメッセージかどうか調べます。\r
         * @param index 行インデックス\r
         * @return Note On または Note Off のとき true\r
@@ -2416,7 +2403,7 @@ class MidiTrackTableModel extends AbstractTableModel
        public boolean addMidiEvent(MidiEvent midiEvent) {\r
                if( !(track.add(midiEvent)) )\r
                        return false;\r
-               if( isTimeSignature(midiEvent.getMessage()) )\r
+               if( MIDISpec.isTimeSignature(midiEvent.getMessage()) )\r
                        parent.fireTimeSignatureChanged();\r
                parent.fireTrackChanged(track);\r
                int last_index = track.size() - 1;\r
@@ -2447,11 +2434,11 @@ class MidiTrackTableModel extends AbstractTableModel
                        }\r
                        if( ! track.add(new MidiEvent(msg, newTick)) ) continue;\r
                        done = true;\r
-                       if( isTimeSignature(msg) ) hasTimeSignature = true;\r
+                       if( MIDISpec.isTimeSignature(msg) ) hasTimeSignature = true;\r
                }\r
                if( done ) {\r
                        if( hasTimeSignature ) parent.fireTimeSignatureChanged();\r
-                       parent.fireTrackChanged( track );\r
+                       parent.fireTrackChanged(track);\r
                        int lastIndex = track.size() - 1;\r
                        int oldLastIndex = lastIndex - midiEvents.length;\r
                        fireTableRowsInserted(oldLastIndex, lastIndex);\r
@@ -2463,12 +2450,13 @@ class MidiTrackTableModel extends AbstractTableModel
         * @param midiEvents 除去するMIDIイベント\r
         */\r
        public void removeMidiEvents(MidiEvent midiEvents[]) {\r
-               boolean hasTimeSignature = false;\r
+               boolean hadTimeSignature = false;\r
                for( MidiEvent e : midiEvents ) {\r
-                       if(isTimeSignature(e.getMessage())) hasTimeSignature = true;\r
+                       if( MIDISpec.isTimeSignature(e.getMessage()) )\r
+                               hadTimeSignature = true;\r
                        track.remove(e);\r
                }\r
-               if(hasTimeSignature) parent.fireTimeSignatureChanged();\r
+               if( hadTimeSignature ) parent.fireTimeSignatureChanged();\r
                parent.fireTrackChanged(track);\r
                int lastIndex = track.size() - 1;\r
                int oldLastIndex = lastIndex + midiEvents.length;\r
index c7da9ee..40c39c9 100644 (file)
@@ -103,7 +103,7 @@ class MeasureIndicator extends JPanel implements ChangeListener {
                public MeasurePositionLabel() {\r
                        setFont( getFont().deriveFont(getFont().getSize2D() + 4) );\r
                        setForeground( new Color(0x80,0x00,0x00) );\r
-                       setText( "0001:01" );\r
+                       setText("0001:01");\r
                        setToolTipText("Measure:beat position - 何小節目:何拍目");\r
                }\r
                public boolean setMeasure(int measure, int beat) {\r
@@ -115,7 +115,7 @@ class MeasureIndicator extends JPanel implements ChangeListener {
        }\r
        private static class MeasureLengthLabel extends MeasureLabel {\r
                public MeasureLengthLabel() {\r
-                       setText( "/0000" );\r
+                       setText("/0000");\r
                        setToolTipText("Measure length - 小節の数");\r
                }\r
                public boolean setMeasure(int measure) {\r
@@ -171,9 +171,46 @@ class MeasureIndicator extends JPanel implements ChangeListener {
 /**\r
  * MIDIシーケンサモデル\r
  */\r
-class MidiSequencerModel extends MidiConnecterListModel\r
-       implements MetaEventListener, BoundedRangeModel, ActionListener\r
-{\r
+class MidiSequencerModel extends MidiConnecterListModel implements BoundedRangeModel {\r
+       /**\r
+        * MIDIシーケンサモデルを構築します。\r
+        * @param deviceModelList 親のMIDIデバイスモデルリスト\r
+        * @param sequencer シーケンサーMIDIデバイス\r
+        * @param modelList MIDIコネクタリストモデルのリスト\r
+        */\r
+       public MidiSequencerModel(\r
+               MidiDeviceModelList deviceModelList,\r
+               Sequencer sequencer,\r
+               List<MidiConnecterListModel> modelList\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 || MidiSequencerModel.this.deviceModelList.editorDialog.loadNext(1) ) {\r
+                                               start();\r
+                                       }\r
+                                       else {\r
+                                               stop();\r
+                                       }\r
+                               }\r
+                       }\r
+               });\r
+       }\r
        /**\r
         * MIDIデバイスモデルリスト\r
         */\r
@@ -195,21 +232,6 @@ class MidiSequencerModel extends MidiConnecterListModel
                );\r
        }};\r
        /**\r
-        * MIDIシーケンサモデルを構築します。\r
-        * @param deviceModelList 親のMIDIデバイスモデルリスト\r
-        * @param sequencer シーケンサーMIDIデバイス\r
-        * @param modelList MIDIコネクタリストモデルのリスト\r
-        */\r
-       public MidiSequencerModel(\r
-               MidiDeviceModelList deviceModelList,\r
-               Sequencer sequencer,\r
-               List<MidiConnecterListModel> modelList\r
-       ) {\r
-               super(sequencer, modelList);\r
-               this.deviceModelList = deviceModelList;\r
-               sequencer.addMetaEventListener(this);\r
-       }\r
-       /**\r
         * MIDIシーケンサを返します。\r
         * @return MIDIシーケンサ\r
         */\r
@@ -251,16 +273,21 @@ class MidiSequencerModel extends MidiConnecterListModel
        /**\r
         * シーケンサに合わせてミリ秒位置を更新するタイマー\r
         */\r
-       private javax.swing.Timer timeRangeUpdater = new javax.swing.Timer(20,this);\r
-       @Override\r
-       public void actionPerformed(ActionEvent e) {\r
-               if( valueIsAdjusting ) {\r
-                       // 手動で移動中の場合、タイマーによる更新は不要\r
-                       return;\r
+       private javax.swing.Timer timeRangeUpdater = new javax.swing.Timer(\r
+               20,\r
+               new ActionListener(){\r
+                       @Override\r
+                       public void actionPerformed(ActionEvent e) {\r
+                               if( valueIsAdjusting || ! getSequencer().isRunning() ) {\r
+                                       // 手動で移動中の場合や、シーケンサが止まっている場合は、\r
+                                       // タイマーによる更新は不要\r
+                                       return;\r
+                               }\r
+                               // リスナーに読み込みを促す\r
+                               fireStateChanged();\r
+                       }\r
                }\r
-               // リスナーに読み込みを促す\r
-               fireStateChanged();\r
-       }\r
+       );\r
        /**\r
         * このモデルのMIDIシーケンサを開始します。\r
         */\r
@@ -386,29 +413,6 @@ class MidiSequencerModel extends MidiConnecterListModel
                }\r
        };\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 || deviceModelList.editorDialog.loadNext(1) ) {\r
-                               start();\r
-                       }\r
-                       else {\r
-                               stop();\r
-                       }\r
-               }\r
-       }\r
-       /**\r
         * MIDIトラックリストテーブルモデル\r
         */\r
        private MidiSequenceTableModel sequenceTableModel = null;\r
index c9beadf..ddbd405 100644 (file)
@@ -1,3 +1,6 @@
+import java.util.HashMap;\r
+import java.util.Map;\r
+\r
 import javax.sound.midi.InvalidMidiDataException;\r
 import javax.sound.midi.MetaMessage;\r
 import javax.sound.midi.MidiEvent;\r
@@ -12,40 +15,71 @@ public class MIDISpec {
        public static final int MAX_CHANNELS = 16;\r
        public static final int PITCH_BEND_NONE = 8192;\r
        /**\r
+        * メタメッセージタイプ名マップ\r
+        */\r
+       private static final Map<Integer,String>\r
+               META_MESSAGE_TYPE_NAMES = new HashMap<Integer,String>() {\r
+                       {\r
+                               put(0x00, "Seq Number");\r
+                               put(0x01, "Text");\r
+                               put(0x02, "Copyright");\r
+                               put(0x03, "Seq/Track Name");\r
+                               put(0x04, "Instrument Name");\r
+                               put(0x05, "Lyric");\r
+                               put(0x06, "Marker");\r
+                               put(0x07, "Cue Point");\r
+                               put(0x08, "Program Name");\r
+                               put(0x09, "Device Name");\r
+                               put(0x20, "MIDI Ch.Prefix");\r
+                               put(0x21, "MIDI Output Port");\r
+                               put(0x2F, "End Of Track");\r
+                               put(0x51, "Tempo");\r
+                               put(0x54, "SMPTE Offset");\r
+                               put(0x58, "Time Signature");\r
+                               put(0x59, "Key Signature");\r
+                               put(0x7F, "Sequencer Specific");\r
+                       }\r
+               };\r
+       /**\r
         * メタメッセージタイプの名前を返します。\r
         * @param metaMessageType メタメッセージタイプ\r
         * @return メタメッセージタイプの名前\r
         */\r
-       public static String getMetaName( int metaMessageType ) {\r
-               if( metaMessageType < 0x10 ) {\r
-                       return META_MESSAGE_TYPE_NAMES[metaMessageType];\r
-               }\r
-               switch( metaMessageType ) {\r
-               case 0x20: return "MIDI Ch.Prefix";\r
-               case 0x21: return "MIDI Output Port";\r
-               case 0x2F: return "End Of Track";\r
-               case 0x51: return "Tempo";\r
-               case 0x54: return "SMPTE Offset";\r
-               case 0x58: return "Time Signature";\r
-               case 0x59: return "Key Signature";\r
-               case 0x7F: return "Sequencer Specific";\r
-               }\r
-               return null;\r
+       public static String getMetaName(int metaMessageType) {\r
+               return META_MESSAGE_TYPE_NAMES.get(metaMessageType);\r
        }\r
        /**\r
         * メタメッセージタイプがテキストのつくものか調べます。\r
         * @param metaMessageType メタメッセージタイプ\r
         * @return テキストがつくときtrue\r
         */\r
-       public static boolean hasMetaText( int metaMessageType ) {\r
+       public static boolean hasMetaText(int metaMessageType) {\r
                return (metaMessageType > 0 && metaMessageType < 10);\r
        }\r
        /**\r
+        * メタメッセージタイプが拍子記号か調べます。\r
+        * @param metaMessageType メタメッセージタイプ\r
+        * @return 拍子記号ならtrue\r
+        */\r
+       public static boolean isTimeSignature(int metaMessageType) {\r
+               return metaMessageType == 0x58;\r
+       }\r
+       /**\r
+        * MIDIメッセージが拍子記号か調べます。\r
+        * @param msg MIDIメッセージ\r
+        * @return 拍子記号ならtrue\r
+        */\r
+       public static boolean isTimeSignature(MidiMessage midiMessage) {\r
+               if ( !(midiMessage instanceof MetaMessage) )\r
+                       return false;\r
+               return isTimeSignature( ((MetaMessage)midiMessage).getType() );\r
+       }\r
+       /**\r
         * メタメッセージタイプが EOT (End Of Track) か調べます。\r
         * @param metaMessageType メタメッセージタイプ\r
         * @return EOTならtrue\r
         */\r
-       public static boolean isEOT( int metaMessageType ) {\r
+       public static boolean isEOT(int metaMessageType) {\r
                return metaMessageType == 0x2F;\r
        }\r
        /**\r
@@ -53,7 +87,7 @@ public class MIDISpec {
         * @param midiMessage MIDIメッセージ\r
         * @return EOTならtrue\r
         */\r
-       public static boolean isEOT( MidiMessage midiMessage ) {\r
+       public static boolean isEOT(MidiMessage midiMessage) {\r
                if ( !(midiMessage instanceof MetaMessage) )\r
                        return false;\r
                return isEOT( ((MetaMessage)midiMessage).getType() );\r
@@ -184,12 +218,6 @@ public class MIDISpec {
                        if( setNameOf(track,name) ) return true;\r
                return false;\r
        }\r
-       private static final String META_MESSAGE_TYPE_NAMES[] = {\r
-               "Seq Number", "Text", "Copyright", "Seq/Track Name",\r
-               "Instrument Name", "Lyric", "Marker","Cue Point",\r
-               "Program Name", "Device Name", null, null,\r
-               null, null, null, null\r
-       };\r
        ///////////////////////////////////////////////////////////////////\r
        //\r
        // Channel Message / System Message\r
index 801c04a..64d5a0c 100644 (file)
@@ -1648,10 +1648,17 @@ public class Music {
                        return str;\r
                }\r
 \r
-               // 指定された小節数、キー、拍子に合わせたコード進行をランダムに自動生成\r
-               //\r
+               /**\r
+                * デフォルトの設定でコード進行を構築します。\r
+                */\r
                public ChordProgression() { }\r
-               public ChordProgression( int measure_length, int timesig_upper ) {\r
+               /**\r
+                * 指定された小節数、キー、拍子に合わせたコード進行を構築します。\r
+                * コード進行の内容は、ランダムに自動生成されます。\r
+                * @param measureLength 小節の長さ\r
+                * @param timeSignatureUpper 拍子の分子\r
+                */\r
+               public ChordProgression( int measureLength, int timeSignatureUpper ) {\r
                        int key_co5 = (int)(Math.random() * 12) - 5;\r
                        key = new Key( key_co5, Key.MAJOR );\r
                        lines = new Vector<Line>();\r
@@ -1660,11 +1667,11 @@ public class Music {
                        Chord chord, prev_chord = new Chord(new NoteSymbol(key_co5));\r
                        int co5_offset, prev_co5_offset;\r
                        double r;\r
-                       for( int mp=0; mp<measure_length; mp++ ) {\r
-                               is_end = (mp == 0 || mp == measure_length - 1); // 最初または最後の小節かを覚えておく\r
+                       for( int mp=0; mp<measureLength; mp++ ) {\r
+                               is_end = (mp == 0 || mp == measureLength - 1); // 最初または最後の小節かを覚えておく\r
                                Measure measure = new Measure();\r
                                ChordStroke last_chord_stroke = null;\r
-                               for( int i=0; i<timesig_upper; i++ ) {\r
+                               for( int i=0; i<timeSignatureUpper; i++ ) {\r
                                        if(\r
                                                i % 4 == 2 && Math.random() < 0.8\r
                                                ||\r
index dc871a5..3cd81f7 100644 (file)
@@ -1,5 +1,4 @@
 \r
-import java.awt.Component;\r
 import java.awt.Dimension;\r
 import java.awt.Graphics;\r
 import java.awt.Graphics2D;\r
@@ -40,28 +39,19 @@ import javax.swing.event.ChangeListener;
  * 新しいMIDIシーケンスを生成するダイアログ\r
  */\r
 class NewSequenceDialog extends JDialog {\r
-       public static final Insets      ZERO_INSETS = new Insets(0,0,0,0);\r
-       private JTextArea chordText = new JTextArea(\r
-               "Key: C\nC G/B | Am Em/G | F C/E | Dm7 G7 C % | F G7 | Csus4 C\n",\r
-               18, 30\r
-       );\r
-       private JTextField seqNameText = new JTextField();\r
-       private static class PPQSelectionComboBox extends JComboBox<Integer> {\r
-               private static final int[] PPQList = {\r
-                       48,60,80,96,120,160,192,240,320,384,480,960\r
-               };\r
-               public PPQSelectionComboBox() {\r
-                       for(int ppq : PPQList) addItem(ppq);\r
-               }\r
-               public int getPPQ() {\r
-                       return (Integer) getSelectedItem();\r
-               }\r
-       }\r
-       private PPQSelectionComboBox ppqComboBox = new PPQSelectionComboBox();\r
-       private TimeSignatureSelecter timesigSelecter = new TimeSignatureSelecter();\r
-       private TempoSelecter tempoSelecter = new TempoSelecter();\r
-       private MeasureSelecter measureSelecter = new MeasureSelecter();\r
-       private TrackSpecPanel trackSpecPanel = new TrackSpecPanel();\r
+       public static final Insets ZERO_INSETS = new Insets(0,0,0,0);\r
+       private static final Integer[] PPQList = {\r
+               48,60,80,96,120,160,192,240,320,384,480,960\r
+       };\r
+       private static final String INITIAL_CHORD_STRING =\r
+               "Key: C\nC G/B | Am Em/G | F C/E | Dm7 G7 C % | F G7 | Csus4 C\n";\r
+       private JTextArea chordText;\r
+       private JTextField seqNameText;\r
+       private JComboBox<Integer> ppqComboBox;\r
+       private TimeSignatureSelecter timesigSelecter;\r
+       private TempoSelecter tempoSelecter;\r
+       private MeasureSelecter measureSelecter;\r
+       private TrackSpecPanel trackSpecPanel;\r
        private MidiEditor midiEditor;\r
        public NewSequenceDialog(MidiEditor midiEditor) {\r
                this.midiEditor = midiEditor;\r
@@ -72,29 +62,31 @@ class NewSequenceDialog extends JDialog {
                                add(new JPanel() {{\r
                                        setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));\r
                                        add(new JLabel("Sequence name:"));\r
-                                       add(seqNameText);\r
+                                       add(seqNameText = new JTextField());\r
                                }});\r
                                add(new JPanel() {{\r
                                        setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));\r
                                        add(new JLabel("Resolution in PPQ ="));\r
-                                       add(ppqComboBox);\r
-                                       add(measureSelecter);\r
+                                       add(ppqComboBox = new JComboBox<Integer>(PPQList));\r
+                                       add(measureSelecter = new MeasureSelecter());\r
                                }});\r
                                add(new JButton("Randomize (Tempo, Time signature, Chord progression)") {{\r
                                        setMargin(ZERO_INSETS);\r
                                        addActionListener(new ActionListener() {\r
                                                @Override\r
                                                public void actionPerformed(ActionEvent e) {\r
-                                                       setRandomChordProgression(measureSelecter.getMeasureDuration());\r
+                                                       setRandomChordProgression(\r
+                                                               measureSelecter.getMeasureDuration()\r
+                                                       );\r
                                                }\r
                                        });\r
                                }});\r
                                add(new JPanel() {{\r
                                        setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));\r
-                                       add(tempoSelecter);\r
+                                       add(tempoSelecter = new TempoSelecter());\r
                                        add(new JPanel() {{\r
                                                add(new JLabel("Time signature ="));\r
-                                               add(timesigSelecter);\r
+                                               add(timesigSelecter = new TimeSignatureSelecter());\r
                                        }});\r
                                }});\r
                                add(new JPanel() {{\r
@@ -146,7 +138,9 @@ class NewSequenceDialog extends JDialog {
                                                });\r
                                        }});\r
                                }});\r
-                               add(new JScrollPane((Component)chordText));\r
+                               add(new JScrollPane(\r
+                                       chordText = new JTextArea(INITIAL_CHORD_STRING, 18, 30)\r
+                               ));\r
                                add(new JPanel() {{\r
                                        setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));\r
                                        add(new JButton(\r
@@ -157,14 +151,14 @@ class NewSequenceDialog extends JDialog {
                                                addActionListener(new ActionListener() {\r
                                                        @Override\r
                                                        public void actionPerformed(ActionEvent e) {\r
-                                                               NewSequenceDialog.this.midiEditor.addSequence(getMidiSequence());\r
+                                                               NewSequenceDialog.this.midiEditor.addSequenceAndPlay(getMidiSequence());\r
                                                                NewSequenceDialog.this.setVisible(false);\r
                                                        }\r
                                                });\r
                                        }});\r
                                }});\r
                        }});\r
-                       add("Track", trackSpecPanel);\r
+                       add("Track", trackSpecPanel = new TrackSpecPanel());\r
                }});\r
                setBounds( 250, 200, 600, 540 );\r
                //\r
@@ -212,7 +206,7 @@ class NewSequenceDialog extends JDialog {
                        timesigSelecter.getByteArray()\r
                );\r
                return getChordProgression().toMidiSequence(\r
-                       ppqComboBox.getPPQ(),\r
+                       (int)ppqComboBox.getSelectedItem(),\r
                        measureSelecter.getStartMeasurePosition(),\r
                        measureSelecter.getEndMeasurePosition(),\r
                        firstTrackSpec,\r