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

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

Merge "Add event scheduling support to USB MIDI Manager code"

parents 3c0439be 2776133b
Loading
Loading
Loading
Loading
+16 −8
Original line number Diff line number Diff line
@@ -27,10 +27,11 @@ import java.util.TreeMap;
public class EventScheduler {
    private static final long NANOS_PER_MILLI = 1000000;

    private final Object lock = new Object();
    private final Object mLock = new Object();
    private SortedMap<Long, FastEventQueue> mEventBuffer;
    private FastEventQueue mEventPool = null;
    private int mMaxPoolSize = 200;
    private boolean mClosed;

    public EventScheduler() {
        mEventBuffer = new TreeMap<Long, FastEventQueue>();
@@ -146,7 +147,7 @@ public class EventScheduler {
     * @param event
     */
    public void add(SchedulableEvent event) {
        synchronized (lock) {
        synchronized (mLock) {
            FastEventQueue list = mEventBuffer.get(event.getTimestamp());
            if (list == null) {
                long lowestTime = mEventBuffer.isEmpty() ? Long.MAX_VALUE
@@ -156,7 +157,7 @@ public class EventScheduler {
                // If the event we added is earlier than the previous earliest
                // event then notify any threads waiting for the next event.
                if (event.getTimestamp() < lowestTime) {
                    lock.notify();
                    mLock.notify();
                }
            } else {
                list.add(event);
@@ -183,7 +184,7 @@ public class EventScheduler {
     */
    public SchedulableEvent getNextEvent(long time) {
        SchedulableEvent event = null;
        synchronized (lock) {
        synchronized (mLock) {
            if (!mEventBuffer.isEmpty()) {
                long lowestTime = mEventBuffer.firstKey();
                // Is it time for this list to be processed?
@@ -206,9 +207,9 @@ public class EventScheduler {
     */
    public SchedulableEvent waitNextEvent() throws InterruptedException {
        SchedulableEvent event = null;
        while (true) {
        synchronized (mLock) {
            while (!mClosed) {
                long millisToWait = Integer.MAX_VALUE;
            synchronized (lock) {
                if (!mEventBuffer.isEmpty()) {
                    long now = System.nanoTime();
                    long lowestTime = mEventBuffer.firstKey();
@@ -228,9 +229,16 @@ public class EventScheduler {
                        }
                    }
                }
                lock.wait((int) millisToWait);
                mLock.wait((int) millisToWait);
            }
        }
        return event;
    }

    public void close() {
        synchronized (mLock) {
            mClosed = true;
            mLock.notify();
        }
    }
}
+31 −4
Original line number Diff line number Diff line
@@ -28,10 +28,16 @@ public class MidiEventScheduler extends EventScheduler {
    // Maintain a pool of scheduled events to reduce memory allocation.
    // This pool increases performance by about 14%.
    private final static int POOL_EVENT_SIZE = 16;
    private MidiReceiver mReceiver = new SchedulingReceiver();

    private class SchedulingReceiver extends MidiReceiver
    {
    private final MidiReceiver[] mReceivers;

    private class SchedulingReceiver extends MidiReceiver {
        private final int mPortNumber;

        public SchedulingReceiver(int portNumber) {
            mPortNumber = portNumber;
        }

        /**
         * Store these bytes in the EventScheduler to be delivered at the specified
         * time.
@@ -41,12 +47,14 @@ public class MidiEventScheduler extends EventScheduler {
                throws IOException {
            MidiEvent event = createScheduledEvent(msg, offset, count, timestamp);
            if (event != null) {
                event.portNumber = mPortNumber;
                add(event);
            }
        }
    }

    public static class MidiEvent extends SchedulableEvent {
        public int portNumber;
        public int count = 0;
        public byte[] data;

@@ -72,6 +80,17 @@ public class MidiEventScheduler extends EventScheduler {
        }
    }

    public MidiEventScheduler() {
        this(0);
    }

    public MidiEventScheduler(int portCount) {
        mReceivers = new MidiReceiver[portCount];
        for (int i = 0; i < portCount; i++) {
            mReceivers[i] = new SchedulingReceiver(i);
        }
    }

    /**
     * Create an event that contains the message.
     */
@@ -113,7 +132,15 @@ public class MidiEventScheduler extends EventScheduler {
     * @return the MidiReceiver
     */
    public MidiReceiver getReceiver() {
        return mReceiver;
        return mReceivers[0];
    }

    /**
     * This MidiReceiver will write date to the scheduling buffer.
     * @return the MidiReceiver
     */
    public MidiReceiver getReceiver(int portNumber) {
        return mReceivers[portNumber];
    }

}
+43 −17
Original line number Diff line number Diff line
@@ -29,6 +29,9 @@ import android.system.OsConstants;
import android.system.StructPollfd;
import android.util.Log;

import com.android.internal.midi.MidiEventScheduler;
import com.android.internal.midi.MidiEventScheduler.MidiEvent;

import libcore.io.IoUtils;

import java.io.Closeable;
@@ -42,7 +45,7 @@ public final class UsbMidiDevice implements Closeable {

    private MidiDeviceServer mServer;

    private final MidiReceiver[] mInputPortReceivers;
    private final MidiEventScheduler mEventScheduler;

    private static final int BUFFER_SIZE = 512;

@@ -99,19 +102,7 @@ public final class UsbMidiDevice implements Closeable {
        for (int i = 0; i < outputCount; i++) {
            mOutputStreams[i] = new FileOutputStream(fileDescriptors[i]);
        }

        mInputPortReceivers = new MidiReceiver[inputCount];
        for (int port = 0; port < inputCount; port++) {
            final int portF = port;
            mInputPortReceivers[port] = new MidiReceiver() {
                @Override
                public void onReceive(byte[] data, int offset, int count, long timestamp)
                        throws IOException {
                    // FIXME - timestamps are ignored, future posting not supported yet.
                    mOutputStreams[portF].write(data, offset, count);
                }
            };
        }
        mEventScheduler = new MidiEventScheduler(inputCount);
    }

    private boolean register(Context context, Bundle properties) {
@@ -121,16 +112,22 @@ public final class UsbMidiDevice implements Closeable {
            return false;
        }

        int inputCount = mInputStreams.length;
        int outputCount = mOutputStreams.length;
        mServer = midiManager.createDeviceServer(mInputPortReceivers, outputCount,
        MidiReceiver[] inputPortReceivers = new MidiReceiver[inputCount];
        for (int port = 0; port < inputCount; port++) {
            inputPortReceivers[port] = mEventScheduler.getReceiver(port);
        }

        mServer = midiManager.createDeviceServer(inputPortReceivers, outputCount,
                null, null, properties, MidiDeviceInfo.TYPE_USB, null);
        if (mServer == null) {
            return false;
        }
        final MidiReceiver[] outputReceivers = mServer.getOutputPortReceivers();

        // FIXME can we only run this when we have a dispatcher that has listeners?
        new Thread() {
        // Create input thread
        new Thread("UsbMidiDevice input thread") {
            @Override
            public void run() {
                byte[] buffer = new byte[BUFFER_SIZE];
@@ -160,6 +157,33 @@ public final class UsbMidiDevice implements Closeable {
                } catch (ErrnoException e) {
                    Log.d(TAG, "reader thread exiting");
                }
                Log.d(TAG, "input thread exit");
            }
        }.start();

        // Create output thread
        new Thread("UsbMidiDevice output thread") {
            @Override
            public void run() {
                while (true) {
                    MidiEvent event;
                    try {
                        event = (MidiEvent)mEventScheduler.waitNextEvent();
                    } catch (InterruptedException e) {
                        // try again
                        continue;
                    }
                    if (event == null) {
                        break;
                    }
                    try {
                        mOutputStreams[event.portNumber].write(event.data, 0, event.count);
                    } catch (IOException e) {
                        Log.e(TAG, "write failed for port " + event.portNumber);
                    }
                    mEventScheduler.addEventToPool(event);
                }
                Log.d(TAG, "output thread exit");
            }
        }.start();

@@ -168,6 +192,8 @@ public final class UsbMidiDevice implements Closeable {

    @Override
    public void close() throws IOException {
        mEventScheduler.close();

        if (mServer != null) {
            mServer.close();
        }