The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1000 lines
34KB

  1. //==============================================================================
  2. public class BluetoothManager extends ScanCallback
  3. {
  4. BluetoothManager()
  5. {
  6. }
  7. public String[] getMidiBluetoothAddresses()
  8. {
  9. return bluetoothMidiDevices.toArray (new String[bluetoothMidiDevices.size()]);
  10. }
  11. public String getHumanReadableStringForBluetoothAddress (String address)
  12. {
  13. BluetoothDevice btDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice (address);
  14. return btDevice.getName();
  15. }
  16. public int getBluetoothDeviceStatus (String address)
  17. {
  18. return getAndroidMidiDeviceManager().getBluetoothDeviceStatus (address);
  19. }
  20. public void startStopScan (boolean shouldStart)
  21. {
  22. BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
  23. if (bluetoothAdapter == null)
  24. {
  25. Log.d ("JUCE", "BluetoothManager error: could not get default Bluetooth adapter");
  26. return;
  27. }
  28. BluetoothLeScanner bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
  29. if (bluetoothLeScanner == null)
  30. {
  31. Log.d ("JUCE", "BluetoothManager error: could not get Bluetooth LE scanner");
  32. return;
  33. }
  34. if (shouldStart)
  35. {
  36. ScanFilter.Builder scanFilterBuilder = new ScanFilter.Builder();
  37. scanFilterBuilder.setServiceUuid (ParcelUuid.fromString (bluetoothLEMidiServiceUUID));
  38. ScanSettings.Builder scanSettingsBuilder = new ScanSettings.Builder();
  39. scanSettingsBuilder.setCallbackType (ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
  40. .setScanMode (ScanSettings.SCAN_MODE_LOW_POWER)
  41. .setScanMode (ScanSettings.MATCH_MODE_STICKY);
  42. bluetoothLeScanner.startScan (Arrays.asList (scanFilterBuilder.build()),
  43. scanSettingsBuilder.build(),
  44. this);
  45. }
  46. else
  47. {
  48. bluetoothLeScanner.stopScan (this);
  49. }
  50. }
  51. public boolean pairBluetoothMidiDevice(String address)
  52. {
  53. BluetoothDevice btDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice (address);
  54. if (btDevice == null)
  55. {
  56. Log.d ("JUCE", "failed to create buletooth device from address");
  57. return false;
  58. }
  59. return getAndroidMidiDeviceManager().pairBluetoothDevice (btDevice);
  60. }
  61. public void unpairBluetoothMidiDevice (String address)
  62. {
  63. getAndroidMidiDeviceManager().unpairBluetoothDevice (address);
  64. }
  65. public void onScanFailed (int errorCode)
  66. {
  67. }
  68. public void onScanResult (int callbackType, ScanResult result)
  69. {
  70. if (callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES
  71. || callbackType == ScanSettings.CALLBACK_TYPE_FIRST_MATCH)
  72. {
  73. BluetoothDevice device = result.getDevice();
  74. if (device != null)
  75. bluetoothMidiDevices.add (device.getAddress());
  76. }
  77. if (callbackType == ScanSettings.CALLBACK_TYPE_MATCH_LOST)
  78. {
  79. Log.d ("JUCE", "ScanSettings.CALLBACK_TYPE_MATCH_LOST");
  80. BluetoothDevice device = result.getDevice();
  81. if (device != null)
  82. {
  83. bluetoothMidiDevices.remove (device.getAddress());
  84. unpairBluetoothMidiDevice (device.getAddress());
  85. }
  86. }
  87. }
  88. public void onBatchScanResults (List<ScanResult> results)
  89. {
  90. for (ScanResult result : results)
  91. onScanResult (ScanSettings.CALLBACK_TYPE_ALL_MATCHES, result);
  92. }
  93. private BluetoothLeScanner scanner;
  94. private static final String bluetoothLEMidiServiceUUID = "03B80E5A-EDE8-4B33-A751-6CE34EC4C700";
  95. private HashSet<String> bluetoothMidiDevices = new HashSet<String>();
  96. }
  97. public static class JuceMidiInputPort extends MidiReceiver implements JuceMidiPort
  98. {
  99. private native void handleReceive (long host, byte[] msg, int offset, int count, long timestamp);
  100. public JuceMidiInputPort (MidiDeviceManager mm, MidiOutputPort actualPort, MidiPortPath portPathToUse, long hostToUse)
  101. {
  102. owner = mm;
  103. androidPort = actualPort;
  104. portPath = portPathToUse;
  105. juceHost = hostToUse;
  106. isConnected = false;
  107. }
  108. @Override
  109. protected void finalize() throws Throwable
  110. {
  111. close();
  112. super.finalize();
  113. }
  114. @Override
  115. public boolean isInputPort()
  116. {
  117. return true;
  118. }
  119. @Override
  120. public void start()
  121. {
  122. if (owner != null && androidPort != null && ! isConnected) {
  123. androidPort.connect(this);
  124. isConnected = true;
  125. }
  126. }
  127. @Override
  128. public void stop()
  129. {
  130. if (owner != null && androidPort != null && isConnected) {
  131. androidPort.disconnect(this);
  132. isConnected = false;
  133. }
  134. }
  135. @Override
  136. public void close()
  137. {
  138. if (androidPort != null) {
  139. try {
  140. androidPort.close();
  141. } catch (IOException exception) {
  142. Log.d("JUCE", "IO Exception while closing port");
  143. }
  144. }
  145. if (owner != null)
  146. owner.removePort (portPath);
  147. owner = null;
  148. androidPort = null;
  149. }
  150. @Override
  151. public void onSend (byte[] msg, int offset, int count, long timestamp)
  152. {
  153. if (count > 0)
  154. handleReceive (juceHost, msg, offset, count, timestamp);
  155. }
  156. @Override
  157. public void onFlush()
  158. {}
  159. @Override
  160. public void sendMidi (byte[] msg, int offset, int count)
  161. {
  162. }
  163. MidiDeviceManager owner;
  164. MidiOutputPort androidPort;
  165. MidiPortPath portPath;
  166. long juceHost;
  167. boolean isConnected;
  168. }
  169. public static class JuceMidiOutputPort implements JuceMidiPort
  170. {
  171. public JuceMidiOutputPort (MidiDeviceManager mm, MidiInputPort actualPort, MidiPortPath portPathToUse)
  172. {
  173. owner = mm;
  174. androidPort = actualPort;
  175. portPath = portPathToUse;
  176. }
  177. @Override
  178. protected void finalize() throws Throwable
  179. {
  180. close();
  181. super.finalize();
  182. }
  183. @Override
  184. public boolean isInputPort()
  185. {
  186. return false;
  187. }
  188. @Override
  189. public void start()
  190. {
  191. }
  192. @Override
  193. public void stop()
  194. {
  195. }
  196. @Override
  197. public void sendMidi (byte[] msg, int offset, int count)
  198. {
  199. if (androidPort != null)
  200. {
  201. try {
  202. androidPort.send(msg, offset, count);
  203. } catch (IOException exception)
  204. {
  205. Log.d ("JUCE", "send midi had IO exception");
  206. }
  207. }
  208. }
  209. @Override
  210. public void close()
  211. {
  212. if (androidPort != null) {
  213. try {
  214. androidPort.close();
  215. } catch (IOException exception) {
  216. Log.d("JUCE", "IO Exception while closing port");
  217. }
  218. }
  219. if (owner != null)
  220. owner.removePort (portPath);
  221. owner = null;
  222. androidPort = null;
  223. }
  224. MidiDeviceManager owner;
  225. MidiInputPort androidPort;
  226. MidiPortPath portPath;
  227. }
  228. private static class MidiPortPath extends Object
  229. {
  230. public MidiPortPath (int deviceIdToUse, boolean direction, int androidIndex)
  231. {
  232. deviceId = deviceIdToUse;
  233. isInput = direction;
  234. portIndex = androidIndex;
  235. }
  236. public int deviceId;
  237. public int portIndex;
  238. public boolean isInput;
  239. @Override
  240. public int hashCode()
  241. {
  242. Integer i = new Integer ((deviceId * 128) + (portIndex < 128 ? portIndex : 127));
  243. return i.hashCode() * (isInput ? -1 : 1);
  244. }
  245. @Override
  246. public boolean equals (Object obj)
  247. {
  248. if (obj == null)
  249. return false;
  250. if (getClass() != obj.getClass())
  251. return false;
  252. MidiPortPath other = (MidiPortPath) obj;
  253. return (portIndex == other.portIndex && isInput == other.isInput && deviceId == other.deviceId);
  254. }
  255. }
  256. //==============================================================================
  257. public class MidiDeviceManager extends MidiManager.DeviceCallback implements MidiManager.OnDeviceOpenedListener
  258. {
  259. //==============================================================================
  260. private class DummyBluetoothGattCallback extends BluetoothGattCallback
  261. {
  262. public DummyBluetoothGattCallback (MidiDeviceManager mm)
  263. {
  264. super();
  265. owner = mm;
  266. }
  267. public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState)
  268. {
  269. if (newState == BluetoothProfile.STATE_CONNECTED)
  270. {
  271. gatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH);
  272. owner.pairBluetoothDeviceStepTwo (gatt.getDevice());
  273. }
  274. }
  275. public void onServicesDiscovered(BluetoothGatt gatt, int status) {}
  276. public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {}
  277. public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {}
  278. public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {}
  279. public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {}
  280. public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {}
  281. public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {}
  282. public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {}
  283. public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {}
  284. private MidiDeviceManager owner;
  285. }
  286. //==============================================================================
  287. private class MidiDeviceOpenTask extends java.util.TimerTask
  288. {
  289. public MidiDeviceOpenTask (MidiDeviceManager deviceManager, MidiDevice device, BluetoothGatt gattToUse)
  290. {
  291. owner = deviceManager;
  292. midiDevice = device;
  293. btGatt = gattToUse;
  294. }
  295. @Override
  296. public boolean cancel()
  297. {
  298. synchronized (MidiDeviceOpenTask.class)
  299. {
  300. owner = null;
  301. boolean retval = super.cancel();
  302. if (btGatt != null)
  303. {
  304. btGatt.disconnect();
  305. btGatt.close();
  306. btGatt = null;
  307. }
  308. if (midiDevice != null)
  309. {
  310. try
  311. {
  312. midiDevice.close();
  313. }
  314. catch (IOException e)
  315. {}
  316. midiDevice = null;
  317. }
  318. return retval;
  319. }
  320. }
  321. public String getBluetoothAddress()
  322. {
  323. synchronized (MidiDeviceOpenTask.class)
  324. {
  325. if (midiDevice != null)
  326. {
  327. MidiDeviceInfo info = midiDevice.getInfo();
  328. if (info.getType() == MidiDeviceInfo.TYPE_BLUETOOTH)
  329. {
  330. BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
  331. if (btDevice != null)
  332. return btDevice.getAddress();
  333. }
  334. }
  335. }
  336. return "";
  337. }
  338. public BluetoothGatt getGatt() { return btGatt; }
  339. public int getID()
  340. {
  341. return midiDevice.getInfo().getId();
  342. }
  343. @Override
  344. public void run()
  345. {
  346. synchronized (MidiDeviceOpenTask.class)
  347. {
  348. if (owner != null && midiDevice != null)
  349. owner.onDeviceOpenedDelayed (midiDevice);
  350. }
  351. }
  352. private MidiDeviceManager owner;
  353. private MidiDevice midiDevice;
  354. private BluetoothGatt btGatt;
  355. }
  356. //==============================================================================
  357. public MidiDeviceManager()
  358. {
  359. manager = (MidiManager) getSystemService (MIDI_SERVICE);
  360. if (manager == null)
  361. {
  362. Log.d ("JUCE", "MidiDeviceManager error: could not get MidiManager system service");
  363. return;
  364. }
  365. openPorts = new HashMap<MidiPortPath, WeakReference<JuceMidiPort>> ();
  366. midiDevices = new ArrayList<Pair<MidiDevice,BluetoothGatt>>();
  367. openTasks = new HashMap<Integer, MidiDeviceOpenTask>();
  368. btDevicesPairing = new HashMap<String, BluetoothGatt>();
  369. MidiDeviceInfo[] foundDevices = manager.getDevices();
  370. for (MidiDeviceInfo info : foundDevices)
  371. onDeviceAdded (info);
  372. manager.registerDeviceCallback (this, null);
  373. }
  374. protected void finalize() throws Throwable
  375. {
  376. manager.unregisterDeviceCallback (this);
  377. synchronized (MidiDeviceManager.class)
  378. {
  379. btDevicesPairing.clear();
  380. for (Integer deviceID : openTasks.keySet())
  381. openTasks.get (deviceID).cancel();
  382. openTasks = null;
  383. }
  384. for (MidiPortPath key : openPorts.keySet())
  385. openPorts.get (key).get().close();
  386. openPorts = null;
  387. for (Pair<MidiDevice, BluetoothGatt> device : midiDevices)
  388. {
  389. if (device.second != null)
  390. {
  391. device.second.disconnect();
  392. device.second.close();
  393. }
  394. device.first.close();
  395. }
  396. midiDevices.clear();
  397. super.finalize();
  398. }
  399. public String[] getJuceAndroidMidiInputDevices()
  400. {
  401. return getJuceAndroidMidiDevices (MidiDeviceInfo.PortInfo.TYPE_OUTPUT);
  402. }
  403. public String[] getJuceAndroidMidiOutputDevices()
  404. {
  405. return getJuceAndroidMidiDevices (MidiDeviceInfo.PortInfo.TYPE_INPUT);
  406. }
  407. private String[] getJuceAndroidMidiDevices (int portType)
  408. {
  409. // only update the list when JUCE asks for a new list
  410. synchronized (MidiDeviceManager.class)
  411. {
  412. deviceInfos = getDeviceInfos();
  413. }
  414. ArrayList<String> portNames = new ArrayList<String>();
  415. int index = 0;
  416. for (MidiPortPath portInfo = getPortPathForJuceIndex (portType, index); portInfo != null; portInfo = getPortPathForJuceIndex (portType, ++index))
  417. portNames.add (getPortName (portInfo));
  418. String[] names = new String[portNames.size()];
  419. return portNames.toArray (names);
  420. }
  421. private JuceMidiPort openMidiPortWithJuceIndex (int index, long host, boolean isInput)
  422. {
  423. synchronized (MidiDeviceManager.class)
  424. {
  425. int portTypeToFind = (isInput ? MidiDeviceInfo.PortInfo.TYPE_OUTPUT : MidiDeviceInfo.PortInfo.TYPE_INPUT);
  426. MidiPortPath portInfo = getPortPathForJuceIndex (portTypeToFind, index);
  427. if (portInfo != null)
  428. {
  429. // ports must be opened exclusively!
  430. if (openPorts.containsKey (portInfo))
  431. return null;
  432. Pair<MidiDevice,BluetoothGatt> devicePair = getMidiDevicePairForId (portInfo.deviceId);
  433. if (devicePair != null)
  434. {
  435. MidiDevice device = devicePair.first;
  436. if (device != null)
  437. {
  438. JuceMidiPort juceMidiPort = null;
  439. if (isInput)
  440. {
  441. MidiOutputPort outputPort = device.openOutputPort(portInfo.portIndex);
  442. if (outputPort != null)
  443. juceMidiPort = new JuceMidiInputPort(this, outputPort, portInfo, host);
  444. }
  445. else
  446. {
  447. MidiInputPort inputPort = device.openInputPort(portInfo.portIndex);
  448. if (inputPort != null)
  449. juceMidiPort = new JuceMidiOutputPort(this, inputPort, portInfo);
  450. }
  451. if (juceMidiPort != null)
  452. {
  453. openPorts.put(portInfo, new WeakReference<JuceMidiPort>(juceMidiPort));
  454. return juceMidiPort;
  455. }
  456. }
  457. }
  458. }
  459. }
  460. return null;
  461. }
  462. public JuceMidiPort openMidiInputPortWithJuceIndex (int index, long host)
  463. {
  464. return openMidiPortWithJuceIndex (index, host, true);
  465. }
  466. public JuceMidiPort openMidiOutputPortWithJuceIndex (int index)
  467. {
  468. return openMidiPortWithJuceIndex (index, 0, false);
  469. }
  470. /* 0: unpaired, 1: paired, 2: pairing */
  471. public int getBluetoothDeviceStatus (String address)
  472. {
  473. synchronized (MidiDeviceManager.class)
  474. {
  475. if (! address.isEmpty())
  476. {
  477. if (findMidiDeviceForBluetoothAddress (address) != null)
  478. return 1;
  479. if (btDevicesPairing.containsKey (address))
  480. return 2;
  481. if (findOpenTaskForBluetoothAddress (address) != null)
  482. return 2;
  483. }
  484. }
  485. return 0;
  486. }
  487. public boolean pairBluetoothDevice (BluetoothDevice btDevice)
  488. {
  489. String btAddress = btDevice.getAddress();
  490. if (btAddress.isEmpty())
  491. return false;
  492. synchronized (MidiDeviceManager.class)
  493. {
  494. if (getBluetoothDeviceStatus (btAddress) != 0)
  495. return false;
  496. btDevicesPairing.put (btDevice.getAddress(), null);
  497. BluetoothGatt gatt = btDevice.connectGatt (getApplicationContext(), true, new DummyBluetoothGattCallback (this));
  498. if (gatt != null)
  499. {
  500. btDevicesPairing.put (btDevice.getAddress(), gatt);
  501. }
  502. else
  503. {
  504. pairBluetoothDeviceStepTwo (btDevice);
  505. }
  506. }
  507. return true;
  508. }
  509. public void pairBluetoothDeviceStepTwo (BluetoothDevice btDevice)
  510. {
  511. manager.openBluetoothDevice(btDevice, this, null);
  512. }
  513. public void unpairBluetoothDevice (String address)
  514. {
  515. if (address.isEmpty())
  516. return;
  517. synchronized (MidiDeviceManager.class)
  518. {
  519. if (btDevicesPairing.containsKey (address))
  520. {
  521. BluetoothGatt gatt = btDevicesPairing.get (address);
  522. if (gatt != null)
  523. {
  524. gatt.disconnect();
  525. gatt.close();
  526. }
  527. btDevicesPairing.remove (address);
  528. }
  529. MidiDeviceOpenTask openTask = findOpenTaskForBluetoothAddress (address);
  530. if (openTask != null)
  531. {
  532. int deviceID = openTask.getID();
  533. openTask.cancel();
  534. openTasks.remove (deviceID);
  535. }
  536. Pair<MidiDevice, BluetoothGatt> midiDevicePair = findMidiDeviceForBluetoothAddress (address);
  537. if (midiDevicePair != null)
  538. {
  539. MidiDevice midiDevice = midiDevicePair.first;
  540. onDeviceRemoved (midiDevice.getInfo());
  541. try {
  542. midiDevice.close();
  543. }
  544. catch (IOException exception)
  545. {
  546. Log.d ("JUCE", "IOException while closing midi device");
  547. }
  548. }
  549. }
  550. }
  551. private Pair<MidiDevice, BluetoothGatt> findMidiDeviceForBluetoothAddress (String address)
  552. {
  553. for (Pair<MidiDevice,BluetoothGatt> midiDevice : midiDevices)
  554. {
  555. MidiDeviceInfo info = midiDevice.first.getInfo();
  556. if (info.getType() == MidiDeviceInfo.TYPE_BLUETOOTH)
  557. {
  558. BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
  559. if (btDevice != null && btDevice.getAddress().equals (address))
  560. return midiDevice;
  561. }
  562. }
  563. return null;
  564. }
  565. private MidiDeviceOpenTask findOpenTaskForBluetoothAddress (String address)
  566. {
  567. for (Integer deviceID : openTasks.keySet())
  568. {
  569. MidiDeviceOpenTask openTask = openTasks.get (deviceID);
  570. if (openTask.getBluetoothAddress().equals (address))
  571. return openTask;
  572. }
  573. return null;
  574. }
  575. public void removePort (MidiPortPath path)
  576. {
  577. openPorts.remove (path);
  578. }
  579. public String getInputPortNameForJuceIndex (int index)
  580. {
  581. MidiPortPath portInfo = getPortPathForJuceIndex (MidiDeviceInfo.PortInfo.TYPE_OUTPUT, index);
  582. if (portInfo != null)
  583. return getPortName (portInfo);
  584. return "";
  585. }
  586. public String getOutputPortNameForJuceIndex (int index)
  587. {
  588. MidiPortPath portInfo = getPortPathForJuceIndex (MidiDeviceInfo.PortInfo.TYPE_INPUT, index);
  589. if (portInfo != null)
  590. return getPortName (portInfo);
  591. return "";
  592. }
  593. public void onDeviceAdded (MidiDeviceInfo info)
  594. {
  595. // only add standard midi devices
  596. if (info.getType() == info.TYPE_BLUETOOTH)
  597. return;
  598. manager.openDevice (info, this, null);
  599. }
  600. public void onDeviceRemoved (MidiDeviceInfo info)
  601. {
  602. synchronized (MidiDeviceManager.class)
  603. {
  604. Pair<MidiDevice, BluetoothGatt> devicePair = getMidiDevicePairForId (info.getId());
  605. if (devicePair != null)
  606. {
  607. MidiDevice midiDevice = devicePair.first;
  608. BluetoothGatt gatt = devicePair.second;
  609. // close all ports that use this device
  610. boolean removedPort = true;
  611. while (removedPort == true)
  612. {
  613. removedPort = false;
  614. for (MidiPortPath key : openPorts.keySet())
  615. {
  616. if (key.deviceId == info.getId())
  617. {
  618. openPorts.get(key).get().close();
  619. removedPort = true;
  620. break;
  621. }
  622. }
  623. }
  624. if (gatt != null)
  625. {
  626. gatt.disconnect();
  627. gatt.close();
  628. }
  629. midiDevices.remove (devicePair);
  630. }
  631. }
  632. }
  633. public void onDeviceStatusChanged (MidiDeviceStatus status)
  634. {
  635. }
  636. @Override
  637. public void onDeviceOpened (MidiDevice theDevice)
  638. {
  639. synchronized (MidiDeviceManager.class)
  640. {
  641. MidiDeviceInfo info = theDevice.getInfo();
  642. int deviceID = info.getId();
  643. BluetoothGatt gatt = null;
  644. boolean isBluetooth = false;
  645. if (! openTasks.containsKey (deviceID))
  646. {
  647. if (info.getType() == MidiDeviceInfo.TYPE_BLUETOOTH)
  648. {
  649. isBluetooth = true;
  650. BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
  651. if (btDevice != null)
  652. {
  653. String btAddress = btDevice.getAddress();
  654. if (btDevicesPairing.containsKey (btAddress))
  655. {
  656. gatt = btDevicesPairing.get (btAddress);
  657. btDevicesPairing.remove (btAddress);
  658. }
  659. else
  660. {
  661. // unpair was called in the mean time
  662. try
  663. {
  664. Pair<MidiDevice, BluetoothGatt> midiDevicePair = findMidiDeviceForBluetoothAddress (btDevice.getAddress());
  665. if (midiDevicePair != null)
  666. {
  667. gatt = midiDevicePair.second;
  668. if (gatt != null)
  669. {
  670. gatt.disconnect();
  671. gatt.close();
  672. }
  673. }
  674. theDevice.close();
  675. }
  676. catch (IOException e)
  677. {}
  678. return;
  679. }
  680. }
  681. }
  682. MidiDeviceOpenTask openTask = new MidiDeviceOpenTask (this, theDevice, gatt);
  683. openTasks.put (deviceID, openTask);
  684. new java.util.Timer().schedule (openTask, (isBluetooth ? 2000 : 100));
  685. }
  686. }
  687. }
  688. public void onDeviceOpenedDelayed (MidiDevice theDevice)
  689. {
  690. synchronized (MidiDeviceManager.class)
  691. {
  692. int deviceID = theDevice.getInfo().getId();
  693. if (openTasks.containsKey (deviceID))
  694. {
  695. if (! midiDevices.contains(theDevice))
  696. {
  697. BluetoothGatt gatt = openTasks.get (deviceID).getGatt();
  698. openTasks.remove (deviceID);
  699. midiDevices.add (new Pair<MidiDevice,BluetoothGatt> (theDevice, gatt));
  700. }
  701. }
  702. else
  703. {
  704. // unpair was called in the mean time
  705. MidiDeviceInfo info = theDevice.getInfo();
  706. BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
  707. if (btDevice != null)
  708. {
  709. String btAddress = btDevice.getAddress();
  710. Pair<MidiDevice, BluetoothGatt> midiDevicePair = findMidiDeviceForBluetoothAddress (btDevice.getAddress());
  711. if (midiDevicePair != null)
  712. {
  713. BluetoothGatt gatt = midiDevicePair.second;
  714. if (gatt != null)
  715. {
  716. gatt.disconnect();
  717. gatt.close();
  718. }
  719. }
  720. }
  721. try
  722. {
  723. theDevice.close();
  724. }
  725. catch (IOException e)
  726. {}
  727. }
  728. }
  729. }
  730. public String getPortName(MidiPortPath path)
  731. {
  732. int portTypeToFind = (path.isInput ? MidiDeviceInfo.PortInfo.TYPE_INPUT : MidiDeviceInfo.PortInfo.TYPE_OUTPUT);
  733. synchronized (MidiDeviceManager.class)
  734. {
  735. for (MidiDeviceInfo info : deviceInfos)
  736. {
  737. int localIndex = 0;
  738. if (info.getId() == path.deviceId)
  739. {
  740. for (MidiDeviceInfo.PortInfo portInfo : info.getPorts())
  741. {
  742. int portType = portInfo.getType();
  743. if (portType == portTypeToFind)
  744. {
  745. int portIndex = portInfo.getPortNumber();
  746. if (portIndex == path.portIndex)
  747. {
  748. String portName = portInfo.getName();
  749. if (portName.isEmpty())
  750. portName = (String) info.getProperties().get(info.PROPERTY_NAME);
  751. return portName;
  752. }
  753. }
  754. }
  755. }
  756. }
  757. }
  758. return "";
  759. }
  760. public MidiPortPath getPortPathForJuceIndex (int portType, int juceIndex)
  761. {
  762. int portIdx = 0;
  763. for (MidiDeviceInfo info : deviceInfos)
  764. {
  765. for (MidiDeviceInfo.PortInfo portInfo : info.getPorts())
  766. {
  767. if (portInfo.getType() == portType)
  768. {
  769. if (portIdx == juceIndex)
  770. return new MidiPortPath (info.getId(),
  771. (portType == MidiDeviceInfo.PortInfo.TYPE_INPUT),
  772. portInfo.getPortNumber());
  773. portIdx++;
  774. }
  775. }
  776. }
  777. return null;
  778. }
  779. private MidiDeviceInfo[] getDeviceInfos()
  780. {
  781. synchronized (MidiDeviceManager.class)
  782. {
  783. MidiDeviceInfo[] infos = new MidiDeviceInfo[midiDevices.size()];
  784. int idx = 0;
  785. for (Pair<MidiDevice,BluetoothGatt> midiDevice : midiDevices)
  786. infos[idx++] = midiDevice.first.getInfo();
  787. return infos;
  788. }
  789. }
  790. private Pair<MidiDevice, BluetoothGatt> getMidiDevicePairForId (int deviceId)
  791. {
  792. synchronized (MidiDeviceManager.class)
  793. {
  794. for (Pair<MidiDevice,BluetoothGatt> midiDevice : midiDevices)
  795. if (midiDevice.first.getInfo().getId() == deviceId)
  796. return midiDevice;
  797. }
  798. return null;
  799. }
  800. private MidiManager manager;
  801. private HashMap<String, BluetoothGatt> btDevicesPairing;
  802. private HashMap<Integer, MidiDeviceOpenTask> openTasks;
  803. private ArrayList<Pair<MidiDevice, BluetoothGatt>> midiDevices;
  804. private MidiDeviceInfo[] deviceInfos;
  805. private HashMap<MidiPortPath, WeakReference<JuceMidiPort>> openPorts;
  806. }
  807. public MidiDeviceManager getAndroidMidiDeviceManager()
  808. {
  809. if (getSystemService (MIDI_SERVICE) == null)
  810. return null;
  811. synchronized (JuceAppActivity.class)
  812. {
  813. if (midiDeviceManager == null)
  814. midiDeviceManager = new MidiDeviceManager();
  815. }
  816. return midiDeviceManager;
  817. }
  818. public BluetoothManager getAndroidBluetoothManager()
  819. {
  820. BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
  821. if (adapter == null)
  822. return null;
  823. if (adapter.getBluetoothLeScanner() == null)
  824. return null;
  825. synchronized (JuceAppActivity.class)
  826. {
  827. if (bluetoothManager == null)
  828. bluetoothManager = new BluetoothManager();
  829. }
  830. return bluetoothManager;
  831. }