OSDN Git Service

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