Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ae13230b authored by Mike Lockwood's avatar Mike Lockwood Committed by Android (Google) Code Review
Browse files

Merge "MIDI: API changes to support multiple ports per device"

parents 7822e643 b6b9a91c
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -37,7 +37,8 @@ interface IMidiManager
    ParcelFileDescriptor openDevice(IBinder token, in MidiDeviceInfo device);

    // for implementing virtual MIDI devices
    MidiDevice registerVirtualDevice(IBinder token, in Bundle properties);
    MidiDevice registerVirtualDevice(IBinder token, int numInputPorts, int numOutputPorts,
            in Bundle properties);
    void unregisterVirtualDevice(IBinder token, in MidiDeviceInfo device);

    // for use by UsbAudioManager
+106 −67
Original line number Diff line number Diff line
@@ -28,7 +28,7 @@ import java.io.IOException;
import java.util.ArrayList;

/**
 * This class is used for sending and receiving data to and from an midi device
 * 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}.
 * This class can also be used to provide the implementation for a virtual device.
 *
@@ -44,21 +44,29 @@ public final class MidiDevice implements Parcelable {
    private ParcelFileDescriptor mParcelFileDescriptor;
    private FileInputStream mInputStream;
    private FileOutputStream mOutputStream;
    private final ArrayList<MidiReceiver> mReceivers = new ArrayList<MidiReceiver>();

    // lazily populated lists of ports
    private final MidiInputPort[] mInputPorts;
    private final MidiOutputPort[] mOutputPorts;

    // array of receiver lists, indexed by port number
    private final ArrayList<MidiReceiver>[] mReceivers;

    private int mReceiverCount; // total number of receivers for all ports

    /**
     * Minimum size of packed message as sent through our ParcelFileDescriptor
     * 8 bytes for timestamp and 1 to 3 bytes for message
     * 8 bytes for timestamp, 1 byte for port number and 1 to 3 bytes for message
     * @hide
     */
    public static final int MIN_PACKED_MESSAGE_SIZE = 9;
    public static final int MIN_PACKED_MESSAGE_SIZE = 10;

    /**
     * Maximum size of packed message as sent through our ParcelFileDescriptor
     * 8 bytes for timestamp and 1 to 3 bytes for message
     * 8 bytes for timestamp, 1 byte for port number and 1 to 3 bytes for message
     * @hide
     */
    public static final int MAX_PACKED_MESSAGE_SIZE = 11;
    public static final int MAX_PACKED_MESSAGE_SIZE = 12;

    // This thread reads MIDI events from a socket and distributes them to the list of
    // MidiReceivers attached to this device.
