diff options
| -rw-r--r-- | media/java/android/media/midi/package.html | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/media/java/android/media/midi/package.html b/media/java/android/media/midi/package.html new file mode 100644 index 000000000000..9e7e8b1ec35f --- /dev/null +++ b/media/java/android/media/midi/package.html @@ -0,0 +1,324 @@ +<html> +<body> +<p>Android MIDI User Guide</p> + +<h1 id=overview>Overview</h1> + + +<p>This document describes how to use the Android MIDI API in Java.</p> + +<p>The Android MIDI package allows users to:</p> + +<ul> + <li> Connect a MIDI keyboard to Android to play a synthesizer or drive music apps. + <li> Connect alternative MIDI controllers to Android. + <li> Drive external MIDI synths from Android. + <li> Drive external peripherals, lights, show control, etc from Android. + <li> Generate music dynamically from games or music creation apps. + <li> Generate MIDI messages in one app and send them to a second app. + <li> Use an Android device running in <em>peripheral mode</em> as a multitouch controller connected to a laptop. +</ul> + +<h2 id=the_api_features_include>The API features include:</h2> + + +<ul> + <li> Enumeration of currently available devices. Information includes name, vendor, +capabilities, etc. + <li> Provide notification when MIDI devices are plugged in or unplugged. + <li> Support efficient transmission of single or multiple short 1-3 byte MIDI +messages. + <li> Support transmission of arbitrary length data for SysEx, etc. + <li> Timestamps to avoid jitter. + <li> Support direction connection or “patching” of devices for lower latency. +</ul> + +<h2 id=transports_supported>Transports Supported</h2> + + +<p>The API is “transport agnostic”. But there are several transports currently +supported:</p> + +<ul> + <li> USB + <li> software routing + <li> BTLE +</ul> + +<h1 id=android_midi_terminology>Android MIDI Terminology</h1> + + +<h2 id=terminology>Terminology</h2> + + +<p>A <strong>Device</strong> is a MIDI capable object that has zero or more InputPorts and OutputPorts.</p> + +<p>An <strong>InputPort</strong> has 16 channels and can <strong>receive</strong> MIDI messages from an OutputPort or an app.</p> + +<p>An <strong>OutputPort</strong> has 16 channels and can <strong>send</strong> MIDI messages to an InputPort or an app.</p> + +<p><strong>MidiService</strong> is a centralized process that keeps track of all devices and brokers +communication between them.</p> + +<p><strong>MidiManager</strong> is a class that the application or a device manager calls to communicate with +the MidiService.</p> + +<h1 id=writing_a_midi_application>Writing a MIDI Application</h1> + + +<h2 id=the_midimanager>The MidiManager</h2> + + +<p>The primary class for accessing the MIDI package is through the MidiManager.</p> + +<pre class=prettyprint> +MidiManager m = (MidiManager)context.getSystemService(Context.MIDI_SERVICE); +</pre> + + +<h2 id=get_list_of_already_plugged_in_entities>Get List of Already Plugged In Entities</h2> + + +<p>When an app starts, it can get a list of all the available MIDI devices. This +information can be presented to a user, allowing them to choose a device.</p> + +<pre class=prettyprint> +MidiDeviceInfo[] infos = m.getDeviceList(); +</pre> + + +<h2 id=notification_of_midi_devices_hotplug_events>Notification of MIDI Devices HotPlug Events</h2> + + +<p>The application can request notification when, for example, keyboards are +plugged in or unplugged.</p> + +<pre class=prettyprint> +m.registerDeviceCallback(new MidiManager.DeviceCallback() { + public void onDeviceAdded( MidiDeviceInfo info ) { + ... + } + public void onDeviceRemoved( MidiDeviceInfo info ) { + ... + } + }); +</pre> + + +<h2 id=device_and_port_information>Device and Port Information</h2> + + +<p>You can query the number of input and output ports.</p> + +<pre class=prettyprint> +int numInputs = info.getInputPortCount(); +int numOutputs = info.getOutputPortCount(); +</pre> + + +<p>Note that “input” and “output” are from the standpoint of the device. So a +synthesizer will have an “input” port that receives messages. A keyboard will +have an “output” port that sends messages.</p> + +<p>The MidiDeviceInfo has a bundle of properties.</p> + +<pre class=prettyprint> +Bundle properties = info.getProperties(); +String manufacturer = properties + .getString(MidiDeviceInfo.PROPERTY_MANUFACTURER); +</pre> + + +<p>Other properties include PROPERTY_PRODUCT, PROPERTY_NAME, +PROPERTY_SERIAL_NUMBER</p> + +<p>You can get the names of the ports from a PortInfo object.</p> + +<pre class=prettyprint> +PortInfo portInfo = info.getInputPortInfo(i); +String portName = portInfo.getName(); +</pre> + + +<h2 id=open_a_midi_device>Open a MIDI Device</h2> + + +<p>To access a MIDI device you need to open it first. The open is asynchronous so +you need to provide a callback for completion. You can specify an optional +Handler if you want the callback to occur on a specific Thread.</p> + +<pre class=prettyprint> +m.openDevice(info, new MidiManager.DeviceOpenCallback() { + @Override + public void onDeviceOpened(MidiDeviceInfo deviceInfo, + MidiDevice device) { + if (device == null) { + Log.e(TAG, "could not open " + deviceInfo); + } else { + ... + }, new Handler(Looper.getMainLooper()) + ); +</pre> + + +<h2 id=open_a_midi_input_port>Open a MIDI Input Port</h2> + + +<p>If you want to send a message to a MIDI Device then you need to open an “input” +port with exclusive access.</p> + +<pre class=prettyprint> +MidiInputPort inputPort = device.openInputPort(index); +</pre> + + +<h2 id=send_a_noteon>Send a NoteOn</h2> + + +<p>MIDI messages are sent as byte arrays. Here we encode a NoteOn message.</p> + +<pre class=prettyprint> +byte[] buffer = new buffer[64]; +int numBytes = 0; +buffer[numBytes++] = 0x90 + channel; // note on +buffer[numBytes++] = pitch; +buffer[numBytes++] = velocity; +int offset = 0; +// post is non-blocking +inputPort.send(buffer, offset, numBytes); +</pre> + + +<p>Sometimes it is convenient to send MIDI messages with a timestamp. By +scheduling events in the future we can mask scheduling jitter. Android MIDI +timestamps are based on the monotonic nanosecond system timer. This is +consistent with the other audio and input timers.</p> + +<p>Here we send a message with a timestamp 2 seconds in the future.</p> + +<pre class=prettyprint> +long now = System.nanoTime(); +long future = now + (2 * 1000000000); +inputPort.sendWithTimestamp(buffer, offset, numBytes, future); +</pre> + + +<p>If you want to cancel events that you have scheduled in the future then call +flush().</p> + +<pre class=prettyprint> +inputPort.flush(); // discard events +</pre> + + +<p>If there were any MIDI NoteOff message left in the buffer then they will be +discarded and you may get stuck notes. So we recommend sending “all notes off” +after doing a flush.</p> + +<h2 id=receive_a_note>Receive a Note</h2> + + +<p>To receive MIDI data from a device you need to extend MidiReceiver. Then +connect your receiver to an output port of the device.</p> + +<pre class=prettyprint> +class MyReceiver extends MidiReceiver { + public void onReceive(byte[] data, int offset, + int count, long timestamp) throws IOException { + // parse MIDI or whatever + } +} +MidiOutputPort outputPort = device.openOutputPort(index); +outputPort.connect(new MyReceiver()); +</pre> + + +<p>The data that arrives is not validated or aligned in any particular way. It is +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. Some applications have their own MIDI parsers so pre-parsing the data +would be redundant. If an application wants the data parsed and aligned then +they can use the MidiFramer utility.</p> + +<h1 id=creating_a_midi_virtual_device_service>Creating a MIDI Virtual Device Service</h1> + + +<p>An app can provide a MIDI Service that can be used by other apps. For example, +an app can provide a custom synthesizer that other apps can send messages to. </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.</p> + +<pre class=prettyprint> +<service android:name="<strong>MySynthDeviceService</strong>"> + <intent-filter> + <action android:name="android.media.midi.MidiDeviceService" /> + </intent-filter> + <meta-data android:name="android.media.midi.MidiDeviceService" + android:resource="@xml/<strong>synth_device_info</strong>" /> +</service> +</pre> + + +<p>The details of the resource in this example is stored in +“res/xml/synth_device_info.xml”.</p> + +<pre class=prettyprint> +<devices> + <device manufacturer="MyCompany" product="MidiSynthBasic"> + <input-port name="input" /> + </device> +</devices> +</pre> + + +<h2 id=extend_midideviceservice>Extend MidiDeviceService</h2> + + +<p>You then define your server by extending android.media.midi.MidiDeviceService. +Let’s assume you have a MySynthEngine class that extends MidiReceiver.</p> + +<pre class=prettyprint> +import android.media.midi.MidiDeviceService; +import android.media.midi.MidiDeviceStatus; +import android.media.midi.MidiReceiver; + +public class MidiSynthDeviceService extends MidiDeviceService { + private static final String TAG = "MidiSynthDeviceService"; + private MySynthEngine mSynthEngine = new MySynthEngine(); + @Override + public void onCreate() { + super.onCreate(); + } + + @Override + public void onDestroy() { + mSynthEngine.stop(); + super.onDestroy(); + } + + @Override + // Declare the receivers associated with your input ports. + public MidiReceiver[] onGetInputPortReceivers() { + return new MidiReceiver[] { mSynthEngine }; + } + + /** + * This will get called when clients connect or disconnect. + * You can use it to turn on your synth only when needed. + */ + @Override + public void onDeviceStatusChanged(MidiDeviceStatus status) { + if (status.isInputPortOpen(0)) { + mSynthEngine.start(); + } else { + mSynthEngine.stop(); + } + } +} +</pre> +</body> +</html> |