Java examples for javax.sound.midi:MidiMessage
Formats a midi message to a readable String.
/*/* 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; } }