/**
* 空のトラックリストモデル
*/
- SequenceTrackListTableModel emptyTrackListTableModel;
+ SequenceTrackListTableModel emptyTrackListTableModel = new SequenceTrackListTableModel(this, null, null);
/**
* 空のイベントリストモデル
*/
- TrackEventListTableModel emptyEventListTableModel;
+ TrackEventListTableModel emptyEventListTableModel = new TrackEventListTableModel(emptyTrackListTableModel, null);
/**
* 選択されているシーケンスのインデックス
*/
Object src = event.getSource();
if( src instanceof MidiSequencerModel ) {
int newValue = ((MidiSequencerModel)src).getValue() / 1000;
- if(value == newValue) return;
- value = newValue;
- fireTableCellUpdated(indexOfSequenceOnSequencer(), Column.POSITION.ordinal());
+ if(value != newValue) {
+ value = newValue;
+ fireTableCellUpdated(indexOfSequenceOnSequencer(), Column.POSITION.ordinal());
+ }
}
}
@Override
public PlaylistTableModel(MidiSequencerModel sequencerModel) {
this.sequencerModel = sequencerModel;
sequencerModel.addChangeListener(mmssPosition);
- // EOF(0x2F)が来たら曲の終わりなので次の曲へ進める
sequencerModel.getSequencer().addMetaEventListener(msg->{
+ // EOF(0x2F)が来て曲が終わったら次の曲へ進める
if(msg.getType() == 0x2F) SwingUtilities.invokeLater(()->{
try {
goNext();
}
});
});
- emptyTrackListTableModel = new SequenceTrackListTableModel(this, null, null);
- emptyEventListTableModel = new TrackEventListTableModel(emptyTrackListTableModel, null);
}
/**
* 次の曲へ進みます。
* 次の曲がなければ、そこで停止します。いずれの場合も曲の先頭へ戻ります。
* </p>
* @throws InvalidMidiDataException {@link Sequencer#setSequence(Sequence)} を参照
+ * @throws IllegalStateException MIDIシーケンサデバイスが閉じている場合
*/
private void goNext() throws InvalidMidiDataException {
// とりあえず曲の先頭へ戻る
public boolean isCellEditable() { return true; } // ダブルクリックだけ有効
@Override
public Object getValueOf(SequenceTrackListTableModel sequenceModel) {
- return sequenceModel.isOnSequencer() ? sequenceModel.getParent().mmssPosition : "";
+ if( ! sequenceModel.isOnSequencer() ) return "";
+ return sequenceModel.getParent().mmssPosition;
}
},
/** シーケンスの時間長(分:秒) */
LENGTH("Length", String.class, 80) {
@Override
public Object getValueOf(SequenceTrackListTableModel sequenceModel) {
- long usec = sequenceModel.getSequence().getMicrosecondLength();
- int sec = (int)( (usec < 0 ? usec += 0x100000000L : usec) / 1000L / 1000L );
+ int sec = (int)( sequenceModel.getMicrosecondLength() / 1000L / 1000L );
return String.format( "%02d:%02d", sec/60, sec%60 );
}
},
return dtLabel == null ? "[Unknown]" : dtLabel;
}
};
- /**
- * タイミング分割形式に対応するラベル文字列
- */
+ /** タイミング分割形式に対応するラベル文字列 */
private static final Map<Float,String> divisionTypeLabels = new HashMap<Float,String>() {
{
put(Sequence.PPQ, "PPQ");
* このプレイリストに読み込まれた全シーケンスの合計時間長を返します。
* @return 全シーケンスの合計時間長 [秒]
*/
- public int getTotalTimeInSeconds() {
- return sequenceModelList.stream().mapToInt(m->{
- long usec = m.getSequence().getMicrosecondLength();
- return (int)( (usec < 0 ? usec += 0x100000000L : usec)/1000L/1000L );
- }).sum();
+ public int getSecondLength() {
+ // マイクロ秒単位での桁あふれを回避しつつ、丸め誤差を最小限にするため、ミリ秒単位で合計を算出する。
+ return (int)(sequenceModelList.stream().mapToLong(m -> m.getMicrosecondLength() / 1000L).sum() / 1000L);
}
/**
* 選択されたMIDIシーケンスのテーブルモデルを返します。
if( sequence == null ) sequence = (new ChordProgression()).toMidiSequence();
sequenceModelList.add(new SequenceTrackListTableModel(this, sequence, filename));
//
- // æ\9c«å°¾ã\81«è¡\8cã\81\8c追å\8a ã\81\95ã\82\8cã\81\9fã\81®ã\81§ã\80\81å\86\8dæ\8f\8fç\94»ã\81\97ã\81¦ã\82\82ã\82\89ã\81\86
+ // æ\9c«å°¾ã\81«è¿½å\8a ã\81\95ã\82\8cã\81\9fè¡\8cã\82\92é\81¸æ\8a\9eã\81\97ã\80\81å\86\8dæ\8f\8fç\94»
int lastIndex = sequenceModelList.size() - 1;
- fireTableRowsInserted(lastIndex, lastIndex);
sequenceListSelectionModel.setSelectionInterval(lastIndex, lastIndex);
+ fireTableRowsInserted(lastIndex, lastIndex);
return lastIndex;
}
/**
* @param sequence MIDIシーケンス
* @return 追加されたシーケンスのインデックス(先頭が 0)
* @throws InvalidMidiDataException {@link Sequencer#setSequence(Sequence)} を参照
+ * @throws IllegalStateException MIDIシーケンサデバイスが閉じている場合
*/
public int play(Sequence sequence) throws InvalidMidiDataException {
int lastIndex = add(sequence,"");
* 除去されたシーケンスがシーケンサーにロード済みの場合、アンロードします。
*
* @throws InvalidMidiDataException {@link Sequencer#setSequence(Sequence)} を参照
+ * @throws IllegalStateException MIDIシーケンサデバイスが閉じている場合
*/
public void removeSelectedSequence() throws InvalidMidiDataException {
if( sequenceListSelectionModel.isSelectionEmpty() ) return;
int selectedIndex = sequenceListSelectionModel.getMinSelectionIndex();
SequenceTrackListTableModel removedSequence = sequenceModelList.remove(selectedIndex);
- if( removedSequence.isOnSequencer() ) sequencerModel.setSequenceTrackListTableModel(null);
fireTableRowsDeleted(selectedIndex, selectedIndex);
+ if( removedSequence.isOnSequencer() ) {
+ sequencerModel.setSequenceTrackListTableModel(null);
+ }
}
/**
* テーブル内の指定したインデックス位置にあるシーケンスをシーケンサーにロードします。
*
* @param newRowIndex ロードするシーケンスのインデックス位置、アンロードするときは -1
* @throws InvalidMidiDataException {@link Sequencer#setSequence(Sequence)} を参照
+ * @throws IllegalStateException MIDIシーケンサデバイスが閉じているときにアンロードしようとした場合
*/
public void loadToSequencer(int newRowIndex) throws InvalidMidiDataException {
SequenceTrackListTableModel oldSeq = sequencerModel.getSequenceTrackListTableModel();
- SequenceTrackListTableModel newSeq = (newRowIndex < 0 ? null : sequenceModelList.get(newRowIndex));
+ SequenceTrackListTableModel newSeq = (newRowIndex < 0 || sequenceModelList.isEmpty() ? null : sequenceModelList.get(newRowIndex));
if( oldSeq == newSeq ) return;
sequencerModel.setSequenceTrackListTableModel(newSeq);
int columnIndices[] = {