1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.bluetoothmidiservice; 18 19 import android.media.midi.MidiReceiver; 20 import android.util.Log; 21 22 import java.io.IOException; 23 24 /** 25 * This is an abstract base class that decodes a BLE-MIDI packet 26 * buffer and passes it to a {@link android.media.midi.MidiReceiver} 27 */ 28 public class BluetoothPacketDecoder extends PacketDecoder { 29 30 private static final String TAG = "BluetoothPacketDecoder"; 31 32 private final byte[] mBuffer; 33 private int mBytesInBuffer; 34 private MidiBtleTimeTracker mTimeTracker; 35 36 private int mLowTimestamp; 37 private long mNanoTimestamp; 38 39 private static final int TIMESTAMP_MASK_HIGH = 0x1F80; // top 7 bits 40 private static final int TIMESTAMP_MASK_LOW = 0x7F; // bottom 7 bits 41 private static final int HEADER_TIMESTAMP_MASK = 0x3F; // bottom 6 bits 42 BluetoothPacketDecoder(int maxPacketSize)43 public BluetoothPacketDecoder(int maxPacketSize) { 44 mBuffer = new byte[maxPacketSize]; 45 } 46 flushOutput(MidiReceiver receiver)47 private void flushOutput(MidiReceiver receiver) { 48 if (mBytesInBuffer > 0) { 49 try { 50 receiver.send(mBuffer, 0, mBytesInBuffer, mNanoTimestamp); 51 } catch (IOException e) { 52 // ??? 53 } 54 mBytesInBuffer = 0; 55 } 56 } 57 58 // NOTE: this code allows running status across packets, 59 // although the specification does not allow that. 60 @Override decodePacket(byte[] buffer, MidiReceiver receiver)61 public void decodePacket(byte[] buffer, MidiReceiver receiver) { 62 if (mTimeTracker == null) { 63 mTimeTracker = new MidiBtleTimeTracker(System.nanoTime()); 64 } 65 66 int length = buffer.length; 67 if (length < 1) { 68 Log.e(TAG, "empty packet"); 69 return; 70 } 71 72 byte header = buffer[0]; 73 // Check for the header bit 7. 74 // Ignore the reserved bit 6. 75 if ((header & 0x80) != 0x80) { 76 Log.e(TAG, "packet does not start with header"); 77 return; 78 } 79 80 // shift bits 0 - 5 to bits 7 - 12 81 int highTimestamp = (header & HEADER_TIMESTAMP_MASK) << 7; 82 boolean lastWasTimestamp = false; 83 int previousLowTimestamp = 0; 84 int currentTimestamp = highTimestamp | mLowTimestamp; 85 86 // Iterate through the rest of the packet, separating MIDI data from timestamps. 87 for (int i = 1; i < buffer.length; i++) { 88 byte b = buffer[i]; 89 90 // Is this a timestamp byte? 91 if ((b & 0x80) != 0 && !lastWasTimestamp) { 92 lastWasTimestamp = true; 93 mLowTimestamp = b & TIMESTAMP_MASK_LOW; 94 95 // If the low timestamp byte wraps within the packet then 96 // increment the high timestamp byte. 97 if (mLowTimestamp < previousLowTimestamp) { 98 highTimestamp = (highTimestamp + 0x0080) & TIMESTAMP_MASK_HIGH; 99 } 100 previousLowTimestamp = mLowTimestamp; 101 102 // If the timestamp advances then send any pending data. 103 int newTimestamp = highTimestamp | mLowTimestamp; 104 if (newTimestamp != currentTimestamp) { 105 // Send previous message separately since it has a different timestamp. 106 flushOutput(receiver); 107 currentTimestamp = newTimestamp; 108 } 109 110 // Calculate MIDI nanosecond timestamp from BLE timestamp. 111 long now = System.nanoTime(); 112 mNanoTimestamp = mTimeTracker.convertTimestampToNanotime(currentTimestamp, now); 113 } else { 114 lastWasTimestamp = false; 115 // Flush if full before adding more data. 116 if (mBytesInBuffer == mBuffer.length) { 117 flushOutput(receiver); 118 } 119 mBuffer[mBytesInBuffer++] = b; 120 } 121 } 122 123 flushOutput(receiver); 124 } 125 } 126