Loading media/java/android/media/midi/package.html 0 → 100644 +324 −0 Original line number Diff line number Diff line <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> Loading
media/java/android/media/midi/package.html 0 → 100644 +324 −0 Original line number Diff line number Diff line <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>