MIDI: Update docs with MidiUmpDeviceService
I moved up the Bluetooth section so MIDI 2.0 sections are adjacent. Bug: 291115176 Test: viewed html locally Change-Id: I7f8c8c7c3b30dc4c77500bba5733ba781d4f0e4c
This commit is contained in:
@@ -267,6 +267,56 @@ raw MIDI data and can contain multiple messages or partial messages. It might
|
||||
contain System Real-Time messages, which can be interleaved inside other
|
||||
messages.</p>
|
||||
|
||||
<h1 id=using_midi_btle>Using MIDI Over Bluetooth LE</h1>
|
||||
|
||||
<p>MIDI devices can be connected to Android using Bluetooth LE.</p>
|
||||
|
||||
<p>Before using the device, the app must scan for available BTLE devices and then allow
|
||||
the user to connect.
|
||||
See the Android developer website for an
|
||||
<a href="https://source.android.com/devices/audio/midi_test#apps" target="_blank">example
|
||||
program</a>.</p>
|
||||
|
||||
<h2 id=btle_location_permissions>Request Location Permission for BTLE</h2>
|
||||
|
||||
<p>Applications that scan for Bluetooth devices must request permission in the
|
||||
manifest file. This LOCATION permission is required because it may be possible to
|
||||
guess the location of an Android device by seeing which BTLE devices are nearby.</p>
|
||||
|
||||
<pre class=prettyprint>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH"/>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||
</pre>
|
||||
|
||||
<p>Apps must also request location permission from the user at run-time.
|
||||
See the documentation for <code>Activity.requestPermissions()</code> for details and an example.
|
||||
</p>
|
||||
|
||||
<h2 id=btle_scan_devices>Scan for MIDI Devices</h2>
|
||||
|
||||
<p>The app will only want to see MIDI devices and not mice or other non-MIDI devices.
|
||||
So construct a ScanFilter using the UUID for standard MIDI over BTLE.</p>
|
||||
|
||||
<pre class=prettyprint>
|
||||
MIDI over BTLE UUID = "03B80E5A-EDE8-4B33-A751-6CE34EC4C700"
|
||||
</pre>
|
||||
|
||||
<h2 id=btle_open_device>Open a MIDI Bluetooth Device</h2>
|
||||
|
||||
<p>See the documentation for <code>android.bluetooth.le.BluetoothLeScanner.startScan()</code>
|
||||
method for details. When the user selects a MIDI/BTLE device then you can open it
|
||||
using the MidiManager.</p>
|
||||
|
||||
<pre class=prettyprint>
|
||||
m.openBluetoothDevice(bluetoothDevice, callback, handler);
|
||||
</pre>
|
||||
|
||||
<p>Once the MIDI/BTLE device has been opened by one app then it will also become available to other
|
||||
apps using the
|
||||
<a href="#get_list_of_already_plugged_in_entities">MIDI device discovery calls described above</a>.
|
||||
</p>
|
||||
|
||||
<h1 id=creating_a_midi_virtual_device_service>Creating a MIDI Virtual Device Service</h1>
|
||||
|
||||
|
||||
@@ -355,62 +405,17 @@ public class MidiSynthDeviceService extends MidiDeviceService {
|
||||
}
|
||||
</pre>
|
||||
|
||||
<h1 id=using_midi_btle>Using MIDI Over Bluetooth LE</h1>
|
||||
|
||||
<p>MIDI devices can be connected to Android using Bluetooth LE.</p>
|
||||
<h1 id=using_midi_2_0>Using MIDI 2.0</h1>
|
||||
|
||||
<p>Before using the device, the app must scan for available BTLE devices and then allow
|
||||
the user to connect.
|
||||
See the Android developer website for an
|
||||
<a href="https://source.android.com/devices/audio/midi_test#apps" target="_blank">example
|
||||
program</a>.</p>
|
||||
|
||||
<h2 id=btle_location_permissions>Request Location Permission for BTLE</h2>
|
||||
|
||||
<p>Applications that scan for Bluetooth devices must request permission in the
|
||||
manifest file. This LOCATION permission is required because it may be possible to
|
||||
guess the location of an Android device by seeing which BTLE devices are nearby.</p>
|
||||
|
||||
<pre class=prettyprint>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH"/>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||
</pre>
|
||||
|
||||
<p>Apps must also request location permission from the user at run-time.
|
||||
See the documentation for <code>Activity.requestPermissions()</code> for details and an example.
|
||||
</p>
|
||||
|
||||
<h2 id=btle_scan_devices>Scan for MIDI Devices</h2>
|
||||
|
||||
<p>The app will only want to see MIDI devices and not mice or other non-MIDI devices.
|
||||
So construct a ScanFilter using the UUID for standard MIDI over BTLE.</p>
|
||||
|
||||
<pre class=prettyprint>
|
||||
MIDI over BTLE UUID = "03B80E5A-EDE8-4B33-A751-6CE34EC4C700"
|
||||
</pre>
|
||||
|
||||
<h2 id=btle_open_device>Open a MIDI Bluetooth Device</h2>
|
||||
|
||||
<p>See the documentation for <code>android.bluetooth.le.BluetoothLeScanner.startScan()</code>
|
||||
method for details. When the user selects a MIDI/BTLE device then you can open it
|
||||
using the MidiManager.</p>
|
||||
|
||||
<pre class=prettyprint>
|
||||
m.openBluetoothDevice(bluetoothDevice, callback, handler);
|
||||
</pre>
|
||||
|
||||
<p>Once the MIDI/BTLE device has been opened by one app then it will also become available to other
|
||||
apps using the
|
||||
<a href="#get_list_of_already_plugged_in_entities">MIDI device discovery calls described above</a>.
|
||||
</p>
|
||||
|
||||
<h1 id=using_midi_2_0_over_usb>Using MIDI 2.0 over USB</h1>
|
||||
|
||||
<p>An app can use MIDI 2.0 over USB starting in Android T. MIDI 2.0 packets are embedded in
|
||||
<p>An app can use <a href=
|
||||
"https://www.midi.org/midi-articles/details-about-midi-2-0-midi-ci-profiles-and-property-exchange"
|
||||
class="external">MIDI 2.0</a> 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.</p>
|
||||
For more info about MIDI 2.0 and UMP, please read the MIDI 2.0 and UMP spec. Starting from Android
|
||||
V, apps can also open <a href="#creating_a_midi_2_0_virtual_device_service"> MIDI 2.0 virtual</a>
|
||||
devices.</p>
|
||||
|
||||
<p>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
|
||||
@@ -421,15 +426,15 @@ Collection<MidiDeviceInfo> universalDeviceInfos = midiManager.getDevices
|
||||
MidiManager.TRANSPORT_UNIVERSAL_MIDI_PACKETS);
|
||||
</pre>
|
||||
|
||||
<p>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. </p>
|
||||
<p>UMP packet sizes are always a 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. </p>
|
||||
|
||||
<pre class=prettyprint>
|
||||
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)(0x20 + group); // MIDI 1.0 Channel Voice Message
|
||||
buffer[numBytes++] = (byte)(0x90 + (channel - 1)); // note on
|
||||
buffer[numBytes++] = (byte)60; // pitch is middle C
|
||||
buffer[numBytes++] = (byte)127; // max velocity
|
||||
@@ -446,5 +451,108 @@ For a MidiDeviceInfo, you can query the defaultProtocol.</p>
|
||||
int defaultProtocol = info.getDefaultProtocol();
|
||||
</pre>
|
||||
|
||||
<h1 id=creating_a_midi_2_0_virtual_device_service>Creating a MIDI 2.0 Virtual Device Service</h1>
|
||||
|
||||
|
||||
<p>Starting in Android V, an app can provide a MIDI 2.0 Service that can be used by other apps.
|
||||
MIDI 2.0 packets are embedded in Universal MIDI Packets, or UMP for short. The service must be
|
||||
guarded with permission "android.permission.BIND_MIDI_DEVICE_SERVICE".</p>
|
||||
|
||||
<h2 id=manifest_files>Manifest Files</h2>
|
||||
|
||||
|
||||
<p>An app declares that it will function as a MIDI server in the AndroidManifest.xml file. Unlike
|
||||
MIDI 1.0 virtual devices, android.media.midi.MidiUmpDeviceService is used</p>
|
||||
|
||||
<pre class=prettyprint>
|
||||
<service android:name="<strong>MidiEchoDeviceService</strong>"
|
||||
android:permission="android.permission.BIND_MIDI_DEVICE_SERVICE">
|
||||
<intent-filter>
|
||||
<action android:name="android.media.midi.MidiUmpDeviceService" />
|
||||
</intent-filter>
|
||||
<meta-data android:name="android.media.midi.MidiUmpDeviceService"
|
||||
android:resource="@xml/<strong>echo_device_info</strong>" />
|
||||
</service>
|
||||
</pre>
|
||||
|
||||
|
||||
<p>The details of the resource in this example is stored in “res/xml/echo_device_info.xml
|
||||
”. The port names that you declare in this file will be available from PortInfo.getName().
|
||||
Unlike MIDI 1.0, MIDI 2.0 ports are bidirectional. If you declare a port in this service, then it
|
||||
automatically creates an input port and an output port with the same name. Clients can use those
|
||||
two ports like the MIDI 1.0 ports.</p>
|
||||
|
||||
<pre class=prettyprint>
|
||||
<devices>
|
||||
<device manufacturer="MyCompany" product="MidiEcho">
|
||||
<port name="port1" />
|
||||
</device>
|
||||
</devices>
|
||||
</pre>
|
||||
|
||||
|
||||
<h2 id=extend_midiumpdeviceservice>Extend MidiUmpDeviceService</h2>
|
||||
|
||||
|
||||
<p>You then define your server by extending android.media.midi.MidiUmpDeviceService.</p>
|
||||
|
||||
<pre class=prettyprint>
|
||||
import android.media.midi.MidiDeviceStatus;
|
||||
import android.media.midi.MidiReceiver;
|
||||
import android.media.midi.MidiUmpDeviceService;
|
||||
|
||||
public class MidiEchoDeviceService extends MidiUmpDeviceService {
|
||||
private static final String TAG = "MidiEchoDeviceService";
|
||||
// Other apps will write to this port.
|
||||
private MidiReceiver mInputReceiver = new MyReceiver();
|
||||
// This app will copy the data to this port.
|
||||
private MidiReceiver mOutputReceiver;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
// Declare the receivers associated with your input ports.
|
||||
public List<MidiReceiver> onGetInputPortReceivers() {
|
||||
return new ArrayList<MidiReceiver>(Collections.singletonList(mInputReceiver));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sample receiver to echo from the input port to the output port.
|
||||
* In this example, we are just echoing the data and not parsing it.
|
||||
* You will probably want to convert the bytes to a packet and then interpret the packet.
|
||||
* See the MIDI 2.0 spec at the MMA site. Packets are either 4, 8, 12 or 16 bytes.
|
||||
*/
|
||||
class MyReceiver extends MidiReceiver {
|
||||
@Override
|
||||
public void onSend(byte[] data, int offset, int count, long timestamp)
|
||||
throws IOException {
|
||||
if (mOutputReceiver == null) {
|
||||
mOutputReceiver = getOutputPortReceivers().get(0);
|
||||
}
|
||||
// Copy input to output.
|
||||
mOutputReceiver.send(data, offset, count, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This will get called when clients connect or disconnect.
|
||||
* You can use it to figure out how many devices are connected.
|
||||
*/
|
||||
@Override
|
||||
public void onDeviceStatusChanged(MidiDeviceStatus status) {
|
||||
// inputOpened = status.isInputPortOpen(0);
|
||||
// outputOpenCount = status.getOutputPortOpenCount(0);
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user