diff options
-rw-r--r-- | api/current.txt | 1 | ||||
-rw-r--r-- | api/system-current.txt | 1 | ||||
-rw-r--r-- | core/java/com/android/internal/midi/EventScheduler.java | 11 | ||||
-rw-r--r-- | core/java/com/android/internal/midi/MidiDispatcher.java | 7 | ||||
-rw-r--r-- | core/java/com/android/internal/midi/MidiEventScheduler.java | 37 | ||||
-rw-r--r-- | media/java/android/media/midi/MidiInputPort.java | 13 | ||||
-rw-r--r-- | media/java/android/media/midi/MidiOutputPort.java | 24 | ||||
-rw-r--r-- | media/java/android/media/midi/MidiPortImpl.java | 70 | ||||
-rw-r--r-- | media/java/android/media/midi/MidiReceiver.java | 7 | ||||
-rw-r--r-- | services/usb/java/com/android/server/usb/UsbMidiDevice.java | 66 |
10 files changed, 152 insertions, 85 deletions
diff --git a/api/current.txt b/api/current.txt index d3ae427..90020d1 100644 --- a/api/current.txt +++ b/api/current.txt @@ -16847,6 +16847,7 @@ package android.media.midi { public abstract class MidiReceiver { ctor public MidiReceiver(); + method public void flush() throws java.io.IOException; method public int getMaxMessageSize(); method public abstract void onReceive(byte[], int, int, long) throws java.io.IOException; method public void send(byte[], int, int) throws java.io.IOException; diff --git a/api/system-current.txt b/api/system-current.txt index 431809e..b13ecd3 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -18122,6 +18122,7 @@ package android.media.midi { public abstract class MidiReceiver { ctor public MidiReceiver(); + method public void flush() throws java.io.IOException; method public int getMaxMessageSize(); method public abstract void onReceive(byte[], int, int, long) throws java.io.IOException; method public void send(byte[], int, int) throws java.io.IOException; diff --git a/core/java/com/android/internal/midi/EventScheduler.java b/core/java/com/android/internal/midi/EventScheduler.java index 7b9a48c..506902f 100644 --- a/core/java/com/android/internal/midi/EventScheduler.java +++ b/core/java/com/android/internal/midi/EventScheduler.java @@ -16,6 +16,7 @@ package com.android.internal.midi; +import java.util.Iterator; import java.util.SortedMap; import java.util.TreeMap; @@ -28,7 +29,7 @@ public class EventScheduler { private static final long NANOS_PER_MILLI = 1000000; private final Object mLock = new Object(); - private SortedMap<Long, FastEventQueue> mEventBuffer; + volatile private SortedMap<Long, FastEventQueue> mEventBuffer; private FastEventQueue mEventPool = null; private int mMaxPoolSize = 200; private boolean mClosed; @@ -68,6 +69,7 @@ public class EventScheduler { mEventsRemoved++; SchedulableEvent event = mFirst; mFirst = event.mNext; + event.mNext = null; return event; } @@ -87,7 +89,7 @@ public class EventScheduler { */ public static class SchedulableEvent { private long mTimestamp; - private SchedulableEvent mNext = null; + volatile private SchedulableEvent mNext = null; /** * @param timestamp @@ -235,6 +237,11 @@ public class EventScheduler { return event; } + protected void flush() { + // Replace our event buffer with a fresh empty one + mEventBuffer = new TreeMap<Long, FastEventQueue>(); + } + public void close() { synchronized (mLock) { mClosed = true; diff --git a/core/java/com/android/internal/midi/MidiDispatcher.java b/core/java/com/android/internal/midi/MidiDispatcher.java index 377bc68..70e699a 100644 --- a/core/java/com/android/internal/midi/MidiDispatcher.java +++ b/core/java/com/android/internal/midi/MidiDispatcher.java @@ -83,4 +83,11 @@ public final class MidiDispatcher extends MidiReceiver { } } } + + @Override + public void flush() throws IOException { + for (MidiReceiver receiver : mReceivers) { + receiver.flush(); + } + } } diff --git a/core/java/com/android/internal/midi/MidiEventScheduler.java b/core/java/com/android/internal/midi/MidiEventScheduler.java index 42d70f6..4dc5838 100644 --- a/core/java/com/android/internal/midi/MidiEventScheduler.java +++ b/core/java/com/android/internal/midi/MidiEventScheduler.java @@ -28,16 +28,9 @@ 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 final MidiReceiver[] mReceivers; + private MidiReceiver mReceiver = new SchedulingReceiver(); 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. @@ -47,14 +40,17 @@ public class MidiEventScheduler extends EventScheduler { throws IOException { MidiEvent event = createScheduledEvent(msg, offset, count, timestamp); if (event != null) { - event.portNumber = mPortNumber; add(event); } } + + @Override + public void flush() { + MidiEventScheduler.this.flush(); + } } public static class MidiEvent extends SchedulableEvent { - public int portNumber; public int count = 0; public byte[] data; @@ -80,17 +76,6 @@ 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. */ @@ -132,15 +117,7 @@ public class MidiEventScheduler extends EventScheduler { * @return the MidiReceiver */ public MidiReceiver getReceiver() { - return mReceivers[0]; - } - - /** - * This MidiReceiver will write date to the scheduling buffer. - * @return the MidiReceiver - */ - public MidiReceiver getReceiver(int portNumber) { - return mReceivers[portNumber]; + return mReceiver; } } diff --git a/media/java/android/media/midi/MidiInputPort.java b/media/java/android/media/midi/MidiInputPort.java index 1d3b37a..ff16a57 100644 --- a/media/java/android/media/midi/MidiInputPort.java +++ b/media/java/android/media/midi/MidiInputPort.java @@ -83,7 +83,18 @@ public final class MidiInputPort extends MidiReceiver implements Closeable { if (mOutputStream == null) { throw new IOException("MidiInputPort is closed"); } - int length = MidiPortImpl.packMessage(msg, offset, count, timestamp, mBuffer); + int length = MidiPortImpl.packData(msg, offset, count, timestamp, mBuffer); + mOutputStream.write(mBuffer, 0, length); + } + } + + @Override + public void flush() throws IOException { + synchronized (mBuffer) { + if (mOutputStream == null) { + throw new IOException("MidiInputPort is closed"); + } + int length = MidiPortImpl.packFlush(mBuffer); mOutputStream.write(mBuffer, 0, length); } } diff --git a/media/java/android/media/midi/MidiOutputPort.java b/media/java/android/media/midi/MidiOutputPort.java index 0290a76..7491f3c 100644 --- a/media/java/android/media/midi/MidiOutputPort.java +++ b/media/java/android/media/midi/MidiOutputPort.java @@ -62,12 +62,24 @@ public final class MidiOutputPort extends MidiSender implements Closeable { // FIXME - inform receivers here? } - int offset = MidiPortImpl.getMessageOffset(buffer, count); - int size = MidiPortImpl.getMessageSize(buffer, count); - long timestamp = MidiPortImpl.getMessageTimeStamp(buffer, count); - - // dispatch to all our receivers - mDispatcher.sendWithTimestamp(buffer, offset, size, timestamp); + int packetType = MidiPortImpl.getPacketType(buffer, count); + switch (packetType) { + case MidiPortImpl.PACKET_TYPE_DATA: { + int offset = MidiPortImpl.getDataOffset(buffer, count); + int size = MidiPortImpl.getDataSize(buffer, count); + long timestamp = MidiPortImpl.getPacketTimestamp(buffer, count); + + // dispatch to all our receivers + mDispatcher.sendWithTimestamp(buffer, offset, size, timestamp); + break; + } + case MidiPortImpl.PACKET_TYPE_FLUSH: + mDispatcher.flush(); + break; + default: + Log.e(TAG, "Unknown packet type " + packetType); + break; + } } } catch (IOException e) { // FIXME report I/O failure? diff --git a/media/java/android/media/midi/MidiPortImpl.java b/media/java/android/media/midi/MidiPortImpl.java index 5795045..16fc214 100644 --- a/media/java/android/media/midi/MidiPortImpl.java +++ b/media/java/android/media/midi/MidiPortImpl.java @@ -24,6 +24,16 @@ package android.media.midi; private static final String TAG = "MidiPort"; /** + * Packet type for data packet + */ + public static final int PACKET_TYPE_DATA = 1; + + /** + * Packet type for flush packet + */ + public static final int PACKET_TYPE_FLUSH = 2; + + /** * Maximum size of a packet that can pass through our ParcelFileDescriptor. */ public static final int MAX_PACKET_SIZE = 1024; @@ -34,12 +44,17 @@ package android.media.midi; private static final int TIMESTAMP_SIZE = 8; /** + * Data packet overhead is timestamp size plus packet type byte + */ + private static final int DATA_PACKET_OVERHEAD = TIMESTAMP_SIZE + 1; + + /** * 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; + public static final int MAX_PACKET_DATA_SIZE = MAX_PACKET_SIZE - DATA_PACKET_OVERHEAD; /** - * Utility function for packing a MIDI message to be sent through our ParcelFileDescriptor + * Utility function for packing MIDI data to be sent through our ParcelFileDescriptor * * message byte array contains variable length MIDI message. * messageSize is size of variable length MIDI message @@ -47,46 +62,65 @@ package android.media.midi; * dest is buffer to pack into * returns size of packed message */ - public static int packMessage(byte[] message, int offset, int size, long timestamp, + public static int packData(byte[] message, int offset, int size, long timestamp, byte[] dest) { - if (size + TIMESTAMP_SIZE > MAX_PACKET_SIZE) { - size = MAX_PACKET_SIZE - TIMESTAMP_SIZE; + if (size > MAX_PACKET_DATA_SIZE) { + size = MAX_PACKET_DATA_SIZE; } - // message data goes first - System.arraycopy(message, offset, dest, 0, size); + int length = 0; + // packet type goes first + dest[length++] = PACKET_TYPE_DATA; + // data goes next + System.arraycopy(message, offset, dest, length, size); + length += size; // followed by timestamp for (int i = 0; i < TIMESTAMP_SIZE; i++) { - dest[size++] = (byte)timestamp; + dest[length++] = (byte)timestamp; timestamp >>= 8; } - return size; + return length; + } + + /** + * Utility function for packing a flush command to be sent through our ParcelFileDescriptor + */ + public static int packFlush(byte[] dest) { + dest[0] = PACKET_TYPE_FLUSH; + return 1; + } + + /** + * Returns the packet type (PACKET_TYPE_DATA or PACKET_TYPE_FLUSH) + */ + public static int getPacketType(byte[] buffer, int bufferLength) { + return buffer[0]; } /** - * Utility function for unpacking a MIDI message received from our ParcelFileDescriptor + * Utility function for unpacking MIDI data received from our ParcelFileDescriptor * returns the offset of the MIDI message in packed buffer */ - public static int getMessageOffset(byte[] buffer, int bufferLength) { - // message is at the beginning - return 0; + public static int getDataOffset(byte[] buffer, int bufferLength) { + // data follows packet type byte + return 1; } /** - * Utility function for unpacking a MIDI message received from our ParcelFileDescriptor + * Utility function for unpacking MIDI data received from our ParcelFileDescriptor * returns size of MIDI data in packed buffer */ - public static int getMessageSize(byte[] buffer, int bufferLength) { + public static int getDataSize(byte[] buffer, int bufferLength) { // message length is total buffer length minus size of the timestamp - return bufferLength - TIMESTAMP_SIZE; + return bufferLength - DATA_PACKET_OVERHEAD; } /** - * Utility function for unpacking a MIDI message received from our ParcelFileDescriptor + * Utility function for unpacking MIDI data received from our ParcelFileDescriptor * unpacks timestamp from packed buffer */ - public static long getMessageTimeStamp(byte[] buffer, int bufferLength) { + public static long getPacketTimestamp(byte[] buffer, int bufferLength) { // timestamp is at end of the packet int offset = bufferLength; long timestamp = 0; diff --git a/media/java/android/media/midi/MidiReceiver.java b/media/java/android/media/midi/MidiReceiver.java index 6f4c266..d069075 100644 --- a/media/java/android/media/midi/MidiReceiver.java +++ b/media/java/android/media/midi/MidiReceiver.java @@ -42,6 +42,13 @@ abstract public class MidiReceiver { throws IOException; /** + * Instructs the receiver to discard all pending events. + * @throws IOException + */ + public void flush() throws IOException { + } + + /** * Returns the maximum size of a message this receiver can receive. * Defaults to {@link java.lang.Integer#MAX_VALUE} unless overridden. * @return maximum message size diff --git a/services/usb/java/com/android/server/usb/UsbMidiDevice.java b/services/usb/java/com/android/server/usb/UsbMidiDevice.java index 6ece888..671cf01 100644 --- a/services/usb/java/com/android/server/usb/UsbMidiDevice.java +++ b/services/usb/java/com/android/server/usb/UsbMidiDevice.java @@ -45,7 +45,8 @@ public final class UsbMidiDevice implements Closeable { private MidiDeviceServer mServer; - private final MidiEventScheduler mEventScheduler; + // event schedulers for each output port + private final MidiEventScheduler[] mEventSchedulers; private static final int BUFFER_SIZE = 512; @@ -99,10 +100,11 @@ public final class UsbMidiDevice implements Closeable { } mOutputStreams = new FileOutputStream[outputCount]; + mEventSchedulers = new MidiEventScheduler[outputCount]; for (int i = 0; i < outputCount; i++) { mOutputStreams[i] = new FileOutputStream(fileDescriptors[i]); + mEventSchedulers[i] = new MidiEventScheduler(); } - mEventScheduler = new MidiEventScheduler(inputCount); } private boolean register(Context context, Bundle properties) { @@ -116,7 +118,7 @@ public final class UsbMidiDevice implements Closeable { int outputCount = mOutputStreams.length; MidiReceiver[] inputPortReceivers = new MidiReceiver[inputCount]; for (int port = 0; port < inputCount; port++) { - inputPortReceivers[port] = mEventScheduler.getReceiver(port); + inputPortReceivers[port] = mEventSchedulers[port].getReceiver(); } mServer = midiManager.createDeviceServer(inputPortReceivers, outputCount, @@ -126,7 +128,7 @@ public final class UsbMidiDevice implements Closeable { } final MidiReceiver[] outputReceivers = mServer.getOutputPortReceivers(); - // Create input thread + // Create input thread which will read from all input ports new Thread("UsbMidiDevice input thread") { @Override public void run() { @@ -161,38 +163,46 @@ public final class UsbMidiDevice implements Closeable { } }.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); + // Create output thread for each output port + for (int port = 0; port < outputCount; port++) { + final MidiEventScheduler eventSchedulerF = mEventSchedulers[port]; + final FileOutputStream outputStreamF = mOutputStreams[port]; + final int portF = port; + + new Thread("UsbMidiDevice output thread " + port) { + @Override + public void run() { + while (true) { + MidiEvent event; + try { + event = (MidiEvent)eventSchedulerF.waitNextEvent(); + } catch (InterruptedException e) { + // try again + continue; + } + if (event == null) { + break; + } + try { + outputStreamF.write(event.data, 0, event.count); + } catch (IOException e) { + Log.e(TAG, "write failed for port " + portF); + } + eventSchedulerF.addEventToPool(event); } - mEventScheduler.addEventToPool(event); + Log.d(TAG, "output thread exit"); } - Log.d(TAG, "output thread exit"); - } - }.start(); + }.start(); + } return true; } @Override public void close() throws IOException { - mEventScheduler.close(); + for (int i = 0; i < mEventSchedulers.length; i++) { + mEventSchedulers[i].close(); + } if (mServer != null) { mServer.close(); |