diff options
-rw-r--r-- | core/java/android/midi/MidiInputPort.java | 15 | ||||
-rw-r--r-- | core/java/android/midi/MidiOutputPort.java | 6 | ||||
-rw-r--r-- | core/java/android/midi/MidiPort.java | 56 | ||||
-rw-r--r-- | core/java/android/midi/MidiReceiver.java | 2 | ||||
-rw-r--r-- | core/java/android/midi/MidiUtils.java | 65 | ||||
-rw-r--r-- | services/usb/java/com/android/server/usb/UsbMidiDevice.java | 31 |
6 files changed, 53 insertions, 122 deletions
diff --git a/core/java/android/midi/MidiInputPort.java b/core/java/android/midi/MidiInputPort.java index 5d806cf..88ace5f 100644 --- a/core/java/android/midi/MidiInputPort.java +++ b/core/java/android/midi/MidiInputPort.java @@ -33,7 +33,7 @@ public 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[MAX_PACKED_MESSAGE_SIZE]; + private final byte[] mBuffer = new byte[MAX_PACKET_SIZE]; /* package */ MidiInputPort(ParcelFileDescriptor pfd, int portNumber) { super(portNumber); @@ -50,10 +50,19 @@ public class MidiInputPort extends MidiPort implements MidiReceiver { * {@link java.lang.System#nanoTime} */ public void onPost(byte[] msg, int offset, int count, long timestamp) throws IOException { + assert(offset >= 0 && count >= 0 && offset + count <= msg.length); + synchronized (mBuffer) { - int length = packMessage(msg, offset, count, timestamp, mBuffer); try { - mOutputStream.write(mBuffer, 0, length); + while (count > 0) { + int length = packMessage(msg, offset, count, timestamp, mBuffer); + mOutputStream.write(mBuffer, 0, length); + int sent = getMessageSize(mBuffer, length); + assert(sent >= 0 && sent <= length); + + offset += sent; + count -= sent; + } } catch (IOException e) { IoUtils.closeQuietly(mOutputStream); // report I/O failure diff --git a/core/java/android/midi/MidiOutputPort.java b/core/java/android/midi/MidiOutputPort.java index b550ed4..00b7bad 100644 --- a/core/java/android/midi/MidiOutputPort.java +++ b/core/java/android/midi/MidiOutputPort.java @@ -45,7 +45,7 @@ public class MidiOutputPort extends MidiPort implements MidiSender { private final Thread mThread = new Thread() { @Override public void run() { - byte[] buffer = new byte[MAX_PACKED_MESSAGE_SIZE]; + byte[] buffer = new byte[MAX_PACKET_SIZE]; ArrayList<MidiReceiver> deadReceivers = new ArrayList<MidiReceiver>(); try { @@ -54,9 +54,6 @@ public class MidiOutputPort extends MidiPort implements MidiSender { int count = mInputStream.read(buffer); if (count < 0) { break; - } else if (count < MIN_PACKED_MESSAGE_SIZE || count > MAX_PACKED_MESSAGE_SIZE) { - Log.e(TAG, "Number of bytes read out of range: " + count); - continue; } int offset = getMessageOffset(buffer, count); @@ -96,7 +93,6 @@ public class MidiOutputPort extends MidiPort implements MidiSender { } }; - /* package */ MidiOutputPort(ParcelFileDescriptor pfd, int portNumber) { super(portNumber); mInputStream = new ParcelFileDescriptor.AutoCloseInputStream(pfd); diff --git a/core/java/android/midi/MidiPort.java b/core/java/android/midi/MidiPort.java index 1fa7946..44d1a88 100644 --- a/core/java/android/midi/MidiPort.java +++ b/core/java/android/midi/MidiPort.java @@ -32,16 +32,19 @@ abstract public class MidiPort implements Closeable { private final int mPortNumber; /** - * Minimum size of packed message as sent through our ParcelFileDescriptor - * 8 bytes for timestamp and 1 to 3 bytes for message + * Maximum size of a packet that can pass through our ParcelFileDescriptor */ - protected static final int MIN_PACKED_MESSAGE_SIZE = 9; + protected static final int MAX_PACKET_SIZE = 1024; /** - * Maximum size of packed message as sent through our ParcelFileDescriptor - * 8 bytes for timestamp and 1 to 3 bytes for message + * size of message timestamp in bytes */ - protected static final int MAX_PACKED_MESSAGE_SIZE = 11; + private static final int TIMESTAMP_SIZE = 8; + + /** + * 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; /* package */ MidiPort(int portNumber) { @@ -76,49 +79,52 @@ abstract public class MidiPort implements Closeable { */ protected static int packMessage(byte[] message, int offset, int size, long timestamp, byte[] dest) { - // pack variable length message first + if (size + TIMESTAMP_SIZE > MAX_PACKET_SIZE) { + size = MAX_PACKET_SIZE - TIMESTAMP_SIZE; + } + // message data goes first System.arraycopy(message, offset, dest, 0, size); - int destOffset = size; - // timestamp takes 8 bytes - for (int i = 0; i < 8; i++) { - dest[destOffset++] = (byte)timestamp; + + // followed by timestamp + for (int i = 0; i < TIMESTAMP_SIZE; i++) { + dest[size++] = (byte)timestamp; timestamp >>= 8; } - return destOffset; + return size; } /** - * Utility function for unpacking a MIDI message to be sent through our ParcelFileDescriptor - * returns the offet of of MIDI message in packed buffer + * Utility function for unpacking a MIDI message received from our ParcelFileDescriptor + * returns the offset of the MIDI message in packed buffer */ protected static int getMessageOffset(byte[] buffer, int bufferLength) { - // message is at start of buffer + // message is at the beginning return 0; } /** - * Utility function for unpacking a MIDI message to be sent through our ParcelFileDescriptor - * returns size of MIDI message in packed buffer + * Utility function for unpacking a MIDI message received from our ParcelFileDescriptor + * returns size of MIDI data in packed buffer */ protected static int getMessageSize(byte[] buffer, int bufferLength) { - // message length is total buffer length minus size of the timestamp and port number - return bufferLength - 8 /* sizeof(timestamp) */; + // message length is total buffer length minus size of the timestamp + return bufferLength - TIMESTAMP_SIZE; } /** - * Utility function for unpacking a MIDI message to be sent through our ParcelFileDescriptor + * Utility function for unpacking a MIDI message received from our ParcelFileDescriptor * unpacks timestamp from packed buffer */ protected static long getMessageTimeStamp(byte[] buffer, int bufferLength) { + // timestamp is at end of the packet + int offset = bufferLength; long timestamp = 0; - // timestamp follows variable length message data - int dataLength = getMessageSize(buffer, bufferLength); - for (int i = dataLength + 7; i >= dataLength; i--) { - int b = (int)buffer[i] & 0xFF; + for (int i = 0; i < TIMESTAMP_SIZE; i++) { + int b = (int)buffer[--offset] & 0xFF; timestamp = (timestamp << 8) | b; } return timestamp; - } + } } diff --git a/core/java/android/midi/MidiReceiver.java b/core/java/android/midi/MidiReceiver.java index 0b183cc..a4e1a10 100644 --- a/core/java/android/midi/MidiReceiver.java +++ b/core/java/android/midi/MidiReceiver.java @@ -30,6 +30,8 @@ public interface MidiReceiver { * NOTE: the msg array parameter is only valid within the context of this call. * The msg bytes should be copied by the receiver rather than retaining a reference * to this parameter. + * Also, modifying the contents of the msg array parameter may result in other receivers + * in the same application receiving incorrect values in their onPost() method. * * @param msg a byte array containing the MIDI message * @param offset the offset of the first byte of the message in the byte array diff --git a/core/java/android/midi/MidiUtils.java b/core/java/android/midi/MidiUtils.java deleted file mode 100644 index e60e2db..0000000 --- a/core/java/android/midi/MidiUtils.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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 android.util.Log; - -/** - * Class containing miscellaneous MIDI utilities. - * - * @hide - */ -public final class MidiUtils { - private static final String TAG = "MidiUtils"; - - private MidiUtils() { } - - /** - * Returns data size of a MIDI message based on the message's command byte - * @param b the message command byte - * @return the message's data length - */ - public static int getMessageDataSize(byte b) { - switch (b & 0xF0) { - case 0x80: - case 0x90: - case 0xA0: - case 0xB0: - case 0xE0: - return 2; - case 0xC0: - case 0xD0: - return 1; - case 0xF0: - switch (b & 0x0F) { - case 0x00: - Log.e(TAG, "System Exclusive not supported yet"); - return -1; - case 0x01: - case 0x03: - return 1; - case 0x02: - return 2; - default: - return 0; - } - default: - Log.e(TAG, "unknown MIDI command " + b); - return -1; - } - } -} diff --git a/services/usb/java/com/android/server/usb/UsbMidiDevice.java b/services/usb/java/com/android/server/usb/UsbMidiDevice.java index 01b2df9..0ca52f6 100644 --- a/services/usb/java/com/android/server/usb/UsbMidiDevice.java +++ b/services/usb/java/com/android/server/usb/UsbMidiDevice.java @@ -21,9 +21,9 @@ import android.hardware.usb.UsbDevice; import android.midi.MidiDeviceInfo; import android.midi.MidiDeviceServer; import android.midi.MidiManager; +import android.midi.MidiPort; import android.midi.MidiReceiver; import android.midi.MidiSender; -import android.midi.MidiUtils; import android.os.Bundle; import android.system.ErrnoException; import android.system.Os; @@ -45,7 +45,9 @@ public final class UsbMidiDevice implements Closeable { // for polling multiple FileDescriptors for MIDI events private final StructPollfd[] mPollFDs; + // streams for reading from ALSA driver private final FileInputStream[] mInputStreams; + // streams for writing to ALSA driver private final FileOutputStream[] mOutputStreams; public static UsbMidiDevice create(Context context, UsbDevice usbDevice, int card, int device) { @@ -131,7 +133,7 @@ public final class UsbMidiDevice implements Closeable { new Thread() { @Override public void run() { - byte[] buffer = new byte[3]; + byte[] buffer = new byte[MidiPort.MAX_PACKET_DATA_SIZE]; try { boolean done = false; while (!done) { @@ -141,7 +143,8 @@ public final class UsbMidiDevice implements Closeable { if ((pfd.revents & OsConstants.POLLIN) != 0) { // clear readable flag pfd.revents = 0; - int count = readMessage(buffer, index); + + int count = mInputStreams[index].read(buffer); mOutputPortReceivers[index].onPost(buffer, 0, count, System.nanoTime()); } else if ((pfd.revents & (OsConstants.POLLERR @@ -150,7 +153,7 @@ public final class UsbMidiDevice implements Closeable { } } - // poll if none are readable + // wait until we have a readable port Os.poll(mPollFDs, -1 /* infinite timeout */); } } catch (IOException e) { @@ -174,26 +177,6 @@ public final class UsbMidiDevice implements Closeable { } } - private int readMessage(byte[] buffer, int index) throws IOException { - FileInputStream inputStream = mInputStreams[index]; - - if (inputStream.read(buffer, 0, 1) != 1) { - Log.e(TAG, "could not read command byte"); - return -1; - } - int dataSize = MidiUtils.getMessageDataSize(buffer[0]); - if (dataSize < 0) { - return -1; - } - if (dataSize > 0) { - if (inputStream.read(buffer, 1, dataSize) != dataSize) { - Log.e(TAG, "could not read command data"); - return -1; - } - } - return dataSize + 1; - } - private static native int nativeGetSubdeviceCount(int card, int device); private static native FileDescriptor[] nativeOpen(int card, int device, int subdeviceCount); } |