Android Open Source - BLE-MIDI-for-Android Midi Parser






From Project

Back to project page BLE-MIDI-for-Android.

License

The source code is released under:

Apache License

If you think the Android project BLE-MIDI-for-Android listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package jp.kshoji.blemidi.util;
/*  w w  w .java2  s. c  o m*/
import java.util.Timer;
import java.util.TimerTask;

import jp.kshoji.blemidi.device.MidiInputDevice;
import jp.kshoji.blemidi.listener.OnMidiInputEventListener;

/**
 * MIDI Parser<br />
 * includes all of MIDI 1.0 specification
 *
 * @author K.Shoji
 */
public final class MidiParser {
    private int midiState = MIDI_STATE_TIMESTAMP;
    private int midiEventKind = 0;
    private int midiEventNote = 0;
    private int midiEventVelocity = 0;

    // for RPN/NRPN messages
    private static final int PARAMETER_MODE_NONE = 0;
    private static final int PARAMETER_MODE_RPN = 1;
    private static final int PARAMETER_MODE_NRPN = 2;
    private int parameterMode = PARAMETER_MODE_NONE;
    private int parameterNumber = 0x3fff;
    private int parameterValue = 0x3fff;

    private final ReusableByteArrayOutputStream systemExclusiveStream = new ReusableByteArrayOutputStream();

    private static final int MIDI_STATE_TIMESTAMP = 0; // receives timestamp
    private static final int MIDI_STATE_WAIT = 1;
    private static final int MIDI_STATE_SIGNAL_2BYTES_2 = 21;
    private static final int MIDI_STATE_SIGNAL_3BYTES_2 = 31;
    private static final int MIDI_STATE_SIGNAL_3BYTES_3 = 32;
    private static final int MIDI_STATE_SIGNAL_SYSEX = 41;

    private int timestamp = 0;

    OnMidiInputEventListener midiInputEventListener;
    final MidiInputDevice sender;

    final Timer timer;

    public MidiParser(MidiInputDevice sender) {
        this.sender = sender;
        timer = new Timer();

        midiState = MIDI_STATE_TIMESTAMP;
        midiEventKind = 0;
        midiEventNote = 0;
        midiEventVelocity = 0;
    }

    public void setMidiInputEventListener(OnMidiInputEventListener midiInputEventListener) {
        this.midiInputEventListener = midiInputEventListener;
    }

