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.

867 lines
28KB

  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 MidiDeviceOpenTask extends java.util.TimerTask
  261. {
  262. public MidiDeviceOpenTask (MidiDeviceManager deviceManager, MidiDevice device)
  263. {
  264. owner = deviceManager;
  265. midiDevice = device;
  266. }
  267. @Override
  268. public boolean cancel()
  269. {
  270. synchronized (MidiDeviceOpenTask.class)
  271. {
  272. owner = null;
  273. boolean retval = super.cancel();
  274. if (midiDevice != null)
  275. {
  276. try
  277. {
  278. midiDevice.close();
  279. }
  280. catch (IOException e)
  281. {}
  282. midiDevice = null;
  283. }
  284. return retval;
  285. }
  286. }
  287. public String getBluetoothAddress()
  288. {
  289. synchronized (MidiDeviceOpenTask.class)
  290. {
  291. if (midiDevice != null)
  292. {
  293. MidiDeviceInfo info = midiDevice.getInfo();
  294. if (info.getType() == MidiDeviceInfo.TYPE_BLUETOOTH)
  295. {
  296. BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
  297. if (btDevice != null)
  298. return btDevice.getAddress();
  299. }
  300. }
  301. }
  302. return "";
  303. }
  304. public int getID()
  305. {
  306. return midiDevice.getInfo().getId();
  307. }
  308. @Override
  309. public void run()
  310. {
  311. synchronized (MidiDeviceOpenTask.class)
  312. {
  313. if (owner != null && midiDevice != null)
  314. owner.onDeviceOpenedDelayed (midiDevice);
  315. }
  316. }
  317. private MidiDeviceManager owner;
  318. private MidiDevice midiDevice;
  319. }
  320. //==============================================================================
  321. public MidiDeviceManager()
  322. {
  323. manager = (MidiManager) getSystemService (MIDI_SERVICE);
  324. if (manager == null)
  325. {
  326. Log.d ("JUCE", "MidiDeviceManager error: could not get MidiManager system service");
  327. return;
  328. }
  329. openPorts = new HashMap<MidiPortPath, WeakReference<JuceMidiPort>> ();
  330. midiDevices = new ArrayList<MidiDevice>();
  331. openTasks = new HashMap<Integer, MidiDeviceOpenTask>();
  332. btDevicesPairing = new HashSet<String>();
  333. MidiDeviceInfo[] foundDevices = manager.getDevices();
  334. for (MidiDeviceInfo info : foundDevices)
  335. onDeviceAdded (info);
  336. manager.registerDeviceCallback (this, null);
  337. }
  338. protected void finalize() throws Throwable
  339. {
  340. manager.unregisterDeviceCallback (this);
  341. synchronized (MidiDeviceManager.class)
  342. {
  343. btDevicesPairing.clear();
  344. for (Integer deviceID : openTasks.keySet())
  345. openTasks.get (deviceID).cancel();
  346. openTasks = null;
  347. }
  348. for (MidiPortPath key : openPorts.keySet())
  349. openPorts.get (key).get().close();
  350. openPorts = null;
  351. for (MidiDevice device : midiDevices)
  352. device.close();
  353. super.finalize();
  354. }
  355. public String[] getJuceAndroidMidiInputDevices()
  356. {
  357. return getJuceAndroidMidiDevices (MidiDeviceInfo.PortInfo.TYPE_OUTPUT);
  358. }
  359. public String[] getJuceAndroidMidiOutputDevices()
  360. {
  361. return getJuceAndroidMidiDevices (MidiDeviceInfo.PortInfo.TYPE_INPUT);
  362. }
  363. private String[] getJuceAndroidMidiDevices (int portType)
  364. {
  365. // only update the list when JUCE asks for a new list
  366. synchronized (MidiDeviceManager.class)
  367. {
  368. deviceInfos = getDeviceInfos();
  369. }
  370. ArrayList<String> portNames = new ArrayList<String>();
  371. int index = 0;
  372. for (MidiPortPath portInfo = getPortPathForJuceIndex (portType, index); portInfo != null; portInfo = getPortPathForJuceIndex (portType, ++index))
  373. portNames.add (getPortName (portInfo));
  374. String[] names = new String[portNames.size()];
  375. return portNames.toArray (names);
  376. }
  377. private JuceMidiPort openMidiPortWithJuceIndex (int index, long host, boolean isInput)
  378. {
  379. synchronized (MidiDeviceManager.class)
  380. {
  381. int portTypeToFind = (isInput ? MidiDeviceInfo.PortInfo.TYPE_OUTPUT : MidiDeviceInfo.PortInfo.TYPE_INPUT);
  382. MidiPortPath portInfo = getPortPathForJuceIndex (portTypeToFind, index);
  383. if (portInfo != null)
  384. {
  385. // ports must be opened exclusively!
  386. if (openPorts.containsKey (portInfo))
  387. return null;
  388. MidiDevice device = getMidiDeviceForId (portInfo.deviceId);
  389. if (device != null)
  390. {
  391. JuceMidiPort juceMidiPort = null;
  392. if (isInput)
  393. {
  394. MidiOutputPort outputPort = device.openOutputPort (portInfo.portIndex);
  395. if (outputPort != null)
  396. juceMidiPort = new JuceMidiInputPort(this, outputPort, portInfo, host);
  397. }
  398. else
  399. {
  400. MidiInputPort inputPort = device.openInputPort (portInfo.portIndex);
  401. if (inputPort != null)
  402. juceMidiPort = new JuceMidiOutputPort(this, inputPort, portInfo);
  403. }
  404. if (juceMidiPort != null) {
  405. openPorts.put(portInfo, new WeakReference<JuceMidiPort>(juceMidiPort));
  406. return juceMidiPort;
  407. }
  408. }
  409. }
  410. }
  411. return null;
  412. }
  413. public JuceMidiPort openMidiInputPortWithJuceIndex (int index, long host)
  414. {
  415. return openMidiPortWithJuceIndex (index, host, true);
  416. }
  417. public JuceMidiPort openMidiOutputPortWithJuceIndex (int index)
  418. {
  419. return openMidiPortWithJuceIndex (index, 0, false);
  420. }
  421. /* 0: unpaired, 1: paired, 2: pairing */
  422. public int getBluetoothDeviceStatus (String address)
  423. {
  424. synchronized (MidiDeviceManager.class)
  425. {
  426. if (! address.isEmpty())
  427. {
  428. if (findMidiDeviceForBluetoothAddress (address) != null)
  429. return 1;
  430. if (btDevicesPairing.contains (address))
  431. return 2;
  432. if (findOpenTaskForBluetoothAddress (address) != null)
  433. return 2;
  434. }
  435. }
  436. return 0;
  437. }
  438. public boolean pairBluetoothDevice (BluetoothDevice btDevice)
  439. {
  440. String btAddress = btDevice.getAddress();
  441. if (btAddress.isEmpty())
  442. return false;
  443. synchronized (MidiDeviceManager.class)
  444. {
  445. if (getBluetoothDeviceStatus (btAddress) != 0)
  446. return false;
  447. btDevicesPairing.add (btDevice.getAddress());
  448. manager.openBluetoothDevice(btDevice, this, null);
  449. }
  450. return true;
  451. }
  452. public void unpairBluetoothDevice (String address)
  453. {
  454. if (address.isEmpty())
  455. return;
  456. synchronized (MidiDeviceManager.class)
  457. {
  458. btDevicesPairing.remove (address);
  459. MidiDeviceOpenTask openTask = findOpenTaskForBluetoothAddress (address);
  460. if (openTask != null)
  461. {
  462. int deviceID = openTask.getID();
  463. openTask.cancel();
  464. openTasks.remove (deviceID);
  465. }
  466. MidiDevice midiDevice = findMidiDeviceForBluetoothAddress (address);
  467. if (midiDevice != null)
  468. {
  469. onDeviceRemoved (midiDevice.getInfo());
  470. try {
  471. midiDevice.close();
  472. }
  473. catch (IOException exception)
  474. {
  475. Log.d ("JUCE", "IOException while closing midi device");
  476. }
  477. }
  478. }
  479. }
  480. private MidiDevice findMidiDeviceForBluetoothAddress (String address)
  481. {
  482. for (MidiDevice midiDevice : midiDevices)
  483. {
  484. MidiDeviceInfo info = midiDevice.getInfo();
  485. if (info.getType() == MidiDeviceInfo.TYPE_BLUETOOTH)
  486. {
  487. BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
  488. if (btDevice != null && btDevice.getAddress().equals (address))
  489. return midiDevice;
  490. }
  491. }
  492. return null;
  493. }
  494. private MidiDeviceOpenTask findOpenTaskForBluetoothAddress (String address)
  495. {
  496. for (Integer deviceID : openTasks.keySet())
  497. {
  498. MidiDeviceOpenTask openTask = openTasks.get (deviceID);
  499. if (openTask.getBluetoothAddress().equals (address))
  500. return openTask;
  501. }
  502. return null;
  503. }
  504. public void removePort (MidiPortPath path)
  505. {
  506. openPorts.remove (path);
  507. }
  508. public String getInputPortNameForJuceIndex (int index)
  509. {
  510. MidiPortPath portInfo = getPortPathForJuceIndex (MidiDeviceInfo.PortInfo.TYPE_OUTPUT, index);
  511. if (portInfo != null)
  512. return getPortName (portInfo);
  513. return "";
  514. }
  515. public String getOutputPortNameForJuceIndex (int index)
  516. {
  517. MidiPortPath portInfo = getPortPathForJuceIndex (MidiDeviceInfo.PortInfo.TYPE_INPUT, index);
  518. if (portInfo != null)
  519. return getPortName (portInfo);
  520. return "";
  521. }
  522. public void onDeviceAdded (MidiDeviceInfo info)
  523. {
  524. // only add standard midi devices
  525. if (info.getType() == info.TYPE_BLUETOOTH)
  526. return;
  527. manager.openDevice (info, this, null);
  528. }
  529. public void onDeviceRemoved (MidiDeviceInfo info)
  530. {
  531. synchronized (MidiDeviceManager.class)
  532. {
  533. MidiDevice device = getMidiDeviceForId (info.getId());
  534. // close all ports that use this device
  535. boolean removedPort = true;
  536. while (removedPort == true) {
  537. removedPort = false;
  538. for (MidiPortPath key : openPorts.keySet()) {
  539. if (key.deviceId == info.getId()) {
  540. openPorts.get(key).get().close();
  541. removedPort = true;
  542. break;
  543. }
  544. }
  545. }
  546. if (device != null)
  547. midiDevices.remove (device);
  548. }
  549. }
  550. public void onDeviceStatusChanged (MidiDeviceStatus status)
  551. {
  552. }
  553. @Override
  554. public void onDeviceOpened (MidiDevice theDevice)
  555. {
  556. synchronized (MidiDeviceManager.class)
  557. {
  558. MidiDeviceInfo info = theDevice.getInfo();
  559. int deviceID = info.getId();
  560. if (! openTasks.containsKey (deviceID))
  561. {
  562. if (info.getType() == MidiDeviceInfo.TYPE_BLUETOOTH)
  563. {
  564. BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
  565. if (btDevice != null)
  566. {
  567. String btAddress = btDevice.getAddress();
  568. if (btDevicesPairing.contains (btAddress))
  569. {
  570. btDevicesPairing.remove (btAddress);
  571. }
  572. else
  573. {
  574. // unpair was called in the mean time
  575. try
  576. {
  577. theDevice.close();
  578. }
  579. catch (IOException e)
  580. {}
  581. return;
  582. }
  583. }
  584. }
  585. MidiDeviceOpenTask openTask = new MidiDeviceOpenTask (this, theDevice);
  586. openTasks.put (deviceID, openTask);
  587. new java.util.Timer().schedule (openTask, 3000);
  588. }
  589. }
  590. }
  591. public void onDeviceOpenedDelayed (MidiDevice theDevice)
  592. {
  593. synchronized (MidiDeviceManager.class)
  594. {
  595. int deviceID = theDevice.getInfo().getId();
  596. if (openTasks.containsKey (deviceID))
  597. {
  598. if (! midiDevices.contains(theDevice))
  599. {
  600. openTasks.remove (deviceID);
  601. midiDevices.add (theDevice);
  602. }
  603. }
  604. else
  605. {
  606. // unpair was called in the mean time
  607. try
  608. {
  609. theDevice.close();
  610. }
  611. catch (IOException e)
  612. {}
  613. }
  614. }
  615. }
  616. public String getPortName(MidiPortPath path)
  617. {
  618. int portTypeToFind = (path.isInput ? MidiDeviceInfo.PortInfo.TYPE_INPUT : MidiDeviceInfo.PortInfo.TYPE_OUTPUT);
  619. synchronized (MidiDeviceManager.class)
  620. {
  621. for (MidiDeviceInfo info : deviceInfos)
  622. {
  623. int localIndex = 0;
  624. if (info.getId() == path.deviceId)
  625. {
  626. for (MidiDeviceInfo.PortInfo portInfo : info.getPorts())
  627. {
  628. int portType = portInfo.getType();
  629. if (portType == portTypeToFind)
  630. {
  631. int portIndex = portInfo.getPortNumber();
  632. if (portIndex == path.portIndex)
  633. {
  634. String portName = portInfo.getName();
  635. if (portName.isEmpty())
  636. portName = (String) info.getProperties().get(info.PROPERTY_NAME);
  637. return portName;
  638. }
  639. }
  640. }
  641. }
  642. }
  643. }
  644. return "";
  645. }
  646. public MidiPortPath getPortPathForJuceIndex (int portType, int juceIndex)
  647. {
  648. int portIdx = 0;
  649. for (MidiDeviceInfo info : deviceInfos)
  650. {
  651. for (MidiDeviceInfo.PortInfo portInfo : info.getPorts())
  652. {
  653. if (portInfo.getType() == portType)
  654. {
  655. if (portIdx == juceIndex)
  656. return new MidiPortPath (info.getId(),
  657. (portType == MidiDeviceInfo.PortInfo.TYPE_INPUT),
  658. portInfo.getPortNumber());
  659. portIdx++;
  660. }
  661. }
  662. }
  663. return null;
  664. }
  665. private MidiDeviceInfo[] getDeviceInfos()
  666. {
  667. synchronized (MidiDeviceManager.class)
  668. {
  669. MidiDeviceInfo[] infos = new MidiDeviceInfo[midiDevices.size()];
  670. int idx = 0;
  671. for (MidiDevice midiDevice : midiDevices)
  672. infos[idx++] = midiDevice.getInfo();
  673. return infos;
  674. }
  675. }
  676. private MidiDevice getMidiDeviceForId (int deviceId)
  677. {
  678. synchronized (MidiDeviceManager.class)
  679. {
  680. for (MidiDevice midiDevice : midiDevices)
  681. if (midiDevice.getInfo().getId() == deviceId)
  682. return midiDevice;
  683. }
  684. return null;
  685. }
  686. private MidiManager manager;
  687. private HashSet<String> btDevicesPairing;
  688. private HashMap<Integer, MidiDeviceOpenTask> openTasks;
  689. private ArrayList<MidiDevice> midiDevices;
  690. private MidiDeviceInfo[] deviceInfos;
  691. private HashMap<MidiPortPath, WeakReference<JuceMidiPort>> openPorts;
  692. }
  693. public MidiDeviceManager getAndroidMidiDeviceManager()
  694. {
  695. if (getSystemService (MIDI_SERVICE) == null)
  696. return null;
  697. synchronized (JuceAppActivity.class)
  698. {
  699. if (midiDeviceManager == null)
  700. midiDeviceManager = new MidiDeviceManager();
  701. }
  702. return midiDeviceManager;
  703. }
  704. public BluetoothManager getAndroidBluetoothManager()
  705. {
  706. BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
  707. if (adapter == null)
  708. return null;
  709. if (adapter.getBluetoothLeScanner() == null)
  710. return null;
  711. synchronized (JuceAppActivity.class)
  712. {
  713. if (bluetoothManager == null)
  714. bluetoothManager = new BluetoothManager();
  715. }
  716. return bluetoothManager;
  717. }