Try Live
Add Docs
Rankings
Pricing
Enterprise
Docs
Install
Install
Docs
Pricing
Enterprise
More...
More...
Try Live
Rankings
Add Docs
Android USB MIDI Driver
https://github.com/kshoji/usb-midi-driver
Admin
Android USB MIDI Driver is a library that enables Android applications to connect to and communicate
...
Tokens:
21,487
Snippets:
193
Trust Score:
9.2
Update:
3 weeks ago
Context
Skills
Chat
Benchmark
82.7
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Android USB MIDI Driver Android USB MIDI Driver is a library that enables Android applications to communicate with USB MIDI devices without requiring root privileges. It leverages the Android USB Host API (available on Android 3.1+) to connect to standard USB MIDI devices like synthesizers, keyboards, sequencers, and instruments. The library also supports non-standard USB MIDI devices from manufacturers like YAMAHA, Roland, and MOTU through custom device filters. The driver provides multiple integration patterns including Activity-based, Service-based, Fragment-based, and standalone driver implementations. It supports both single and multiple device connections simultaneously, offers `javax.sound.midi` compatible classes for Java developers familiar with desktop MIDI programming, and handles all the low-level USB protocol details including virtual MIDI cables (0-15) that extend MIDI channel capabilities. ## Installation and Dependencies Add the library to your Android project using JitPack. ```gradle // In your root build.gradle allprojects { repositories { maven { url 'https://jitpack.io' } } } // In your app's build.gradle dependencies { implementation 'com.github.kshoji:USB-MIDI-Driver:0.1.14' } ``` ## AndroidManifest Configuration Configure the manifest to enable USB host mode and set the proper activity launch mode. ```xml <manifest xmlns:android="http://schemas.android.com/apk/res/android"> <!-- Required for USB MIDI device access --> <uses-feature android:name="android.hardware.usb.host" /> <application> <activity android:name=".MyMidiActivity" android:label="@string/app_name" android:launchMode="singleTask"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest> ``` ## AbstractSingleMidiActivity Base Activity class for applications that connect to a single USB MIDI device. Automatically handles device attachment/detachment and provides callback methods for all MIDI events. ```java public class MySingleMidiActivity extends AbstractSingleMidiActivity { // Handler for UI updates from MIDI callback threads private final Handler midiEventHandler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { TextView textView = findViewById(R.id.midiEventText); textView.setText((String) msg.obj); return true; } }); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_midi); } // Called when a MIDI output device is attached @Override public void onMidiOutputDeviceAttached(@NonNull MidiOutputDevice midiOutputDevice) { Toast.makeText(this, "MIDI Device connected: " + midiOutputDevice.getProductName(), Toast.LENGTH_SHORT).show(); } // Called when a MIDI output device is detached @Override public void onMidiOutputDeviceDetached(@NonNull MidiOutputDevice midiOutputDevice) { Toast.makeText(this, "MIDI Device disconnected", Toast.LENGTH_SHORT).show(); } @Override public void onMidiInputDeviceAttached(@NonNull MidiInputDevice midiInputDevice) { // Input device ready to receive MIDI events } @Override public void onMidiInputDeviceDetached(@NonNull MidiInputDevice midiInputDevice) { // Input device disconnected } // Note On event callback (called from background thread) @Override public void onMidiNoteOn(@NonNull MidiInputDevice sender, int cable, int channel, int note, int velocity) { String message = String.format("Note On - Channel: %d, Note: %d, Velocity: %d", channel, note, velocity); midiEventHandler.sendMessage(Message.obtain(midiEventHandler, 0, message)); // Play sound or trigger action based on note playNote(note, velocity); } // Note Off event callback @Override public void onMidiNoteOff(@NonNull MidiInputDevice sender, int cable, int channel, int note, int velocity) { String message = String.format("Note Off - Channel: %d, Note: %d", channel, note); midiEventHandler.sendMessage(Message.obtain(midiEventHandler, 0, message)); stopNote(note); } // Control Change callback (knobs, sliders, pedals) @Override public void onMidiControlChange(@NonNull MidiInputDevice sender, int cable, int channel, int function, int value) { // function: CC number (0-127), value: 0-127 handleControlChange(function, value); } // Program Change callback (instrument selection) @Override public void onMidiProgramChange(@NonNull MidiInputDevice sender, int cable, int channel, int program) { // program: 0-127 selectInstrument(program); } // Pitch Bend callback @Override public void onMidiPitchWheel(@NonNull MidiInputDevice sender, int cable, int channel, int amount) { // amount: 0 (lowest) - 8192 (center) - 16383 (highest) applyPitchBend(amount); } // System Exclusive message callback @Override public void onMidiSystemExclusive(@NonNull MidiInputDevice sender, int cable, byte[] systemExclusive) { // Process SysEx data (starts with 0xF0, ends with 0xF7) processSysEx(systemExclusive); } // Required callback implementations @Override public void onDeviceAttached(@NonNull UsbDevice usbDevice) { } @Override public void onDeviceDetached(@NonNull UsbDevice usbDevice) { } @Override public void onMidiMiscellaneousFunctionCodes(@NonNull MidiInputDevice sender, int cable, int byte1, int byte2, int byte3) { } @Override public void onMidiCableEvents(@NonNull MidiInputDevice sender, int cable, int byte1, int byte2, int byte3) { } @Override public void onMidiSystemCommonMessage(@NonNull MidiInputDevice sender, int cable, byte[] bytes) { } @Override public void onMidiSingleByte(@NonNull MidiInputDevice sender, int cable, int byte1) { } @Override public void onMidiPolyphonicAftertouch(@NonNull MidiInputDevice sender, int cable, int channel, int note, int pressure) { } @Override public void onMidiChannelAftertouch(@NonNull MidiInputDevice sender, int cable, int channel, int pressure) { } @Override public void onMidiTimeCodeQuarterFrame(@NonNull MidiInputDevice sender, int cable, int timing) { } @Override public void onMidiSongSelect(@NonNull MidiInputDevice sender, int cable, int song) { } @Override public void onMidiSongPositionPointer(@NonNull MidiInputDevice sender, int cable, int position) { } @Override public void onMidiTuneRequest(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiTimingClock(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiStart(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiContinue(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiStop(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiActiveSensing(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiReset(@NonNull MidiInputDevice sender, int cable) { } } ``` ## AbstractMultipleMidiActivity Base Activity class for applications that need to connect to multiple USB MIDI devices simultaneously. Provides Set collections for managing multiple input and output devices. ```java public class MyMultipleMidiActivity extends AbstractMultipleMidiActivity { private ArrayAdapter<String> deviceAdapter; private Map<String, MidiOutputDevice> outputDeviceMap = new HashMap<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_multi_midi); // Setup device list Spinner deviceSpinner = findViewById(R.id.deviceSpinner); deviceAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item); deviceSpinner.setAdapter(deviceAdapter); } @Override public void onMidiOutputDeviceAttached(@NonNull MidiOutputDevice midiOutputDevice) { String deviceName = midiOutputDevice.getProductName(); if (deviceName == null) { deviceName = midiOutputDevice.getDeviceAddress(); } outputDeviceMap.put(deviceName, midiOutputDevice); deviceAdapter.add(deviceName); deviceAdapter.notifyDataSetChanged(); } @Override public void onMidiOutputDeviceDetached(@NonNull MidiOutputDevice midiOutputDevice) { String deviceName = midiOutputDevice.getProductName(); if (deviceName == null) { deviceName = midiOutputDevice.getDeviceAddress(); } outputDeviceMap.remove(deviceName); deviceAdapter.remove(deviceName); deviceAdapter.notifyDataSetChanged(); } @Override public void onMidiInputDeviceAttached(@NonNull MidiInputDevice midiInputDevice) { // Each input device will trigger callbacks when receiving MIDI Log.d("MIDI", "Input device attached: " + midiInputDevice.getProductName()); } @Override public void onMidiInputDeviceDetached(@NonNull MidiInputDevice midiInputDevice) { Log.d("MIDI", "Input device detached: " + midiInputDevice.getProductName()); } // Note events include the sender device for identification @Override public void onMidiNoteOn(@NonNull MidiInputDevice sender, int cable, int channel, int note, int velocity) { String deviceName = sender.getProductName(); Log.d("MIDI", "Note On from " + deviceName + ": " + note); // Forward to all output devices (MIDI thru) for (MidiOutputDevice outputDevice : getMidiOutputDevices()) { outputDevice.sendMidiNoteOn(cable, channel, note, velocity); } } @Override public void onMidiNoteOff(@NonNull MidiInputDevice sender, int cable, int channel, int note, int velocity) { for (MidiOutputDevice outputDevice : getMidiOutputDevices()) { outputDevice.sendMidiNoteOff(cable, channel, note, velocity); } } // Get all connected input devices public void listConnectedDevices() { Set<MidiInputDevice> inputDevices = getMidiInputDevices(); Set<MidiOutputDevice> outputDevices = getMidiOutputDevices(); for (MidiInputDevice device : inputDevices) { Log.d("MIDI", "Input: " + device.getProductName() + " (" + device.getManufacturerName() + ")"); } for (MidiOutputDevice device : outputDevices) { Log.d("MIDI", "Output: " + device.getProductName() + " (" + device.getManufacturerName() + ")"); } } // Remaining callbacks... @Override public void onDeviceAttached(@NonNull UsbDevice usbDevice) { } @Override public void onDeviceDetached(@NonNull UsbDevice usbDevice) { } @Override public void onMidiMiscellaneousFunctionCodes(@NonNull MidiInputDevice sender, int cable, int byte1, int byte2, int byte3) { } @Override public void onMidiCableEvents(@NonNull MidiInputDevice sender, int cable, int byte1, int byte2, int byte3) { } @Override public void onMidiSystemCommonMessage(@NonNull MidiInputDevice sender, int cable, byte[] bytes) { } @Override public void onMidiSingleByte(@NonNull MidiInputDevice sender, int cable, int byte1) { } @Override public void onMidiPolyphonicAftertouch(@NonNull MidiInputDevice sender, int cable, int channel, int note, int pressure) { } @Override public void onMidiChannelAftertouch(@NonNull MidiInputDevice sender, int cable, int channel, int pressure) { } @Override public void onMidiControlChange(@NonNull MidiInputDevice sender, int cable, int channel, int function, int value) { } @Override public void onMidiProgramChange(@NonNull MidiInputDevice sender, int cable, int channel, int program) { } @Override public void onMidiPitchWheel(@NonNull MidiInputDevice sender, int cable, int channel, int amount) { } @Override public void onMidiSystemExclusive(@NonNull MidiInputDevice sender, int cable, byte[] systemExclusive) { } @Override public void onMidiTimeCodeQuarterFrame(@NonNull MidiInputDevice sender, int cable, int timing) { } @Override public void onMidiSongSelect(@NonNull MidiInputDevice sender, int cable, int song) { } @Override public void onMidiSongPositionPointer(@NonNull MidiInputDevice sender, int cable, int position) { } @Override public void onMidiTuneRequest(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiTimingClock(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiStart(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiContinue(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiStop(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiActiveSensing(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiReset(@NonNull MidiInputDevice sender, int cable) { } } ``` ## MidiOutputDevice - Sending MIDI Messages The MidiOutputDevice class provides methods for sending all types of MIDI messages to connected devices. All parameters use standard MIDI ranges: cable (0-15), channel (0-15), note/velocity/values (0-127). ```java public class MidiSender { private MidiOutputDevice midiOutputDevice; public void setOutputDevice(MidiOutputDevice device) { this.midiOutputDevice = device; } // Send Note On message public void sendNoteOn(int channel, int note, int velocity) { if (midiOutputDevice != null) { int cable = 0; // Virtual cable ID (0-15) midiOutputDevice.sendMidiNoteOn(cable, channel, note, velocity); } } // Send Note Off message public void sendNoteOff(int channel, int note, int velocity) { if (midiOutputDevice != null) { midiOutputDevice.sendMidiNoteOff(0, channel, note, velocity); } } // Send Control Change (CC) message public void sendControlChange(int channel, int controller, int value) { if (midiOutputDevice != null) { // Common controllers: 1=Mod Wheel, 7=Volume, 10=Pan, 64=Sustain midiOutputDevice.sendMidiControlChange(0, channel, controller, value); } } // Send Program Change (instrument select) public void sendProgramChange(int channel, int program) { if (midiOutputDevice != null) { midiOutputDevice.sendMidiProgramChange(0, channel, program); } } // Send Pitch Bend public void sendPitchBend(int channel, int amount) { if (midiOutputDevice != null) { // amount: 0 (lowest) - 8192 (center) - 16383 (highest) midiOutputDevice.sendMidiPitchWheel(0, channel, amount); } } // Send Polyphonic Aftertouch (per-note pressure) public void sendPolyAftertouch(int channel, int note, int pressure) { if (midiOutputDevice != null) { midiOutputDevice.sendMidiPolyphonicAftertouch(0, channel, note, pressure); } } // Send Channel Aftertouch (channel-wide pressure) public void sendChannelAftertouch(int channel, int pressure) { if (midiOutputDevice != null) { midiOutputDevice.sendMidiChannelAftertouch(0, channel, pressure); } } // Send System Exclusive message public void sendSysEx(byte[] sysexData) { if (midiOutputDevice != null) { // SysEx must start with 0xF0 and end with 0xF7 midiOutputDevice.sendMidiSystemExclusive(0, sysexData); } } // Send RPN (Registered Parameter Number) message public void sendRPN(int channel, int parameterNumber, int value) { if (midiOutputDevice != null) { // Common RPNs: 0=Pitch Bend Sensitivity, 1=Fine Tuning, 2=Coarse Tuning midiOutputDevice.sendRPNMessage(0, channel, parameterNumber, value); } } // Send NRPN (Non-Registered Parameter Number) message public void sendNRPN(int channel, int parameterNumber, int value) { if (midiOutputDevice != null) { midiOutputDevice.sendNRPNMessage(0, channel, parameterNumber, value); } } // Transport control messages public void sendStart() { if (midiOutputDevice != null) { midiOutputDevice.sendMidiStart(0); } } public void sendStop() { if (midiOutputDevice != null) { midiOutputDevice.sendMidiStop(0); } } public void sendContinue() { if (midiOutputDevice != null) { midiOutputDevice.sendMidiContinue(0); } } public void sendTimingClock() { if (midiOutputDevice != null) { midiOutputDevice.sendMidiTimingClock(0); } } // Send Song Position Pointer public void sendSongPosition(int position) { if (midiOutputDevice != null) { // position: 0-16383 (in MIDI beats, 6 per quarter note) midiOutputDevice.sendMidiSongPositionPointer(0, position); } } // Send Song Select public void sendSongSelect(int song) { if (midiOutputDevice != null) { midiOutputDevice.sendMidiSongSelect(0, song); } } // Send raw MIDI message (3 bytes) public void sendRawMidi(int byte1, int byte2, int byte3) { if (midiOutputDevice != null) { midiOutputDevice.sendMidiMessage(0, byte1, byte2, byte3); } } } ``` ## UsbMidiDriver - Standalone Driver A standalone driver class that can be used without extending Activity. Useful for Service-based architectures or custom implementations. ```java public class MidiManager { private UsbMidiDriver midiDriver; private OnMidiEventCallback callback; public interface OnMidiEventCallback { void onNoteOn(int channel, int note, int velocity); void onNoteOff(int channel, int note, int velocity); void onControlChange(int channel, int controller, int value); } public void initialize(Context context, OnMidiEventCallback callback) { this.callback = callback; midiDriver = new UsbMidiDriver(context) { @Override public void onDeviceAttached(@NonNull UsbDevice usbDevice) { Log.d("MIDI", "Device attached: " + usbDevice.getDeviceName()); } @Override public void onDeviceDetached(@NonNull UsbDevice usbDevice) { Log.d("MIDI", "Device detached: " + usbDevice.getDeviceName()); } @Override public void onMidiInputDeviceAttached(@NonNull MidiInputDevice midiInputDevice) { Log.d("MIDI", "MIDI Input ready: " + midiInputDevice.getProductName()); } @Override public void onMidiInputDeviceDetached(@NonNull MidiInputDevice midiInputDevice) { Log.d("MIDI", "MIDI Input disconnected"); } @Override public void onMidiOutputDeviceAttached(@NonNull MidiOutputDevice midiOutputDevice) { Log.d("MIDI", "MIDI Output ready: " + midiOutputDevice.getProductName()); } @Override public void onMidiOutputDeviceDetached(@NonNull MidiOutputDevice midiOutputDevice) { Log.d("MIDI", "MIDI Output disconnected"); } @Override public void onMidiNoteOn(@NonNull MidiInputDevice sender, int cable, int channel, int note, int velocity) { if (callback != null) { callback.onNoteOn(channel, note, velocity); } } @Override public void onMidiNoteOff(@NonNull MidiInputDevice sender, int cable, int channel, int note, int velocity) { if (callback != null) { callback.onNoteOff(channel, note, velocity); } } @Override public void onMidiControlChange(@NonNull MidiInputDevice sender, int cable, int channel, int function, int value) { if (callback != null) { callback.onControlChange(channel, function, value); } } // Implement remaining callbacks... @Override public void onMidiPolyphonicAftertouch(@NonNull MidiInputDevice sender, int cable, int channel, int note, int pressure) { } @Override public void onMidiChannelAftertouch(@NonNull MidiInputDevice sender, int cable, int channel, int pressure) { } @Override public void onMidiProgramChange(@NonNull MidiInputDevice sender, int cable, int channel, int program) { } @Override public void onMidiPitchWheel(@NonNull MidiInputDevice sender, int cable, int channel, int amount) { } @Override public void onMidiSystemExclusive(@NonNull MidiInputDevice sender, int cable, byte[] systemExclusive) { } @Override public void onMidiSystemCommonMessage(@NonNull MidiInputDevice sender, int cable, byte[] bytes) { } @Override public void onMidiSingleByte(@NonNull MidiInputDevice sender, int cable, int byte1) { } @Override public void onMidiMiscellaneousFunctionCodes(@NonNull MidiInputDevice sender, int cable, int byte1, int byte2, int byte3) { } @Override public void onMidiCableEvents(@NonNull MidiInputDevice sender, int cable, int byte1, int byte2, int byte3) { } @Override public void onMidiTimeCodeQuarterFrame(@NonNull MidiInputDevice sender, int cable, int timing) { } @Override public void onMidiSongSelect(@NonNull MidiInputDevice sender, int cable, int song) { } @Override public void onMidiSongPositionPointer(@NonNull MidiInputDevice sender, int cable, int position) { } @Override public void onMidiTuneRequest(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiTimingClock(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiStart(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiContinue(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiStop(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiActiveSensing(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiReset(@NonNull MidiInputDevice sender, int cable) { } }; // Start the driver midiDriver.open(); } public Set<MidiOutputDevice> getOutputDevices() { return midiDriver.getMidiOutputDevices(); } public Set<UsbDevice> getConnectedDevices() { return midiDriver.getConnectedUsbDevices(); } public void suspend() { if (midiDriver != null) { // Suspend MIDI communication (e.g., when app goes to background) // Events will be discarded until resume() is called } } public void shutdown() { if (midiDriver != null) { midiDriver.close(); midiDriver = null; } } } ``` ## MultipleMidiService - Background MIDI Service Android Service for handling MIDI connections in the background, allowing MIDI communication to continue even when the Activity is not in the foreground. ```java // In your Activity public class MidiServiceActivity extends AppCompatActivity { private MultipleMidiService midiService; private boolean bound = false; private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { MultipleMidiService.LocalBinder binder = (MultipleMidiService.LocalBinder) service; midiService = binder.getService(); bound = true; // Set up listeners midiService.setOnMidiDeviceAttachedListener(new OnMidiDeviceAttachedListener() { @Override public void onDeviceAttached(@NonNull UsbDevice usbDevice) { Log.d("MIDI", "USB device attached"); } @Override public void onMidiInputDeviceAttached(@NonNull MidiInputDevice midiInputDevice) { // Set up event listener for this input device midiInputDevice.setMidiEventListener(midiEventListener); Log.d("MIDI", "Input device ready: " + midiInputDevice.getProductName()); } @Override public void onMidiOutputDeviceAttached(@NonNull MidiOutputDevice midiOutputDevice) { Log.d("MIDI", "Output device ready: " + midiOutputDevice.getProductName()); } }); midiService.setOnMidiDeviceDetachedListener(new OnMidiDeviceDetachedListener() { @Override public void onDeviceDetached(@NonNull UsbDevice usbDevice) { } @Override public void onMidiInputDeviceDetached(@NonNull MidiInputDevice midiInputDevice) { Log.d("MIDI", "Input device detached"); } @Override public void onMidiOutputDeviceDetached(@NonNull MidiOutputDevice midiOutputDevice) { Log.d("MIDI", "Output device detached"); } }); } @Override public void onServiceDisconnected(ComponentName name) { bound = false; } }; private final OnMidiInputEventListener midiEventListener = new OnMidiInputEventListener() { @Override public void onMidiNoteOn(@NonNull MidiInputDevice sender, int cable, int channel, int note, int velocity) { runOnUiThread(() -> handleNoteOn(channel, note, velocity)); } @Override public void onMidiNoteOff(@NonNull MidiInputDevice sender, int cable, int channel, int note, int velocity) { runOnUiThread(() -> handleNoteOff(channel, note, velocity)); } // Implement other callbacks... @Override public void onMidiPolyphonicAftertouch(@NonNull MidiInputDevice sender, int cable, int channel, int note, int pressure) { } @Override public void onMidiControlChange(@NonNull MidiInputDevice sender, int cable, int channel, int function, int value) { } @Override public void onMidiProgramChange(@NonNull MidiInputDevice sender, int cable, int channel, int program) { } @Override public void onMidiChannelAftertouch(@NonNull MidiInputDevice sender, int cable, int channel, int pressure) { } @Override public void onMidiPitchWheel(@NonNull MidiInputDevice sender, int cable, int channel, int amount) { } @Override public void onMidiSystemExclusive(@NonNull MidiInputDevice sender, int cable, byte[] systemExclusive) { } @Override public void onMidiSystemCommonMessage(@NonNull MidiInputDevice sender, int cable, byte[] bytes) { } @Override public void onMidiSingleByte(@NonNull MidiInputDevice sender, int cable, int byte1) { } @Override public void onMidiMiscellaneousFunctionCodes(@NonNull MidiInputDevice sender, int cable, int byte1, int byte2, int byte3) { } @Override public void onMidiCableEvents(@NonNull MidiInputDevice sender, int cable, int byte1, int byte2, int byte3) { } @Override public void onMidiTimeCodeQuarterFrame(@NonNull MidiInputDevice sender, int cable, int timing) { } @Override public void onMidiSongSelect(@NonNull MidiInputDevice sender, int cable, int song) { } @Override public void onMidiSongPositionPointer(@NonNull MidiInputDevice sender, int cable, int position) { } @Override public void onMidiTuneRequest(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiTimingClock(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiStart(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiContinue(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiStop(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiActiveSensing(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiReset(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiRPNReceived(@NonNull MidiInputDevice sender, int cable, int channel, int function, int valueMSB, int valueLSB) { } @Override public void onMidiNRPNReceived(@NonNull MidiInputDevice sender, int cable, int channel, int function, int valueMSB, int valueLSB) { } @Override public void onMidiRPNReceived(@NonNull MidiInputDevice sender, int cable, int channel, int function, int value) { } @Override public void onMidiNRPNReceived(@NonNull MidiInputDevice sender, int cable, int channel, int function, int value) { } }; @Override protected void onStart() { super.onStart(); // Start and bind to the service Intent intent = new Intent(this, MultipleMidiService.class); startService(intent); bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); } @Override protected void onStop() { super.onStop(); if (bound) { unbindService(serviceConnection); bound = false; } } public void sendNoteToAllDevices(int note, int velocity) { if (bound && midiService != null) { for (MidiOutputDevice device : midiService.getMidiOutputDevices()) { device.sendMidiNoteOn(0, 0, note, velocity); } } } } ``` ## UsbMidiSystem - javax.sound.midi Compatibility Provides `javax.sound.midi` compatible API for developers familiar with Java SE MIDI programming. Enables use of standard MIDI classes like MidiSystem, MidiDevice, Receiver, and Transmitter. ```java public class JavaxSoundMidiExample extends AppCompatActivity { private UsbMidiSystem usbMidiSystem; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Initialize the USB MIDI system usbMidiSystem = new UsbMidiSystem(this); usbMidiSystem.initialize(); } @Override protected void onDestroy() { super.onDestroy(); if (usbMidiSystem != null) { usbMidiSystem.terminate(); } } // Using javax.sound.midi API public void useJavaxSoundMidi() { try { // Get all MIDI devices MidiDevice.Info[] infos = MidiSystem.getMidiDeviceInfo(); for (MidiDevice.Info info : infos) { MidiDevice device = MidiSystem.getMidiDevice(info); device.open(); // Get Receiver for sending MIDI if (device.getMaxReceivers() != 0) { Receiver receiver = device.getReceiver(); // Send Note On ShortMessage noteOn = new ShortMessage(); noteOn.setMessage(ShortMessage.NOTE_ON, 0, 60, 100); receiver.send(noteOn, -1); // Send Note Off after delay new Handler().postDelayed(() -> { try { ShortMessage noteOff = new ShortMessage(); noteOff.setMessage(ShortMessage.NOTE_OFF, 0, 60, 0); receiver.send(noteOff, -1); } catch (InvalidMidiDataException e) { e.printStackTrace(); } }, 500); } // Get Transmitter for receiving MIDI if (device.getMaxTransmitters() != 0) { Transmitter transmitter = device.getTransmitter(); transmitter.setReceiver(new Receiver() { @Override public void send(MidiMessage message, long timeStamp) { if (message instanceof ShortMessage) { ShortMessage sm = (ShortMessage) message; int command = sm.getCommand(); int channel = sm.getChannel(); int data1 = sm.getData1(); int data2 = sm.getData2(); Log.d("MIDI", "Received: cmd=" + command + ", ch=" + channel + ", d1=" + data1 + ", d2=" + data2); } } @Override public void close() { } }); } } } catch (MidiUnavailableException | InvalidMidiDataException e) { e.printStackTrace(); } } // Get available synthesizers public void listSynthesizers() { Synthesizer[] synthesizers = MidiSystem.getAvailableSynthesizers(); for (Synthesizer synth : synthesizers) { Log.d("MIDI", "Synthesizer: " + synth.getDeviceInfo().getName()); } } } ``` ## Suspend and Resume MIDI Communication Temporarily suspend MIDI event processing to conserve resources when the app is in the background. ```java public class MidiLifecycleActivity extends AbstractMultipleMidiActivity { @Override protected void onPause() { super.onPause(); // Suspend MIDI when app goes to background // All events will be discarded until resume suspendMidiDevices(); } @Override protected void onResume() { super.onResume(); // Resume MIDI when app comes to foreground resumeMidiDevices(); } // For AbstractSingleMidiActivity, use the same methods: // suspendMidiDevices() and resumeMidiDevices() // For UsbMidiDriver: // midiDriver.suspend() and midiDriver.resume() // For MultipleMidiService: // midiService.suspend() and midiService.resume() // Callback implementations... @Override public void onDeviceAttached(@NonNull UsbDevice usbDevice) { } @Override public void onDeviceDetached(@NonNull UsbDevice usbDevice) { } @Override public void onMidiInputDeviceAttached(@NonNull MidiInputDevice device) { } @Override public void onMidiInputDeviceDetached(@NonNull MidiInputDevice device) { } @Override public void onMidiOutputDeviceAttached(@NonNull MidiOutputDevice device) { } @Override public void onMidiOutputDeviceDetached(@NonNull MidiOutputDevice device) { } @Override public void onMidiNoteOn(@NonNull MidiInputDevice sender, int cable, int channel, int note, int velocity) { } @Override public void onMidiNoteOff(@NonNull MidiInputDevice sender, int cable, int channel, int note, int velocity) { } @Override public void onMidiPolyphonicAftertouch(@NonNull MidiInputDevice sender, int cable, int channel, int note, int pressure) { } @Override public void onMidiControlChange(@NonNull MidiInputDevice sender, int cable, int channel, int function, int value) { } @Override public void onMidiProgramChange(@NonNull MidiInputDevice sender, int cable, int channel, int program) { } @Override public void onMidiChannelAftertouch(@NonNull MidiInputDevice sender, int cable, int channel, int pressure) { } @Override public void onMidiPitchWheel(@NonNull MidiInputDevice sender, int cable, int channel, int amount) { } @Override public void onMidiSystemExclusive(@NonNull MidiInputDevice sender, int cable, byte[] systemExclusive) { } @Override public void onMidiSystemCommonMessage(@NonNull MidiInputDevice sender, int cable, byte[] bytes) { } @Override public void onMidiSingleByte(@NonNull MidiInputDevice sender, int cable, int byte1) { } @Override public void onMidiMiscellaneousFunctionCodes(@NonNull MidiInputDevice sender, int cable, int byte1, int byte2, int byte3) { } @Override public void onMidiCableEvents(@NonNull MidiInputDevice sender, int cable, int byte1, int byte2, int byte3) { } @Override public void onMidiTimeCodeQuarterFrame(@NonNull MidiInputDevice sender, int cable, int timing) { } @Override public void onMidiSongSelect(@NonNull MidiInputDevice sender, int cable, int song) { } @Override public void onMidiSongPositionPointer(@NonNull MidiInputDevice sender, int cable, int position) { } @Override public void onMidiTuneRequest(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiTimingClock(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiStart(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiContinue(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiStop(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiActiveSensing(@NonNull MidiInputDevice sender, int cable) { } @Override public void onMidiReset(@NonNull MidiInputDevice sender, int cable) { } } ``` ## Summary Android USB MIDI Driver is ideal for building music applications such as synthesizers, MIDI controllers, sequencers, DAW controllers, MIDI monitors/loggers, and instruments that need to communicate with external USB MIDI hardware. The library supports multiple integration patterns: Activity-based for simple single-screen apps, Service-based for background MIDI processing, and standalone UsbMidiDriver for custom architectures. Key use cases include receiving MIDI input from keyboards and controllers to trigger sounds or control parameters, sending MIDI output to synthesizers and sound modules, building MIDI thru/routing applications, and creating `javax.sound.midi` compatible applications for cross-platform code sharing. Integration typically involves extending one of the abstract Activity classes (AbstractSingleMidiActivity or AbstractMultipleMidiActivity), implementing the required callback methods for device attachment/detachment and MIDI events, and using MidiOutputDevice methods to send MIDI data. The library handles USB permission requests, device enumeration, and low-level USB MIDI protocol details automatically. For applications requiring background operation, MultipleMidiService provides a bound service pattern, while UsbMidiDriver offers maximum flexibility for custom implementations. The `javax.sound.midi` compatibility layer through UsbMidiSystem enables developers to use familiar Java SE MIDI APIs on Android.