123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836 |
- //==============================================================================
- 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;
- }
|