1 /*
2  * Copyright (C) 2020 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 static org.junit.Assert.assertArrayEquals;
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertTrue;
22 
23 import android.util.Log;
24 
25 import androidx.test.filters.SmallTest;
26 import androidx.test.runner.AndroidJUnit4;
27 
28 import com.android.internal.midi.MidiFramer;
29 
30 import org.junit.Test;
31 import org.junit.runner.RunWith;
32 
33 import java.io.IOException;
34 
35 @RunWith(AndroidJUnit4.class)
36 @SmallTest
37 public class BluetoothMidiDecoderTest {
38 
39     private static final String TAG = "BluetoothMidiDecoderTest";
40     private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
41     private static final long NANOS_PER_MSEC = 1000000L;
42 
43     static class DecoderChecker {
44         AccumulatingMidiReceiver mReceiver;
45         BluetoothPacketDecoder mDecoder;
46 
DecoderChecker()47         DecoderChecker() {
48             mReceiver = new AccumulatingMidiReceiver();
49             final int maxBytes = 20;
50             mDecoder = new BluetoothPacketDecoder(maxBytes);
51         }
52 
compareWithExpected(final byte[][] expectedMessages)53         void compareWithExpected(final byte[][] expectedMessages) {
54             byte[][] actualRows = mReceiver.getBuffers();
55             Long[] actualTimestamps = mReceiver.getTimestamps();
56             long previousTime = 0;
57             // Compare the gathered with the expected.
58             assertEquals(expectedMessages.length, actualRows.length);
59             for (int i = 0; i < expectedMessages.length; i++) {
60                 byte[] expectedRow = expectedMessages[i];
61                 Log.d(TAG, "expectedRow = "
62                         + MidiFramer.formatMidiData(expectedRow, 0, expectedRow.length));
63                 byte[] actualRow = actualRows[i];
64                 Log.d(TAG, "actualRow   = "
65                         + MidiFramer.formatMidiData(actualRow, 0, actualRow.length));
66                 assertArrayEquals(expectedRow, actualRow);
67                 // Are the timestamps monotonic?
68                 long currentTime =  actualTimestamps[i];
69                 Log.d(TAG, "previousTime   = " + previousTime + ", currentTime   = " + currentTime);
70                 assertTrue(currentTime >= previousTime);
71                 previousTime = currentTime;
72             }
73         }
74 
decodePacket(byte[] packet)75         void decodePacket(byte[] packet) throws IOException {
76             mDecoder.decodePacket(packet, mReceiver);
77         }
78 
decodePackets(byte[][] multiplePackets)79         void decodePackets(byte[][] multiplePackets) throws IOException {
80             try {
81                 for (int i = 0; i < multiplePackets.length; i++) {
82                     byte[] packet = multiplePackets[i];
83                     mDecoder.decodePacket(packet, mReceiver);
84                 }
85             } catch (Exception e) {
86                 assertEquals(null, e);
87             }
88         }
89 
test(byte[] encoded, byte[][] decoded)90         void test(byte[] encoded, byte[][] decoded) throws IOException {
91             decodePacket(encoded);
92             compareWithExpected(decoded);
93         }
94 
test(byte[][] encoded, byte[][] decoded)95         void test(byte[][] encoded, byte[][] decoded) throws IOException {
96             decodePackets(encoded);
97             compareWithExpected(decoded);
98         }
99     }
100 
101     @Test
testOneNoteOn()102     public void testOneNoteOn() throws IOException {
103         final byte[] encoded = {
104                 (byte) 0x80, // high bit of header must be set
105                 (byte) 0x80, // high bit of timestamp
106                 (byte) 0x90, 0x40, 0x64
107                 };
108         final byte[][] decoded = {
109                 {(byte) 0x90, 0x40, 0x64}
110                 };
111         new DecoderChecker().test(encoded, decoded);
112     }
113 
114     @Test
testReservedHeaderBit()115     public void testReservedHeaderBit() throws IOException {
116         final byte[] encoded = {
117                 // Decoder should ignore the reserved bit.
118                 (byte) (0x80 | 0x40), // set RESERVED bit in header!
119                 (byte) 0x80, // high bit of timestamp
120                 (byte) 0x90, 0x40, 0x64
121                 };
122         final byte[][] decoded = {
123                 {(byte) 0x90, 0x40, 0x64}
124                 };
125         new DecoderChecker().test(encoded, decoded);
126     }
127 
128     @Test
testTwoNotesOnRunning()129     public void testTwoNotesOnRunning() throws IOException {
130         final byte[] encoded = {
131                 (byte) 0x80, // high bit of header must be set
132                 (byte) 0x80, // high bit of timestamp
133                 (byte) 0x90, 0x40, 0x64,
134                 (byte) 0x85, // timestamp
135                 (byte) 0x42, 0x70
136                 };
137         final byte[][] decoded = {
138                 {(byte) 0x90, 0x40, 0x64},
139                 {(byte) 0x42, 0x70}
140                 };
141         new DecoderChecker().test(encoded, decoded);
142     }
143 
144     @Test
testTwoNoteOnsTwoChannels()145     public void testTwoNoteOnsTwoChannels() throws IOException {
146         final byte[] encoded = {
147                 (byte) 0x80, // high bit of header must be set
148                 (byte) 0x80, // high bit of timestamp
149                 (byte) 0x93, 0x40, 0x60,
150                 // two channels so no running status
151                 (byte) 0x80, // high bit of timestamp
152                 (byte) 0x95, 0x47, 0x64
153                 };
154         final byte[][] decoded = {
155                 {(byte) 0x93, 0x40, 0x60,  (byte) 0x95, 0x47, 0x64}
156                 };
157         new DecoderChecker().test(encoded, decoded);
158     }
159 
160     @Test
testTwoNoteOnsOverTime()161     public void testTwoNoteOnsOverTime() throws IOException {
162         final byte[][] encoded = {{
163                 (byte) 0x80, // high bit of header must be set
164                 (byte) 0x80, // high bit of timestamp
165                 (byte) 0x98, 0x45, 0x60
166                 },
167                 {
168                 (byte) 0x80, // high bit of header must be set
169                 (byte) 0x82, // timestamp advanced by 2 msec
170                 (byte) 0x90, 0x40, 0x64,
171                 (byte) 0x84, // timestamp needed because of time delay
172                 // encoder uses running status
173                 0x47, 0x72
174                 }};
175         final byte[][] decoded = {
176                 {(byte) 0x98, 0x45, 0x60},
177                 {(byte) 0x90, 0x40, 0x64},
178                 {(byte) 0x47, 0x72}
179                 };
180         new DecoderChecker().test(encoded, decoded);
181     }
182 
183     @Test
testSysExBasic()184     public void testSysExBasic() throws IOException {
185         final byte[][] encoded = {{
186                 (byte) 0x80, // high bit of header must be set
187                 (byte) 0x80, // timestamp
188                 (byte) 0xF0, 0x7D, // Begin prototyping SysEx
189                 0x01, 0x02, 0x03, 0x04, 0x05,
190                 (byte) 0x80, // timestamp
191                 (byte) 0xF7 // End SysEx
192                 }};
193         final byte[][] decoded = {
194                 {(byte) 0xF0, 0x7D, // experimental SysEx
195                 0x01, 0x02, 0x03, 0x04, 0x05, (byte) 0xF7}
196                 };
197         new DecoderChecker().test(encoded, decoded);
198     }
199 
200     @Test
testSysExTwoPackets()201     public void testSysExTwoPackets() throws IOException {
202         final byte[][] encoded = {{
203                 (byte) 0x80, // high bit of header must be set
204                 (byte) 0x80, // timestamp
205                 (byte) 0xF0, 0x7D, // Begin prototyping SysEx
206                 0x01, 0x02
207                 },
208                 {
209                 (byte) 0x80, // high bit of header must be set
210                 0x03, 0x04, 0x05,
211                 (byte) 0x80, // timestamp
212                 (byte) 0xF7 // End SysEx
213                 }};
214         final byte[][] decoded = {
215             {(byte) 0xF0, 0x7D, 0x01, 0x02}, // experimental SysEx
216             {0x03, 0x04, 0x05, (byte) 0xF7}
217         };
218         new DecoderChecker().test(encoded, decoded);
219     }
220 
221     @Test
testSysExThreePackets()222     public void testSysExThreePackets() throws IOException {
223         final byte[][] encoded = {
224                 {(byte) 0x80, // high bit of header must be set
225                 (byte) 0x80, // timestamp
226                 (byte) 0xF0, 0x7D, // Begin prototyping SysEx
227                 0x01, 0x02
228                 },
229                 {
230                 (byte) 0x80, // high bit of header must be set
231                 0x03, 0x04, 0x05,
232                 },
233                 {
234                 (byte) 0x80, // high bit of header must be set
235                 0x06, 0x07, 0x08,
236                 (byte) 0x80, // timestamp
237                 (byte) 0xF7 // End SysEx
238                 }};
239         final byte[][] decoded = {
240                 {(byte) 0xF0, 0x7D, 0x01, 0x02}, // experimental SysEx
241                 {0x03, 0x04, 0x05},
242                 {0x06, 0x07, 0x08, (byte) 0xF7}
243                 };
244         new DecoderChecker().test(encoded, decoded);
245     }
246 
247 }
248