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

Commit 2776133b authored by Mike Lockwood's avatar Mike Lockwood
Browse files

Add event scheduling support to USB MIDI Manager code

Change-Id: I9fdaaac35c296acc67ee0cf346b0c7b56bf58393
parent 607f1f09
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();
        }