Home | History | Annotate | Download | only in midi
      1 <html>
      2 <body>
      3 <p>Android MIDI User Guide</p>
      4 
      5 <h1 id=overview>Overview</h1>
      6 
      7 
      8 <p>This document describes how to use the Android MIDI API in Java.</p>
      9 
     10 <p>The Android MIDI package allows users to:</p>
     11 
     12 <ul>
     13   <li> Connect a MIDI keyboard to Android to play a synthesizer or drive music apps.</li>
     14   <li> Connect alternative MIDI controllers to Android.</li>
     15   <li> Drive external MIDI synths from Android.</li>
     16   <li> Drive external peripherals, lights, show control, etc from Android.</li>
     17   <li> Generate music dynamically from games or music creation apps.</li>
     18   <li> Generate MIDI messages in one app and send them to a second app.</li>
     19   <li> Use an Android device running in <em>peripheral mode</em> as a multitouch controller
     20   connected to a laptop.</li>
     21 </ul>
     22 
     23 <h2 id=the_api_features_include>The API features include:</h2>
     24 
     25 <ul>
     26   <li> Enumeration of currently available devices. Information includes name, vendor,
     27 capabilities, etc.</li>
     28   <li> Provide notification when MIDI devices are plugged in or unplugged.</li>
     29   <li> Support efficient transmission of single or multiple short 1-3 byte MIDI messages.</li>
     30   <li> Support transmission of arbitrary length data for SysEx, etc.</li>
     31   <li> Timestamps to avoid jitter.</li>
     32   <li> Support creation of <em>virtual MIDI devices</em> that can be connected to other devices.
     33   An example might be a synthesizer app that can be controlled by a composing app.</li>
     34   <li> Support direct connection or &ldquo;patching&rdquo; of devices for lower latency.</li>
     35 </ul>
     36 
     37 <h2 id=transports_supported>Transports Supported</h2>
     38 
     39 
     40 <p>The API is &ldquo;transport agnostic&rdquo;. But there are several transports currently
     41 supported:</p>
     42 
     43 <ul>
     44   <li> USB
     45   <li> software routing
     46   <li> BTLE
     47 </ul>
     48 
     49 <h1 id=android_midi_terminology>Android MIDI Terminology</h1>
     50 
     51 
     52 <h2 id=terminology>Terminology</h2>
     53 
     54 
     55 <p>A <strong>Device</strong> is a MIDI capable object that has zero or more InputPorts and OutputPorts.</p>
     56 
     57 <p>An <strong>InputPort</strong> has 16 channels and can <strong>receive</strong> MIDI messages from an OutputPort or an app.</p>
     58 
     59 <p>An <strong>OutputPort</strong> has 16 channels and can <strong>send</strong> MIDI messages to an InputPort or an app.</p>
     60 
     61 <p><strong>MidiService</strong> is a centralized process that keeps track of all devices and brokers
     62 communication between them.</p>
     63 
     64 <p><strong>MidiManager</strong> is a class that the application or a device manager calls to communicate with
     65 the MidiService.</p>
     66 
     67 <h1 id=writing_a_midi_application>Writing a MIDI Application</h1>
     68 
     69 <h2 id=manifest_feature>Declare Feature in Manifest</h2>
     70 
     71 <p>An app that requires the MIDI API should declare that in the AndroidManifest.xml file.
     72 Then the app will not appear in the Play Store for old devices that do not support the MIDI API.</p>
     73 
     74 <pre class=prettyprint>
     75 &lt;uses-feature android:name="android.software.midi" android:required="true"/>
     76 </pre>
     77 
     78 <h2 id=check_feature>Check for Feature Support</h2>
     79 
     80 <p>An app can also check at run-time whether the MIDI feature is supported on a platform.
     81 This is particularly useful during development when you install apps directly on a device.
     82 </p>
     83 
     84 <pre class=prettyprint>
     85 if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI)) {
     86     // do MIDI stuff
     87 }
     88 </pre>
     89 
     90 <h2 id=the_midimanager>The MidiManager</h2>
     91 
     92 
     93 <p>The primary class for accessing the MIDI package is through the MidiManager.</p>
     94 
     95 <pre class=prettyprint>
     96 MidiManager m = (MidiManager)context.getSystemService(Context.MIDI_SERVICE);
     97 </pre>
     98 
     99 
    100 <h2 id=get_list_of_already_plugged_in_entities>Get List of Already Plugged In Entities</h2>
    101 
    102 
    103 <p>When an app starts, it can get a list of all the available MIDI devices. This
    104 information can be presented to a user, allowing them to choose a device.</p>
    105 
    106 <pre class=prettyprint>
    107 MidiDeviceInfo[] infos = m.getDevices();
    108 </pre>
    109 
    110 
    111 <h2 id=notification_of_midi_devices_hotplug_events>Notification of MIDI Devices HotPlug Events</h2>
    112 
    113 
    114 <p>The application can request notification when, for example, keyboards are
    115 plugged in or unplugged.</p>
    116 
    117 <pre class=prettyprint>
    118 m.registerDeviceCallback(new MidiManager.DeviceCallback() {
    119     public void onDeviceAdded( MidiDeviceInfo info ) {
    120       ...
    121     }
    122     public void onDeviceRemoved( MidiDeviceInfo info ) {
    123       ...
    124     }
    125   });
    126 </pre>
    127 
    128 
    129 <h2 id=device_and_port_information>Device and Port Information</h2>
    130 
    131 
    132 <p>You can query the number of input and output ports.</p>
    133 
    134 <pre class=prettyprint>
    135 int numInputs = info.getInputPortCount();
    136 int numOutputs = info.getOutputPortCount();
    137 </pre>
    138 
    139 
    140 <p>Note that &ldquo;input&rdquo; and &ldquo;output&rdquo; are from the standpoint of the device. So a
    141 synthesizer will have an &ldquo;input&rdquo; port that receives messages. A keyboard will
    142 have an &ldquo;output&rdquo; port that sends messages.</p>
    143 
    144 <p>The MidiDeviceInfo has a bundle of properties.</p>
    145 
    146 <pre class=prettyprint>
    147 Bundle properties = info.getProperties();
    148 String manufacturer = properties
    149       .getString(MidiDeviceInfo.PROPERTY_MANUFACTURER);
    150 </pre>
    151 
    152 
    153 <p>Other properties include PROPERTY_PRODUCT, PROPERTY_NAME,
    154 PROPERTY_SERIAL_NUMBER</p>
    155 
    156 <p>You can get the names and types of the ports from a PortInfo object.
    157 The type will be either TYPE_INPUT or TYPE_OUTPUT.</p>
    158 
    159 <pre class=prettyprint>
    160 MidiDeviceInfo.PortInfo[] portInfos = info.getPorts();
    161 String portName = portInfos[0].getName();
    162 if (portInfos[0].getType() == MidiDeviceInfo.PortInfo.TYPE_INPUT) {
    163     ...
    164 }
    165 </pre>
    166 
    167 
    168 <h2 id=open_a_midi_device>Open a MIDI Device</h2>
    169 
    170 
    171 <p>To access a MIDI device you need to open it first. The open is asynchronous so
    172 you need to provide a callback for completion. You can specify an optional
    173 Handler if you want the callback to occur on a specific Thread.</p>
    174 
    175 <pre class=prettyprint>
    176 m.openDevice(info, new MidiManager.OnDeviceOpenedListener() {
    177     &#64;Override
    178     public void onDeviceOpened(MidiDevice device) {
    179         if (device == null) {
    180             Log.e(TAG, "could not open device " + info);
    181         } else {
    182             ...
    183         }, new Handler(Looper.getMainLooper())
    184     );
    185 </pre>
    186 
    187 
    188 <h2 id=open_a_midi_input_port>Open a MIDI Input Port</h2>
    189 
    190 
    191 <p>If you want to send a message to a MIDI Device then you need to open an &ldquo;input&rdquo;
    192 port with exclusive access.</p>
    193 
    194 <pre class=prettyprint>
    195 MidiInputPort inputPort = device.openInputPort(index);
    196 </pre>
    197 
    198 
    199 <h2 id=send_a_noteon>Send a NoteOn</h2>
    200 
    201 
    202 <p>MIDI messages are sent as byte arrays. Here we encode a NoteOn message.</p>
    203 
    204 <pre class=prettyprint>
    205 byte[] buffer = new byte[32];
    206 int numBytes = 0;
    207 int channel = 3; // MIDI channels 1-16 are encoded as 0-15.
    208 buffer[numBytes++] = (byte)(0x90 + (channel - 1)); // note on
    209 buffer[numBytes++] = (byte)60; // pitch is middle C
    210 buffer[numBytes++] = (byte)127; // max velocity
    211 int offset = 0;
    212 // post is non-blocking
    213 inputPort.send(buffer, offset, numBytes);
    214 </pre>
    215 
    216 
    217 <p>Sometimes it is convenient to send MIDI messages with a timestamp. By
    218 scheduling events in the future we can mask scheduling jitter. Android MIDI
    219 timestamps are based on the monotonic nanosecond system timer. This is
    220 consistent with the other audio and input timers.</p>
    221 
    222 <p>Here we send a message with a timestamp 2 seconds in the future.</p>
    223 
    224 <pre class=prettyprint>
    225 final long NANOS_PER_SECOND = 1000000000L;
    226 long now = System.nanoTime();
    227 long future = now + (2 * NANOS_PER_SECOND);
    228 inputPort.send(buffer, offset, numBytes, future);
    229 </pre>
    230 
    231 
    232 <p>If you want to cancel events that you have scheduled in the future then call
    233 flush().</p>
    234 
    235 <pre class=prettyprint>
    236 inputPort.flush(); // discard events
    237 </pre>
    238 
    239 
    240 <p>If there were any MIDI NoteOff message left in the buffer then they will be
    241 discarded and you may get stuck notes. So we recommend sending &ldquo;all notes off&rdquo;
    242 after doing a flush.</p>
    243 
    244 <h2 id=receive_a_note>Receive a Note</h2>
    245 
    246 
    247 <p>To receive MIDI data from a device you need to extend MidiReceiver. Then
    248 connect your receiver to an output port of the device.</p>
    249 
    250 <pre class=prettyprint>
    251 class MyReceiver extends MidiReceiver {
    252     public void onSend(byte[] data, int offset,
    253             int count, long timestamp) throws IOException {
    254         // parse MIDI or whatever
    255     }
    256 }
    257 MidiOutputPort outputPort = device.openOutputPort(index);
    258 outputPort.connect(new MyReceiver());
    259 </pre>
    260 
    261 
    262 <p>The data that arrives is not validated or aligned in any particular way. It is
    263 raw MIDI data and can contain multiple messages or partial messages. It might
    264 contain System Real-Time messages, which can be interleaved inside other
    265 messages.</p>
    266 
    267 <h1 id=creating_a_midi_virtual_device_service>Creating a MIDI Virtual Device Service</h1>
    268 
    269 
    270 <p>An app can provide a MIDI Service that can be used by other apps. For example,
    271 an app can provide a custom synthesizer that other apps can send messages to.
    272 The service must be guarded with permission &quot;android.permission.BIND_MIDI_DEVICE_SERVICE&quot;.</p>
    273 
    274 <h2 id=manifest_files>Manifest Files</h2>
    275 
    276 
    277 <p>An app declares that it will function as a MIDI server in the
    278 AndroidManifest.xml file.</p>
    279 
    280 <pre class=prettyprint>
    281 &lt;service android:name="<strong>MySynthDeviceService</strong>"
    282   android:permission="android.permission.BIND_MIDI_DEVICE_SERVICE">
    283   &lt;intent-filter>
    284     &lt;action android:name="android.media.midi.MidiDeviceService" />
    285   &lt;/intent-filter>
    286   &lt;meta-data android:name="android.media.midi.MidiDeviceService"
    287       android:resource="@xml/<strong>synth_device_info</strong>" />
    288 &lt;/service>
    289 </pre>
    290 
    291 
    292 <p>The details of the resource in this example is stored in
    293 &ldquo;res/xml/synth_device_info.xml&rdquo;. The port names that you
    294 declare in this file will be available from PortInfo.getName().</p>
    295 
    296 <pre class=prettyprint>
    297 &lt;devices>
    298     &lt;device manufacturer="MyCompany" product="MidiSynthBasic">
    299         &lt;input-port name="input" />
    300     &lt;/device>
    301 &lt;/devices>
    302 </pre>
    303 
    304 
    305 <h2 id=extend_midideviceservice>Extend MidiDeviceService</h2>
    306 
    307 
    308 <p>You then define your server by extending android.media.midi.MidiDeviceService.
    309 Let&lsquo;s assume you have a MySynthEngine class that extends MidiReceiver.</p>
    310 
    311 <pre class=prettyprint>
    312 import android.media.midi.MidiDeviceService;
    313 import android.media.midi.MidiDeviceStatus;
    314 import android.media.midi.MidiReceiver;
    315 
    316 public class MidiSynthDeviceService extends MidiDeviceService {
    317     private static final String TAG = "MidiSynthDeviceService";
    318     private MySynthEngine mSynthEngine = new MySynthEngine();
    319     private boolean synthStarted = false;
    320 
    321     &#64;Override
    322     public void onCreate() {
    323         super.onCreate();
    324     }
    325 
    326     &#64;Override
    327     public void onDestroy() {
    328         mSynthEngine.stop();
    329         super.onDestroy();
    330     }
    331 
    332     &#64;Override
    333     // Declare the receivers associated with your input ports.
    334     public MidiReceiver[] onGetInputPortReceivers() {
    335         return new MidiReceiver[] { mSynthEngine };
    336     }
    337 
    338     /**
    339      * This will get called when clients connect or disconnect.
    340      * You can use it to turn on your synth only when needed.
    341      */
    342     &#64;Override
    343     public void onDeviceStatusChanged(MidiDeviceStatus status) {
    344         if (status.isInputPortOpen(0) && !synthStarted) {
    345             mSynthEngine.start();
    346             synthStarted = true;
    347         } else if (!status.isInputPortOpen(0) && synthStarted){
    348             mSynthEngine.stop();
    349             synthStarted = false;
    350         }
    351     }
    352 }
    353 </pre>
    354 
    355 <h1 id=using_midi_btle>Using MIDI Over Bluetooth LE</h1>
    356 
    357 <p>MIDI devices can be connected to Android using Bluetooth LE.</p>
    358 
    359 <p>Before using the device, the app must scan for available BTLE devices and then allow
    360 the user to connect. An example program
    361 will be provided so look for it on the Android developer website.</p>
    362 
    363 <h2 id=btle_location_permissions>Request Location Permission for BTLE</h2>
    364 
    365 <p>Applications that scan for Bluetooth devices must request permission in the
    366 manifest file. This LOCATION permission is required because it may be possible to
    367 guess the location of an Android device by seeing which BTLE devices are nearby.</p>
    368 
    369 <pre class=prettyprint>
    370 &lt;uses-permission android:name="android.permission.BLUETOOTH"/>
    371 &lt;uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    372 &lt;uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    373 </pre>
    374 
    375 <p>Apps must also request location permission from the user at run-time.
    376 See the documentation for <code>Activity.requestPermissions()</code> for details and an example.
    377 </p>
    378 
    379 <h2 id=btle_scan_devices>Scan for MIDI Devices</h2>
    380 
    381 <p>The app will only want to see MIDI devices and not mice or other non-MIDI devices.
    382 So construct a ScanFilter using the UUID for standard MIDI over BTLE.</p>
    383 
    384 <pre class=prettyprint>
    385 MIDI over BTLE UUID = "03B80E5A-EDE8-4B33-A751-6CE34EC4C700"
    386 </pre>
    387 
    388 <h2 id=btle_open_device>Open a MIDI Bluetooth Device</h2>
    389 
    390 <p>See the documentation for <code>android.bluetooth.le.BluetoothLeScanner.startScan()</code>
    391 method for details. When the user selects a MIDI/BTLE device then you can open it
    392 using the MidiManager.</p>
    393 
    394 <pre class=prettyprint>
    395 m.openBluetoothDevice(bluetoothDevice, callback, handler);
    396 </pre>
    397 
    398 <p>Once the MIDI/BTLE device has been opened by one app then it will also become available to other
    399 apps using the
    400 <a href="#get_list_of_already_plugged_in_entities">MIDI device discovery calls described above</a>.
    401 </p>
    402 
    403 </body>
    404 </html>
    405