fix macOS build (following Projucer changes made in Windows, which removed /Applications/JUCE/modules from its headers). move JUCE headers under source control, so that Windows and macOS can both build against same version of JUCE. remove AUv3 target (I think it's an iOS thing, so it will never work with this macOS fluidsynth dylib).

This commit is contained in:
Alex Birch
2018-06-17 13:34:53 +01:00
parent a2be47c887
commit dff4d13a1d
1563 changed files with 601601 additions and 3466 deletions

View File

@ -0,0 +1,999 @@
//==============================================================================
public class BluetoothManager extends ScanCallback
{
BluetoothManager()
{
}
public String[] getMidiBluetoothAddresses()
{
return bluetoothMidiDevices.toArray (new String[bluetoothMidiDevices.size()]);
}
public String getHumanReadableStringForBluetoothAddress (String address)
{
BluetoothDevice btDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice (address);
return btDevice.getName();
}
public int getBluetoothDeviceStatus (String address)
{
return getAndroidMidiDeviceManager().getBluetoothDeviceStatus (address);
}
public void startStopScan (boolean shouldStart)
{
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null)
{
Log.d ("JUCE", "BluetoothManager error: could not get default Bluetooth adapter");
return;
}
BluetoothLeScanner bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
if (bluetoothLeScanner == null)
{
Log.d ("JUCE", "BluetoothManager error: could not get Bluetooth LE scanner");
return;
}
if (shouldStart)
{
ScanFilter.Builder scanFilterBuilder = new ScanFilter.Builder();
scanFilterBuilder.setServiceUuid (ParcelUuid.fromString (bluetoothLEMidiServiceUUID));
ScanSettings.Builder scanSettingsBuilder = new ScanSettings.Builder();
scanSettingsBuilder.setCallbackType (ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.setScanMode (ScanSettings.SCAN_MODE_LOW_POWER)
.setScanMode (ScanSettings.MATCH_MODE_STICKY);
bluetoothLeScanner.startScan (Arrays.asList (scanFilterBuilder.build()),
scanSettingsBuilder.build(),
this);
}
else
{
bluetoothLeScanner.stopScan (this);
}
}
public boolean pairBluetoothMidiDevice(String address)
{
BluetoothDevice btDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice (address);
if (btDevice == null)
{
Log.d ("JUCE", "failed to create buletooth device from address");
return false;
}
return getAndroidMidiDeviceManager().pairBluetoothDevice (btDevice);
}
public void unpairBluetoothMidiDevice (String address)
{
getAndroidMidiDeviceManager().unpairBluetoothDevice (address);
}
public void onScanFailed (int errorCode)
{
}
public void onScanResult (int callbackType, ScanResult result)
{
if (callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES
|| callbackType == ScanSettings.CALLBACK_TYPE_FIRST_MATCH)
{
BluetoothDevice device = result.getDevice();
if (device != null)
bluetoothMidiDevices.add (device.getAddress());
}
if (callbackType == ScanSettings.CALLBACK_TYPE_MATCH_LOST)
{
Log.d ("JUCE", "ScanSettings.CALLBACK_TYPE_MATCH_LOST");
BluetoothDevice device = result.getDevice();
if (device != null)
{
bluetoothMidiDevices.remove (device.getAddress());
unpairBluetoothMidiDevice (device.getAddress());
}
}
}
public void onBatchScanResults (List<ScanResult> results)
{
for (ScanResult result : results)
onScanResult (ScanSettings.CALLBACK_TYPE_ALL_MATCHES, result);
}
private BluetoothLeScanner scanner;
private static final String bluetoothLEMidiServiceUUID = "03B80E5A-EDE8-4B33-A751-6CE34EC4C700";
private HashSet<String> bluetoothMidiDevices = new HashSet<String>();
}
public static class JuceMidiInputPort extends MidiReceiver implements JuceMidiPort
{
private native void handleReceive (long host, byte[] msg, int offset, int count, long timestamp);
public JuceMidiInputPort (MidiDeviceManager mm, MidiOutputPort actualPort, MidiPortPath portPathToUse, long hostToUse)
{
owner = mm;
androidPort = actualPort;
portPath = portPathToUse;
juceHost = hostToUse;
isConnected = false;
}
@Override
protected void finalize() throws Throwable
{
close();
super.finalize();
}
@Override
public boolean isInputPort()
{
return true;
}
@Override
public void start()
{
if (owner != null && androidPort != null && ! isConnected) {
androidPort.connect(this);
isConnected = true;
}
}
@Override
public void stop()
{
if (owner != null && androidPort != null && isConnected) {
androidPort.disconnect(this);
isConnected = false;
}
}
@Override
public void close()
{
if (androidPort != null) {
try {
androidPort.close();
} catch (IOException exception) {
Log.d("JUCE", "IO Exception while closing port");
}
}
if (owner != null)
owner.removePort (portPath);
owner = null;
androidPort = null;
}
@Override
public void onSend (byte[] msg, int offset, int count, long timestamp)
{
if (count > 0)
handleReceive (juceHost, msg, offset, count, timestamp);
}
@Override
public void onFlush()
{}
@Override
public void sendMidi (byte[] msg, int offset, int count)
{
}
MidiDeviceManager owner;
MidiOutputPort androidPort;
MidiPortPath portPath;
long juceHost;
boolean isConnected;
}
public static class JuceMidiOutputPort implements JuceMidiPort
{
public JuceMidiOutputPort (MidiDeviceManager mm, MidiInputPort actualPort, MidiPortPath portPathToUse)
{
owner = mm;
androidPort = actualPort;
portPath = portPathToUse;
}
@Override
protected void finalize() throws Throwable
{
close();
super.finalize();
}
@Override
public boolean isInputPort()
{
return false;
}
@Override
public void start()
{
}
@Override
public void stop()
{
}
@Override
public void sendMidi (byte[] msg, int offset, int count)
{
if (androidPort != null)
{
try {
androidPort.send(msg, offset, count);
} catch (IOException exception)
{
Log.d ("JUCE", "send midi had IO exception");
}
}
}
@Override
public void close()
{
if (androidPort != null) {
try {
androidPort.close();
} catch (IOException exception) {
Log.d("JUCE", "IO Exception while closing port");
}
}
if (owner != null)
owner.removePort (portPath);
owner = null;
androidPort = null;
}
MidiDeviceManager owner;
MidiInputPort androidPort;
MidiPortPath portPath;
}
private static class MidiPortPath extends Object
{
public MidiPortPath (int deviceIdToUse, boolean direction, int androidIndex)
{
deviceId = deviceIdToUse;
isInput = direction;
portIndex = androidIndex;
}
public int deviceId;
public int portIndex;
public boolean isInput;
@Override
public int hashCode()
{
Integer i = new Integer ((deviceId * 128) + (portIndex < 128 ? portIndex : 127));
return i.hashCode() * (isInput ? -1 : 1);
}
@Override
public boolean equals (Object obj)
{
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MidiPortPath other = (MidiPortPath) obj;
return (portIndex == other.portIndex && isInput == other.isInput && deviceId == other.deviceId);
}
}
//==============================================================================
public class MidiDeviceManager extends MidiManager.DeviceCallback implements MidiManager.OnDeviceOpenedListener
{
//==============================================================================
private class DummyBluetoothGattCallback extends BluetoothGattCallback
{
public DummyBluetoothGattCallback (MidiDeviceManager mm)
{
super();
owner = mm;
}
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState)
{
if (newState == BluetoothProfile.STATE_CONNECTED)
{
gatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH);
owner.pairBluetoothDeviceStepTwo (gatt.getDevice());
}
}
public void onServicesDiscovered(BluetoothGatt gatt, int status) {}
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {}
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {}
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {}
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {}
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {}
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {}
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {}
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {}
private MidiDeviceManager owner;
}
//==============================================================================
private class MidiDeviceOpenTask extends java.util.TimerTask
{
public MidiDeviceOpenTask (MidiDeviceManager deviceManager, MidiDevice device, BluetoothGatt gattToUse)
{
owner = deviceManager;
midiDevice = device;
btGatt = gattToUse;
}
@Override
public boolean cancel()
{
synchronized (MidiDeviceOpenTask.class)
{
owner = null;
boolean retval = super.cancel();
if (btGatt != null)
{
btGatt.disconnect();
btGatt.close();
btGatt = null;
}
if (midiDevice != null)
{
try
{
midiDevice.close();
}
catch (IOException e)
{}
midiDevice = null;
}
return retval;
}
}
public String getBluetoothAddress()
{
synchronized (MidiDeviceOpenTask.class)
{
if (midiDevice != null)
{
MidiDeviceInfo info = midiDevice.getInfo();
if (info.getType() == MidiDeviceInfo.TYPE_BLUETOOTH)
{
BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
if (btDevice != null)
return btDevice.getAddress();
}
}
}
return "";
}
public BluetoothGatt getGatt() { return btGatt; }
public int getID()
{
return midiDevice.getInfo().getId();
}
@Override
public void run()
{
synchronized (MidiDeviceOpenTask.class)
{
if (owner != null && midiDevice != null)
owner.onDeviceOpenedDelayed (midiDevice);
}
}
private MidiDeviceManager owner;
private MidiDevice midiDevice;
private BluetoothGatt btGatt;
}
//==============================================================================
public MidiDeviceManager()
{
manager = (MidiManager) getSystemService (MIDI_SERVICE);
if (manager == null)
{
Log.d ("JUCE", "MidiDeviceManager error: could not get MidiManager system service");
return;
}
openPorts = new HashMap<MidiPortPath, WeakReference<JuceMidiPort>> ();
midiDevices = new ArrayList<Pair<MidiDevice,BluetoothGatt>>();
openTasks = new HashMap<Integer, MidiDeviceOpenTask>();
btDevicesPairing = new HashMap<String, BluetoothGatt>();
MidiDeviceInfo[] foundDevices = manager.getDevices();
for (MidiDeviceInfo info : foundDevices)
onDeviceAdded (info);
manager.registerDeviceCallback (this, null);
}
protected void finalize() throws Throwable
{
manager.unregisterDeviceCallback (this);
synchronized (MidiDeviceManager.class)
{
btDevicesPairing.clear();
for (Integer deviceID : openTasks.keySet())
openTasks.get (deviceID).cancel();
openTasks = null;
}
for (MidiPortPath key : openPorts.keySet())
openPorts.get (key).get().close();
openPorts = null;
for (Pair<MidiDevice, BluetoothGatt> device : midiDevices)
{
if (device.second != null)
{
device.second.disconnect();
device.second.close();
}
device.first.close();
}
midiDevices.clear();
super.finalize();
}
public String[] getJuceAndroidMidiInputDevices()
{
return getJuceAndroidMidiDevices (MidiDeviceInfo.PortInfo.TYPE_OUTPUT);
}
public String[] getJuceAndroidMidiOutputDevices()
{
return getJuceAndroidMidiDevices (MidiDeviceInfo.PortInfo.TYPE_INPUT);
}
private String[] getJuceAndroidMidiDevices (int portType)
{
// only update the list when JUCE asks for a new list
synchronized (MidiDeviceManager.class)
{
deviceInfos = getDeviceInfos();
}
ArrayList<String> portNames = new ArrayList<String>();
int index = 0;
for (MidiPortPath portInfo = getPortPathForJuceIndex (portType, index); portInfo != null; portInfo = getPortPathForJuceIndex (portType, ++index))
portNames.add (getPortName (portInfo));
String[] names = new String[portNames.size()];
return portNames.toArray (names);
}
private JuceMidiPort openMidiPortWithJuceIndex (int index, long host, boolean isInput)
{
synchronized (MidiDeviceManager.class)
{
int portTypeToFind = (isInput ? MidiDeviceInfo.PortInfo.TYPE_OUTPUT : MidiDeviceInfo.PortInfo.TYPE_INPUT);
MidiPortPath portInfo = getPortPathForJuceIndex (portTypeToFind, index);
if (portInfo != null)
{
// ports must be opened exclusively!
if (openPorts.containsKey (portInfo))
return null;
Pair<MidiDevice,BluetoothGatt> devicePair = getMidiDevicePairForId (portInfo.deviceId);
if (devicePair != null)
{
MidiDevice device = devicePair.first;
if (device != null)
{
JuceMidiPort juceMidiPort = null;
if (isInput)
{
MidiOutputPort outputPort = device.openOutputPort(portInfo.portIndex);
if (outputPort != null)
juceMidiPort = new JuceMidiInputPort(this, outputPort, portInfo, host);
}
else
{
MidiInputPort inputPort = device.openInputPort(portInfo.portIndex);
if (inputPort != null)
juceMidiPort = new JuceMidiOutputPort(this, inputPort, portInfo);
}
if (juceMidiPort != null)
{
openPorts.put(portInfo, new WeakReference<JuceMidiPort>(juceMidiPort));
return juceMidiPort;
}
}
}
}
}
return null;
}
public JuceMidiPort openMidiInputPortWithJuceIndex (int index, long host)
{
return openMidiPortWithJuceIndex (index, host, true);
}
public JuceMidiPort openMidiOutputPortWithJuceIndex (int index)
{
return openMidiPortWithJuceIndex (index, 0, false);
}
/* 0: unpaired, 1: paired, 2: pairing */
public int getBluetoothDeviceStatus (String address)
{
synchronized (MidiDeviceManager.class)
{
if (! address.isEmpty())
{
if (findMidiDeviceForBluetoothAddress (address) != null)
return 1;
if (btDevicesPairing.containsKey (address))
return 2;
if (findOpenTaskForBluetoothAddress (address) != null)
return 2;
}
}
return 0;
}
public boolean pairBluetoothDevice (BluetoothDevice btDevice)
{
String btAddress = btDevice.getAddress();
if (btAddress.isEmpty())
return false;
synchronized (MidiDeviceManager.class)
{
if (getBluetoothDeviceStatus (btAddress) != 0)
return false;
btDevicesPairing.put (btDevice.getAddress(), null);
BluetoothGatt gatt = btDevice.connectGatt (getApplicationContext(), true, new DummyBluetoothGattCallback (this));
if (gatt != null)
{
btDevicesPairing.put (btDevice.getAddress(), gatt);
}
else
{
pairBluetoothDeviceStepTwo (btDevice);
}
}
return true;
}
public void pairBluetoothDeviceStepTwo (BluetoothDevice btDevice)
{
manager.openBluetoothDevice(btDevice, this, null);
}
public void unpairBluetoothDevice (String address)
{
if (address.isEmpty())
return;
synchronized (MidiDeviceManager.class)
{
if (btDevicesPairing.containsKey (address))
{
BluetoothGatt gatt = btDevicesPairing.get (address);
if (gatt != null)
{
gatt.disconnect();
gatt.close();
}
btDevicesPairing.remove (address);
}
MidiDeviceOpenTask openTask = findOpenTaskForBluetoothAddress (address);
if (openTask != null)
{
int deviceID = openTask.getID();
openTask.cancel();
openTasks.remove (deviceID);
}
Pair<MidiDevice, BluetoothGatt> midiDevicePair = findMidiDeviceForBluetoothAddress (address);
if (midiDevicePair != null)
{
MidiDevice midiDevice = midiDevicePair.first;
onDeviceRemoved (midiDevice.getInfo());
try {
midiDevice.close();
}
catch (IOException exception)
{
Log.d ("JUCE", "IOException while closing midi device");
}
}
}
}
private Pair<MidiDevice, BluetoothGatt> findMidiDeviceForBluetoothAddress (String address)
{
for (Pair<MidiDevice,BluetoothGatt> midiDevice : midiDevices)
{
MidiDeviceInfo info = midiDevice.first.getInfo();
if (info.getType() == MidiDeviceInfo.TYPE_BLUETOOTH)
{
BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
if (btDevice != null && btDevice.getAddress().equals (address))
return midiDevice;
}
}
return null;
}
private MidiDeviceOpenTask findOpenTaskForBluetoothAddress (String address)
{
for (Integer deviceID : openTasks.keySet())
{
MidiDeviceOpenTask openTask = openTasks.get (deviceID);
if (openTask.getBluetoothAddress().equals (address))
return openTask;
}
return null;
}
public void removePort (MidiPortPath path)
{
openPorts.remove (path);
}
public String getInputPortNameForJuceIndex (int index)
{
MidiPortPath portInfo = getPortPathForJuceIndex (MidiDeviceInfo.PortInfo.TYPE_OUTPUT, index);
if (portInfo != null)
return getPortName (portInfo);
return "";
}
public String getOutputPortNameForJuceIndex (int index)
{
MidiPortPath portInfo = getPortPathForJuceIndex (MidiDeviceInfo.PortInfo.TYPE_INPUT, index);
if (portInfo != null)
return getPortName (portInfo);
return "";
}
public void onDeviceAdded (MidiDeviceInfo info)
{
// only add standard midi devices
if (info.getType() == info.TYPE_BLUETOOTH)
return;
manager.openDevice (info, this, null);
}
public void onDeviceRemoved (MidiDeviceInfo info)
{
synchronized (MidiDeviceManager.class)
{
Pair<MidiDevice, BluetoothGatt> devicePair = getMidiDevicePairForId (info.getId());
if (devicePair != null)
{
MidiDevice midiDevice = devicePair.first;
BluetoothGatt gatt = devicePair.second;
// close all ports that use this device
boolean removedPort = true;
while (removedPort == true)
{
removedPort = false;
for (MidiPortPath key : openPorts.keySet())
{
if (key.deviceId == info.getId())
{
openPorts.get(key).get().close();
removedPort = true;
break;
}
}
}
if (gatt != null)
{
gatt.disconnect();
gatt.close();
}
midiDevices.remove (devicePair);
}
}
}
public void onDeviceStatusChanged (MidiDeviceStatus status)
{
}
@Override
public void onDeviceOpened (MidiDevice theDevice)
{
synchronized (MidiDeviceManager.class)
{
MidiDeviceInfo info = theDevice.getInfo();
int deviceID = info.getId();
BluetoothGatt gatt = null;
boolean isBluetooth = false;
if (! openTasks.containsKey (deviceID))
{
if (info.getType() == MidiDeviceInfo.TYPE_BLUETOOTH)
{
isBluetooth = true;
BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
if (btDevice != null)
{
String btAddress = btDevice.getAddress();
if (btDevicesPairing.containsKey (btAddress))
{
gatt = btDevicesPairing.get (btAddress);
btDevicesPairing.remove (btAddress);
}
else
{
// unpair was called in the mean time
try
{
Pair<MidiDevice, BluetoothGatt> midiDevicePair = findMidiDeviceForBluetoothAddress (btDevice.getAddress());
if (midiDevicePair != null)
{
gatt = midiDevicePair.second;
if (gatt != null)
{
gatt.disconnect();
gatt.close();
}
}
theDevice.close();
}
catch (IOException e)
{}
return;
}
}
}
MidiDeviceOpenTask openTask = new MidiDeviceOpenTask (this, theDevice, gatt);
openTasks.put (deviceID, openTask);
new java.util.Timer().schedule (openTask, (isBluetooth ? 2000 : 100));
}
}
}
public void onDeviceOpenedDelayed (MidiDevice theDevice)
{
synchronized (MidiDeviceManager.class)
{
int deviceID = theDevice.getInfo().getId();
if (openTasks.containsKey (deviceID))
{
if (! midiDevices.contains(theDevice))
{
BluetoothGatt gatt = openTasks.get (deviceID).getGatt();
openTasks.remove (deviceID);
midiDevices.add (new Pair<MidiDevice,BluetoothGatt> (theDevice, gatt));
}
}
else
{
// unpair was called in the mean time
MidiDeviceInfo info = theDevice.getInfo();
BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
if (btDevice != null)
{
String btAddress = btDevice.getAddress();
Pair<MidiDevice, BluetoothGatt> midiDevicePair = findMidiDeviceForBluetoothAddress (btDevice.getAddress());
if (midiDevicePair != null)
{
BluetoothGatt gatt = midiDevicePair.second;
if (gatt != null)
{
gatt.disconnect();
gatt.close();
}
}
}
try
{
theDevice.close();
}
catch (IOException e)
{}
}
}
}
public String getPortName(MidiPortPath path)
{
int portTypeToFind = (path.isInput ? MidiDeviceInfo.PortInfo.TYPE_INPUT : MidiDeviceInfo.PortInfo.TYPE_OUTPUT);
synchronized (MidiDeviceManager.class)
{
for (MidiDeviceInfo info : deviceInfos)
{
int localIndex = 0;
if (info.getId() == path.deviceId)
{
for (MidiDeviceInfo.PortInfo portInfo : info.getPorts())
{
int portType = portInfo.getType();
if (portType == portTypeToFind)
{
int portIndex = portInfo.getPortNumber();
if (portIndex == path.portIndex)
{
String portName = portInfo.getName();
if (portName.isEmpty())
portName = (String) info.getProperties().get(info.PROPERTY_NAME);
return portName;
}
}
}
}
}
}
return "";
}
public MidiPortPath getPortPathForJuceIndex (int portType, int juceIndex)
{
int portIdx = 0;
for (MidiDeviceInfo info : deviceInfos)
{
for (MidiDeviceInfo.PortInfo portInfo : info.getPorts())
{
if (portInfo.getType() == portType)
{
if (portIdx == juceIndex)
return new MidiPortPath (info.getId(),
(portType == MidiDeviceInfo.PortInfo.TYPE_INPUT),
portInfo.getPortNumber());
portIdx++;
}
}
}
return null;
}
private MidiDeviceInfo[] getDeviceInfos()
{
synchronized (MidiDeviceManager.class)
{
MidiDeviceInfo[] infos = new MidiDeviceInfo[midiDevices.size()];
int idx = 0;
for (Pair<MidiDevice,BluetoothGatt> midiDevice : midiDevices)
infos[idx++] = midiDevice.first.getInfo();
return infos;
}
}
private Pair<MidiDevice, BluetoothGatt> getMidiDevicePairForId (int deviceId)
{
synchronized (MidiDeviceManager.class)
{
for (Pair<MidiDevice,BluetoothGatt> midiDevice : midiDevices)
if (midiDevice.first.getInfo().getId() == deviceId)
return midiDevice;
}
return null;
}
private MidiManager manager;
private HashMap<String, BluetoothGatt> btDevicesPairing;
private HashMap<Integer, MidiDeviceOpenTask> openTasks;
private ArrayList<Pair<MidiDevice, BluetoothGatt>> midiDevices;
private MidiDeviceInfo[] deviceInfos;
private HashMap<MidiPortPath, WeakReference<JuceMidiPort>> openPorts;
}
public MidiDeviceManager getAndroidMidiDeviceManager()
{
if (getSystemService (MIDI_SERVICE) == null)
return null;
synchronized (JuceAppActivity.class)
{
if (midiDeviceManager == null)
midiDeviceManager = new MidiDeviceManager();
}
return midiDeviceManager;
}
public BluetoothManager getAndroidBluetoothManager()
{
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter == null)
return null;
if (adapter.getBluetoothLeScanner() == null)
return null;
synchronized (JuceAppActivity.class)
{
if (bluetoothManager == null)
bluetoothManager = new BluetoothManager();
}
return bluetoothManager;
}