    private void parseMidiEvent(int header, byte event) {
        int midiEvent = event & 0xff;
        if (midiState == MIDI_STATE_TIMESTAMP) {
            timestamp = ((header & 0x3f) << 7) | (event & 0x7f);
            midiState = MIDI_STATE_WAIT;
        } else if (midiState == MIDI_STATE_WAIT) {
            switch (midiEvent & 0xf0) {
                case 0xf0: {
                    switch (midiEvent) {
                        case 0xf0:
                            synchronized (systemExclusiveStream) {
                                systemExclusiveStream.reset();
                                systemExclusiveStream.write(midiEvent);
                                midiState = MIDI_STATE_SIGNAL_SYSEX;
                            }
                            break;

                        case 0xf1:
                        case 0xf3:
                            // 0xf1 MIDI Time Code Quarter Frame. : 2bytes
                            // 0xf3 Song Select. : 2bytes
                            midiEventKind = midiEvent;
                            midiState = MIDI_STATE_SIGNAL_2BYTES_2;
                            break;

                        case 0xf2:
                            // 0xf2 Song Position Pointer. : 3bytes
                            midiEventKind = midiEvent;
                            midiState = MIDI_STATE_SIGNAL_3BYTES_2;
                            break;

                        case 0xf6:
                            // 0xf6 Tune Request : 1byte
                            if (midiInputEventListener != null) {
                                if (timestamp > 0) {
                                    timer.schedule(new TimerTask() {
                                        @Override
                                        public void run() {
                                            midiInputEventListener.onMidiTuneRequest(sender);
                                        }
                                    }, timestamp);
                                } else {
                                    midiInputEventListener.onMidiTuneRequest(sender);
                                }
                            }
                            midiState = MIDI_STATE_TIMESTAMP;
                            break;
                        case 0xf8:
                            // 0xf8 Timing Clock : 1byte
                            if (midiInputEventListener != null) {
                                if (timestamp > 0) {
                                    timer.schedule(new TimerTask() {
                                        @Override
                                        public void run() {
                                            midiInputEventListener.onMidiTimingClock(sender);
                                        }
                                    }, timestamp);
                                } else {
                                    midiInputEventListener.onMidiTimingClock(sender);
                                }
                            }
                            midiState = MIDI_STATE_TIMESTAMP;
                            break;
                        case 0xfa:
                            // 0xfa Start : 1byte
                            if (midiInputEventListener != null) {
                                if (timestamp > 0) {
                                    timer.schedule(new TimerTask() {
                                        @Override
                                        public void run() {
                                            midiInputEventListener.onMidiStart(sender);
                                        }
                                    }, timestamp);
                                } else {
                                    midiInputEventListener.onMidiStart(sender);
                                }
                            }
                            midiState = MIDI_STATE_TIMESTAMP;
                            break;
                        case 0xfb:
                            // 0xfb Continue : 1byte
                            if (midiInputEventListener != null) {
                                if (timestamp > 0) {
                                    timer.schedule(new TimerTask() {
                                        @Override
                                        public void run() {
                                            midiInputEventListener.onMidiContinue(sender);
                                        }
                                    }, timestamp);
                                } else {
                                    midiInputEventListener.onMidiContinue(sender);
                                }
                            }
                            midiState = MIDI_STATE_TIMESTAMP;
                            break;
                        case 0xfc:
                            // 0xfc Stop : 1byte
                            if (midiInputEventListener != null) {
                                if (timestamp > 0) {
                                    timer.schedule(new TimerTask() {
                                        @Override
                                        public void run() {
                                            midiInputEventListener.onMidiStop(sender);
                                        }
                                    }, timestamp);
                                } else {
                                    midiInputEventListener.onMidiStop(sender);
                                }
                            }
                            midiState = MIDI_STATE_TIMESTAMP;
                            break;
                        case 0xfe:
                            // 0xfe Active Sensing : 1byte
                            if (midiInputEventListener != null) {
                                if (timestamp > 0) {
                                    timer.schedule(new TimerTask() {
                                        @Override
                                        public void run() {
                                            midiInputEventListener.onMidiActiveSensing(sender);
                                        }
                                    }, timestamp);
                                } else {
                                    midiInputEventListener.onMidiActiveSensing(sender);
                                }
                            }
                            midiState = MIDI_STATE_TIMESTAMP;
                            break;
                        case 0xff:
                            // 0xff Reset : 1byte
                            if (midiInputEventListener != null) {
                                if (timestamp > 0) {
                                    timer.schedule(new TimerTask() {
                                        @Override
                                        public void run() {
                                            midiInputEventListener.onMidiReset(sender);
                                        }
                                    }, timestamp);
                                } else {
                                    midiInputEventListener.onMidiReset(sender);
                                }
                            }
                            midiState = MIDI_STATE_TIMESTAMP;
                            break;

                        default:
                            break;
                    }
                }
                break;
                case 0x80:
                case 0x90:
                case 0xa0:
                case 0xb0:
                case 0xe0:
                    // 3bytes pattern
                    midiEventKind = midiEvent;
                    midiState = MIDI_STATE_SIGNAL_3BYTES_2;
                    break;
                case 0xc0: // program change
                case 0xd0: // channel after-touch
                    // 2bytes pattern
                    midiEventKind = midiEvent;
                    midiState = MIDI_STATE_SIGNAL_2BYTES_2;
                    break;
                default:
                    // 0x00 - 0x70: running status
                    if ((midiEventKind & 0xf0) != 0xf0) {
                        // previous event kind is multi-bytes pattern
                        midiEventNote = midiEvent;
                        midiState = MIDI_STATE_SIGNAL_3BYTES_3;
                    }
                    break;
            }
        } else if (midiState == MIDI_STATE_SIGNAL_2BYTES_2) {
            switch (midiEventKind & 0xf0) {
                // 2bytes pattern
                case 0xc0: // program change
                    midiEventNote = midiEvent;
                    if (midiInputEventListener != null) {
                        if (timestamp > 0) {
                            timer.schedule(new TimerTask() {
                                @Override
                                public void run() {
                                    midiInputEventListener.onMidiProgramChange(sender, midiEventKind & 0xf, midiEventNote);
                                }
                            }, timestamp);
                        } else {
                            midiInputEventListener.onMidiProgramChange(sender, midiEventKind & 0xf, midiEventNote);
                        }
                    }
                    midiState = MIDI_STATE_TIMESTAMP;
                    break;
                case 0xd0: // channel after-touch
                    midiEventNote = midiEvent;
                    if (midiInputEventListener != null) {
                        if (timestamp > 0) {
                            timer.schedule(new TimerTask() {
                                @Override
                                public void run() {
                                    midiInputEventListener.onMidiChannelAftertouch(sender, midiEventKind & 0xf, midiEventNote);
                                }
                            }, timestamp);
                        } else {
                            midiInputEventListener.onMidiChannelAftertouch(sender, midiEventKind & 0xf, midiEventNote);
                        }
                    }
                    midiState = MIDI_STATE_TIMESTAMP;
                    break;
                case 0xf0: {
                    switch (midiEventKind) {
                        case 0xf1:
                            // 0xf1 MIDI Time Code Quarter Frame. : 2bytes
                            midiEventNote = midiEvent;
                            if (midiInputEventListener != null) {
                                if (timestamp > 0) {
                                    timer.schedule(new TimerTask() {
                                        @Override
                                        public void run() {
                                            midiInputEventListener.onMidiTimeCodeQuarterFrame(sender, midiEventNote);
                                        }
                                    }, timestamp);
                                } else {
                                    midiInputEventListener.onMidiTimeCodeQuarterFrame(sender, midiEventNote);
                                }
                            }
                            midiState = MIDI_STATE_TIMESTAMP;
                            break;
                        case 0xf3:
                            // 0xf3 Song Select. : 2bytes
                            midiEventNote = midiEvent;
                            if (midiInputEventListener != null) {
                                if (timestamp > 0) {
                                    timer.schedule(new TimerTask() {
                                        @Override
                                        public void run() {
                                            midiInputEventListener.onMidiSongSelect(sender, midiEventNote);
                                        }
                                    }, timestamp);
                                } else {
                                    midiInputEventListener.onMidiSongSelect(sender, midiEventNote);
                                }
                            }
                            midiState = MIDI_STATE_TIMESTAMP;
                            break;
                        default:
                            // illegal state
                            midiState = MIDI_STATE_TIMESTAMP;
                            break;
                    }
                }
                    break;
                default:
                    // illegal state
                    midiState = MIDI_STATE_TIMESTAMP;
                    break;
            }
        } else if (midiState == MIDI_STATE_SIGNAL_3BYTES_2) {
            switch (midiEventKind & 0xf0) {
                case 0x80:
                case 0x90:
                case 0xa0:
                case 0xb0:
                case 0xe0:
                case 0xf0:
                    // 3bytes pattern
                    midiEventNote = midiEvent;
                    midiState = MIDI_STATE_SIGNAL_3BYTES_3;
                    break;
                default:
                    // illegal state
                    midiState = MIDI_STATE_TIMESTAMP;
                    break;
            }
        } else if (midiState == MIDI_STATE_SIGNAL_3BYTES_3) {
            switch (midiEventKind & 0xf0) {
                // 3bytes pattern
                case 0x80: // note off
                    midiEventVelocity = midiEvent;
                    if (midiInputEventListener != null) {
                        if (timestamp > 0) {
                            timer.schedule(new TimerTask() {
                                @Override
                                public void run() {
                                    midiInputEventListener.onMidiNoteOff(sender, midiEventKind & 0xf, midiEventNote, midiEventVelocity);
                                }
                            }, timestamp);
                        } else {
                            midiInputEventListener.onMidiNoteOff(sender, midiEventKind & 0xf, midiEventNote, midiEventVelocity);
                        }
                    }
                    midiState = MIDI_STATE_TIMESTAMP;
                    break;
                case 0x90: // note on
                    midiEventVelocity = midiEvent;
                    if (midiInputEventListener != null) {
                        if (timestamp > 0) {
                            timer.schedule(new TimerTask() {
                                @Override
                                public void run() {
                                    if (midiEventVelocity == 0) {
                                        midiInputEventListener.onMidiNoteOff(sender, midiEventKind & 0xf, midiEventNote, midiEventVelocity);
                                    } else {
                                        midiInputEventListener.onMidiNoteOn(sender, midiEventKind & 0xf, midiEventNote, midiEventVelocity);
                                    }
                                }
                            }, timestamp);
                        } else {
                            if (midiEventVelocity == 0) {
                                midiInputEventListener.onMidiNoteOff(sender, midiEventKind & 0xf, midiEventNote, midiEventVelocity);
                            } else {
                                midiInputEventListener.onMidiNoteOn(sender, midiEventKind & 0xf, midiEventNote, midiEventVelocity);
                            }
                        }
                    }
                    midiState = MIDI_STATE_TIMESTAMP;
                    break;
                case 0xa0: // control polyphonic key pressure
                    midiEventVelocity = midiEvent;
                    if (midiInputEventListener != null) {
                        if (timestamp > 0) {
                            timer.schedule(new TimerTask() {
                                @Override
                                public void run() {
                                    midiInputEventListener.onMidiPolyphonicAftertouch(sender, midiEventKind & 0xf, midiEventNote, midiEventVelocity);
                                }
                            }, timestamp);
                        } else {
                            midiInputEventListener.onMidiPolyphonicAftertouch(sender, midiEventKind & 0xf, midiEventNote, midiEventVelocity);
                        }
                    }
                    midiState = MIDI_STATE_TIMESTAMP;
                    break;
                case 0xb0: // control change
                    midiEventVelocity = midiEvent;
                    switch (midiEventNote & 0x7f) {
                        case 98:
                            // NRPN LSB
                            parameterNumber &= 0x3f80;
                            parameterNumber |= midiEventVelocity & 0x7f;
                            parameterMode = PARAMETER_MODE_NRPN;
                            break;
                        case 99:
                            // NRPN MSB
                            parameterNumber &= 0x007f;
                            parameterNumber |= (midiEventVelocity & 0x7f) << 7;
                            parameterMode = PARAMETER_MODE_NRPN;
                            break;
                        case 100:
                            // RPN LSB
                            parameterNumber &= 0x3f80;
                            parameterNumber |= midiEventVelocity & 0x7f;
                            parameterMode = PARAMETER_MODE_RPN;
                            break;
                        case 101:
                            // RPN MSB
                            parameterNumber &= 0x007f;
                            parameterNumber |= (midiEventVelocity & 0x7f) << 7;
                            parameterMode = PARAMETER_MODE_RPN;
                            break;
                        case 38:
                            // data LSB
                            parameterValue &= 0x3f80;
                            parameterValue |= midiEventVelocity & 0x7f;

                            if (parameterNumber != 0x3fff) {
                                if (parameterMode == PARAMETER_MODE_RPN) {
                                    if (midiInputEventListener != null) {
                                        if (timestamp > 0) {
                                            timer.schedule(new TimerTask() {
                                                @Override
                                                public void run() {
                                                    midiInputEventListener.onRPNMessage(sender, midiEventKind & 0xf, parameterNumber & 0x3fff, parameterValue & 0x3fff);
                                                }
                                            }, timestamp);
                                        } else {
                                            midiInputEventListener.onRPNMessage(sender, midiEventKind & 0xf, parameterNumber & 0x3fff, parameterValue & 0x3fff);
                                        }
                                    }
                                } else if (parameterMode == PARAMETER_MODE_NRPN) {
                                    if (midiInputEventListener != null) {
                                        if (timestamp > 0) {
                                            timer.schedule(new TimerTask() {
                                                @Override
                                                public void run() {
                                                    midiInputEventListener.onNRPNMessage(sender, midiEventKind & 0xf, parameterNumber & 0x3fff, parameterValue & 0x3fff);
                                                }
                                            }, timestamp);
                                        } else {
                                            midiInputEventListener.onNRPNMessage(sender, midiEventKind & 0xf, parameterNumber & 0x3fff, parameterValue & 0x3fff);
                                        }
                                    }
                                }
                            }
                            break;
                        case 6:
                            // data MSB
                            parameterValue &= 0x007f;
                            parameterValue |= (midiEventVelocity & 0x7f) << 7;

                            if (parameterNumber != 0x3fff) {
                                if (parameterMode == PARAMETER_MODE_RPN) {
                                    if (midiInputEventListener != null) {
                                        if (timestamp > 0) {
                                            timer.schedule(new TimerTask() {
                                                @Override
                                                public void run() {
                                                    midiInputEventListener.onRPNMessage(sender, midiEventKind & 0xf, parameterNumber & 0x3fff, parameterValue & 0x3fff);
                                                }
                                            }, timestamp);
                                        } else {
                                            midiInputEventListener.onRPNMessage(sender, midiEventKind & 0xf, parameterNumber & 0x3fff, parameterValue & 0x3fff);
                                        }
                                    }
                                } else if (parameterMode == PARAMETER_MODE_NRPN) {
                                    if (midiInputEventListener != null) {
                                        if (timestamp > 0) {
                                            timer.schedule(new TimerTask() {
                                                @Override
                                                public void run() {
                                                    midiInputEventListener.onNRPNMessage(sender, midiEventKind & 0xf, parameterNumber & 0x3fff, parameterValue & 0x3fff);
                                                }
                                            }, timestamp);
                                        } else {
                                            midiInputEventListener.onNRPNMessage(sender, midiEventKind & 0xf, parameterNumber & 0x3fff, parameterValue & 0x3fff);
                                        }
                                    }
                                }
                            }
                            break;
                    }

                    if (midiInputEventListener != null) {
                        if (timestamp > 0) {
                            timer.schedule(new TimerTask() {
                                @Override
                                public void run() {
                                    midiInputEventListener.onMidiControlChange(sender, midiEventKind & 0xf, midiEventNote, midiEventVelocity);
                                }
                            }, timestamp);
                        } else {
                            midiInputEventListener.onMidiControlChange(sender, midiEventKind & 0xf, midiEventNote, midiEventVelocity);
                        }
                    }
                    midiState = MIDI_STATE_TIMESTAMP;
                    break;
                case 0xe0: // pitch bend
                    midiEventVelocity = midiEvent;
                    if (midiInputEventListener != null) {
                        if (timestamp > 0) {
                            timer.schedule(new TimerTask() {
                                @Override
                                public void run() {
                                    midiInputEventListener.onMidiPitchWheel(sender, midiEventKind & 0xf, (midiEventNote & 0x7f) | ((midiEventVelocity & 0x7f) << 7));
                                }
                            }, timestamp);
                        } else {
                            midiInputEventListener.onMidiPitchWheel(sender, midiEventKind & 0xf, (midiEventNote & 0x7f) | ((midiEventVelocity & 0x7f) << 7));
                        }
                    }
                    midiState = MIDI_STATE_TIMESTAMP;
                    break;
                case 0xf0: // Song Position Pointer.
                    midiEventVelocity = midiEvent;
                    if (midiInputEventListener != null) {
                        if (timestamp > 0) {
                            timer.schedule(new TimerTask() {
                                @Override
                                public void run() {
                                    midiInputEventListener.onMidiSongPositionPointer(sender, (midiEventNote & 0x7f) | ((midiEventVelocity & 0x7f) << 7));
                                }
                            }, timestamp);
                        } else {
                            midiInputEventListener.onMidiSongPositionPointer(sender, (midiEventNote & 0x7f) | ((midiEventVelocity & 0x7f) << 7));
                        }
                    }
                    midiState = MIDI_STATE_TIMESTAMP;
                    break;
                default:
                    // illegal state
                    midiState = MIDI_STATE_TIMESTAMP;
                    break;
            }
        } else if (midiState == MIDI_STATE_SIGNAL_SYSEX) {
            if (midiEvent == 0xf7) {
                // the end of message
                synchronized (systemExclusiveStream) {
                    systemExclusiveStream.write(midiEvent);
                    if (midiInputEventListener != null) {
                        if (timestamp > 0) {
                            timer.schedule(new TimerTask() {
                                @Override
                                public void run() {
                                    midiInputEventListener.onMidiSystemExclusive(sender, systemExclusiveStream.toByteArray());
                                }
                            }, timestamp);
                        } else {
                            midiInputEventListener.onMidiSystemExclusive(sender, systemExclusiveStream.toByteArray());
                        }
                    }
                }
                midiState = MIDI_STATE_TIMESTAMP;
            } else {
                synchronized (systemExclusiveStream) {
                    systemExclusiveStream.write(midiEvent);
                }
            }
        }
    }

