summaryrefslogtreecommitdiffstats
path: root/media/packages
diff options
context:
space:
mode:
authorMike Lockwood <lockwood@google.com>2015-05-01 14:36:44 -0700
committerMike Lockwood <lockwood@google.com>2015-05-01 16:16:42 -0700
commit8c26d843a786e5ee56046245fbf72a81b533bcb9 (patch)
tree07981579d72fea54553b8da512f1b7b059c6623c /media/packages
parent5793f22e68f74522dfe5780b96b22d18617467be (diff)
downloadframeworks_base-8c26d843a786e5ee56046245fbf72a81b533bcb9.zip
frameworks_base-8c26d843a786e5ee56046245fbf72a81b533bcb9.tar.gz
frameworks_base-8c26d843a786e5ee56046245fbf72a81b533bcb9.tar.bz2
BluetoothMidiService: Add support for sending SysEx messages that span multiple Bluetooth packets
Change-Id: Id56f7c82ec97b6a46258111bbfd46ab1dc14dfe9
Diffstat (limited to 'media/packages')
-rw-r--r--media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java22
-rw-r--r--media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothPacketEncoder.java108
2 files changed, 94 insertions, 36 deletions
diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
index 8d194e5..63a8da7 100644
--- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
+++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
@@ -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(", ");
}
diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothPacketEncoder.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothPacketEncoder.java
index 463edcf..99ea353 100644
--- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothPacketEncoder.java
+++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothPacketEncoder.java
@@ -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 {
- int milliTimestamp = (int)(timestamp / MILLISECOND_NANOS) & MILLISECOND_MASK;
- int status = msg[0] & 0xFF;
-
synchronized (mLock) {
+ int milliTimestamp = (int)(timestamp / MILLISECOND_NANOS) & MILLISECOND_MASK;
+ 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;
+ }
+
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 (status != mRunningStatus) {
- mAccumulationBuffer[mAccumulatedBytes++] = (byte)status;
- if (MidiConstants.allowRunningStatus(status)) {
- mRunningStatus = status;
- } else if (MidiConstants.allowRunningStatus(status)) {
- mRunningStatus = 0;
+ 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;
}
- }
- // now copy data bytes
- int dataLength = count - 1;
- System.arraycopy(msg, 1, mAccumulationBuffer, mAccumulatedBytes, dataLength);
- // FIXME - handle long SysEx properly
- mAccumulatedBytes += dataLength;
+ 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++] = status;
+ if (MidiConstants.allowRunningStatus(status)) {
+ mRunningStatus = status;
+ } else if (MidiConstants.cancelsRunningStatus(status)) {
+ mRunningStatus = 0;
+ }
+ }
+
+ // now copy data bytes
+ int dataLength = count - 1;
+ 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);