1 package camidion.chordhelper.mididevice;
4 import java.util.Vector;
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;
15 * 1個の{@link MidiDevice}で開かれている{@link Transmitter}/{@link Receiver}のリストモデル
17 public class MidiTransceiverListModel extends AbstractListModel<AutoCloseable> {
18 protected MidiDevice device;
19 protected MidiTransceiverListModelList deviceModelList;
20 private MidiDeviceInOutType ioType;
21 private TreePath treePath;
23 * 実体のない新規{@link Transmitter}を表すインターフェース
25 public interface NewTransmitter extends Transmitter {};
26 private NewTransmitter newTransmitter;
28 * 指定のMIDIデバイスに属する {@link Transmitter}/{@link Receiver} のリストモデルを構築します。
30 * @param device 対象MIDIデバイス
31 * @param deviceModelList 接続相手となりうるMIDIデバイスのリスト
33 public MidiTransceiverListModel(MidiDevice device, MidiTransceiverListModelList deviceModelList) {
35 this.deviceModelList = deviceModelList;
37 newTransmitter = new NewTransmitter() {
39 public void setReceiver(Receiver receiver) { }
41 public Receiver getReceiver() { return null; }
43 public void close() { }
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});
55 public MidiDevice getMidiDevice() { return device; }
57 * {@link javax.swing.JTree}で使用するツリー表示のパスを返します。
60 public TreePath getTreePath() { return treePath; }
65 public String toString() { return device.getDeviceInfo().toString(); }
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);
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;
79 public int getSize() {
80 int txSize = txSupported() ? device.getTransmitters().size() + 1 : 0;
81 return device.getReceivers().size() + txSize;
84 * 指定の要素がこのリストモデルで最初に見つかった位置を返します。
86 * @param element 探したい要素
87 * @return 位置のインデックス(先頭が 0、見つからないとき -1)
89 public int indexOf(AutoCloseable element) {
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();
99 * このリストが {@link Transmitter} をサポートしているか調べます。
100 * @return {@link Transmitter} をサポートしていたら true
102 public boolean txSupported() { return device.getMaxTransmitters() != 0; }
104 * このリストが {@link Receiver} をサポートしているか調べます。
105 * @return {@link Receiver} をサポートしていたら true
107 public boolean rxSupported() { return device.getMaxReceivers() != 0; }
109 * このリストのMIDIデバイスの入出力タイプを返します。
110 * @return このリストのMIDIデバイスの入出力タイプ
112 public MidiDeviceInOutType getMidiDeviceInOutType() { return ioType; }
114 * 未接続の{@link Transmitter}を、引数で指定されたリストモデルの最初の{@link Receiver}に接続します。
115 * @param anotherModel 接続可能な{@link Receiver}を持つリストモデル
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));
124 * レシーバに未接続の最初の{@link Transmitter}を開いて返します。
125 * ない場合は {@link MidiDevice#getTransmitter} で新たに取得して返します。
127 * @return 新しく開かれた未接続の{@link Transmitter}
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;
135 tx = device.getTransmitter();
136 } catch( MidiUnavailableException e ) {
140 fireIntervalAdded(this,0,getSize());
144 * このリストモデルで開いている指定の{@link Transmitter}があれば、
147 * @param txToClose このリストモデルで開いている{@link Transmitter}
149 public void closeTransmitter(Transmitter txToClose) {
150 if( ! device.getTransmitters().contains(txToClose) ) return;
152 fireIntervalRemoved(this,0,getSize());
155 * {@link Receiver}を1個だけ開きます。サポートしていない場合は無視されます。
157 * @throws MidiUnavailableException デバイスを開くことができない場合
159 public void openReceiver() throws MidiUnavailableException {
160 if( rxSupported() && device.getReceivers().isEmpty() ) device.getReceiver();
163 * {@link MidiDevice}を閉じる前に、{@link Receiver}を閉じます。
165 * 閉じようとしている{@link Receiver}を他デバイスの{@link Transmitter}が使用していた場合は、
166 * その{@link Transmitter}も閉じます。
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);
185 * <p>マイクロ秒位置はMIDIデバイスを開いてからの時間で表されます。
186 * このメソッドではMIDIデバイスをいったん閉じて再び開くことによって
188 * 接続相手のデバイスがあった場合、元通りに接続を復元します。
190 * <p>MIDIデバイスからリアルタイムレコーディングを開始するときは、
191 * 必ずマイクロ秒位置をリセットする必要があります。
192 * (リセットされていないマイクロ秒位置がそのままシーケンサに記録されると、
193 * 大幅に後ろのほうにずれて記録されてしまいます)
196 public void resetMicrosecondPosition() {
197 if( ! txSupported() || device instanceof Sequencer ) return;
199 // デバイスを閉じる前に接続相手の情報を保存
200 List<Transmitter> myTxList = device.getTransmitters();
201 List<Receiver> peerRxList = new Vector<Receiver>();
203 for( Transmitter tx : myTxList ) {
204 if( (rx = tx.getReceiver()) != null ) peerRxList.add(rx);
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);
220 // いったん閉じて開く(ここでマイクロ秒位置がリセットされる)
224 } catch( MidiUnavailableException e ) {
228 for( Receiver peerRx : peerRxList ) {
229 Transmitter tx = openTransmitter();
230 if( tx != null ) tx.setReceiver(peerRx);
232 if( peerTxList != null ) {
233 rx = device.getReceivers().get(0);
234 for( Transmitter peerTx : peerTxList ) peerTx.setReceiver(rx);