OSDN Git Service

MIDIデバイス周りのリファクタリング
[midichordhelper/MIDIChordHelper.git] / src / camidion / chordhelper / mididevice / TransmitterListModel.java
1 package camidion.chordhelper.mididevice;
2
3 import java.util.List;
4 import java.util.Vector;
5
6 import javax.sound.midi.MidiDevice;
7 import javax.sound.midi.MidiUnavailableException;
8 import javax.sound.midi.Receiver;
9 import javax.sound.midi.Sequencer;
10 import javax.sound.midi.Transmitter;
11 import javax.swing.AbstractListModel;
12
13 /**
14  * {@link Transmitter} のリストを表す {@link javax.swing.ListModel}
15  */
16 public class TransmitterListModel extends AbstractListModel<Transmitter> {
17         protected MidiDeviceModel deviceModel;
18         public TransmitterListModel(MidiDeviceModel deviceModel) { this.deviceModel = deviceModel; }
19         private Transmitter dummyTx = new DummyTransmitter();
20         @Override
21         public Transmitter getElementAt(int index) {
22                 List<Transmitter> txList = deviceModel.getMidiDevice().getTransmitters();
23                 int length = txList.size();
24                 if( index == length ) return dummyTx;
25                 if( index > length || index < 0 ) return null;
26                 return txList.get(index);
27         }
28         @Override
29         public int getSize() {
30                 return deviceModel.getMidiDevice().getTransmitters().size() + 1;
31         }
32         public int indexOf(Object element) {
33                 List<Transmitter> txList = deviceModel.getMidiDevice().getTransmitters();
34                 return dummyTx.equals(element) ? txList.size() : txList.indexOf(element);
35         }
36         /**
37          * 新しい{@link Transmitter}を{@link MidiDevice#getTransmitter}で生成し、
38          * このモデルを参照しているビューに通知します。
39          *
40          * @return 未接続の{@link Transmitter}
41          * @throws MidiUnavailableException リソースの制約のためにトランスミッタを使用できない場合にスローされる
42          */
43         public Transmitter openTransmitter() throws MidiUnavailableException {
44                 Transmitter tx = deviceModel.getMidiDevice().getTransmitter();
45                 fireIntervalAdded(this, 0, getSize());
46                 return tx;
47         }
48         /**
49          * 相手のMIDIデバイスが持つ最初の{@link Receiver}を、
50          * このリストモデルの新規{@link Transmitter}に接続します。
51          *
52          * @param anotherDeviceModel 接続相手のMIDIデバイス
53          * @throws MidiUnavailableException リソースの制約のためにトランスミッタを使用できない場合にスローされる
54          */
55         public void connectToFirstReceiverOfDevice(MidiDeviceModel anotherDeviceModel) throws MidiUnavailableException {
56                 List<Receiver> rxList = anotherDeviceModel.getMidiDevice().getReceivers();
57                 if( rxList.isEmpty() ) return;
58                 deviceModel.getTransmitterListModel().openTransmitter().setReceiver(rxList.get(0));
59         }
60         /**
61          * 指定の{@link Transmitter}を閉じ、要素が減ったことを、
62          * このモデルを参照しているビューに通知します。
63          *
64          * @param tx このリストモデルで開いている{@link Transmitter}
65          */
66         public void closeTransmitter(Transmitter tx) {
67                 tx.close();
68                 fireIntervalRemoved(this, 0, getSize());
69         }
70         /**
71          * このリストモデルにある{@link Transmitter}のうち、
72          * 引数で指定された{@link Receiver}へデータを送信しているものを全て閉じます。
73          * 閉じるとリストから自動的に削除されるので、表示の更新も行います。
74          */
75         public void closePeerTransmitterOf(Receiver rx) {
76                 List<Transmitter> closingTxList = new Vector<Transmitter>();
77                 List<Transmitter> txList = deviceModel.getMidiDevice().getTransmitters();
78                 for( Transmitter tx : txList ) if( tx.getReceiver() == rx ) closingTxList.add(tx);
79                 if( closingTxList.isEmpty() ) return;
80                 int length = getSize();
81                 for( Transmitter tx : closingTxList ) tx.close();
82                 fireIntervalRemoved(this, 0, length);
83         }
84         /**
85          * マイクロ秒位置をリセットします。
86          * <p>マイクロ秒位置はMIDIデバイスを開いてからの時間で表されます。
87          * このメソッドではMIDIデバイスをいったん閉じて再び開くことによって
88          * 時間位置をリセットします。
89          * 接続相手のデバイスがあった場合、元通りに接続を復元します。
90          * </p>
91          * <p>MIDIデバイスからリアルタイムレコーディングを開始するときは、
92          * 必ずマイクロ秒位置をリセットする必要があります。
93          * (リセットされていないマイクロ秒位置がそのままシーケンサに記録されると、
94          * 大幅に後ろのほうにずれて記録されてしまいます)
95          * </p>
96          */
97         public void resetMicrosecondPosition() {
98                 MidiDevice device = deviceModel.getMidiDevice();
99                 //
100                 // シーケンサはこのメソッドでのリセット対象外
101                 if( device instanceof Sequencer ) return;
102                 //
103                 // デバイスを閉じる前に接続状態を把握
104                 List<Receiver> peerRxList = new Vector<Receiver>();
105                 List<Transmitter> txList = device.getTransmitters();
106                 for( Transmitter tx : txList ) {
107                         Receiver rx = tx.getReceiver();
108                         if( rx != null ) peerRxList.add(rx);
109                 }
110                 List<Transmitter> peerTxList = new Vector<Transmitter>();
111                 MidiDeviceModelList deviceModelList = deviceModel.getDeviceModelList();
112                 List<Receiver> rxList = device.getReceivers();
113                 for( Receiver rx : rxList ) {
114                         for( MidiDeviceModel m : deviceModelList ) {
115                                 if( m == deviceModel ) continue;
116                                 List<Transmitter> peerSourceTxList = m.getMidiDevice().getTransmitters();
117                                 for( Transmitter tx : peerSourceTxList ) if( tx.getReceiver() == rx ) peerTxList.add(tx);
118                         }
119                 }
120                 // いったん閉じて開く(ここでマイクロ秒位置がリセットされる)
121                 // その後、元通りに接続し直す
122                 device.close();
123                 try {
124                         device.open();
125                         for( Receiver peerRx : peerRxList ) openTransmitter().setReceiver(peerRx);
126                         if( ! rxList.isEmpty() ) {
127                                 Receiver rx = rxList.get(0);
128                                 for( Transmitter peerTx : peerTxList ) peerTx.setReceiver(rx);
129                         }
130                 } catch( MidiUnavailableException e ) {
131                         e.printStackTrace();
132                 }
133         }
134 }