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