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
* 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
/**\r
* BASE64テキスト入力ダイアログ\r
*/\r
- private Base64Dialog base64Dialog = new Base64Dialog(this);\r
+ Base64Dialog base64Dialog = new Base64Dialog(this);\r
/**\r
* BASE64エンコードボタン(ライブラリが見えている場合のみ有効)\r
*/\r
//\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
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
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
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
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
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
/**\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
* @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
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
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
/** ファイル名 */\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
return total;\r
}\r
}\r
- MidiSequencerModel sequencerModel;\r
+ /**\r
+ * MIDIシーケンサーモデル\r
+ */\r
+ protected MidiSequencerModel sequencerModel;\r
/**\r
* 新しいプレイリストのテーブルモデルを構築します。\r
* @param deviceManager MIDIデバイスマネージャ\r
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
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
}\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
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
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
}\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
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
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
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
* 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
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
sequence.deleteTrack(tracks[i]);\r
trackModelList.remove(i);\r
}\r
- fireTableRowsDeleted( minIndex, maxIndex );\r
+ fireTableRowsDeleted(minIndex, maxIndex);\r
}\r
/**\r
* MIDIシーケンサを返します。\r
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
/**\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
*/\r
public Track getTrack() { return track; }\r
/**\r
- * 文字列としてトラック名を返します。\r
+ * トラック名を返します。\r
*/\r
@Override\r
public String toString() { return MIDISpec.getNameOf(track); }\r
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
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
}\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
* @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
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
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
}\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
* @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