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

Commit cb096273 authored by Mike Lockwood's avatar Mike Lockwood
Browse files

MIDI Manager: Remove message packetization from MIDI transport

Instead of attempting to package exactly one MIDI message in each call to
MidiReceiver.onPost(), we now pass messages as a raw stream of bytes.
This means we may now receive multiple MIDI messages in MidiReceiver.onPost().

We make this change to avoid the complexity of taking the single message approach
with SysEx and realtime messages. This shifts some of the burden of parsing
MIDI messages to the client application. But the parsing is best handled in the
application anyway, rather than having the framework impose its own policy
on how the messages are parsed.

Change-Id: Idb6c200037f827cc618bc7d9455a7aa864b494a7
parent ab75f637
Loading
Loading
Loading
Loading
+12 −3
Original line number Diff line number Diff line
@@ -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 {
                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
+1 −5
Original line number Diff line number Diff line
@@ -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);
+31 −25
Original line number Diff line number Diff line
@@ -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,47 +79,50 @@ 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;
+2 −0
Original line number Diff line number Diff line
@@ -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
+0 −65
Original line number Diff line number Diff line
/*
 * 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;
        }
    }
}
Loading