summaryrefslogtreecommitdiffstats
path: root/media/java
diff options
context:
space:
mode:
authorMike Lockwood <lockwood@google.com>2015-02-20 08:28:47 -0800
committerMike Lockwood <lockwood@google.com>2015-02-23 14:32:23 -0800
commitb673770f7172d4fca9bc05de1f36bc53e93eb247 (patch)
tree5d58c97188d3d7c4f9d7faf5376e586374a17624 /media/java
parentc3c2ff70a23ff31bec72e7794ea33552ca106438 (diff)
downloadframeworks_base-b673770f7172d4fca9bc05de1f36bc53e93eb247.zip
frameworks_base-b673770f7172d4fca9bc05de1f36bc53e93eb247.tar.gz
frameworks_base-b673770f7172d4fca9bc05de1f36bc53e93eb247.tar.bz2
Add MIDI Manager API tweaks:
- Change the package name from android.midi to android.media.midi - Add option for specifying a Handler for DeviceCallback notifications Change-Id: Ia9e9817a651c06299f4e02ee1da3c9666ff64cb9
Diffstat (limited to 'media/java')
-rw-r--r--media/java/android/media/midi/IMidiDeviceServer.aidl26
-rw-r--r--media/java/android/media/midi/IMidiListener.aidl26
-rw-r--r--media/java/android/media/midi/IMidiManager.aidl41
-rw-r--r--media/java/android/media/midi/MidiDevice.java102
-rw-r--r--media/java/android/media/midi/MidiDeviceInfo.aidl19
-rw-r--r--media/java/android/media/midi/MidiDeviceInfo.java204
-rw-r--r--media/java/android/media/midi/MidiDeviceServer.java268
-rw-r--r--media/java/android/media/midi/MidiInputPort.java80
-rw-r--r--media/java/android/media/midi/MidiManager.java222
-rw-r--r--media/java/android/media/midi/MidiOutputPort.java135
-rw-r--r--media/java/android/media/midi/MidiPort.java148
-rw-r--r--media/java/android/media/midi/MidiReceiver.java44
-rw-r--r--media/java/android/media/midi/MidiSender.java40
13 files changed, 1355 insertions, 0 deletions
diff --git a/media/java/android/media/midi/IMidiDeviceServer.aidl b/media/java/android/media/midi/IMidiDeviceServer.aidl
new file mode 100644
index 0000000..71914ad
--- /dev/null
+++ b/media/java/android/media/midi/IMidiDeviceServer.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.midi;
+
+import android.os.ParcelFileDescriptor;
+
+/** @hide */
+interface IMidiDeviceServer
+{
+ ParcelFileDescriptor openInputPort(int portNumber);
+ ParcelFileDescriptor openOutputPort(int portNumber);
+}
diff --git a/media/java/android/media/midi/IMidiListener.aidl b/media/java/android/media/midi/IMidiListener.aidl
new file mode 100644
index 0000000..a4129e9
--- /dev/null
+++ b/media/java/android/media/midi/IMidiListener.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.midi;
+
+import android.media.midi.MidiDeviceInfo;
+
+/** @hide */
+oneway interface IMidiListener
+{
+ void onDeviceAdded(in MidiDeviceInfo device);
+ void onDeviceRemoved(in MidiDeviceInfo device);
+}
diff --git a/media/java/android/media/midi/IMidiManager.aidl b/media/java/android/media/midi/IMidiManager.aidl
new file mode 100644
index 0000000..bba35f5
--- /dev/null
+++ b/media/java/android/media/midi/IMidiManager.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.midi;
+
+import android.media.midi.IMidiDeviceServer;
+import android.media.midi.IMidiListener;
+import android.media.midi.MidiDeviceInfo;
+import android.os.Bundle;
+import android.os.IBinder;
+
+/** @hide */
+interface IMidiManager
+{
+ MidiDeviceInfo[] getDeviceList();
+
+ // for device creation & removal notifications
+ void registerListener(IBinder token, in IMidiListener listener);
+ void unregisterListener(IBinder token, in IMidiListener listener);
+
+ // for communicating with MIDI devices
+ IMidiDeviceServer openDevice(IBinder token, in MidiDeviceInfo device);
+
+ // for implementing virtual MIDI devices
+ MidiDeviceInfo registerDeviceServer(in IMidiDeviceServer server, int numInputPorts,
+ int numOutputPorts, in Bundle properties, boolean isPrivate, int type);
+ void unregisterDeviceServer(in IMidiDeviceServer server);
+}
diff --git a/media/java/android/media/midi/MidiDevice.java b/media/java/android/media/midi/MidiDevice.java
new file mode 100644
index 0000000..36710fd
--- /dev/null
+++ b/media/java/android/media/midi/MidiDevice.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.midi;
+
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * This class is used for sending and receiving data to and from an MIDI device
+ * Instances of this class are created by {@link MidiManager#openDevice}.
+ *
+ * CANDIDATE FOR PUBLIC API
+ * @hide
+ */
+public final class MidiDevice {
+ private static final String TAG = "MidiDevice";
+
+ private final MidiDeviceInfo mDeviceInfo;
+ private final IMidiDeviceServer mServer;
+
+ /**
+ * MidiDevice should only be instantiated by MidiManager
+ * @hide
+ */
+ public MidiDevice(MidiDeviceInfo deviceInfo, IMidiDeviceServer server) {
+ mDeviceInfo = deviceInfo;
+ mServer = server;
+ }
+
+ /**
+ * Returns a {@link MidiDeviceInfo} object, which describes this device.
+ *
+ * @return the {@link MidiDeviceInfo} object
+ */
+ public MidiDeviceInfo getInfo() {
+ return mDeviceInfo;
+ }
+
+ /**
+ * Called to open a {@link MidiInputPort} for the specified port number.
+ *
+ * @param portNumber the number of the input port to open
+ * @return the {@link MidiInputPort}
+ */
+ public MidiInputPort openInputPort(int portNumber) {
+ try {
+ ParcelFileDescriptor pfd = mServer.openInputPort(portNumber);
+ if (pfd == null) {
+ return null;
+ }
+ return new MidiInputPort(pfd, portNumber);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in openInputPort");
+ return null;
+ }
+ }
+
+ /**
+ * Called to open a {@link MidiOutputPort} for the specified port number.
+ *
+ * @param portNumber the number of the output port to open
+ * @return the {@link MidiOutputPort}
+ */
+ public MidiOutputPort openOutputPort(int portNumber) {
+ try {
+ ParcelFileDescriptor pfd = mServer.openOutputPort(portNumber);
+ if (pfd == null) {
+ return null;
+ }
+ return new MidiOutputPort(pfd, portNumber);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in openOutputPort");
+ return null;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return ("MidiDevice: " + mDeviceInfo.toString());
+ }
+}
diff --git a/media/java/android/media/midi/MidiDeviceInfo.aidl b/media/java/android/media/midi/MidiDeviceInfo.aidl
new file mode 100644
index 0000000..f2f37a2
--- /dev/null
+++ b/media/java/android/media/midi/MidiDeviceInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.midi;
+
+parcelable MidiDeviceInfo;
diff --git a/media/java/android/media/midi/MidiDeviceInfo.java b/media/java/android/media/midi/MidiDeviceInfo.java
new file mode 100644
index 0000000..fd35052
--- /dev/null
+++ b/media/java/android/media/midi/MidiDeviceInfo.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.midi;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class contains information to describe a MIDI device.
+ * For now we only have information that can be retrieved easily for USB devices,
+ * but we will probably expand this in the future.
+ *
+ * This class is just an immutable object to encapsulate the MIDI device description.
+ * Use the MidiDevice class to actually communicate with devices.
+ *
+ * CANDIDATE FOR PUBLIC API
+ * @hide
+ */
+public class MidiDeviceInfo implements Parcelable {
+
+ private static final String TAG = "MidiDeviceInfo";
+
+ /**
+ * Constant representing USB MIDI devices for {@link #getType}
+ */
+ public static final int TYPE_USB = 1;
+
+ /**
+ * Constant representing virtual (software based) MIDI devices for {@link #getType}
+ */
+ public static final int TYPE_VIRTUAL = 2;
+
+ private final int mType; // USB or virtual
+ private final int mId; // unique ID generated by MidiService
+ private final int mInputPortCount;
+ private final int mOutputPortCount;
+ private final Bundle mProperties;
+
+ /**
+ * Bundle key for the device's manufacturer name property.
+ * Used with the {@link android.os.Bundle} returned by {@link #getProperties}.
+ * Matches the USB device manufacturer name string for USB MIDI devices.
+ */
+ public static final String PROPERTY_MANUFACTURER = "manufacturer";
+
+ /**
+ * Bundle key for the device's model name property.
+ * Used with the {@link android.os.Bundle} returned by {@link #getProperties}
+ * Matches the USB device product name string for USB MIDI devices.
+ */
+ public static final String PROPERTY_MODEL = "model";
+
+ /**
+ * Bundle key for the device's serial number property.
+ * Used with the {@link android.os.Bundle} returned by {@link #getProperties}
+ * Matches the USB device serial number for USB MIDI devices.
+ */
+ public static final String PROPERTY_SERIAL_NUMBER = "serial_number";
+
+ /**
+ * Bundle key for the device's {@link android.hardware.usb.UsbDevice}.
+ * Only set for USB MIDI devices.
+ * Used with the {@link android.os.Bundle} returned by {@link #getProperties}
+ */
+ public static final String PROPERTY_USB_DEVICE = "usb_device";
+
+ /**
+ * Bundle key for the device's ALSA card number.
+ * Only set for USB MIDI devices.
+ * Used with the {@link android.os.Bundle} returned by {@link #getProperties}
+ */
+ public static final String PROPERTY_ALSA_CARD = "alsa_card";
+
+ /**
+ * Bundle key for the device's ALSA device number.
+ * Only set for USB MIDI devices.
+ * Used with the {@link android.os.Bundle} returned by {@link #getProperties}
+ */
+ public static final String PROPERTY_ALSA_DEVICE = "alsa_device";
+
+ /**
+ * MidiDeviceInfo should only be instantiated by MidiService implementation
+ * @hide
+ */
+ public MidiDeviceInfo(int type, int id, int numInputPorts, int numOutputPorts,
+ Bundle properties) {
+ mType = type;
+ mId = id;
+ mInputPortCount = numInputPorts;
+ mOutputPortCount = numOutputPorts;
+ mProperties = properties;
+ }
+
+ /**
+ * Returns the type of the device.
+ *
+ * @return the device's type
+ */
+ public int getType() {
+ return mType;
+ }
+
+ /**
+ * Returns the ID of the device.
+ * This ID is generated by the MIDI service and is not persistent across device unplugs.
+ *
+ * @return the device's ID
+ */
+ public int getId() {
+ return mId;
+ }
+
+ /**
+ * Returns the device's number of input ports.
+ *
+ * @return the number of input ports
+ */
+ public int getInputPortCount() {
+ return mInputPortCount;
+ }
+
+ /**
+ * Returns the device's number of output ports.
+ *
+ * @return the number of output ports
+ */
+ public int getOutputPortCount() {
+ return mOutputPortCount;
+ }
+
+ /**
+ * Returns the {@link android.os.Bundle} containing the device's properties.
+ *
+ * @return the device's properties
+ */
+ public Bundle getProperties() {
+ return mProperties;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof MidiDeviceInfo) {
+ return (((MidiDeviceInfo)o).mId == mId);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return mId;
+ }
+
+ @Override
+ public String toString() {
+ return ("MidiDeviceInfo[mType=" + mType +
+ ",mInputPortCount=" + mInputPortCount +
+ ",mOutputPortCount=" + mOutputPortCount +
+ ",mProperties=" + mProperties);
+ }
+
+ public static final Parcelable.Creator<MidiDeviceInfo> CREATOR =
+ new Parcelable.Creator<MidiDeviceInfo>() {
+ public MidiDeviceInfo createFromParcel(Parcel in) {
+ int type = in.readInt();
+ int id = in.readInt();
+ int inputPorts = in.readInt();
+ int outputPorts = in.readInt();
+ Bundle properties = in.readBundle();
+ return new MidiDeviceInfo(type, id, inputPorts, outputPorts, properties);
+ }
+
+ public MidiDeviceInfo[] newArray(int size) {
+ return new MidiDeviceInfo[size];
+ }
+ };
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mType);
+ parcel.writeInt(mId);
+ parcel.writeInt(mInputPortCount);
+ parcel.writeInt(mOutputPortCount);
+ parcel.writeBundle(mProperties);
+ }
+}
diff --git a/media/java/android/media/midi/MidiDeviceServer.java b/media/java/android/media/midi/MidiDeviceServer.java
new file mode 100644
index 0000000..3317baa
--- /dev/null
+++ b/media/java/android/media/midi/MidiDeviceServer.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.midi;
+
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.system.OsConstants;
+import android.util.Log;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * This class is used to provide the implemention of MIDI device.
+ * Applications may call {@link MidiManager#createDeviceServer}
+ * to create an instance of this class to implement a virtual MIDI device.
+ *
+ * CANDIDATE FOR PUBLIC API
+ * @hide
+ */
+public final class MidiDeviceServer implements Closeable {
+ private static final String TAG = "MidiDeviceServer";
+
+ private final IMidiManager mMidiManager;
+
+ // MidiDeviceInfo for the device implemented by this server
+ private MidiDeviceInfo mDeviceInfo;
+ private int mInputPortCount;
+ private int mOutputPortCount;
+
+ // output ports for receiving messages from our clients
+ // we can have only one per port number
+ private MidiOutputPort[] mInputPortSenders;
+
+ // receivers attached to our input ports
+ private ArrayList<MidiReceiver>[] mInputPortReceivers;
+
+ // input ports for sending messages to our clients
+ // we can have multiple outputs per port number
+ private ArrayList<MidiInputPort>[] mOutputPortReceivers;
+
+ // subclass of MidiInputPort for passing to clients
+ // that notifies us when the connection has failed
+ private class ServerInputPort extends MidiInputPort {
+ ServerInputPort(ParcelFileDescriptor pfd, int portNumber) {
+ super(pfd, portNumber);
+ }
+
+ @Override
+ public void onIOException() {
+ synchronized (mOutputPortReceivers) {
+ mOutputPortReceivers[getPortNumber()].clear();
+ }
+ }
+ }
+
+ // subclass of MidiOutputPort for passing to clients
+ // that notifies us when the connection has failed
+ private class ServerOutputPort extends MidiOutputPort {
+ ServerOutputPort(ParcelFileDescriptor pfd, int portNumber) {
+ super(pfd, portNumber);
+ }
+
+ @Override
+ public void onIOException() {
+ synchronized (mInputPortSenders) {
+ mInputPortSenders[getPortNumber()] = null;
+ }
+ }
+ }
+
+ // Binder interface stub for receiving connection requests from clients
+ private final IMidiDeviceServer mServer = new IMidiDeviceServer.Stub() {
+
+ @Override
+ public ParcelFileDescriptor openInputPort(int portNumber) {
+ if (portNumber < 0 || portNumber >= mInputPortCount) {
+ Log.e(TAG, "portNumber out of range in openInputPort: " + portNumber);
+ return null;
+ }
+
+ ParcelFileDescriptor result = null;
+
+ synchronized (mInputPortSenders) {
+ if (mInputPortSenders[portNumber] != null) {
+ Log.d(TAG, "port " + portNumber + " already open");
+ return null;
+ }
+
+ try {
+ ParcelFileDescriptor[] pair = ParcelFileDescriptor.createSocketPair(
+ OsConstants.SOCK_SEQPACKET);
+ MidiOutputPort newOutputPort = new ServerOutputPort(pair[0], portNumber);
+ mInputPortSenders[portNumber] = newOutputPort;
+ result = pair[1];
+
+ ArrayList<MidiReceiver> receivers = mInputPortReceivers[portNumber];
+ synchronized (receivers) {
+ for (int i = 0; i < receivers.size(); i++) {
+ newOutputPort.connect(receivers.get(i));
+ }
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "unable to create ParcelFileDescriptors in openInputPort");
+ return null;
+ }
+ }
+
+ return result;
+ }
+
+ @Override
+ public ParcelFileDescriptor openOutputPort(int portNumber) {
+ if (portNumber < 0 || portNumber >= mOutputPortCount) {
+ Log.e(TAG, "portNumber out of range in openOutputPort: " + portNumber);
+ return null;
+ }
+ synchronized (mOutputPortReceivers) {
+ try {
+ ParcelFileDescriptor[] pair = ParcelFileDescriptor.createSocketPair(
+ OsConstants.SOCK_SEQPACKET);
+ mOutputPortReceivers[portNumber].add(new ServerInputPort(pair[0], portNumber));
+ return pair[1];
+ } catch (IOException e) {
+ Log.e(TAG, "unable to create ParcelFileDescriptors in openOutputPort");
+ return null;
+ }
+ }
+ }
+ };
+
+ /* package */ MidiDeviceServer(IMidiManager midiManager) {
+ mMidiManager = midiManager;
+ }
+
+ /* package */ IMidiDeviceServer getBinderInterface() {
+ return mServer;
+ }
+
+ /* package */ void setDeviceInfo(MidiDeviceInfo deviceInfo) {
+ if (mDeviceInfo != null) {
+ throw new IllegalStateException("setDeviceInfo should only be called once");
+ }
+ mDeviceInfo = deviceInfo;
+ mInputPortCount = deviceInfo.getInputPortCount();
+ mOutputPortCount = deviceInfo.getOutputPortCount();
+ mInputPortSenders = new MidiOutputPort[mInputPortCount];
+
+ mInputPortReceivers = new ArrayList[mInputPortCount];
+ for (int i = 0; i < mInputPortCount; i++) {
+ mInputPortReceivers[i] = new ArrayList<MidiReceiver>();
+ }
+
+ mOutputPortReceivers = new ArrayList[mOutputPortCount];
+ for (int i = 0; i < mOutputPortCount; i++) {
+ mOutputPortReceivers[i] = new ArrayList<MidiInputPort>();
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ try {
+ // FIXME - close input and output ports too?
+ mMidiManager.unregisterDeviceServer(mServer);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in unregisterDeviceServer");
+ }
+ }
+
+ /**
+ * Returns a {@link MidiDeviceInfo} object, which describes this device.
+ *
+ * @return the {@link MidiDeviceInfo} object
+ */
+ public MidiDeviceInfo getInfo() {
+ return mDeviceInfo;
+ }
+
+ /**
+ * Called to open a {@link MidiSender} to allow receiving MIDI messages
+ * on the device's input port for the specified port number.
+ *
+ * @param portNumber the number of the input port
+ * @return the {@link MidiSender}
+ */
+ public MidiSender openInputPortSender(int portNumber) {
+ if (portNumber < 0 || portNumber >= mDeviceInfo.getInputPortCount()) {
+ throw new IllegalArgumentException("portNumber " + portNumber + " out of range");
+ }
+ final int portNumberF = portNumber;
+ return new MidiSender() {
+
+ @Override
+ public void connect(MidiReceiver receiver) {
+ // We always synchronize on mInputPortSenders before receivers if we need to
+ // synchronize on both.
+ synchronized (mInputPortSenders) {
+ ArrayList<MidiReceiver> receivers = mInputPortReceivers[portNumberF];
+ synchronized (receivers) {
+ receivers.add(receiver);
+ MidiOutputPort outputPort = mInputPortSenders[portNumberF];
+ if (outputPort != null) {
+ outputPort.connect(receiver);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void disconnect(MidiReceiver receiver) {
+ // We always synchronize on mInputPortSenders before receivers if we need to
+ // synchronize on both.
+ synchronized (mInputPortSenders) {
+ ArrayList<MidiReceiver> receivers = mInputPortReceivers[portNumberF];
+ synchronized (receivers) {
+ receivers.remove(receiver);
+ MidiOutputPort outputPort = mInputPortSenders[portNumberF];
+ if (outputPort != null) {
+ outputPort.disconnect(receiver);
+ }
+ }
+ }
+ }
+ };
+ }
+
+ /**
+ * Called to open a {@link MidiReceiver} to allow sending MIDI messages
+ * on the virtual device's output port for the specified port number.
+ *
+ * @param portNumber the number of the output port
+ * @return the {@link MidiReceiver}
+ */
+ public MidiReceiver openOutputPortReceiver(int portNumber) {
+ if (portNumber < 0 || portNumber >= mDeviceInfo.getOutputPortCount()) {
+ throw new IllegalArgumentException("portNumber " + portNumber + " out of range");
+ }
+ final int portNumberF = portNumber;
+ return new MidiReceiver() {
+
+ @Override
+ public void post(byte[] msg, int offset, int count, long timestamp) throws IOException {
+ ArrayList<MidiInputPort> receivers = mOutputPortReceivers[portNumberF];
+ synchronized (receivers) {
+ for (int i = 0; i < receivers.size(); i++) {
+ // FIXME catch errors and remove dead ones
+ receivers.get(i).post(msg, offset, count, timestamp);
+ }
+ }
+ }
+ };
+ }
+}
diff --git a/media/java/android/media/midi/MidiInputPort.java b/media/java/android/media/midi/MidiInputPort.java
new file mode 100644
index 0000000..730d364
--- /dev/null
+++ b/media/java/android/media/midi/MidiInputPort.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.midi;
+
+import android.os.ParcelFileDescriptor;
+
+import libcore.io.IoUtils;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * This class is used for sending data to a port on a MIDI device
+ *
+ * CANDIDATE FOR PUBLIC API
+ * @hide
+ */
+public class MidiInputPort extends MidiPort implements MidiReceiver {
+
+ private final FileOutputStream mOutputStream;
+
+ // buffer to use for sending messages out our output stream
+ private final byte[] mBuffer = new byte[MAX_PACKET_SIZE];
+
+ /* package */ MidiInputPort(ParcelFileDescriptor pfd, int portNumber) {
+ super(portNumber);
+ mOutputStream = new ParcelFileDescriptor.AutoCloseOutputStream(pfd);
+ }
+
+ /**
+ * Writes a MIDI message to the input port
+ *
+ * @param msg byte array containing the message
+ * @param offset offset of first byte of the message in msg byte array
+ * @param count size of the message in bytes
+ * @param timestamp future time to post the message (based on
+ * {@link java.lang.System#nanoTime}
+ */
+ public void post(byte[] msg, int offset, int count, long timestamp) throws IOException {
+ assert(offset >= 0 && count >= 0 && offset + count <= msg.length);
+
+ synchronized (mBuffer) {
+ try {
+ while (count > 0) {
+ int length = packMessage(msg, offset, count, timestamp, mBuffer);
+ mOutputStream.write(mBuffer, 0, length);
+ int sent = getMessageSize(mBuffer, length);
+ assert(sent >= 0 && sent <= length);
+
+ offset += sent;
+ count -= sent;
+ }
+ } catch (IOException e) {
+ IoUtils.closeQuietly(mOutputStream);
+ // report I/O failure
+ onIOException();
+ throw e;
+ }
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ mOutputStream.close();
+ }
+}
diff --git a/media/java/android/media/midi/MidiManager.java b/media/java/android/media/midi/MidiManager.java
new file mode 100644
index 0000000..410120d
--- /dev/null
+++ b/media/java/android/media/midi/MidiManager.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.midi;
+
+import android.content.Context;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+/**
+ * This class is the public application interface to the MIDI service.
+ *
+ * <p>You can obtain an instance of this class by calling
+ * {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
+ *
+ * {@samplecode
+ * MidiManager manager = (MidiManager) getSystemService(Context.MIDI_SERVICE);}
+ *
+ * CANDIDATE FOR PUBLIC API
+ * @hide
+ */
+public class MidiManager {
+ private static final String TAG = "MidiManager";
+
+ private final Context mContext;
+ private final IMidiManager mService;
+ private final IBinder mToken = new Binder();
+
+ private HashMap<DeviceCallback,DeviceListener> mDeviceListeners =
+ new HashMap<DeviceCallback,DeviceListener>();
+
+ // Binder stub for receiving device notifications from MidiService
+ private class DeviceListener extends IMidiListener.Stub {
+ private final DeviceCallback mCallback;
+ private final Handler mHandler;
+
+ public DeviceListener(DeviceCallback callback, Handler handler) {
+ mCallback = callback;
+ mHandler = handler;
+ }
+
+ public void onDeviceAdded(MidiDeviceInfo device) {
+ if (mHandler != null) {
+ final MidiDeviceInfo deviceF = device;
+ mHandler.post(new Runnable() {
+ @Override public void run() {
+ mCallback.onDeviceAdded(deviceF);
+ }
+ });
+ } else {
+ mCallback.onDeviceAdded(device);
+ }
+ }
+
+ public void onDeviceRemoved(MidiDeviceInfo device) {
+ if (mHandler != null) {
+ final MidiDeviceInfo deviceF = device;
+ mHandler.post(new Runnable() {
+ @Override public void run() {
+ mCallback.onDeviceRemoved(deviceF);
+ }
+ });
+ } else {
+ mCallback.onDeviceRemoved(device);
+ }
+ }
+ }
+
+ /**
+ * Callback class used for clients to receive MIDI device added and removed notifications
+ */
+ public static class DeviceCallback {
+ /**
+ * Called to notify when a new MIDI device has been added
+ *
+ * @param device a {@link MidiDeviceInfo} for the newly added device
+ */
+ public void onDeviceAdded(MidiDeviceInfo device) {
+ }
+
+ /**
+ * Called to notify when a MIDI device has been removed
+ *
+ * @param device a {@link MidiDeviceInfo} for the removed device
+ */
+ public void onDeviceRemoved(MidiDeviceInfo device) {
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public MidiManager(Context context, IMidiManager service) {
+ mContext = context;
+ mService = service;
+ }
+
+ /**
+ * Registers a callback to receive notifications when MIDI devices are added and removed.
+ *
+ * @param callback a {@link DeviceCallback} for MIDI device notifications
+ * @param handler The {@link android.os.Handler Handler} that will be used for delivering the
+ * device notifications. If handler is null, then the thread used for the
+ * callback is unspecified.
+ */
+ public void registerDeviceCallback(DeviceCallback callback, Handler handler) {
+ DeviceListener deviceListener = new DeviceListener(callback, handler);
+ try {
+ mService.registerListener(mToken, deviceListener);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in registerDeviceListener");
+ return;
+ }
+ mDeviceListeners.put(callback, deviceListener);
+ }
+
+ /**
+ * Unregisters a {@link DeviceCallback}.
+ *
+ * @param callback a {@link DeviceCallback} to unregister
+ */
+ public void unregisterDeviceCallback(DeviceCallback callback) {
+ DeviceListener deviceListener = mDeviceListeners.remove(callback);
+ if (deviceListener != null) {
+ try {
+ mService.unregisterListener(mToken, deviceListener);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in unregisterDeviceListener");
+ }
+ }
+ }
+
+ /**
+ * Gets the list of all connected MIDI devices.
+ *
+ * @return an array of all MIDI devices
+ */
+ public MidiDeviceInfo[] getDeviceList() {
+ try {
+ return mService.getDeviceList();
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in getDeviceList");
+ return new MidiDeviceInfo[0];
+ }
+ }
+
+ /**
+ * Opens a MIDI device for reading and writing.
+ *
+ * @param deviceInfo a {@link android.media.midi.MidiDeviceInfo} to open
+ * @return a {@link MidiDevice} object for the device
+ */
+ public MidiDevice openDevice(MidiDeviceInfo deviceInfo) {
+ try {
+ IMidiDeviceServer server = mService.openDevice(mToken, deviceInfo);
+ if (server == null) {
+ Log.e(TAG, "could not open device " + deviceInfo);
+ return null;
+ }
+ return new MidiDevice(deviceInfo, server);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in openDevice");
+ }
+ return null;
+ }
+
+ /** @hide */
+ public MidiDeviceServer createDeviceServer(int numInputPorts, int numOutputPorts,
+ Bundle properties, boolean isPrivate, int type) {
+ try {
+ MidiDeviceServer server = new MidiDeviceServer(mService);
+ MidiDeviceInfo deviceInfo = mService.registerDeviceServer(server.getBinderInterface(),
+ numInputPorts, numOutputPorts, properties, isPrivate, type);
+ if (deviceInfo == null) {
+ Log.e(TAG, "registerVirtualDevice failed");
+ return null;
+ }
+ server.setDeviceInfo(deviceInfo);
+ return server;
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in createVirtualDevice");
+ return null;
+ }
+ }
+
+ /**
+ * Creates a new MIDI virtual device.
+ *
+ * @param numInputPorts number of input ports for the virtual device
+ * @param numOutputPorts number of output ports for the virtual device
+ * @param properties a {@link android.os.Bundle} containing properties describing the device
+ * @param isPrivate true if this device should only be visible and accessible to apps
+ * with the same UID as the caller
+ * @return a {@link MidiDeviceServer} object to locally represent the device
+ */
+ public MidiDeviceServer createDeviceServer(int numInputPorts, int numOutputPorts,
+ Bundle properties, boolean isPrivate) {
+ return createDeviceServer(numInputPorts, numOutputPorts, properties,
+ isPrivate, MidiDeviceInfo.TYPE_VIRTUAL);
+ }
+
+}
diff --git a/media/java/android/media/midi/MidiOutputPort.java b/media/java/android/media/midi/MidiOutputPort.java
new file mode 100644
index 0000000..83ddeeb
--- /dev/null
+++ b/media/java/android/media/midi/MidiOutputPort.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.midi;
+
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import libcore.io.IoUtils;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * This class is used for receiving data from a port on a MIDI device
+ *
+ * CANDIDATE FOR PUBLIC API
+ * @hide
+ */
+public class MidiOutputPort extends MidiPort implements MidiSender {
+ private static final String TAG = "MidiOutputPort";
+
+ private final FileInputStream mInputStream;
+
+ // array of receiver lists, indexed by port number
+ private final ArrayList<MidiReceiver> mReceivers = new ArrayList<MidiReceiver>();
+
+ private int mReceiverCount; // total number of receivers for all ports
+
+ // This thread reads MIDI events from a socket and distributes them to the list of
+ // MidiReceivers attached to this device.
+ private final Thread mThread = new Thread() {
+ @Override
+ public void run() {
+ byte[] buffer = new byte[MAX_PACKET_SIZE];
+ ArrayList<MidiReceiver> deadReceivers = new ArrayList<MidiReceiver>();
+
+ try {
+ while (true) {
+ // read next event
+ int count = mInputStream.read(buffer);
+ if (count < 0) {
+ break;
+ }
+
+ int offset = getMessageOffset(buffer, count);
+ int size = getMessageSize(buffer, count);
+ long timestamp = getMessageTimeStamp(buffer, count);
+
+ synchronized (mReceivers) {
+ for (int i = 0; i < mReceivers.size(); i++) {
+ MidiReceiver receiver = mReceivers.get(i);
+ try {
+ receiver.post(buffer, offset, size, timestamp);
+ } catch (IOException e) {
+ Log.e(TAG, "post failed");
+ deadReceivers.add(receiver);
+ }
+ }
+ // remove any receivers that failed
+ if (deadReceivers.size() > 0) {
+ for (MidiReceiver receiver: deadReceivers) {
+ mReceivers.remove(receiver);
+ mReceiverCount--;
+ }
+ deadReceivers.clear();
+ }
+ // exit if we have no receivers left
+ if (mReceiverCount == 0) {
+ break;
+ }
+ }
+ }
+ } catch (IOException e) {
+ // report I/O failure
+ Log.e(TAG, "read failed");
+ } finally {
+ IoUtils.closeQuietly(mInputStream);
+ onIOException();
+ }
+ }
+ };
+
+ /* package */ MidiOutputPort(ParcelFileDescriptor pfd, int portNumber) {
+ super(portNumber);
+ mInputStream = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+ }
+
+ /**
+ * Connects a {@link MidiReceiver} to the output port to allow receiving
+ * MIDI messages from the port.
+ *
+ * @param receiver the receiver to connect
+ */
+ public void connect(MidiReceiver receiver) {
+ synchronized (mReceivers) {
+ mReceivers.add(receiver);
+ if (mReceiverCount++ == 0) {
+ mThread.start();
+ }
+ }
+ }
+
+ /**
+ * Disconnects a {@link MidiReceiver} from the output port.
+ *
+ * @param receiver the receiver to connect
+ */
+ public void disconnect(MidiReceiver receiver) {
+ synchronized (mReceivers) {
+ if (mReceivers.remove(receiver)) {
+ mReceiverCount--;
+ }
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ mInputStream.close();
+ }
+}
diff --git a/media/java/android/media/midi/MidiPort.java b/media/java/android/media/midi/MidiPort.java
new file mode 100644
index 0000000..4d3c91d
--- /dev/null
+++ b/media/java/android/media/midi/MidiPort.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.midi;
+
+import android.util.Log;
+
+import java.io.Closeable;
+
+/**
+ * This class represents a MIDI input or output port.
+ * Base class for {@link MidiInputPort} and {@link MidiOutputPort}
+ *
+ * CANDIDATE FOR PUBLIC API
+ * @hide
+ */
+abstract public class MidiPort implements Closeable {
+ private static final String TAG = "MidiPort";
+
+ private final int mPortNumber;
+
+ /**
+ * Maximum size of a packet that can pass through our ParcelFileDescriptor.
+ * For internal use only. Implementation details may change in the future.
+ * @hide
+ */
+ public static final int MAX_PACKET_SIZE = 1024;
+
+ /**
+ * size of message timestamp in bytes
+ * For internal use only. Implementation details may change in the future.
+ * @hide
+ */
+ private static final int TIMESTAMP_SIZE = 8;
+
+ /**
+ * Maximum amount of MIDI data that can be included in a packet
+ */
+ public static final int MAX_PACKET_DATA_SIZE = MAX_PACKET_SIZE - TIMESTAMP_SIZE;
+
+
+ /* package */ MidiPort(int portNumber) {
+ mPortNumber = portNumber;
+ }
+
+ /**
+ * Returns the port number of this port
+ *
+ * @return the port's port number
+ */
+ public final int getPortNumber() {
+ return mPortNumber;
+ }
+
+ /**
+ * Called when an IOExeption occurs while sending or receiving data.
+ * Subclasses can override to be notified of such errors
+ *
+ * @hide
+ */
+ public void onIOException() {
+ }
+
+ /**
+ * Utility function for packing a MIDI message to be sent through our ParcelFileDescriptor
+ *
+ * message byte array contains variable length MIDI message.
+ * messageSize is size of variable length MIDI message
+ * timestamp is message timestamp to pack
+ * dest is buffer to pack into
+ * returns size of packed message
+ *
+ * For internal use only. Implementation details may change in the future.
+ * @hide
+ */
+ public static int packMessage(byte[] message, int offset, int size, long timestamp,
+ byte[] dest) {
+ if (size + TIMESTAMP_SIZE > MAX_PACKET_SIZE) {
+ size = MAX_PACKET_SIZE - TIMESTAMP_SIZE;
+ }
+ // message data goes first
+ System.arraycopy(message, offset, dest, 0, size);
+
+ // followed by timestamp
+ for (int i = 0; i < TIMESTAMP_SIZE; i++) {
+ dest[size++] = (byte)timestamp;
+ timestamp >>= 8;
+ }
+
+ return size;
+ }
+
+ /**
+ * Utility function for unpacking a MIDI message received from our ParcelFileDescriptor
+ * returns the offset of the MIDI message in packed buffer
+ *
+ * For internal use only. Implementation details may change in the future.
+ * @hide
+ */
+ public static int getMessageOffset(byte[] buffer, int bufferLength) {
+ // message is at the beginning
+ return 0;
+ }
+
+ /**
+ * Utility function for unpacking a MIDI message received from our ParcelFileDescriptor
+ * returns size of MIDI data in packed buffer
+ *
+ * For internal use only. Implementation details may change in the future.
+ * @hide
+ */
+ public static int getMessageSize(byte[] buffer, int bufferLength) {
+ // message length is total buffer length minus size of the timestamp
+ return bufferLength - TIMESTAMP_SIZE;
+ }
+
+ /**
+ * Utility function for unpacking a MIDI message received from our ParcelFileDescriptor
+ * unpacks timestamp from packed buffer
+ *
+ * For internal use only. Implementation details may change in the future.
+ * @hide
+ */
+ public static long getMessageTimeStamp(byte[] buffer, int bufferLength) {
+ // timestamp is at end of the packet
+ int offset = bufferLength;
+ long timestamp = 0;
+
+ for (int i = 0; i < TIMESTAMP_SIZE; i++) {
+ int b = (int)buffer[--offset] & 0xFF;
+ timestamp = (timestamp << 8) | b;
+ }
+ return timestamp;
+ }
+}
diff --git a/media/java/android/media/midi/MidiReceiver.java b/media/java/android/media/midi/MidiReceiver.java
new file mode 100644
index 0000000..64c0c07
--- /dev/null
+++ b/media/java/android/media/midi/MidiReceiver.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.midi;
+
+import java.io.IOException;
+
+/**
+ * Interface for sending and receiving data to and from a MIDI device.
+ *
+ * CANDIDATE FOR PUBLIC API
+ * @hide
+ */
+public interface MidiReceiver {
+ /**
+ * Called to pass MIDI data to the receiver.
+ *
+ * NOTE: the msg array parameter is only valid within the context of this call.
+ * The msg bytes should be copied by the receiver rather than retaining a reference
+ * to this parameter.
+ * Also, modifying the contents of the msg array parameter may result in other receivers
+ * in the same application receiving incorrect values in their post() method.
+ *
+ * @param msg a byte array containing the MIDI data
+ * @param offset the offset of the first byte of the data in the byte array
+ * @param count the number of bytes of MIDI data in the array
+ * @param timestamp the timestamp of the message (based on {@link java.lang.System#nanoTime}
+ * @throws IOException
+ */
+ public void post(byte[] msg, int offset, int count, long timestamp) throws IOException;
+}
diff --git a/media/java/android/media/midi/MidiSender.java b/media/java/android/media/midi/MidiSender.java
new file mode 100644
index 0000000..4550476
--- /dev/null
+++ b/media/java/android/media/midi/MidiSender.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.midi;
+
+/**
+ * Interface provided by a device to allow attaching
+ * MidiReceivers to a MIDI device.
+ *
+ * CANDIDATE FOR PUBLIC API
+ * @hide
+ */
+public interface MidiSender {
+ /**
+ * Called to connect a {@link MidiReceiver} to the sender
+ *
+ * @param receiver the receiver to connect
+ */
+ public void connect(MidiReceiver receiver);
+
+ /**
+ * Called to disconnect a {@link MidiReceiver} from the sender
+ *
+ * @param receiver the receiver to disconnect
+ */
+ public void disconnect(MidiReceiver receiver);
+}