From a936a2519848376c1055b65bc6efef90d7fd15ce Mon Sep 17 00:00:00 2001
From: Robert Wu
Date: Wed, 22 Dec 2021 19:47:20 +0000
Subject: Add MIDI 2.0 host mode support
Adding MIDI 2.0 host mode support to Android. MIDI 2.0 peripherals
create an alternative setting for Universal Midi Packets (UMP).
UMP packets can be passed directly through as the host has full
control. Instead of going through the ALSA, UMP packets can be
sent directly through a UsbDeviceConnection.
This CL also adds public apis to expose whether the pipe supports
UMP. This for MidiDeviceInfo in Java and AMidi in c++.
Bug: 201003646
Bug: 214447324
Test: Verified that the MIDI 1.0 path still works as before.
Test: Tested that MIDI 2.0 packets are passed through the pipe
with MIDIScope and MIDIKeyboard
Test: atest MidiSoloTest
Test: atest CtsMidiTestCases
Test: NativeMidiEchoTest
Change-Id: Idff8a3b9bdd05857239260fc3e6af7253f8f992f
---
core/api/current.txt | 13 +
media/java/android/media/midi/IMidiManager.aidl | 4 +-
media/java/android/media/midi/MidiDeviceInfo.java | 140 +++++-
media/java/android/media/midi/MidiManager.java | 170 ++++++--
media/java/android/media/midi/package.html | 41 ++
media/native/midi/MidiDeviceInfo.cpp | 5 +-
media/native/midi/MidiDeviceInfo.h | 14 +
media/native/midi/amidi.cpp | 8 +
media/native/midi/amidi_internal.h | 1 +
media/native/midi/include/amidi/AMidi.h | 96 +++++
media/native/midi/libamidi.map.txt | 1 +
.../bluetoothmidiservice/BluetoothMidiDevice.java | 3 +-
.../java/com/android/server/midi/MidiService.java | 39 +-
.../com/android/server/usb/UsbAlsaManager.java | 5 +-
.../com/android/server/usb/UsbHostManager.java | 31 ++
.../java/com/android/server/usb/UsbMidiDevice.java | 3 +-
.../android/server/usb/UsbUniversalMidiDevice.java | 469 +++++++++++++++++++++
.../usb/descriptors/UsbACAudioControlEndpoint.java | 4 +-
.../usb/descriptors/UsbACAudioStreamEndpoint.java | 4 +-
.../server/usb/descriptors/UsbACEndpoint.java | 26 +-
.../usb/descriptors/UsbACMidi10Endpoint.java | 67 +++
.../usb/descriptors/UsbACMidi20Endpoint.java | 67 +++
.../server/usb/descriptors/UsbACMidiEndpoint.java | 65 ---
.../usb/descriptors/UsbDescriptorParser.java | 115 ++++-
.../usb/descriptors/UsbEndpointDescriptor.java | 5 +-
.../usb/descriptors/UsbInterfaceDescriptor.java | 15 +-
.../server/usb/descriptors/UsbMSMidiHeader.java | 11 +-
.../server/usb/descriptors/UsbMidiBlockParser.java | 173 ++++++++
28 files changed, 1450 insertions(+), 145 deletions(-)
create mode 100644 services/usb/java/com/android/server/usb/UsbUniversalMidiDevice.java
create mode 100644 services/usb/java/com/android/server/usb/descriptors/UsbACMidi10Endpoint.java
create mode 100644 services/usb/java/com/android/server/usb/descriptors/UsbACMidi20Endpoint.java
delete mode 100644 services/usb/java/com/android/server/usb/descriptors/UsbACMidiEndpoint.java
create mode 100644 services/usb/java/com/android/server/usb/descriptors/UsbMidiBlockParser.java
diff --git a/core/api/current.txt b/core/api/current.txt
index fd429f037895..14c8bb4a18f5 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -25339,6 +25339,7 @@ package android.media.midi {
public final class MidiDeviceInfo implements android.os.Parcelable {
method public int describeContents();
+ method public int getDefaultProtocol();
method public int getId();
method public int getInputPortCount();
method public int getOutputPortCount();
@@ -25355,6 +25356,14 @@ package android.media.midi {
field public static final String PROPERTY_SERIAL_NUMBER = "serial_number";
field public static final String PROPERTY_USB_DEVICE = "usb_device";
field public static final String PROPERTY_VERSION = "version";
+ field public static final int PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS = 3; // 0x3
+ field public static final int PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS_AND_JRTS = 4; // 0x4
+ field public static final int PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS = 1; // 0x1
+ field public static final int PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS_AND_JRTS = 2; // 0x2
+ field public static final int PROTOCOL_UMP_MIDI_2_0 = 17; // 0x11
+ field public static final int PROTOCOL_UMP_MIDI_2_0_AND_JRTS = 18; // 0x12
+ field public static final int PROTOCOL_UMP_USE_MIDI_CI = 0; // 0x0
+ field public static final int PROTOCOL_UNKNOWN = -1; // 0xffffffff
field public static final int TYPE_BLUETOOTH = 3; // 0x3
field public static final int TYPE_USB = 1; // 0x1
field public static final int TYPE_VIRTUAL = 2; // 0x2
@@ -25396,10 +25405,14 @@ package android.media.midi {
public final class MidiManager {
method public android.media.midi.MidiDeviceInfo[] getDevices();
+ method @NonNull public java.util.Collection getDevicesForTransport(int);
method public void openBluetoothDevice(android.bluetooth.BluetoothDevice, android.media.midi.MidiManager.OnDeviceOpenedListener, android.os.Handler);
method public void openDevice(android.media.midi.MidiDeviceInfo, android.media.midi.MidiManager.OnDeviceOpenedListener, android.os.Handler);
method public void registerDeviceCallback(android.media.midi.MidiManager.DeviceCallback, android.os.Handler);
+ method public void registerDeviceCallbackForTransport(@NonNull android.media.midi.MidiManager.DeviceCallback, @Nullable android.os.Handler, int);
method public void unregisterDeviceCallback(android.media.midi.MidiManager.DeviceCallback);
+ field public static final int TRANSPORT_MIDI_BYTE_STREAM = 1; // 0x1
+ field public static final int TRANSPORT_UNIVERSAL_MIDI_PACKETS = 2; // 0x2
}
public static class MidiManager.DeviceCallback {
diff --git a/media/java/android/media/midi/IMidiManager.aidl b/media/java/android/media/midi/IMidiManager.aidl
index d3c8e0adb3ce..b03f78504635 100644
--- a/media/java/android/media/midi/IMidiManager.aidl
+++ b/media/java/android/media/midi/IMidiManager.aidl
@@ -31,6 +31,8 @@ interface IMidiManager
{
MidiDeviceInfo[] getDevices();
+ MidiDeviceInfo[] getDevicesForTransport(int transport);
+
// for device creation & removal notifications
void registerListener(IBinder clientToken, in IMidiDeviceListener listener);
void unregisterListener(IBinder clientToken, in IMidiDeviceListener listener);
@@ -43,7 +45,7 @@ interface IMidiManager
// for registering built-in MIDI devices
MidiDeviceInfo registerDeviceServer(in IMidiDeviceServer server, int numInputPorts,
int numOutputPorts, in String[] inputPortNames, in String[] outputPortNames,
- in Bundle properties, int type);
+ in Bundle properties, int type, int defaultProtocol);
// for unregistering built-in MIDI devices
void unregisterDeviceServer(in IMidiDeviceServer server);
diff --git a/media/java/android/media/midi/MidiDeviceInfo.java b/media/java/android/media/midi/MidiDeviceInfo.java
index dd3b6dbd6a39..b888fb00df9b 100644
--- a/media/java/android/media/midi/MidiDeviceInfo.java
+++ b/media/java/android/media/midi/MidiDeviceInfo.java
@@ -16,11 +16,15 @@
package android.media.midi;
+import android.annotation.IntDef;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* This class contains information to describe a MIDI device.
* For now we only have information that can be retrieved easily for USB devices,
@@ -53,6 +57,110 @@ public final class MidiDeviceInfo implements Parcelable {
*/
public static final int TYPE_BLUETOOTH = 3;
+ /**
+ * Constant representing a default protocol with Universal MIDI Packets (UMP).
+ * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+ * All UMP data should be a multiple of 4 bytes.
+ * Use UMP to negotiate with the device with MIDI-CI.
+ * MIDI-CI is defined in "MIDI Capability Inquiry (MIDI-CI)" spec.
+ * Call {@link MidiManager#getDevicesForTransport} with parameter
+ * {@link MidiManager#TRANSPORT_UNIVERSAL_MIDI_PACKETS} to get devices with this transport.
+ * @see MidiDeviceInfo#getDefaultProtocol
+ */
+ public static final int PROTOCOL_UMP_USE_MIDI_CI = 0;
+
+ /**
+ * Constant representing a default protocol with Universal MIDI Packets (UMP).
+ * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+ * All UMP data should be a multiple of 4 bytes.
+ * Use MIDI 1.0 through UMP with packet sizes up to 64 bits.
+ * Call {@link MidiManager#getDevicesForTransport} with parameter
+ * {@link MidiManager#TRANSPORT_UNIVERSAL_MIDI_PACKETS} to get devices with this transport.
+ * @see MidiDeviceInfo#getDefaultProtocol
+ */
+ public static final int PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS = 1;
+
+ /**
+ * Constant representing a default protocol with Universal MIDI Packets (UMP).
+ * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+ * All UMP data should be a multiple of 4 bytes.
+ * Use MIDI 1.0 through UMP with packet sizes up to 64 bits and jitter reduction timestamps.
+ * Call {@link MidiManager#getDevicesForTransport} with parameter
+ * {@link MidiManager#TRANSPORT_UNIVERSAL_MIDI_PACKETS} to get devices with this transport.
+ * @see MidiDeviceInfo#getDefaultProtocol
+ */
+ public static final int PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS_AND_JRTS = 2;
+
+ /**
+ * Constant representing a default protocol with Universal MIDI Packets (UMP).
+ * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+ * All UMP data should be a multiple of 4 bytes.
+ * Use MIDI 1.0 through UMP with packet sizes up to 128 bits.
+ * Call {@link MidiManager#getDevicesForTransport} with parameter
+ * {@link MidiManager#TRANSPORT_UNIVERSAL_MIDI_PACKETS} to get devices with this transport.
+ * @see MidiDeviceInfo#getDefaultProtocol
+ */
+ public static final int PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS = 3;
+
+ /**
+ * Constant representing a default protocol with Universal MIDI Packets (UMP).
+ * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+ * All UMP data should be a multiple of 4 bytes.
+ * Use MIDI 1.0 through UMP with packet sizes up to 128 bits and jitter reduction timestamps.
+ * Call {@link MidiManager#getDevicesForTransport} with parameter
+ * {@link MidiManager#TRANSPORT_UNIVERSAL_MIDI_PACKETS} to get devices with this transport.
+ * @see MidiDeviceInfo#getDefaultProtocol
+ */
+ public static final int PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS_AND_JRTS = 4;
+
+ /**
+ * Constant representing a default protocol with Universal MIDI Packets (UMP).
+ * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+ * All UMP data should be a multiple of 4 bytes.
+ * Use MIDI 2.0 through UMP.
+ * Call {@link MidiManager#getDevicesForTransport} with parameter
+ * {@link MidiManager#TRANSPORT_UNIVERSAL_MIDI_PACKETS} to get devices with this transport.
+ * @see MidiDeviceInfo#getDefaultProtocol
+ */
+ public static final int PROTOCOL_UMP_MIDI_2_0 = 17;
+
+ /**
+ * Constant representing a default protocol with Universal MIDI Packets (UMP).
+ * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+ * All UMP data should be a multiple of 4 bytes.
+ * Use MIDI 2.0 through UMP and jitter reduction timestamps.
+ * Call {@link MidiManager#getDevicesForTransport} with parameter
+ * {@link MidiManager#TRANSPORT_UNIVERSAL_MIDI_PACKETS} to get devices with this transport.
+ * @see MidiDeviceInfo#getDefaultProtocol
+ */
+ public static final int PROTOCOL_UMP_MIDI_2_0_AND_JRTS = 18;
+
+ /**
+ * Constant representing a device with an unknown default protocol.
+ * If Universal MIDI Packets (UMP) are needed, use MIDI-CI through MIDI 1.0.
+ * UMP is defined in "Universal MIDI Packet (UMP) Format and MIDI 2.0 Protocol" spec.
+ * MIDI-CI is defined in "MIDI Capability Inquiry (MIDI-CI)" spec.
+ * @see MidiDeviceInfo#getDefaultProtocol
+ */
+ public static final int PROTOCOL_UNKNOWN = -1;
+
+ /**
+ * @see MidiDeviceInfo#getDefaultProtocol
+ * @hide
+ */
+ @IntDef(prefix = { "PROTOCOL_" }, value = {
+ PROTOCOL_UMP_USE_MIDI_CI,
+ PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS,
+ PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS_AND_JRTS,
+ PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS,
+ PROTOCOL_UMP_MIDI_1_0_UP_TO_128_BITS_AND_JRTS,
+ PROTOCOL_UMP_MIDI_2_0,
+ PROTOCOL_UMP_MIDI_2_0_AND_JRTS,
+ PROTOCOL_UNKNOWN
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DefaultProtocol {}
+
/**
* Bundle key for the device's user visible name property.
* The value for this property is of type {@link java.lang.String}.
@@ -196,6 +304,7 @@ public final class MidiDeviceInfo implements Parcelable {
private final String[] mOutputPortNames;
private final Bundle mProperties;
private final boolean mIsPrivate;
+ private final int mDefaultProtocol;
/**
* MidiDeviceInfo should only be instantiated by MidiService implementation
@@ -203,7 +312,7 @@ public final class MidiDeviceInfo implements Parcelable {
*/
public MidiDeviceInfo(int type, int id, int numInputPorts, int numOutputPorts,
String[] inputPortNames, String[] outputPortNames, Bundle properties,
- boolean isPrivate) {
+ boolean isPrivate, int defaultProtocol) {
// Check num ports for out-of-range values. Typical values will be
// between zero and three. More than 16 would be very unlikely
// because the port index field in the USB packet is only 4 bits.
@@ -234,6 +343,7 @@ public final class MidiDeviceInfo implements Parcelable {
}
mProperties = properties;
mIsPrivate = isPrivate;
+ mDefaultProtocol = defaultProtocol;
}
/**
@@ -312,6 +422,18 @@ public final class MidiDeviceInfo implements Parcelable {
return mIsPrivate;
}
+ /**
+ * Returns the default protocol. For most devices, this will be {@link #PROTOCOL_UNKNOWN}.
+ * Returning {@link #PROTOCOL_UNKNOWN} is not an error; the device just doesn't support
+ * Universal MIDI Packets by default.
+ *
+ * @return the device's default protocol.
+ */
+ @DefaultProtocol
+ public int getDefaultProtocol() {
+ return mDefaultProtocol;
+ }
+
@Override
public boolean equals(Object o) {
if (o instanceof MidiDeviceInfo) {
@@ -331,11 +453,12 @@ public final class MidiDeviceInfo implements Parcelable {
// This is a hack to force the mProperties Bundle to unparcel so we can
// print all the names and values.
mProperties.getString(PROPERTY_NAME);
- return ("MidiDeviceInfo[mType=" + mType +
- ",mInputPortCount=" + mInputPortCount +
- ",mOutputPortCount=" + mOutputPortCount +
- ",mProperties=" + mProperties +
- ",mIsPrivate=" + mIsPrivate);
+ return ("MidiDeviceInfo[mType=" + mType
+ + ",mInputPortCount=" + mInputPortCount
+ + ",mOutputPortCount=" + mOutputPortCount
+ + ",mProperties=" + mProperties
+ + ",mIsPrivate=" + mIsPrivate
+ + ",mDefaultProtocol=" + mDefaultProtocol);
}
public static final @android.annotation.NonNull Parcelable.Creator CREATOR =
@@ -349,10 +472,12 @@ public final class MidiDeviceInfo implements Parcelable {
String[] inputPortNames = in.createStringArray();
String[] outputPortNames = in.createStringArray();
boolean isPrivate = (in.readInt() == 1);
+ int defaultProtocol = in.readInt();
Bundle basicPropertiesIgnored = in.readBundle();
Bundle properties = in.readBundle();
return new MidiDeviceInfo(type, id, inputPortCount, outputPortCount,
- inputPortNames, outputPortNames, properties, isPrivate);
+ inputPortNames, outputPortNames, properties, isPrivate,
+ defaultProtocol);
}
public MidiDeviceInfo[] newArray(int size) {
@@ -390,6 +515,7 @@ public final class MidiDeviceInfo implements Parcelable {
parcel.writeStringArray(mInputPortNames);
parcel.writeStringArray(mOutputPortNames);
parcel.writeInt(mIsPrivate ? 1 : 0);
+ parcel.writeInt(mDefaultProtocol);
// "Basic" properties only contain properties of primitive types
// and thus can be read back by native code. "Extra" properties is
// a superset that contains all properties.
diff --git a/media/java/android/media/midi/MidiManager.java b/media/java/android/media/midi/MidiManager.java
index dee94c681e87..a049a8891f48 100644
--- a/media/java/android/media/midi/MidiManager.java
+++ b/media/java/android/media/midi/MidiManager.java
@@ -16,18 +16,25 @@
package android.media.midi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresFeature;
import android.annotation.SystemService;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
-import android.os.IBinder;
import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
/**
@@ -38,6 +45,39 @@ import java.util.concurrent.ConcurrentHashMap;
public final class MidiManager {
private static final String TAG = "MidiManager";
+ /**
+ * Constant representing MIDI devices.
+ * These devices do NOT support Universal MIDI Packets by default.
+ * These support the original MIDI 1.0 byte stream.
+ * When communicating to a USB device, a raw byte stream will be padded for USB.
+ * Likewise, for a Bluetooth device, the raw bytes will be converted for Bluetooth.
+ * For virtual devices, the byte stream will be passed directly.
+ * If Universal MIDI Packets are needed, please use MIDI-CI.
+ * @see MidiManager#getDevicesForTransport
+ */
+ public static final int TRANSPORT_MIDI_BYTE_STREAM = 1;
+
+ /**
+ * Constant representing Universal MIDI devices.
+ * These devices do support Universal MIDI Packets (UMP) by default.
+ * When sending data to these devices, please send UMP.
+ * Packets should always be a multiple of 4 bytes.
+ * UMP is defined in the USB MIDI 2.0 spec. Please read the standard for more info.
+ * @see MidiManager#getDevicesForTransport
+ */
+ public static final int TRANSPORT_UNIVERSAL_MIDI_PACKETS = 2;
+
+ /**
+ * @see MidiManager#getDevicesForTransport
+ * @hide
+ */
+ @IntDef(prefix = { "TRANSPORT_" }, value = {
+ TRANSPORT_MIDI_BYTE_STREAM,
+ TRANSPORT_UNIVERSAL_MIDI_PACKETS
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Transport {}
+
/**
* Intent for starting BluetoothMidiService
* @hide
@@ -68,37 +108,43 @@ public final class MidiManager {
private class DeviceListener extends IMidiDeviceListener.Stub {
private final DeviceCallback mCallback;
private final Handler mHandler;
+ private final int mTransport;
- public DeviceListener(DeviceCallback callback, Handler handler) {
+ DeviceListener(DeviceCallback callback, Handler handler, int transport) {
mCallback = callback;
mHandler = handler;
+ mTransport = transport;
}
@Override
public void onDeviceAdded(MidiDeviceInfo device) {
- if (mHandler != null) {
- final MidiDeviceInfo deviceF = device;
- mHandler.post(new Runnable() {
- @Override public void run() {
- mCallback.onDeviceAdded(deviceF);
- }
- });
- } else {
- mCallback.onDeviceAdded(device);
+ if (shouldInvokeCallback(device)) {
+ if (mHandler != null) {
+ final MidiDeviceInfo deviceF = device;
+ mHandler.post(new Runnable() {
+ @Override public void run() {
+ mCallback.onDeviceAdded(deviceF);
+ }
+ });
+ } else {
+ mCallback.onDeviceAdded(device);
+ }
}
}
@Override
public void onDeviceRemoved(MidiDeviceInfo device) {
- if (mHandler != null) {
- final MidiDeviceInfo deviceF = device;
- mHandler.post(new Runnable() {
- @Override public void run() {
- mCallback.onDeviceRemoved(deviceF);
- }
- });
- } else {
- mCallback.onDeviceRemoved(device);
+ if (shouldInvokeCallback(device)) {
+ if (mHandler != null) {
+ final MidiDeviceInfo deviceF = device;
+ mHandler.post(new Runnable() {
+ @Override public void run() {
+ mCallback.onDeviceRemoved(deviceF);
+ }
+ });
+ } else {
+ mCallback.onDeviceRemoved(device);
+ }
}
}
@@ -115,6 +161,25 @@ public final class MidiManager {
mCallback.onDeviceStatusChanged(status);
}
}
+
+ /**
+ * Used to figure out whether callbacks should be invoked. Only invoke callbacks of
+ * the correct type.
+ *
+ * @param MidiDeviceInfo the device to check
+ * @return whether to invoke a callback
+ */
+ private boolean shouldInvokeCallback(MidiDeviceInfo device) {
+ // UMP devices have protocols that are not PROTOCOL_UNKNOWN
+ if (mTransport == TRANSPORT_UNIVERSAL_MIDI_PACKETS) {
+ return (device.getDefaultProtocol() != MidiDeviceInfo.PROTOCOL_UNKNOWN);
+ } else if (mTransport == TRANSPORT_MIDI_BYTE_STREAM) {
+ return (device.getDefaultProtocol() == MidiDeviceInfo.PROTOCOL_UNKNOWN);
+ } else {
+ Log.e(TAG, "Invalid transport type: " + mTransport);
+ return false;
+ }
+ }
}
/**
@@ -167,8 +232,10 @@ public final class MidiManager {
}
/**
- * Registers a callback to receive notifications when MIDI devices are added and removed.
- *
+ * Registers a callback to receive notifications when MIDI 1.0 devices are added and removed.
+ * These are devices that do not default to Universal MIDI Packets. To register for a callback
+ * for those, call {@link #registerDeviceCallbackForTransport} instead.
+
* The {@link DeviceCallback#onDeviceStatusChanged} method will be called immediately
* for any devices that have open ports. This allows applications to know which input
* ports are already in use and, therefore, unavailable.
@@ -182,7 +249,30 @@ public final class MidiManager {
* callback is unspecified.
*/
public void registerDeviceCallback(DeviceCallback callback, Handler handler) {
- DeviceListener deviceListener = new DeviceListener(callback, handler);
+ registerDeviceCallbackForTransport(callback, handler, TRANSPORT_MIDI_BYTE_STREAM);
+ }
+
+ /**
+ * Registers a callback to receive notifications when MIDI devices are added and removed
+ * for a specific transport type.
+ *
+ * The {@link DeviceCallback#onDeviceStatusChanged} method will be called immediately
+ * for any devices that have open ports. This allows applications to know which input
+ * ports are already in use and, therefore, unavailable.
+ *
+ * Applications should call {@link #getDevicesForTransport} before registering the callback
+ * to get a list of devices already added.
+ *
+ * @param callback a {@link DeviceCallback} for MIDI device notifications
+ * @param handler The {@link android.os.Handler Handler} that will be used for delivering the
+ * device notifications. If handler is null, then the thread used for the
+ * callback is unspecified.
+ * @param transport The transport to be used. This is either TRANSPORT_MIDI_BYTE_STREAM or
+ * TRANSPORT_UNIVERSAL_MIDI_PACKETS.
+ */
+ public void registerDeviceCallbackForTransport(@NonNull DeviceCallback callback,
+ @Nullable Handler handler, @Transport int transport) {
+ DeviceListener deviceListener = new DeviceListener(callback, handler, transport);
try {
mService.registerListener(mToken, deviceListener);
} catch (RemoteException e) {
@@ -208,9 +298,11 @@ public final class MidiManager {
}
/**
- * Gets the list of all connected MIDI devices.
+ * Gets a list of connected MIDI devices. This returns all devices that do
+ * not default to Universal MIDI Packets. To get those instead, please call
+ * {@link #getDevicesForTransport} instead.
*
- * @return an array of all MIDI devices
+ * @return an array of MIDI devices
*/
public MidiDeviceInfo[] getDevices() {
try {
@@ -220,6 +312,29 @@ public final class MidiManager {
}
}
+ /**
+ * Gets a list of connected MIDI devices by transport. TRANSPORT_MIDI_BYTE_STREAM
+ * is used for MIDI 1.0 and is the most common.
+ * For devices with built in Universal MIDI Packet support, use
+ * TRANSPORT_UNIVERSAL_MIDI_PACKETS instead.
+ *
+ * @param transport The transport to be used. This is either TRANSPORT_MIDI_BYTE_STREAM or
+ * TRANSPORT_UNIVERSAL_MIDI_PACKETS.
+ * @return a collection of MIDI devices
+ */
+ public @NonNull Collection getDevicesForTransport(@Transport int transport) {
+ try {
+ MidiDeviceInfo[] devices = mService.getDevicesForTransport(transport);
+ Collection out = new ArrayList(devices.length);
+ for (int i = 0; i < devices.length; i++) {
+ out.add(devices[i]);
+ }
+ return out;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private void sendOpenDeviceResponse(final MidiDevice device,
final OnDeviceOpenedListener listener, Handler handler) {
if (handler != null) {
@@ -311,13 +426,14 @@ public final class MidiManager {
/** @hide */
public MidiDeviceServer createDeviceServer(MidiReceiver[] inputPortReceivers,
int numOutputPorts, String[] inputPortNames, String[] outputPortNames,
- Bundle properties, int type, MidiDeviceServer.Callback callback) {
+ Bundle properties, int type, int defaultProtocol,
+ MidiDeviceServer.Callback callback) {
try {
MidiDeviceServer server = new MidiDeviceServer(mService, inputPortReceivers,
numOutputPorts, callback);
MidiDeviceInfo deviceInfo = mService.registerDeviceServer(server.getBinderInterface(),
inputPortReceivers.length, numOutputPorts, inputPortNames, outputPortNames,
- properties, type);
+ properties, type, defaultProtocol);
if (deviceInfo == null) {
Log.e(TAG, "registerVirtualDevice failed");
return null;
diff --git a/media/java/android/media/midi/package.html b/media/java/android/media/midi/package.html
index 33c54900cd07..67df1b2fa315 100644
--- a/media/java/android/media/midi/package.html
+++ b/media/java/android/media/midi/package.html
@@ -405,5 +405,46 @@ apps using the
MIDI device discovery calls described above.
+Using MIDI 2.0 over USB
+
+An app can use MIDI 2.0 over USB starting in Android T. MIDI 2.0 packets are embedded in
+Universal MIDI Packets, or UMP for short. A MIDI 2.0 USB device should create two interfaces,
+one endpoint that accepts only MIDI 1.0 packets and one that accepts only UMP packets.
+For more info about MIDI 2.0 and UMP, please read the MIDI 2.0 USB spec.
+
+MidiManager.getDevices() would simply return the 1.0 interface. This interface should work
+exactly the same as before. In order to use the new UMP interface, retrieve the device with the
+following code snippet.
+
+
+Collection<MidiDeviceInfo> universalDeviceInfos = midiManager.getDevicesForTransport(
+ MidiManager.TRANSPORT_UNIVERSAL_MIDI_PACKETS);
+
+
+UMP Packets are always in multiple of 4 bytes. For each set of 4 bytes, they are sent in network
+order. Compare the following NoteOn code snippet with the NoteOn code snippet above.
+
+
+byte[] buffer = new byte[32];
+int numBytes = 0;
+int channel = 3; // MIDI channels 1-16 are encoded as 0-15.
+int group = 0;
+buffer[numBytes++] = (byte)(0x20 + group); // MIDI 1.0 voice message
+buffer[numBytes++] = (byte)(0x90 + (channel - 1)); // note on
+buffer[numBytes++] = (byte)60; // pitch is middle C
+buffer[numBytes++] = (byte)127; // max velocity
+int offset = 0;
+// post is non-blocking
+inputPort.send(buffer, offset, numBytes);
+
+
+MIDI 2.0 messages can be sent through UMP after negotiating with the device. This is called
+MIDI-CI and is documented in the MIDI 2.0 spec. Some USB devices support pre-negotiated MIDI 2.0.
+For a MidiDeviceInfo, you can query the defaultProtocol.
+
+
+int defaultProtocol = info.getDefaultProtocol();
+
+