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

Commit 46326e59 authored by Mike Lockwood's avatar Mike Lockwood
Browse files

MidiDevice: Add support for making direct connections between ports

The output port of one device can be connected to the input port of another
device using the new MidiDevice.connectPorts() method.
This allows an application to direct the output of one device directly
to the input port of another without having to copy data from one to another.

Change-Id: I4d361c4e0950b9b9516b0c2f0c158677b1aca208
parent 0c7342f0
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -24,4 +24,7 @@ interface IMidiDeviceServer
    ParcelFileDescriptor openInputPort(IBinder token, int portNumber);
    ParcelFileDescriptor openOutputPort(IBinder token, int portNumber);
    void closePort(IBinder token);

    // connects the input port pfd to the specified output port
    void connectPorts(IBinder token, in ParcelFileDescriptor pfd, int outputPortNumber);
}
+53 −0
Original line number Diff line number Diff line
@@ -26,6 +26,8 @@ import android.util.Log;

import dalvik.system.CloseGuard;

import libcore.io.IoUtils;

import java.io.Closeable;
import java.io.IOException;

@@ -44,8 +46,29 @@ public final class MidiDevice implements Closeable {
    private Context mContext;
    private ServiceConnection mServiceConnection;


    private final CloseGuard mGuard = CloseGuard.get();

    public class MidiConnection implements Closeable {
        private final IBinder mToken;
        private final MidiInputPort mInputPort;

        MidiConnection(IBinder token, MidiInputPort inputPort) {
            mToken = token;
            mInputPort = inputPort;
        }

        @Override
        public void close() throws IOException {
            try {
                mDeviceServer.closePort(mToken);
                IoUtils.closeQuietly(mInputPort);
            } catch (RemoteException e) {
                Log.e(TAG, "RemoteException in MidiConnection.close");
            }
        }
    }

    /* package */ MidiDevice(MidiDeviceInfo deviceInfo, IMidiDeviceServer server) {
        this(deviceInfo, server, null, null);
    }
@@ -108,6 +131,36 @@ public final class MidiDevice implements Closeable {
        }
    }

    /**
     * Connects the supplied {@link MidiInputPort} to the output port of this device
     * with the specified port number. Once the connection is made, the MidiInput port instance
     * can no longer receive data via its {@link MidiReciever.receive} method.
     * This method returns a {@link #MidiConnection} object, which can be used to close the connection
     * @param inputPort the inputPort to connect
     * @param outputPortNumber the port number of the output port to connect inputPort to.
     * @return {@link #MidiConnection} object if the connection is successful, or null in case of failure
     */
    public MidiConnection connectPorts(MidiInputPort inputPort, int outputPortNumber) {
        if (outputPortNumber < 0 || outputPortNumber >= mDeviceInfo.getOutputPortCount()) {
            throw new IllegalArgumentException("outputPortNumber out of range");
        }

        ParcelFileDescriptor pfd = inputPort.claimFileDescriptor();
        if (pfd == null) {
            return null;
        }
         try {
            IBinder token = new Binder();
            mDeviceServer.connectPorts(token, pfd, outputPortNumber);
            // close our copy of the file descriptor
            IoUtils.closeQuietly(pfd);
            return new MidiConnection(token, inputPort);
        } catch (RemoteException e) {
            Log.e(TAG, "RemoteException in connectPorts");
            return null;
        }
    }

    @Override
    public void close() throws IOException {
        synchronized (mGuard) {
+12 −0
Original line number Diff line number Diff line
@@ -200,6 +200,18 @@ public final class MidiDeviceServer implements Closeable {
                }
            }
        }

        @Override
        public void connectPorts(IBinder token, ParcelFileDescriptor pfd,
                int outputPortNumber) {
            MidiInputPort inputPort = new MidiInputPort(pfd, outputPortNumber);
            mOutputPortDispatchers[outputPortNumber].getSender().connect(inputPort);
            mInputPorts.add(inputPort);
            OutputPortClient client = new OutputPortClient(token, inputPort);
            synchronized (mPortClients) {
                mPortClients.put(token, client);
            }
        }
    };

    /* package */ MidiDeviceServer(IMidiManager midiManager, MidiReceiver[] inputPortReceivers,
+30 −3
Original line number Diff line number Diff line
@@ -41,7 +41,8 @@ public final class MidiInputPort extends MidiReceiver implements Closeable {
    private IMidiDeviceServer mDeviceServer;
    private final IBinder mToken;
    private final int mPortNumber;
    private final FileOutputStream mOutputStream;
    private ParcelFileDescriptor mParcelFileDescriptor;
    private FileOutputStream mOutputStream;

    private final CloseGuard mGuard = CloseGuard.get();
    private boolean mIsClosed;
@@ -53,8 +54,9 @@ public final class MidiInputPort extends MidiReceiver implements Closeable {
            ParcelFileDescriptor pfd, int portNumber) {
        mDeviceServer = server;
        mToken = token;
        mParcelFileDescriptor = pfd;
        mPortNumber = portNumber;
        mOutputStream = new ParcelFileDescriptor.AutoCloseOutputStream(pfd);
        mOutputStream = new FileOutputStream(pfd.getFileDescriptor());
        mGuard.open("close");
    }

@@ -89,11 +91,27 @@ public final class MidiInputPort extends MidiReceiver implements Closeable {
        }

        synchronized (mBuffer) {
            if (mOutputStream == null) {
                throw new IOException("MidiInputPort is closed");
            }
            int length = MidiPortImpl.packMessage(msg, offset, count, timestamp, mBuffer);
            mOutputStream.write(mBuffer, 0, length);
        }
    }

    // used by MidiDevice.connectInputPort() to connect our socket directly to another device
    /* package */ ParcelFileDescriptor claimFileDescriptor() {
        synchronized (mBuffer) {
            ParcelFileDescriptor pfd = mParcelFileDescriptor;
            if (pfd != null) {
                IoUtils.closeQuietly(mOutputStream);
                mParcelFileDescriptor = null;
                mOutputStream = null;
            }
            return pfd;
        }
    }

    @Override
    public int getMaxMessageSize() {
        return MidiPortImpl.MAX_PACKET_DATA_SIZE;
@@ -104,7 +122,16 @@ public final class MidiInputPort extends MidiReceiver implements Closeable {
        synchronized (mGuard) {
            if (mIsClosed) return;
            mGuard.close();
            synchronized (mBuffer) {
                if (mParcelFileDescriptor != null) {
                    mParcelFileDescriptor.close();
                    mParcelFileDescriptor = null;
                }
                if (mOutputStream != null) {
                    mOutputStream.close();
                    mOutputStream = null;
                }
            }
            if (mDeviceServer != null) {
                try {
                    mDeviceServer.closePort(mToken);