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.example.android.common.midi; 18 19 import android.media.midi.MidiReceiver; 20 import android.util.Log; 21 22 import java.io.IOException; 23 24 /** 25 * Convert stream of arbitrary MIDI bytes into discrete messages. 26 * 27 * Parses the incoming bytes and then posts individual messages to the receiver 28 * specified in the constructor. Short messages of 1-3 bytes will be complete. 29 * System Exclusive messages may be posted in pieces. 30 * 31 * Resolves Running Status and interleaved System Real-Time messages. 32 */ 33 public class MidiFramer extends MidiReceiver { 34 private MidiReceiver mReceiver; 35 private byte[] mBuffer = new byte[3]; 36 private int mCount; 37 private byte mRunningStatus; 38 private int mNeeded; 39 private boolean mInSysEx; 40 41 public MidiFramer(MidiReceiver receiver) { 42 mReceiver = receiver; 43 } 44 45 /* 46 * @see android.midi.MidiReceiver#onSend(byte[], int, int, long) 47 */ 48 @Override 49 public void onSend(byte[] data, int offset, int count, long timestamp) 50 throws IOException { 51 int sysExStartOffset = (mInSysEx ? offset : -1); 52 53 for (int i = 0; i < count; i++) { 54 final byte currentByte = data[offset]; 55 final int currentInt = currentByte & 0xFF; 56 if (currentInt >= 0x80) { // status byte? 57 if (currentInt < 0xF0) { // channel message? 58 mRunningStatus = currentByte; 59 mCount = 1; 60 mNeeded = MidiConstants.getBytesPerMessage(currentByte) - 1; 61 } else if (currentInt < 0xF8) { // system common? 62 if (currentInt == 0xF0 /* SysEx Start */) { 63 // Log.i(TAG, "SysEx Start"); 64 mInSysEx = true; 65 sysExStartOffset = offset; 66 } else if (currentInt == 0xF7 /* SysEx End */) { 67 // Log.i(TAG, "SysEx End"); 68 if (mInSysEx) { 69 mReceiver.send(data, sysExStartOffset, 70 offset - sysExStartOffset + 1, timestamp); 71 mInSysEx = false; 72 sysExStartOffset = -1; 73 } 74 } else { 75 mBuffer[0] = currentByte; 76 mRunningStatus = 0; 77 mCount = 1; 78 mNeeded = MidiConstants.getBytesPerMessage(currentByte) - 1; 79 } 80 } else { // real-time? 81 // Single byte message interleaved with other data. 82 if (mInSysEx) { 83 mReceiver.send(data, sysExStartOffset, 84 offset - sysExStartOffset, timestamp); 85 sysExStartOffset = offset + 1; 86 } 87 mReceiver.send(data, offset, 1, timestamp); 88 } 89 } else { // data byte 90 if (!mInSysEx) { 91 mBuffer[mCount++] = currentByte; 92 if (--mNeeded == 0) { 93 if (mRunningStatus != 0) { 94 mBuffer[0] = mRunningStatus; 95 } 96 mReceiver.send(mBuffer, 0, mCount, timestamp); 97 mNeeded = MidiConstants.getBytesPerMessage(mBuffer[0]) - 1; 98 mCount = 1; 99 } 100 } 101 } 102 ++offset; 103 } 104 105 // send any accumulatedSysEx data 106 if (sysExStartOffset >= 0 && sysExStartOffset < offset) { 107 mReceiver.send(data, sysExStartOffset, 108 offset - sysExStartOffset, timestamp); 109 } 110 } 111 112 } 113