blob: 8d18b77700b515f9a90cfcdf4294e2caf7c3401e [file] [log] [blame]
Mike Lockwoodf0a41d12015-03-24 08:27:11 -07001/*
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
17package com.android.bluetoothmidiservice;
18
19import android.media.midi.MidiReceiver;
20import android.util.Log;
21
22import java.io.IOException;
23
24/**
Phil Burk51aaf2e2020-02-20 14:23:39 -080025 * This is an abstract base class that decodes a BLE-MIDI packet
26 * buffer and passes it to a {@link android.media.midi.MidiReceiver}
Mike Lockwoodf0a41d12015-03-24 08:27:11 -070027 */
28public class BluetoothPacketDecoder extends PacketDecoder {
29
30 private static final String TAG = "BluetoothPacketDecoder";
31
32 private final byte[] mBuffer;
Phil Burk51aaf2e2020-02-20 14:23:39 -080033 private int mBytesInBuffer;
Mike Lockwoodff001802015-04-21 09:33:09 -070034 private MidiBtleTimeTracker mTimeTracker;
Mike Lockwoodf0a41d12015-03-24 08:27:11 -070035
Phil Burk51aaf2e2020-02-20 14:23:39 -080036 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
Mike Lockwoodf0a41d12015-03-24 08:27:11 -070042
43 public BluetoothPacketDecoder(int maxPacketSize) {
44 mBuffer = new byte[maxPacketSize];
45 }
46
Phil Burk51aaf2e2020-02-20 14:23:39 -080047 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.
Mike Lockwoodf0a41d12015-03-24 08:27:11 -070060 @Override
61 public void decodePacket(byte[] buffer, MidiReceiver receiver) {
Mike Lockwoodff001802015-04-21 09:33:09 -070062 if (mTimeTracker == null) {
63 mTimeTracker = new MidiBtleTimeTracker(System.nanoTime());
64 }
65
Mike Lockwoodf0a41d12015-03-24 08:27:11 -070066 int length = buffer.length;
Mike Lockwoodf0a41d12015-03-24 08:27:11 -070067 if (length < 1) {
68 Log.e(TAG, "empty packet");
69 return;
70 }
Phil Burk51aaf2e2020-02-20 14:23:39 -080071
Mike Lockwoodf0a41d12015-03-24 08:27:11 -070072 byte header = buffer[0];
Phil Burka5769592020-03-27 17:56:57 -070073 // Check for the header bit 7.
74 // Ignore the reserved bit 6.
75 if ((header & 0x80) != 0x80) {
Mike Lockwoodf0a41d12015-03-24 08:27:11 -070076 Log.e(TAG, "packet does not start with header");
77 return;
78 }
79
80 // shift bits 0 - 5 to bits 7 - 12
Mike Lockwoodff001802015-04-21 09:33:09 -070081 int highTimestamp = (header & HEADER_TIMESTAMP_MASK) << 7;
Mike Lockwoodf0a41d12015-03-24 08:27:11 -070082 boolean lastWasTimestamp = false;
Mike Lockwoodf0a41d12015-03-24 08:27:11 -070083 int previousLowTimestamp = 0;
Phil Burk51aaf2e2020-02-20 14:23:39 -080084 int currentTimestamp = highTimestamp | mLowTimestamp;
Mike Lockwoodf0a41d12015-03-24 08:27:11 -070085
Phil Burk51aaf2e2020-02-20 14:23:39 -080086 // Iterate through the rest of the packet, separating MIDI data from timestamps.
Mike Lockwoodf0a41d12015-03-24 08:27:11 -070087 for (int i = 1; i < buffer.length; i++) {
88 byte b = buffer[i];
89
Phil Burk51aaf2e2020-02-20 14:23:39 -080090 // Is this a timestamp byte?
Mike Lockwoodf0a41d12015-03-24 08:27:11 -070091 if ((b & 0x80) != 0 && !lastWasTimestamp) {
92 lastWasTimestamp = true;
Phil Burk51aaf2e2020-02-20 14:23:39 -080093 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) {
Mike Lockwoodff001802015-04-21 09:33:09 -070098 highTimestamp = (highTimestamp + 0x0080) & TIMESTAMP_MASK_HIGH;
Mike Lockwoodf0a41d12015-03-24 08:27:11 -070099 }
Phil Burk51aaf2e2020-02-20 14:23:39 -0800100 previousLowTimestamp = mLowTimestamp;
Mike Lockwoodf0a41d12015-03-24 08:27:11 -0700101
Phil Burk51aaf2e2020-02-20 14:23:39 -0800102 // If the timestamp advances then send any pending data.
103 int newTimestamp = highTimestamp | mLowTimestamp;
Mike Lockwoodff001802015-04-21 09:33:09 -0700104 if (newTimestamp != currentTimestamp) {
Phil Burk51aaf2e2020-02-20 14:23:39 -0800105 // Send previous message separately since it has a different timestamp.
106 flushOutput(receiver);
Mike Lockwoodff001802015-04-21 09:33:09 -0700107 currentTimestamp = newTimestamp;
Mike Lockwoodf0a41d12015-03-24 08:27:11 -0700108 }
Mike Lockwoodff001802015-04-21 09:33:09 -0700109
Phil Burk51aaf2e2020-02-20 14:23:39 -0800110 // Calculate MIDI nanosecond timestamp from BLE timestamp.
Mike Lockwoodff001802015-04-21 09:33:09 -0700111 long now = System.nanoTime();
Phil Burk51aaf2e2020-02-20 14:23:39 -0800112 mNanoTimestamp = mTimeTracker.convertTimestampToNanotime(currentTimestamp, now);
Mike Lockwoodf0a41d12015-03-24 08:27:11 -0700113 } else {
114 lastWasTimestamp = false;
Phil Burk51aaf2e2020-02-20 14:23:39 -0800115 // Flush if full before adding more data.
116 if (mBytesInBuffer == mBuffer.length) {
117 flushOutput(receiver);
118 }
119 mBuffer[mBytesInBuffer++] = b;
Mike Lockwoodf0a41d12015-03-24 08:27:11 -0700120 }
121 }
122
Phil Burk51aaf2e2020-02-20 14:23:39 -0800123 flushOutput(receiver);
Mike Lockwoodf0a41d12015-03-24 08:27:11 -0700124 }
125}