    /**
     * Updates incoming data
     * @param data incoming data
     */
    public void parse(byte[] data) {
        if (data.length > 1) {
            int header = data[0] & 0xff;
            for (int i = 1; i < data.length; i++) {
                parseMidiEvent(header, data[i]);
            }
        }
    }
}




Java Source Code List

jp.kshoji.blemidi.central.BleMidiCallback.java
jp.kshoji.blemidi.central.BleMidiCentralProvider.java
jp.kshoji.blemidi.central.MidiInputDevice.java
jp.kshoji.blemidi.central.MidiOutputDevice.java
jp.kshoji.blemidi.device.MidiInputDevice.java
jp.kshoji.blemidi.device.MidiOutputDevice.java
jp.kshoji.blemidi.listener.OnMidiDeviceAttachedListener.java
jp.kshoji.blemidi.listener.OnMidiDeviceDetachedListener.java
jp.kshoji.blemidi.listener.OnMidiInputEventListener.java
jp.kshoji.blemidi.listener.OnMidiScanStatusListener.java
jp.kshoji.blemidi.peripheral.BleMidiPeripheralProvider.java
jp.kshoji.blemidi.peripheral.MidiInputDevice.java
jp.kshoji.blemidi.peripheral.MidiOutputDevice.java
jp.kshoji.blemidi.sample.CentralActivity.java
jp.kshoji.blemidi.sample.PeripheralActivity.java
jp.kshoji.blemidi.sample.util.SoundMaker.java
jp.kshoji.blemidi.sample.util.Tone.java
jp.kshoji.blemidi.util.BleMidiDeviceUtils.java
jp.kshoji.blemidi.util.BleUtils.java
jp.kshoji.blemidi.util.BleUuidUtils.java
jp.kshoji.blemidi.util.Constants.java
jp.kshoji.blemidi.util.MidiParser.java
jp.kshoji.blemidi.util.ReusableByteArrayOutputStream.java