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

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

Merge "BluetoothMidiService: Add support for sending SysEx messages that span...

Merge "BluetoothMidiService: Add support for sending SysEx messages that span multiple Bluetooth packets" into mnc-dev
parents 9eb25fad 8c26d843
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -87,13 +87,13 @@ public final class MidiConstants {
    }

    // Returns true if this command can be used for running status
    public static boolean allowRunningStatus(int command) {
    public static boolean allowRunningStatus(byte command) {
        // only Channel Voice and Channel Mode commands can use running status
        return (command >= STATUS_NOTE_OFF && command < STATUS_SYSTEM_EXCLUSIVE);
    }

    // Returns true if this command cancels running status
    public static boolean cancelsRunningStatus(int command) {
    public static boolean cancelsRunningStatus(byte command) {
        // System Common messages cancel running status
        return (command >= STATUS_SYSTEM_EXCLUSIVE && command <= STATUS_END_SYSEX);
    }
+10 −12
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ import java.util.UUID;
public final class BluetoothMidiDevice {

    private static final String TAG = "BluetoothMidiDevice";
    private static final boolean DEBUG = false;

    private static final int MAX_PACKET_SIZE = 20;

@@ -152,8 +153,10 @@ public final class BluetoothMidiDevice {
        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt,
                                            BluetoothGattCharacteristic characteristic) {
//            logByteArray("Received ", characteristic.getValue(), 0,
//                    characteristic.getValue().length);
            if (DEBUG) {
                logByteArray("Received ", characteristic.getValue(), 0,
                        characteristic.getValue().length);
            }
            mPacketDecoder.decodePacket(characteristic.getValue(), mOutputReceiver);
        }
    };
@@ -182,8 +185,10 @@ public final class BluetoothMidiDevice {
            byte[] writeBuffer = mWriteBuffers[count];
            System.arraycopy(buffer, 0, writeBuffer, 0, count);
            mCharacteristic.setValue(writeBuffer);
//            logByteArray("Sent ", mCharacteristic.getValue(), 0,
//                    mCharacteristic.getValue().length);
            if (DEBUG) {
                logByteArray("Sent ", mCharacteristic.getValue(), 0,
                       mCharacteristic.getValue().length);
            }
            mBluetoothGatt.writeCharacteristic(mCharacteristic);
        }
    }
@@ -259,14 +264,7 @@ public final class BluetoothMidiDevice {
    private static void logByteArray(String prefix, byte[] value, int offset, int count) {
        StringBuilder builder = new StringBuilder(prefix);
        for (int i = offset; i < count; i++) {
            String hex = Integer.toHexString(value[i]);
            int length = hex.length();
            if (length == 1) {
                hex = "0x" + hex;
            } else {
                hex = hex.substring(length - 2, length);
            }
            builder.append(hex);
            builder.append(String.format("0x%02X", value[i]));
            if (i != value.length - 1) {
                builder.append(", ");
            }
+84 −24
Original line number Diff line number Diff line
@@ -44,7 +44,7 @@ public class BluetoothPacketEncoder extends PacketEncoder {
    // timestamp for first message in current packet
    private int mPacketTimestamp;
    // current running status, or zero if none
    private int mRunningStatus;
    private byte mRunningStatus;

    private boolean mWritePending;

@@ -56,12 +56,28 @@ public class BluetoothPacketEncoder extends PacketEncoder {
        public void onReceive(byte[] msg, int offset, int count, long timestamp)
                throws IOException {

            synchronized (mLock) {
                int milliTimestamp = (int)(timestamp / MILLISECOND_NANOS) & MILLISECOND_MASK;
            int status = msg[0] & 0xFF;
                byte status = msg[offset];
                boolean isSysExStart = (status == MidiConstants.STATUS_SYSTEM_EXCLUSIVE);
                boolean isSysExContinuation = ((status & 0x80) == 0);

                int bytesNeeded;
                if (isSysExStart || isSysExContinuation) {
                    // SysEx messages can be split into multiple packets
                    bytesNeeded = 1;
                } else {
                    bytesNeeded = count;
                }

            synchronized (mLock) {
                boolean needsTimestamp = (milliTimestamp != mPacketTimestamp);
                int bytesNeeded = count;
                if (isSysExStart) {
                    // SysEx start byte must be preceded by a timestamp
                    needsTimestamp = true;
                } else if (isSysExContinuation) {
                    // SysEx continuation packets must not have timestamp byte
                    needsTimestamp = false;
                }
                if (needsTimestamp) bytesNeeded++;  // add one for timestamp byte
                if (status == mRunningStatus) bytesNeeded--;    // subtract one for status byte

@@ -71,15 +87,12 @@ public class BluetoothPacketEncoder extends PacketEncoder {
                    flushLocked(true);
                }

                // write header if we are starting a new packet
                if (mAccumulatedBytes == 0) {
                    // header byte with timestamp bits 7 - 12
                    mAccumulationBuffer[mAccumulatedBytes++] = (byte)(0x80 | (milliTimestamp >> 7));
                    mPacketTimestamp = milliTimestamp;
                    needsTimestamp = true;
                // write the header if necessary
                if (appendHeader(milliTimestamp)) {
                     needsTimestamp = !isSysExContinuation;
                }

                // write new timestamp byte and status byte if necessary
                // write new timestamp byte if necessary
                if (needsTimestamp) {
                    // timestamp byte with bits 0 - 6 of timestamp
                    mAccumulationBuffer[mAccumulatedBytes++] =
@@ -87,20 +100,55 @@ public class BluetoothPacketEncoder extends PacketEncoder {
                    mPacketTimestamp = milliTimestamp;
                }

                if (isSysExStart || isSysExContinuation) {
                    // MidiFramer will end the packet with SysEx End if there is one in the buffer
                    boolean hasSysExEnd =
                            (msg[offset + count - 1] == MidiConstants.STATUS_END_SYSEX);
                    int remaining = (hasSysExEnd ? count - 1 : count);

                    while (remaining > 0) {
                        if (mAccumulatedBytes == mAccumulationBuffer.length) {
                            // write out our data if there is no more room
                            // if necessary, block until previous packet is sent
                            flushLocked(true);
                            appendHeader(milliTimestamp);
                        }

                        int copy = mAccumulationBuffer.length - mAccumulatedBytes;
                        if (copy > remaining) copy = remaining;
                        System.arraycopy(msg, offset, mAccumulationBuffer, mAccumulatedBytes, copy);
                        mAccumulatedBytes += copy;
                        offset += copy;
                        remaining -= copy;
                    }

                    if (hasSysExEnd) {
                        // SysEx End command must be preceeded by a timestamp byte
                        if (mAccumulatedBytes + 2 > mAccumulationBuffer.length) {
                            // write out our data if there is no more room
                            // if necessary, block until previous packet is sent
                            flushLocked(true);
                            appendHeader(milliTimestamp);
                        }
                        mAccumulationBuffer[mAccumulatedBytes++] = (byte)(0x80 | (milliTimestamp & 0x7F));
                        mAccumulationBuffer[mAccumulatedBytes++] = MidiConstants.STATUS_END_SYSEX;
                    }
                } else {
                    // Non-SysEx message
                    if (status != mRunningStatus) {
                    mAccumulationBuffer[mAccumulatedBytes++] = (byte)status;
                        mAccumulationBuffer[mAccumulatedBytes++] = status;
                        if (MidiConstants.allowRunningStatus(status)) {
                            mRunningStatus = status;
                    } else if (MidiConstants.allowRunningStatus(status)) {
                        } else if (MidiConstants.cancelsRunningStatus(status)) {
                            mRunningStatus = 0;
                        }
                    }

                    // now copy data bytes
                    int dataLength = count - 1;
                System.arraycopy(msg, 1, mAccumulationBuffer, mAccumulatedBytes, dataLength);
                // FIXME - handle long SysEx properly
                    System.arraycopy(msg, offset + 1, mAccumulationBuffer, mAccumulatedBytes, dataLength);
                    mAccumulatedBytes += dataLength;
                }

                // write the packet if possible, but do not block
                flushLocked(false);
@@ -108,6 +156,18 @@ public class BluetoothPacketEncoder extends PacketEncoder {
        }
    };

    private boolean appendHeader(int milliTimestamp) {
        // write header if we are starting a new packet
        if (mAccumulatedBytes == 0) {
            // header byte with timestamp bits 7 - 12
            mAccumulationBuffer[mAccumulatedBytes++] = (byte)(0x80 | ((milliTimestamp >> 7) & 0x3F));
            mPacketTimestamp = milliTimestamp;
            return true;
        } else {
            return false;
        }
    }

    // MidiFramer for normalizing incoming data
    private final MidiFramer mMidiFramer = new MidiFramer(mFramedDataReceiver);