OSDN Git Service

9546bb7635fc2ddc7bf39841d9c3a6499f403434
[midichordhelper/MIDIChordHelper.git] / src / camidion / chordhelper / mididevice / MidiConnecterListModel.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.MidiEvent;
8 import javax.sound.midi.MidiUnavailableException;
9 import javax.sound.midi.Receiver;
10 import javax.sound.midi.Sequencer;
11 import javax.sound.midi.Transmitter;
12 import javax.swing.AbstractListModel;
13
14 /**
15  * 1個の MIDI デバイスに属する Transmitter/Receiver のリストモデル
16  */
17 public class MidiConnecterListModel extends AbstractListModel<AutoCloseable> {
18         protected MidiDevice device;
19         private List<MidiConnecterListModel> modelList;
20         /**
21          * 実体のない新規Transmitterを表すインターフェース
22          */
23         public interface NewTransmitter extends Transmitter {};
24         public NewTransmitter newTransmitter;
25         /**
26          * 指定のMIDIデバイスに属する {@link Transmitter}/{@link Receiver} のリストモデルを構築します。
27          *
28          * @param device 対象MIDIデバイス
29          * @param modelList リストモデルを格納している親リスト
30          */
31         public MidiConnecterListModel(MidiDevice device, List<MidiConnecterListModel> modelList) {
32                 this.device = device;
33                 this.modelList = modelList;
34                 if( txSupported() ) {
35                         newTransmitter = new NewTransmitter() {
36                                 @Override
37                                 public void setReceiver(Receiver receiver) { }
38                                 @Override
39                                 public Receiver getReceiver() { return null; }
40                                 @Override
41                                 public void close() { }
42                         };
43                 }
44         }
45         /**
46          * 対象MIDIデバイスを返します。
47          * @return 対象MIDIデバイス
48          */
49         public MidiDevice getMidiDevice() { return device; }
50         /**
51          * 対象MIDIデバイスの名前を返します。
52          */
53         @Override
54         public String toString() { return device.getDeviceInfo().toString(); }
55         @Override
56         public AutoCloseable getElementAt(int index) {
57                 List<Receiver> rxList = device.getReceivers();
58                 int rxSize = rxList.size();
59                 if( index < rxSize ) return rxList.get(index);
60                 index -= rxSize;
61                 List<Transmitter> txList = device.getTransmitters();
62                 int txSize = txList.size();
63                 if( index < txSize ) return txList.get(index);
64                 if( index == txSize ) return newTransmitter;
65                 return null;
66         }
67         @Override
68         public int getSize() {
69                 int txSize = txSupported() ? device.getTransmitters().size() + 1 : 0;
70                 return device.getReceivers().size() + txSize;
71         }
72         /**
73          * 指定の要素がこのリストモデルで最初に見つかった位置を返します。
74          *
75          * @param element 探したい要素
76          * @return 位置のインデックス(先頭が 0、見つからないとき -1)
77          */
78         public int indexOf(AutoCloseable element) {
79                 int index;
80                 List<Receiver> rxList = device.getReceivers();
81                 if( (index = rxList.indexOf(element)) >= 0 ) return index;
82                 List<Transmitter> txList = device.getTransmitters();
83                 if( (index = txList.indexOf(element)) >= 0 ) return rxList.size() + index;
84                 if( element.equals(newTransmitter) ) return rxList.size() + txList.size();
85                 return -1;
86         }
87         /**
88          * このリストが {@link Transmitter} をサポートしているか調べます。
89          * @return {@link Transmitter} をサポートしていたら true
90          */
91         public boolean txSupported() { return device.getMaxTransmitters() != 0; }
92         /**
93          * このリストが {@link Receiver} をサポートしているか調べます。
94          * @return {@link Receiver} をサポートしていたら true
95          */
96         public boolean rxSupported() { return device.getMaxReceivers() != 0; }
97         /**
98          * このリストのMIDIデバイスの入出力タイプを返します。
99          * @return このリストのMIDIデバイスの入出力タイプ
100          * <ul>
101          * <li>MIDI_OUT: {@link Receiver}から受けた{@link MidiEvent}を音源や画面に出力するデバイス</li>
102          * <li>MIDI_IN: キーボードやシーケンサから入力した{@link MidiEvent}を{@link Transmitter}から{@link Receiver}へ転送するデバイス</li>
103          * <li>MIDI_IN_OUT: 上記両方の機能をサポートしたデバイス</li>
104          * <li>MIDI_NONE: 上記両方ともサポートしていないデバイス</li>
105          * </ul>
106          */
107         public MidiDeviceInOutType getMidiDeviceInOutType() {
108                 if( rxSupported() ) {
109                         if( txSupported() )
110                                 return MidiDeviceInOutType.MIDI_IN_OUT;
111                         else
112                                 return MidiDeviceInOutType.MIDI_OUT;
113                 }
114                 else {
115                         if( txSupported() )
116                                 return MidiDeviceInOutType.MIDI_IN;
117                         else
118                                 return MidiDeviceInOutType.MIDI_NONE;
119                 }
120         }
121         /**
122          * 引数で指定されたトランスミッタをレシーバを接続します。
123          * @param tx トランスミッタ
124          * @param rx レシーバ
125          * @return 接続されたレシーバ(このリストにないレシーバが指定された場合はnull)
126          */
127         public Receiver ConnectToReceiver(Transmitter tx, Receiver rx) {
128                 if( ! device.getReceivers().contains(rx) ) return null;
129                 tx.setReceiver(rx);
130                 fireContentsChanged(this,0,getSize());
131                 return rx;
132         }
133         /**
134          * 未接続のトランスミッタを、
135          * 引数で指定されたリストモデルの最初のレシーバに接続します。
136          * @param anotherModel 接続先レシーバを持つリストモデル
137          */
138         public void connectToReceiverOf(MidiConnecterListModel anotherModel) {
139                 if( ! txSupported() || anotherModel == null || ! anotherModel.rxSupported() ) return;
140                 List<Receiver> rxList = anotherModel.device.getReceivers();
141                 if( rxList.isEmpty() ) return;
142                 openTransmitter().setReceiver(rxList.get(0));
143         }
144         /**
145          * レシーバに未接続の最初のトランスミッタを返します。
146          * ない場合は {@link MidiDevice#getTransmitter} で新たに取得して返します。
147          *
148          * @return 未接続のトランスミッタ
149          */
150         public Transmitter openTransmitter() {
151                 if( ! txSupported() ) return null;
152                 List<Transmitter> txList = device.getTransmitters();
153                 for( Transmitter tx : txList ) if( tx.getReceiver() == null ) return tx;
154                 Transmitter tx;
155                 try {
156                         tx = device.getTransmitter();
157                 } catch( MidiUnavailableException e ) {
158                         e.printStackTrace();
159                         return null;
160                 }
161                 fireIntervalAdded(this,0,getSize());
162                 return tx;
163         }
164         /**
165          * 指定のトランスミッタを閉じます。
166          * このリストモデルにないトランスミッタが指定された場合、無視されます。
167          * @param txToClose 閉じたいトランスミッタ
168          */
169         public void closeTransmitter(Transmitter txToClose) {
170                 if( ! device.getTransmitters().contains(txToClose) ) return;
171                 txToClose.close();
172                 fireIntervalRemoved(this,0,getSize());
173         }
174         /**
175          * 対象MIDIデバイスを開きます。
176          * @throws MidiUnavailableException デバイスを開くことができない場合
177          */
178         public void openDevice() throws MidiUnavailableException {
179                 device.open();
180                 if( rxSupported() && device.getReceivers().isEmpty() ) device.getReceiver();
181         }
182         /**
183          * 対象MIDIデバイスを閉じます。
184          * 対象MIDIデバイスの{@link Receiver}を設定した他デバイスの{@link Transmitter}があれば、
185          * それも閉じます。
186          */
187         public void closeDevice() {
188                 if( rxSupported() ) {
189                         Receiver rx = device.getReceivers().get(0);
190                         for( MidiConnecterListModel m : modelList ) {
191                                 if( m == this || ! m.txSupported() ) continue;
192                                 for( int i=0; i<m.getSize(); i++ ) {
193                                         AutoCloseable ac = m.getElementAt(i);
194                                         if( ! (ac instanceof Transmitter) ) continue;
195                                         Transmitter tx = ((Transmitter)ac);
196                                         if( tx.getReceiver() == rx ) m.closeTransmitter(tx);
197                                 }
198                         }
199                 }
200                 device.close();
201         }
202         /**
203          * マイクロ秒位置をリセットします。
204          * <p>マイクロ秒位置はMIDIデバイスを開いてからの時間で表されます。
205          * このメソッドではMIDIデバイスをいったん閉じて再び開くことによって
206          * 時間位置をリセットします。
207          * 接続相手のデバイスがあった場合、元通りに接続を復元します。
208          * </p>
209          * <p>MIDIデバイスからリアルタイムレコーディングを開始するときは、
210          * 必ずマイクロ秒位置をリセットする必要があります。
211          * (リセットされていないマイクロ秒位置がそのままシーケンサに記録されると、
212          * 大幅に後ろのほうにずれて記録されてしまいます)
213          * </p>
214          */
215         public void resetMicrosecondPosition() {
216                 if( ! txSupported() || device instanceof Sequencer ) return;
217                 //
218                 // デバイスを閉じる前に接続相手の情報を保存
219                 List<Transmitter> txList = device.getTransmitters();
220                 List<Receiver> peerRxList = new Vector<Receiver>();
221                 for( Transmitter tx : txList ) {
222                         Receiver rx = tx.getReceiver();
223                         if( rx != null ) peerRxList.add(rx);
224                 }
225                 List<Transmitter> peerTxList = null;
226                 Receiver rx = null;
227                 if( rxSupported() ) {
228                         rx = device.getReceivers().get(0);
229                         peerTxList = new Vector<Transmitter>();
230                         for( MidiConnecterListModel m : modelList ) {
231                                 if( m == this || ! m.txSupported() ) continue;
232                                 for( int i=0; i<m.getSize(); i++ ) {
233                                         Object obj = m.getElementAt(i);
234                                         if( ! (obj instanceof Transmitter) ) continue;
235                                         Transmitter tx = ((Transmitter)obj);
236                                         if( tx.getReceiver() == rx ) peerTxList.add(tx);
237                                 }
238                         }
239                 }
240                 // いったん閉じて開く(ここでマイクロ秒位置がリセットされる)
241                 device.close();
242                 try {
243                         device.open();
244                 } catch( MidiUnavailableException e ) {
245                         e.printStackTrace();
246                 }
247                 // 元通りに接続し直す
248                 for( Receiver peerRx : peerRxList ) {
249                         Transmitter tx = openTransmitter();
250                         if( tx != null ) tx.setReceiver(peerRx);
251                 }
252                 if( peerTxList != null ) {
253                         rx = device.getReceivers().get(0);
254                         for( Transmitter peerTx : peerTxList ) peerTx.setReceiver(rx);
255                 }
256         }
257 }