|
|
|
@@ -64,6 +64,9 @@ import android.media.MediaScannerConnection; |
|
|
|
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
|
|
|
|
|
|
|
|
|
|
|
|
import android.media.midi.*;
|
|
|
|
import android.bluetooth.*;
|
|
|
|
import android.bluetooth.le.*;
|
|
|
|
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
@@ -125,85 +128,839 @@ public class JuceDemo extends Activity |
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
//==============================================================================
|
|
|
|
public class BluetoothManager
|
|
|
|
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()
|
|
|
|
{
|
|
|
|
String[] bluetoothAddresses = new String[0];
|
|
|
|
return bluetoothAddresses;
|
|
|
|
return bluetoothMidiDevices.toArray (new String[bluetoothMidiDevices.size()]);
|
|
|
|
}
|
|
|
|
|
|
|
|
public String getHumanReadableStringForBluetoothAddress (String address)
|
|
|
|
{
|
|
|
|
return address;
|
|
|
|
BluetoothDevice btDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice (address);
|
|
|
|
return btDevice.getName();
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isBluetoothDevicePaired (String address)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
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
|
|
|
|
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 new String[0];
|
|
|
|
return getJuceAndroidMidiDevices (MidiDeviceInfo.PortInfo.TYPE_INPUT);
|
|
|
|
}
|
|
|
|
|
|
|
|
public String[] getJuceAndroidMidiOutputDevices()
|
|
|
|
{
|
|
|
|
return new String[0];
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
return "";
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
return "";
|
|
|
|
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()
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
if (getSystemService (MIDI_SERVICE) == null)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
synchronized (JuceDemo.class)
|
|
|
|
{
|
|
|
|
if (midiDeviceManager == null)
|
|
|
|
midiDeviceManager = new MidiDeviceManager();
|
|
|
|
}
|
|
|
|
|
|
|
|
return midiDeviceManager;
|
|
|
|
}
|
|
|
|
|
|
|
|
public BluetoothManager getAndroidBluetoothManager()
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
|
|
|
|
|
|
|
|
if (adapter == null)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
if (adapter.getBluetoothLeScanner() == null)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
synchronized (JuceDemo.class)
|
|
|
|
{
|
|
|
|
if (bluetoothManager == null)
|
|
|
|
bluetoothManager = new BluetoothManager();
|
|
|
|
}
|
|
|
|
|
|
|
|
return bluetoothManager;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
|