Skip to content

Commit

Permalink
Merge pull request #24 from kshoji/feature/add-inter-app-midi-support…
Browse files Browse the repository at this point in the history
…-20240506

Add Inter-App MIDI support
  • Loading branch information
kshoji authored May 5, 2024
2 parents 5b2de8a + 6fb8936 commit 27e4b49
Show file tree
Hide file tree
Showing 5 changed files with 1,020 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
package jp.kshoji.javax.sound.midi.interapp;

import android.media.midi.MidiDeviceInfo;
import android.media.midi.MidiInputPort;
import android.media.midi.MidiOutputPort;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.RequiresApi;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import jp.kshoji.javax.sound.midi.MidiDevice;
import jp.kshoji.javax.sound.midi.MidiUnavailableException;
import jp.kshoji.javax.sound.midi.Receiver;
import jp.kshoji.javax.sound.midi.Transmitter;

/**
* {@link jp.kshoji.javax.sound.midi.MidiDevice} implementation
*
* @author K.Shoji
*/
public class InterAppMidiDevice implements MidiDevice {
private final MidiDeviceInfo midiDeviceInfo;
private final List<InterAppMidiReceiver> receivers = new ArrayList<>();
private final List<InterAppMidiTransmitter> transmitters = new ArrayList<>();
private boolean isOpened;

/**
* Constructor
*
* @param midiDevice the MIDI device
*/
@RequiresApi(api = Build.VERSION_CODES.M)
public InterAppMidiDevice(android.media.midi.MidiDevice midiDevice) {
midiDeviceInfo = midiDevice.getInfo();
for (int i = 0; i < midiDeviceInfo.getInputPortCount(); i++) {
// MidiInputPort: used for MIDI sending
MidiInputPort midiInputPort = midiDevice.openInputPort(i);
if (midiInputPort != null) {
receivers.add(new InterAppMidiReceiver(midiInputPort));
}
}

for (int i = 0; i < midiDeviceInfo.getOutputPortCount(); i++) {
// MidiOutputPort: used for MIDI receiving
MidiOutputPort midiOutputPort = midiDevice.openOutputPort(i);
if (midiOutputPort != null) {
transmitters.add(new InterAppMidiTransmitter(midiOutputPort));
}
}
}

@NonNull
@Override
public Info getDeviceInfo() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
Bundle properties = midiDeviceInfo.getProperties();
String name = properties.getString(MidiDeviceInfo.PROPERTY_NAME);
if (name == null) {
name = "(null)";
}
String vendor = properties.getString(MidiDeviceInfo.PROPERTY_MANUFACTURER);
if (vendor == null) {
vendor = "(null)";
}
String product = properties.getString(MidiDeviceInfo.PROPERTY_PRODUCT);
if (product == null) {
product = "(null)";
}
String version = properties.getString(MidiDeviceInfo.PROPERTY_VERSION);
if (version == null) {
version = "(null)";
}
return new MidiDevice.Info(name, vendor, product, version);
}

return new MidiDevice.Info("(null)", "(null)", "(null)", "(null)");
}

@Override
public void open() throws MidiUnavailableException {
if (isOpened) {
return;
}

synchronized (transmitters) {
for (InterAppMidiTransmitter transmitter : transmitters) {
if (transmitter != null) {
transmitter.open();
}
}
}

isOpened = true;
}

@Override
public void close() {
if (!isOpened) {
return;
}

synchronized (transmitters) {
for (InterAppMidiTransmitter transmitter : transmitters) {
if (transmitter != null) {
transmitter.close();
}
}
}

synchronized (receivers) {
for (InterAppMidiReceiver receiver : receivers) {
if (receiver != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
receiver.onFlush();
}
}
}
}

isOpened = false;
}

@Override
public boolean isOpen() {
return isOpened;
}

@Override
public long getMicrosecondPosition() {
return -1;
}

@Override
public int getMaxReceivers() {
return receivers.size();
}

@Override
public int getMaxTransmitters() {
return transmitters.size();
}

@NonNull
@Override
public Receiver getReceiver() throws MidiUnavailableException {
synchronized (receivers) {
for (InterAppMidiReceiver receiver : receivers) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return receiver.getMidiReceiver();
}
}
}

throw new MidiUnavailableException("Receiver not found");
}

@NonNull
@Override
public List<Receiver> getReceivers() {
final List<Receiver> result = new ArrayList<>();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
synchronized (receivers) {
for (InterAppMidiReceiver receiver : receivers) {
if (receiver != null) {
result.add(receiver.getMidiReceiver());
}
}
}
}
return Collections.unmodifiableList(result);
}

@NonNull
@Override
public Transmitter getTransmitter() throws MidiUnavailableException {
synchronized (transmitters) {
for (InterAppMidiTransmitter transmitter : transmitters) {
if (transmitter != null) {
return transmitter;
}
}
}

throw new MidiUnavailableException("Transmitter not found");
}

@NonNull
@Override
public List<Transmitter> getTransmitters() {
final List<Transmitter> result = new ArrayList<>();
synchronized (transmitters) {
for (InterAppMidiTransmitter transmitter : transmitters) {
if (transmitter != null) {
result.add(transmitter);
}
}
}
return Collections.unmodifiableList(result);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package jp.kshoji.javax.sound.midi.interapp;

import android.media.midi.MidiInputPort;
import android.media.midi.MidiReceiver;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.RequiresApi;
import android.util.Log;

import java.io.IOException;
import java.util.Arrays;

import jp.kshoji.javax.sound.midi.MidiMessage;
import jp.kshoji.javax.sound.midi.Receiver;

/**
* {@link jp.kshoji.javax.sound.midi.Receiver} implementation
*
* @author K.Shoji
*/
@RequiresApi(api = Build.VERSION_CODES.M)
public class InterAppMidiReceiver extends MidiReceiver {
InterAppReceiver interAppReceiver;

/**
* Receiver used internally
*/
private static class InterAppReceiver implements Receiver {
MidiReceiver midiReceiver;

/**
* Constructor
*
* @param midiReceiver the MIDI Receiver
*/
@RequiresApi(api = Build.VERSION_CODES.M)
InterAppReceiver(MidiReceiver midiReceiver) {
this.midiReceiver = midiReceiver;
}

@Override
public void send(@NonNull MidiMessage message, long timeStamp) {
try {
byte[] midiMessage = message.getMessage();
if (midiMessage != null) {
midiReceiver.onSend(midiMessage, 0, midiMessage.length, timeStamp);
}
} catch (IOException ignored) {
}
}

private void send(byte[] midiMessage, long timeStamp) {
try {
if (midiMessage != null) {
midiReceiver.onSend(midiMessage, 0, midiMessage.length, timeStamp);
}
} catch (IOException ignored) {
}
}

@Override
public void close() {
}

private void flush() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
try {
midiReceiver.onFlush();
} catch (IOException ignored) {
}
}
}
}

/**
* Constructor
*
* @param midiInputPort the MIDI input port
*/
public InterAppMidiReceiver(MidiInputPort midiInputPort) {
this.interAppReceiver = new InterAppReceiver(midiInputPort);
}

@Override
public void onSend(byte[] midiMessage, int offset, int count, long timestamp) throws IOException {
Log.i("InterAppMidiReceiver", "onSend message: " + Arrays.toString(midiMessage));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
interAppReceiver.send(midiMessage, timestamp);
}
}

@Override
public void onFlush() {
interAppReceiver.flush();
}

/**
* Obtains the Receiver instance
* @return the Receiver instance
*/
public Receiver getMidiReceiver() {
return interAppReceiver;
}
}
Loading

0 comments on commit 27e4b49

Please sign in to comment.