Formats a midi message to a readable String. - Java javax.sound.midi

Java examples for javax.sound.midi:MidiMessage

Description

Formats a midi message to a readable String.

Demo Code

/*/*  ww  w.j av  a 2  s  .  c  o  m*/
 * Copyright (c) 1999 - 2001 by Matthias Pfisterer
 * Copyright (c) 2003 by Florian Bomers
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MetaMessage;
import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Receiver;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.SysexMessage;
import javax.sound.midi.Transmitter;
import org.apache.log4j.Logger;

public class Main{
    public static final String NOTE_ON = "NOTE ON";
    public static final String NOTE_OFF = "NOTE OFF";
    public static final String POLYPHONIC_KEY_PRESSURE = "POLYPHONIC KEY PRESSURE";
    public static final String CONTROL_CHANGE = "CONTROL CHANGE";
    public static final String PROGRAM_CHANGE = "PROGRAM CHANGE";
    public static final String KEY_PRESSURE = "KEY PRESSURE";
    public static final String PITCH_WHEEL_CHANGE = "PITCH WHEEL CHANGE";
    public static final String SYSTEM_MESSAGE = "SYSTEM MESSAGE";
    public static final String UNKNOWN_MESSAGE = "UNKNOWN MESSAGE";
    public static long seByteCount = 0;
    public static long smByteCount = 0;
    public static long seCount = 0;
    public static long smCount = 0;
    private static final String[] sm_astrKeyNames = { "C", "C#", "D", "D#",
            "E", "F", "F#", "G", "G#", "A", "A#", "B" };
    private static final String[] sm_astrKeySignatures = { "Cb", "Gb",
            "Db", "Ab", "Eb", "Bb", "F", "C", "G", "D", "A", "E", "B",
            "F#", "C#" };
    private static final String[] QUARTER_FRAME_MESSAGE_TEXT = {
            "frame count LS: ", "frame count MS: ", "seconds count LS: ",
            "seconds count MS: ", "minutes count LS: ",
            "minutes count MS: ", "hours count LS: ", "hours count MS: " };
    private static final String[] FRAME_TYPE_TEXT = { "24 frames/second",
            "25 frames/second", "30 frames/second (drop)",
            "30 frames/second (non-drop)", };
    private static final char HEX_DIGITS[] = { '0', '1', '2', '3', '4',
            '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
    private static final String[] SYSTEM_MESSAGE_TEXT = {
            "System Exclusive (should not be in ShortMessage!)",
            "MTC Quarter Frame: ", "Song Position: ", "Song Select: ",
            "Undefined", "Undefined", "Tune Request",
            "End of SysEx (should not be in ShortMessage!)",
            "Timing clock", "Undefined", "Start", "Continue", "Stop",
            "Undefined", "Active Sensing", "System Reset" };
    /**
     * Formats a midi message to a readable String.
     * 
     * @param message
     *            The midi message
     * @return a readable String
     */
    public static String messageToString(MidiMessage message) {

        if (message instanceof ShortMessage) {
            return decodeMessage((ShortMessage) message);
        } else if (message instanceof SysexMessage) {
            return decodeMessage((SysexMessage) message);
        } else if (message instanceof MetaMessage) {
            return decodeMessage((MetaMessage) message);
        } else {
            return MidiUtils.UNKNOWN_MESSAGE;
        }
    }
    private static String decodeMessage(ShortMessage message) {
        String strMessage = null;
        String command = decodeCommand(message.getCommand());

        switch (command) {
        case NOTE_OFF:
            strMessage = NOTE_OFF + " " + getKeyName(message.getData1());
            break;

        case NOTE_ON:
            strMessage = NOTE_ON + " " + getKeyName(message.getData1());
            break;

        case POLYPHONIC_KEY_PRESSURE:
            strMessage = POLYPHONIC_KEY_PRESSURE + " "
                    + getKeyName(message.getData1()) + " pressure: "
                    + message.getData2();
            break;

        case CONTROL_CHANGE:
            strMessage = CONTROL_CHANGE + " " + message.getData1()
                    + " value: " + message.getData2();
            break;

        case PROGRAM_CHANGE:
            strMessage = PROGRAM_CHANGE + " " + message.getData1();
            break;

        case KEY_PRESSURE:
            strMessage = KEY_PRESSURE + " "
                    + getKeyName(message.getData1()) + " pressure: "
                    + message.getData2();
            break;

        case PITCH_WHEEL_CHANGE:
            strMessage = PITCH_WHEEL_CHANGE + " "
                    + get14bitValue(message.getData1(), message.getData2());
            break;

        case SYSTEM_MESSAGE:
            strMessage = SYSTEM_MESSAGE_TEXT[message.getChannel()];

            switch (message.getChannel()) {

            case 1:
                int nQType = (message.getData1() & 0x70) >> 4;
                int nQData = message.getData1() & 0x0F;
                if (nQType == 7) {
                    nQData = nQData & 0x1;
                }
                strMessage += QUARTER_FRAME_MESSAGE_TEXT[nQType] + nQData;
                if (nQType == 7) {
                    int nFrameType = (message.getData1() & 0x06) >> 1;
                    strMessage += ", frame type: "
                            + FRAME_TYPE_TEXT[nFrameType];
                }
                break;

            case 2:
                strMessage += get14bitValue(message.getData1(),
                        message.getData2());
                break;

            case 3:
                strMessage += message.getData1();
                break;
            }

            break;

        case UNKNOWN_MESSAGE:
            strMessage = UNKNOWN_MESSAGE + ": status = "
                    + message.getStatus() + ", byte1 = "
                    + message.getData1() + ", byte2 = "
                    + message.getData2();
            break;
        }

        if (command != SYSTEM_MESSAGE) {
            int nChannel = message.getChannel() + 1;
            String strChannel = "channel " + nChannel + ": ";
            strMessage = strChannel + strMessage;
        }

        smCount++;
        smByteCount += message.getLength();
        return strMessage;
    }
    private static String decodeMessage(SysexMessage message) {

        byte[] abData = message.getData();
        String strMessage = null;

        if (message.getStatus() == SysexMessage.SYSTEM_EXCLUSIVE) {

            strMessage = "Sysex message: F0" + getHexString(abData);

        } else if (message.getStatus() == SysexMessage.SPECIAL_SYSTEM_EXCLUSIVE) {
            strMessage = "Continued Sysex message F7"
                    + getHexString(abData);
            seByteCount--; // do not count the F7
        }

        seByteCount += abData.length + 1;
        seCount++; // for the status byte

        return strMessage;
    }
    private static String decodeMessage(MetaMessage message) {
        byte[] abData = message.getData();
        String strMessage = null;
        // System.out.println("data array length: " + abData.length);
        switch (message.getType()) {
        case 0:
            int nSequenceNumber = ((abData[0] & 0xFF) << 8)
                    | (abData[1] & 0xFF);
            strMessage = "Sequence Number: " + nSequenceNumber;
            break;

        case 1:
            String strText = new String(abData);
            strMessage = "Text Event: " + strText;
            break;

        case 2:
            String strCopyrightText = new String(abData);
            strMessage = "Copyright Notice: " + strCopyrightText;
            break;

        case 3:
            String strTrackName = new String(abData);
            strMessage = "Sequence/Track Name: " + strTrackName;
            break;

        case 4:
            String strInstrumentName = new String(abData);
            strMessage = "Instrument Name: " + strInstrumentName;
            break;

        case 5:
            String strLyrics = new String(abData);
            strMessage = "Lyric: " + strLyrics;
            break;

        case 6:
            String strMarkerText = new String(abData);
            strMessage = "Marker: " + strMarkerText;
            break;

        case 7:
            String strCuePointText = new String(abData);
            strMessage = "Cue Point: " + strCuePointText;
            break;

        case 0x20:
            int nChannelPrefix = abData[0] & 0xFF;
            strMessage = "MIDI Channel Prefix: " + nChannelPrefix;
            break;

        case 0x2F:
            strMessage = "End of Track";
            break;

        case 0x51:
            int nTempo = ((abData[0] & 0xFF) << 16)
                    | ((abData[1] & 0xFF) << 8) | (abData[2] & 0xFF); // tempo in microseconds per beat
            float bpm = convertTempo(nTempo);
            // truncate it to 2 digits after dot
            bpm = (float) (Math.round(bpm * 100.0f) / 100.0f);
            strMessage = "Set Tempo: " + bpm + " bpm";
            break;

        case 0x54:
            // System.out.println("data array length: " + abData.length);
            strMessage = "SMTPE Offset: " + (abData[0] & 0xFF) + ":"
                    + (abData[1] & 0xFF) + ":" + (abData[2] & 0xFF) + "."
                    + (abData[3] & 0xFF) + "." + (abData[4] & 0xFF);
            break;

        case 0x58:
            strMessage = "Time Signature: " + (abData[0] & 0xFF) + "/"
                    + (1 << (abData[1] & 0xFF))
                    + ", MIDI clocks per metronome tick: "
                    + (abData[2] & 0xFF) + ", 1/32 per 24 MIDI clocks: "
                    + (abData[3] & 0xFF);
            break;

        case 0x59:
            String strGender = (abData[1] == 1) ? "minor" : "major";
            strMessage = "Key Signature: "
                    + sm_astrKeySignatures[abData[0] + 7] + " " + strGender;
            break;

        case 0x7F:
            String strDataDump = getHexString(abData);
            strMessage = "Sequencer-Specific Meta event: " + strDataDump;
            break;

        default:
            String strUnknownDump = getHexString(abData);
            strMessage = "unknown Meta event: " + strUnknownDump;
            break;

        }
        return strMessage;
    }
    /**
     * Interprets the midi command byte code to a readable String.
     * 
     * @param command
     *            The midi command
     * @return a readable command
     */
    private static String decodeCommand(int command) {
        String result;
        switch (command) {
        case 0x80:
            result = NOTE_OFF;
            break;

        case 0x90:
            result = NOTE_ON;
            break;

        case 0xa0:
            result = POLYPHONIC_KEY_PRESSURE;
            break;

        case 0xb0:
            result = CONTROL_CHANGE;
            break;

        case 0xc0:
            result = PROGRAM_CHANGE;
            break;

        case 0xd0:
            result = KEY_PRESSURE;
            break;

        case 0xe0:
            result = PITCH_WHEEL_CHANGE;
            break;

        case 0xF0:
            result = SYSTEM_MESSAGE;

        default:
            result = UNKNOWN_MESSAGE;
            break;
        }
        return result;
    }
    /**
     * Gets the key name and octave to the corresponding byte value
     * 
     * @param nKeyNumber
     *            The byte value
     * @return The key name and the octave
     */
    private static String getKeyName(int nKeyNumber) {

        if (nKeyNumber > 127) {
            return "illegal value";
        }

        int nNote = nKeyNumber % 12;
        int nOctave = nKeyNumber / 12;
        return sm_astrKeyNames[nNote] + (nOctave - 2);
    }
    private static int get14bitValue(int nLowerPart, int nHigherPart) {
        return (nLowerPart & 0x7F) | ((nHigherPart & 0x7F) << 7);
    }
    /**
     * Converts a byte to a HEX String
     * 
     * @param aByte
     *            The byte
     * @return the HEX value as String
     */
    private static String getHexString(byte[] aByte) {
        StringBuffer sbuf = new StringBuffer(aByte.length * 3 + 2);

        for (int i = 0; i < aByte.length; i++) {
            sbuf.append(' ');
            sbuf.append(HEX_DIGITS[(aByte[i] & 0xF0) >> 4]);
            sbuf.append(HEX_DIGITS[aByte[i] & 0x0F]);
        }

        return new String(sbuf);
    }
    private static float convertTempo(float value) {
        if (value <= 0) {
            value = 0.1f;
        }
        return 60000000.0f / value;
    }
}

Related Tutorials