|
- //==============================================================================
- public class BluetoothManager extends ScanCallback
- {
- BluetoothManager()
- {
- 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);
-
- 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;
- }
-
- bluetoothLeScanner.startScan (Arrays.asList (scanFilterBuilder.build()),
- scanSettingsBuilder.build(),
- this);
- }
-
- 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 boolean isBluetoothDevicePaired (String address)
- {
- return getAndroidMidiDeviceManager().isBluetoothDevicePaired (address);
- }
-
- 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;
- }
-
- MidiManager mm = (MidiManager) getSystemService (MIDI_SERVICE);
-
- PhysicalMidiDevice midiDevice = PhysicalMidiDevice.fromBluetoothLeDevice (btDevice, mm);
-
- if (midiDevice != null)
- {
- getAndroidMidiDeviceManager().addDeviceToList (midiDevice);
- return true;
- }
-
- return false;
- }
-
- 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 (PhysicalMidiDevice device, long host, MidiOutputPort midiPort)
- {
- parent = device;
- juceHost = host;
- port = midiPort;
- }
-
- @Override
- public boolean isInputPort()
- {
- return true;
- }
-
- @Override
- public void start()
- {
- port.connect (this);
- }
-
- @Override
- public void stop()
- {
- port.disconnect (this);
- }
-
- @Override
- public void close()
- {
- stop();
-
- try
- {
- port.close();
- }
- catch (IOException e)
- {
- Log.d ("JUCE", "JuceMidiInputPort::close: IOException = " + e.toString());
- }
-
- if (parent != null)
- {
- parent.removePort (port.getPortNumber(), true);
- parent = null;
- }
- }
-
- public void onSend (byte[] msg, int offset, int count, long timestamp)
- {
- if (count > 0)
- handleReceive (juceHost, msg, offset, count, timestamp);
- }
-
- @Override
- public MidiPortID getPortId()
- {
- return new MidiPortID (port.getPortNumber(), true);
- }
-
- @Override
- public void sendMidi (byte[] msg, int offset, int count)
- {
- }
-
- private PhysicalMidiDevice parent = null;
- private long juceHost = 0;
- private MidiOutputPort port;
- }
-
- public static class JuceMidiOutputPort implements JuceMidiPort
- {
- public JuceMidiOutputPort (PhysicalMidiDevice device, MidiInputPort midiPort)
- {
- parent = device;
- port = midiPort;
- }
-
- @Override
- public boolean isInputPort()
- {
- return false;
- }
-
- @Override
- public void start()
- {
- }
-
- @Override
- public void stop()
- {
- }
-
- @Override
- public void sendMidi (byte[] msg, int offset, int count)
- {
- try
- {
- port.send(msg, offset, count);
- }
- catch (IOException e)
- {
- Log.d ("JUCE", "JuceMidiOutputPort::sendMidi: IOException = " + e.toString());
- }
- }
-
- @Override
- public void close()
- {
- try
- {
- port.close();
- }
- catch (IOException e)
- {
- Log.d ("JUCE", "JuceMidiOutputPort::close: IOException = " + e.toString());
- }
-
- if (parent != null)
- {
- parent.removePort (port.getPortNumber(), false);
- parent = null;
- }
- }
-
-
- @Override
- public MidiPortID getPortId()
- {
- return new MidiPortID (port.getPortNumber(), false);
- }
-
- private PhysicalMidiDevice parent = null;
- private MidiInputPort port;
- }
-
- public static class PhysicalMidiDevice
- {
- private static class MidiDeviceThread extends Thread
- {
- public Handler handler = null;
- public Object sync = null;
-
- public MidiDeviceThread (Object syncrhonization)
- {
- sync = syncrhonization;
- }
-
- public void run()
- {
- Looper.prepare();
-
- synchronized (sync)
- {
- handler = new Handler();
- sync.notifyAll();
- }
-
- Looper.loop();
- }
- }
-
- private static class MidiDeviceOpenCallback implements MidiManager.OnDeviceOpenedListener
- {
- public Object sync = null;
- public boolean isWaiting = true;
- public android.media.midi.MidiDevice theDevice = null;
-
- public MidiDeviceOpenCallback (Object waiter)
- {
- sync = waiter;
- }
-
- public void onDeviceOpened (MidiDevice device)
- {
- synchronized (sync)
- {
- theDevice = device;
- isWaiting = false;
- sync.notifyAll();
- }
- }
- }
-
- public static PhysicalMidiDevice fromBluetoothLeDevice (BluetoothDevice bluetoothDevice, MidiManager mm)
- {
- Object waitForCreation = new Object();
- MidiDeviceThread thread = new MidiDeviceThread (waitForCreation);
- thread.start();
-
- synchronized (waitForCreation)
- {
- while (thread.handler == null)
- {
- try
- {
- waitForCreation.wait();
- }
- catch (InterruptedException e)
- {
- Log.d ("JUCE", "Wait was interrupted but we don't care");
- }
- }
- }
-
- Object waitForDevice = new Object();
-
- MidiDeviceOpenCallback openCallback = new MidiDeviceOpenCallback (waitForDevice);
-
- synchronized (waitForDevice)
- {
- mm.openBluetoothDevice (bluetoothDevice, openCallback, thread.handler);
-
- while (openCallback.isWaiting)
- {
- try
- {
- waitForDevice.wait();
- }
- catch (InterruptedException e)
- {
- Log.d ("JUCE", "Wait was interrupted but we don't care");
- }
- }
- }
-
- if (openCallback.theDevice == null)
- {
- Log.d ("JUCE", "openBluetoothDevice failed");
- return null;
- }
-
- PhysicalMidiDevice device = new PhysicalMidiDevice();
-
- device.handle = openCallback.theDevice;
- device.info = device.handle.getInfo();
- device.bluetoothAddress = bluetoothDevice.getAddress();
- device.midiManager = mm;
-
- return device;
- }
-
- public void unpair()
- {
- if (! bluetoothAddress.equals ("") && handle != null)
- {
- JuceMidiPort ports[] = new JuceMidiPort[0];
- ports = juceOpenedPorts.values().toArray(ports);
-
- for (int i = 0; i < ports.length; ++i)
- ports[i].close();
-
- juceOpenedPorts.clear();
-
- try
- {
- handle.close();
- }
- catch (IOException e)
- {
- Log.d ("JUCE", "handle.close(): IOException = " + e.toString());
- }
-
- handle = null;
- }
- }
-
- public static PhysicalMidiDevice fromMidiDeviceInfo (MidiDeviceInfo info, MidiManager mm)
- {
- PhysicalMidiDevice device = new PhysicalMidiDevice();
- device.info = info;
- device.midiManager = mm;
- return device;
- }
-
- public PhysicalMidiDevice()
- {
- bluetoothAddress = "";
- juceOpenedPorts = new Hashtable<MidiPortID, JuceMidiPort>();
- handle = null;
- }
-
- public MidiDeviceInfo.PortInfo[] getPorts()
- {
- return info.getPorts();
- }
-
- public String getHumanReadableNameForPort (MidiDeviceInfo.PortInfo port, int portIndexToUseInName)
- {
- String portName = port.getName();
-
- if (portName.equals (""))
- portName = ((port.getType() == MidiDeviceInfo.PortInfo.TYPE_OUTPUT) ? "Out " : "In ")
- + Integer.toString (portIndexToUseInName);
-
- return getHumanReadableDeviceName() + " " + portName;
- }
-
- public String getHumanReadableNameForPort (int portType, int androidPortID, int portIndexToUseInName)
- {
- MidiDeviceInfo.PortInfo[] ports = info.getPorts();
-
- for (MidiDeviceInfo.PortInfo port : ports)
- {
- if (port.getType() == portType)
- {
- if (port.getPortNumber() == androidPortID)
- return getHumanReadableNameForPort (port, portIndexToUseInName);
- }
- }
-
- return "Unknown";
- }
-
- public String getHumanReadableDeviceName()
- {
- Bundle bundle = info.getProperties();
- return bundle.getString (MidiDeviceInfo.PROPERTY_NAME , "Unknown device");
- }
-
- public void checkIfDeviceCanBeClosed()
- {
- if (juceOpenedPorts.size() == 0)
- {
- // never close bluetooth LE devices, otherwise they unpair and we have
- // no idea how many ports they have.
- // Only remove bluetooth devices when we specifically unpair
- if (bluetoothAddress.equals (""))
- {
- try
- {
- handle.close();
- handle = null;
- }
- catch (IOException e)
- {
- Log.d ("JUCE", "PhysicalMidiDevice::checkIfDeviceCanBeClosed: IOException = " + e.toString());
- }
- }
- }
- }
-
- public void removePort (int portIdx, boolean isInput)
- {
- MidiPortID portID = new MidiPortID (portIdx, isInput);
- JuceMidiPort port = juceOpenedPorts.get (portID);
-
- if (port != null)
- {
- juceOpenedPorts.remove (portID);
- checkIfDeviceCanBeClosed();
- return;
- }
-
- // tried to remove a port that was never added
- assert false;
- }
-
- public JuceMidiPort openPort (int portIdx, boolean isInput, long host)
- {
- open();
-
- if (handle == null)
- {
- Log.d ("JUCE", "PhysicalMidiDevice::openPort: handle = null, device not open");
- return null;
- }
-
- // make sure that the port is not already open
- if (findPortForIdx (portIdx, isInput) != null)
- {
- Log.d ("JUCE", "PhysicalMidiDevice::openInputPort: port already open, not opening again!");
- return null;
- }
-
- JuceMidiPort retval = null;
-
- if (isInput)
- {
- MidiOutputPort androidPort = handle.openOutputPort (portIdx);
-
- if (androidPort == null)
- {
- Log.d ("JUCE", "PhysicalMidiDevice::openPort: MidiDevice::openOutputPort (portIdx = "
- + Integer.toString (portIdx) + ") failed!");
- return null;
- }
-
- retval = new JuceMidiInputPort (this, host, androidPort);
- }
- else
- {
- MidiInputPort androidPort = handle.openInputPort (portIdx);
-
- if (androidPort == null)
- {
- Log.d ("JUCE", "PhysicalMidiDevice::openPort: MidiDevice::openInputPort (portIdx = "
- + Integer.toString (portIdx) + ") failed!");
- return null;
- }
-
- retval = new JuceMidiOutputPort (this, androidPort);
- }
-
- juceOpenedPorts.put (new MidiPortID (portIdx, isInput), retval);
- return retval;
- }
-
- private JuceMidiPort findPortForIdx (int idx, boolean isInput)
- {
- return juceOpenedPorts.get (new MidiPortID (idx, isInput));
- }
-
- // opens the device
- private synchronized void open()
- {
- if (handle != null)
- return;
-
- Object waitForCreation = new Object();
- MidiDeviceThread thread = new MidiDeviceThread (waitForCreation);
- thread.start();
-
- synchronized(waitForCreation)
- {
- while (thread.handler == null)
- {
- try
- {
- waitForCreation.wait();
- }
- catch (InterruptedException e)
- {
- Log.d ("JUCE", "wait was interrupted but we don't care");
- }
- }
- }
-
- Object waitForDevice = new Object();
-
- MidiDeviceOpenCallback openCallback = new MidiDeviceOpenCallback (waitForDevice);
-
- synchronized (waitForDevice)
- {
- midiManager.openDevice (info, openCallback, thread.handler);
-
- while (openCallback.isWaiting)
- {
- try
- {
- waitForDevice.wait();
- }
- catch (InterruptedException e)
- {
- Log.d ("JUCE", "wait was interrupted but we don't care");
- }
- }
- }
-
- handle = openCallback.theDevice;
- }
-
- private MidiDeviceInfo info;
- private Hashtable<MidiPortID, JuceMidiPort> juceOpenedPorts;
- public MidiDevice handle;
- public String bluetoothAddress;
- private MidiManager midiManager;
- }
-
- //==============================================================================
- public class MidiDeviceManager extends MidiManager.DeviceCallback
- {
- public class MidiPortPath
- {
- public PhysicalMidiDevice midiDevice;
- public int androidMidiPortID;
- public int portIndexToUseInName;
- }
-
- public class JuceDeviceList
- {
- public ArrayList<MidiPortPath> inPorts = new ArrayList<MidiPortPath>();
- public ArrayList<MidiPortPath> outPorts = new ArrayList<MidiPortPath>();
- }
-
- // We need to keep a thread local copy of the devices
- // which we returned the last time
- // getJuceAndroidMidiIn/OutputDevices() was called
- private final ThreadLocal<JuceDeviceList> lastDevicesReturned =
- new ThreadLocal<JuceDeviceList>()
- {
- @Override protected JuceDeviceList initialValue()
- {
- return new JuceDeviceList();
- }
- };
-
- public MidiDeviceManager()
- {
- physicalMidiDevices = new ArrayList<PhysicalMidiDevice>();
- manager = (MidiManager) getSystemService (MIDI_SERVICE);
-
- if (manager == null)
- {
- Log.d ("JUCE", "MidiDeviceManager error: could not get MidiManager system service");
- return;
- }
-
- manager.registerDeviceCallback (this, null);
-
- MidiDeviceInfo[] foundDevices = manager.getDevices();
-
- for (MidiDeviceInfo info : foundDevices)
- physicalMidiDevices.add (PhysicalMidiDevice.fromMidiDeviceInfo (info, manager));
- }
-
- // specifically add a device to the list
- public void addDeviceToList (PhysicalMidiDevice device)
- {
- physicalMidiDevices.add (device);
- }
-
- public void unpairBluetoothDevice (String address)
- {
- for (int i = 0; i < physicalMidiDevices.size(); ++i)
- {
- PhysicalMidiDevice device = physicalMidiDevices.get(i);
-
- if (device.bluetoothAddress.equals (address))
- {
- physicalMidiDevices.remove (i);
- device.unpair();
- return;
- }
- }
- }
-
- public boolean isBluetoothDevicePaired (String address)
- {
- for (int i = 0; i < physicalMidiDevices.size(); ++i)
- {
- PhysicalMidiDevice device = physicalMidiDevices.get(i);
-
- if (device.bluetoothAddress.equals (address))
- return true;
- }
-
- return false;
- }
-
- public String[] getJuceAndroidMidiInputDevices()
- {
- return getJuceAndroidMidiDevices (MidiDeviceInfo.PortInfo.TYPE_INPUT);
- }
-
- public String[] getJuceAndroidMidiOutputDevices()
- {
- return getJuceAndroidMidiDevices (MidiDeviceInfo.PortInfo.TYPE_OUTPUT);
- }
-
- private String[] getJuceAndroidMidiDevices (int portType)
- {
- ArrayList<MidiPortPath> listOfReturnedDevices = new ArrayList<MidiPortPath>();
- List<String> deviceNames = new ArrayList<String>();
-
- for (PhysicalMidiDevice physicalMidiDevice : physicalMidiDevices)
- {
- int portIdx = 0;
- MidiDeviceInfo.PortInfo[] ports = physicalMidiDevice.getPorts();
-
- for (MidiDeviceInfo.PortInfo port : ports)
- {
- if (port.getType() == portType)
- {
- MidiPortPath path = new MidiPortPath();
- path.midiDevice = physicalMidiDevice;
- path.androidMidiPortID = port.getPortNumber();
- path.portIndexToUseInName = ++portIdx;
- listOfReturnedDevices.add (path);
-
- deviceNames.add (physicalMidiDevice.getHumanReadableNameForPort (port,
- path.portIndexToUseInName));
- }
- }
- }
-
- String[] deviceNamesArray = new String[deviceNames.size()];
-
- if (portType == MidiDeviceInfo.PortInfo.TYPE_INPUT)
- {
- lastDevicesReturned.get().inPorts.clear();
- lastDevicesReturned.get().inPorts.addAll (listOfReturnedDevices);
- }
- else
- {
- lastDevicesReturned.get().outPorts.clear();
- lastDevicesReturned.get().outPorts.addAll (listOfReturnedDevices);
- }
-
- return deviceNames.toArray(deviceNamesArray);
- }
-
- public JuceMidiPort openMidiInputPortWithJuceIndex (int index, long host)
- {
- ArrayList<MidiPortPath> lastDevices = lastDevicesReturned.get().inPorts;
-
- if (index >= lastDevices.size() || index < 0)
- return null;
-
- MidiPortPath path = lastDevices.get (index);
- return path.midiDevice.openPort (path.androidMidiPortID, true, host);
- }
-
- public JuceMidiPort openMidiOutputPortWithJuceIndex (int index)
- {
- ArrayList<MidiPortPath> lastDevices = lastDevicesReturned.get().outPorts;
-
- if (index >= lastDevices.size() || index < 0)
- return null;
-
- MidiPortPath path = lastDevices.get (index);
- return path.midiDevice.openPort (path.androidMidiPortID, false, 0);
- }
-
- public String getInputPortNameForJuceIndex (int index)
- {
- ArrayList<MidiPortPath> lastDevices = lastDevicesReturned.get().inPorts;
-
- if (index >= lastDevices.size() || index < 0)
- return "";
-
- MidiPortPath path = lastDevices.get (index);
-
- return path.midiDevice.getHumanReadableNameForPort (MidiDeviceInfo.PortInfo.TYPE_INPUT,
- path.androidMidiPortID,
- path.portIndexToUseInName);
- }
-
- public String getOutputPortNameForJuceIndex (int index)
- {
- ArrayList<MidiPortPath> lastDevices = lastDevicesReturned.get().outPorts;
-
- if (index >= lastDevices.size() || index < 0)
- return "";
-
- MidiPortPath path = lastDevices.get (index);
-
- return path.midiDevice.getHumanReadableNameForPort (MidiDeviceInfo.PortInfo.TYPE_OUTPUT,
- path.androidMidiPortID,
- path.portIndexToUseInName);
- }
-
- public void onDeviceAdded (MidiDeviceInfo info)
- {
- PhysicalMidiDevice device = PhysicalMidiDevice.fromMidiDeviceInfo (info, manager);
-
- // Do not add bluetooth devices as they are already added by the native bluetooth dialog
- if (info.getType() != MidiDeviceInfo.TYPE_BLUETOOTH)
- physicalMidiDevices.add (device);
- }
-
- public void onDeviceRemoved (MidiDeviceInfo info)
- {
- for (int i = 0; i < physicalMidiDevices.size(); ++i)
- {
- if (physicalMidiDevices.get(i).info.getId() == info.getId())
- {
- physicalMidiDevices.remove (i);
- return;
- }
- }
- // Don't assert here as this may be called again after a bluetooth device is unpaired
- }
-
- public void onDeviceStatusChanged (MidiDeviceStatus status)
- {
- }
-
- private ArrayList<PhysicalMidiDevice> physicalMidiDevices;
- private MidiManager manager;
- }
-
- 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;
- }
|