@@ -80,12 +88,15 @@ public final class MidiDevice implements Parcelable {
                    int offset = getMessageOffset(buffer, count);
                    int size = getMessageSize(buffer, count);
                    long timestamp = getMessageTimeStamp(buffer, count);
                    int port = getMessagePortNumber(buffer, count);

                    synchronized (mReceivers) {
                        for (int i = 0; i < mReceivers.size(); i++) {
                            MidiReceiver receiver = mReceivers.get(i);
                        ArrayList<MidiReceiver> receivers = mReceivers[port];
                        if (receivers != null) {
                            for (int i = 0; i < receivers.size(); i++) {
                                MidiReceiver receiver = receivers.get(i);
                                try {
                                mReceivers.get(i).onPost(buffer, offset, size, timestamp);
                                    receivers.get(i).onPost(buffer, offset, size, timestamp);
                                } catch (IOException e) {
                                    Log.e(TAG, "post failed");
                                    deadReceivers.add(receiver);
@@ -94,64 +105,87 @@ public final class MidiDevice implements Parcelable {
                            // remove any receivers that failed
                            if (deadReceivers.size() > 0) {
                                for (MidiReceiver receiver: deadReceivers) {
                                mReceivers.remove(receiver);
                                    receivers.remove(receiver);
                                    mReceiverCount--;
                                }
                                deadReceivers.clear();
                            }
                            if (receivers.size() == 0) {
                                mReceivers[port] = null;
                            }
                            // exit if we have no receivers left
                        if (mReceivers.size() == 0) {
                            if (mReceiverCount == 0) {
                                break;
                            }
                        }
                    }
                }
            } catch (IOException e) {
                Log.e(TAG, "read failed");
            }
        }
    };

    // This is the receiver that clients use for sending events to this device.
    private final MidiReceiver mReceiver = new MidiReceiver() {
        private final byte[] mBuffer = new byte[MAX_PACKED_MESSAGE_SIZE];
        public void onPost(byte[] msg, int offset, int count, long timestamp) throws IOException {
            synchronized (mBuffer) {
                int length = packMessage(msg, offset, count, timestamp, mBuffer);
                mOutputStream.write(mBuffer, 0, length);
   /**
     * MidiDevice should only be instantiated by MidiManager or MidiService
     * @hide
     */
    public MidiDevice(MidiDeviceInfo deviceInfo, ParcelFileDescriptor pfd) {
        mDeviceInfo = deviceInfo;
        mParcelFileDescriptor = pfd;
        int inputPorts = deviceInfo.getInputPortCount();
        int outputPorts = deviceInfo.getOutputPortCount();
        mInputPorts = new MidiInputPort[inputPorts];
        mOutputPorts = new MidiOutputPort[outputPorts];
        mReceivers = new ArrayList[outputPorts];
    }

    public MidiInputPort openInputPort(int portNumber) {
        if (portNumber < 0 || portNumber >= mDeviceInfo.getInputPortCount()) {
            throw new IllegalArgumentException("input port number out of range");
        }
        synchronized (mInputPorts) {
            if (mInputPorts[portNumber] == null) {
                mInputPorts[portNumber] = new MidiInputPort(mOutputStream, portNumber);
            }
            return mInputPorts[portNumber];
        }
    }
    };

    // Our MidiSender object, to which clients can attach MidiReceivers.
    private final MidiSender mSender = new MidiSender() {
        public void connect(MidiReceiver receiver) {
            synchronized (mReceivers) {
                if (mReceivers.size() == 0) {
                    mThread.start();
    public MidiOutputPort openOutputPort(int portNumber) {
        if (portNumber < 0 || portNumber >= mDeviceInfo.getOutputPortCount()) {
            throw new IllegalArgumentException("output port number out of range");
        }
        synchronized (mOutputPorts) {
            if (mOutputPorts[portNumber] == null) {
                mOutputPorts[portNumber] = new MidiOutputPort(this, portNumber);
            }
                mReceivers.add(receiver);
            return mOutputPorts[portNumber];
        }
    }

        public void disconnect(MidiReceiver receiver) {
    /* package */ void connect(MidiReceiver receiver, int portNumber) {
        synchronized (mReceivers) {
                mReceivers.remove(receiver);
                if (mReceivers.size() == 0) {
                    // ???
            if (mReceivers[portNumber] == null) {
                mReceivers[portNumber] = new  ArrayList<MidiReceiver>();
            }
            mReceivers[portNumber].add(receiver);
            if (mReceiverCount++ == 0) {
                mThread.start();
            }
        }
    }
    };

   /**
     * MidiDevice should only be instantiated by MidiManager or MidiService
     * @hide
     */
    public MidiDevice(MidiDeviceInfo deviceInfo, ParcelFileDescriptor pfd) {
        mDeviceInfo = deviceInfo;
        mParcelFileDescriptor = pfd;
    /* package */ void disconnect(MidiReceiver receiver, int portNumber) {
        synchronized (mReceivers) {
            ArrayList<MidiReceiver> receivers = mReceivers[portNumber];
            if (receivers != null && receivers.remove(receiver)) {
                mReceiverCount--;
            }
        }
    }

    public boolean open() {
    /* package */ boolean open() {
        FileDescriptor fd = mParcelFileDescriptor.getFileDescriptor();
        try {
            mInputStream = new FileInputStream(fd);
@@ -187,16 +221,6 @@ public final class MidiDevice implements Parcelable {
        return mDeviceInfo;
    }

    // returns our MidiReceiver, which clients can use for sending events to this device.
    public MidiReceiver getReceiver() {
        return mReceiver;
    }

    // Returns our MidiSender object, to which clients can attach MidiReceivers.
    public MidiSender getSender() {
        return mSender;
    }

    @Override
    public String toString() {
        return ("MidiDevice: " + mDeviceInfo.toString() + " fd: " + mParcelFileDescriptor);
@@ -236,7 +260,7 @@ public final class MidiDevice implements Parcelable {
     * @hide
     */
    public static int packMessage(byte[] message, int offset, int size, long timestamp,
            byte[] dest) {
            int portNumber, byte[] dest) {
        // pack variable length message first
        System.arraycopy(message, offset, dest, 0, size);
        int destOffset = size;
@@ -245,6 +269,9 @@ public final class MidiDevice implements Parcelable {
            dest[destOffset++] = (byte)timestamp;
            timestamp >>= 8;
        }
        // portNumber is last
        dest[destOffset++] = (byte)portNumber;

        return destOffset;
    }

@@ -266,8 +293,8 @@ public final class MidiDevice implements Parcelable {
     * @hide
     */
    public static int getMessageSize(byte[] buffer, int bufferLength) {
        // message length is total buffer length minus size of the timestamp
        return bufferLength - 8;
        // message length is total buffer length minus size of the timestamp and port number
        return bufferLength - 9 /* (sizeof(timestamp) + sizeof(portNumber)) */;
    }

    /**
@@ -289,4 +316,16 @@ public final class MidiDevice implements Parcelable {
        }
        return timestamp;
     }

    /**
     * Utility function for unpacking a MIDI message to be sent through our ParcelFileDescriptor
     * unpacks port number from packed buffer
     *
     * @hide
     */
    public static int getMessagePortNumber(byte[] buffer, int bufferLength) {
        // timestamp follows variable length message data and timestamp
        int dataLength = getMessageSize(buffer, bufferLength);
        return buffer[dataLength + 8 /* sizeof(timestamp) */];
     }
}
+35 −5
Original line number Diff line number Diff line
@@ -39,6 +39,8 @@ public class MidiDeviceInfo implements Parcelable {

    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;

    // used for USB devices only
@@ -77,9 +79,12 @@ public class MidiDeviceInfo implements Parcelable {
     * MidiDeviceInfo should only be instantiated by MidiService implementation
     * @hide
     */
    public MidiDeviceInfo(int type, int id, Bundle properties) {
    public MidiDeviceInfo(int type, int id, int numInputPorts, int numOutputPorts,
            Bundle properties) {
        mType = type;
        mId = id;
        mInputPortCount = numInputPorts;
        mOutputPortCount = numOutputPorts;
        mProperties = properties;
        mAlsaCard = -1;
        mAlsaDevice = -1;
@@ -89,10 +94,12 @@ public class MidiDeviceInfo implements Parcelable {
     * MidiDeviceInfo should only be instantiated by MidiService implementation
     * @hide
     */
    public MidiDeviceInfo(int type, int id, Bundle properties,
            int alsaCard, int alsaDevice) {
    public MidiDeviceInfo(int type, int id, int numInputPorts, int numOutputPorts,
            Bundle properties, int alsaCard, int alsaDevice) {
        mType = type;
        mId = id;
        mInputPortCount = numInputPorts;
        mOutputPortCount = numOutputPorts;
        mProperties = properties;
        mAlsaCard = alsaCard;
        mAlsaDevice = alsaDevice;
@@ -117,6 +124,24 @@ public class MidiDeviceInfo implements Parcelable {
        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.
     *
@@ -157,7 +182,8 @@ public class MidiDeviceInfo implements Parcelable {
    @Override
    public String toString() {
        return ("MidiDeviceInfo[mType=" + mType +
                ",mId=" + mId +
                ",mInputPortCount=" + mInputPortCount +
                ",mOutputPortCount=" + mOutputPortCount +
                ",mProperties=" + mProperties +
                ",mAlsaCard=" + mAlsaCard +
                ",mAlsaDevice=" + mAlsaDevice);
@@ -168,10 +194,12 @@ public class MidiDeviceInfo implements Parcelable {
        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();
            int card = in.readInt();
            int device = in.readInt();
            return new MidiDeviceInfo(type, id, properties, card, device);
            return new MidiDeviceInfo(type, id, inputPorts, outputPorts, properties, card, device);
        }

        public MidiDeviceInfo[] newArray(int size) {
@@ -186,6 +214,8 @@ public class MidiDeviceInfo implements Parcelable {
    public void writeToParcel(Parcel parcel, int flags) {
        parcel.writeInt(mType);
        parcel.writeInt(mId);
        parcel.writeInt(mInputPortCount);
        parcel.writeInt(mOutputPortCount);
        parcel.writeBundle(mProperties);
        parcel.writeInt(mAlsaCard);
        parcel.writeInt(mAlsaDevice);
+53 −0
Original line number Diff line number Diff line
/*
 * 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.midi;

import java.io.FileOutputStream;
import java.io.IOException;

/**
 * This class is used for sending data to a port on a MIDI device
 *
 * @hide
 */
public final 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[MidiDevice.MAX_PACKED_MESSAGE_SIZE];

  /* package */ MidiInputPort(FileOutputStream outputStream, int portNumber) {
        super(portNumber);
        mOutputStream = outputStream;
    }

    /**
     * Writes a MIDI message to the input port
     *
     * @param msg message bytes
     * @param offset offset of first byte of the message in msg array
     * @param count size of the message in bytes
     * @param timestamp future time to post the message
     */
    public void onPost(byte[] msg, int offset, int count, long timestamp) throws IOException {
        synchronized (mBuffer) {
            int length = MidiDevice.packMessage(msg, offset, count, timestamp, mPortNumber,
                    mBuffer);
            mOutputStream.write(mBuffer, 0, length);
        }
    }
}
+4 −2
Original line number Diff line number Diff line
@@ -132,9 +132,11 @@ public class MidiManager {

    // Use this if you want to register and implement a virtual device.
    // The MidiDevice returned by this method is the proxy you use to implement the device.
    public MidiDevice createVirtualDevice(Bundle properties) {
    public MidiDevice createVirtualDevice(int numInputPorts, int numOutputPorts,
            Bundle properties) {
        try {
            MidiDevice device = mService.registerVirtualDevice(mToken, properties);
            MidiDevice device = mService.registerVirtualDevice(mToken,
                    numInputPorts, numOutputPorts, properties);
            if (device != null && !device.open()) {
                device = null;
            }
Loading