/* * ShortMessage.java * * This file is part of Tritonus: http://www.tritonus.org/ */ /* * Copyright (c) 1999 -2002 by Matthias Pfisterer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as published * by the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* |<--- this code is formatted to fit into 80 columns --->| */ package javax.sound.midi; /** Simple MIDI message. This class is used to represent channel voice, control change, channel mode, system real time and system common messages. These are the messages that can be represented in at most three bytes (the status byte plus two data bytes). System exclusive and meta messages are stored in {@link SysexMessage SysexMessage} and {@link MetaMessage MetaMessage}, respective.<p> The way these messages are separated into bytes for storing in {@link MidiMessage#data data} is the same as specified in the MIDI wire protocol. */ public class ShortMessage extends MidiMessage { /** If true, for messages with one data byte it is checked that the second one is zero. This is some kind of debugging flag. */ private static final boolean CHECK_SECOND_BYTE = false; /** Status byte for note off messages (value 128, 0x80). */ public static final int NOTE_OFF = 0x80; /** Status byte for note on messages (value 144, 0x90). */ public static final int NOTE_ON = 0x90; /** Status byte for polyphonic key pressure (aftertouch) messages (value 160, 0xA0). */ public static final int POLY_PRESSURE = 0xA0; /** Status byte for controller cange messages (value 176, 0xB0). */ public static final int CONTROL_CHANGE = 0xB0; /** Status byte for program change messages (value 192, 0xC0). */ public static final int PROGRAM_CHANGE = 0xC0; /** Status byte for channel key pressure (aftertouch) messages (value 208, 0xD0). */ public static final int CHANNEL_PRESSURE = 0xD0; /** Status byte for pitch bend messages (value 224, 0xE0). */ public static final int PITCH_BEND = 0xE0; // system // in class SysexMessage: // public static final int SYSTEM_EXCLUSIVE = 0xF0; /** Status byte for MIDI time code quarter frame messages (value 241, 0xF1). */ public static final int MIDI_TIME_CODE = 0xF1; /** Status byte for song position pointer messages (value 242, 0xF2). */ public static final int SONG_POSITION_POINTER = 0xF2; /** Status byte for song select messages (value 243, 0xF3). */ public static final int SONG_SELECT = 0xF3; // not defined: 0xF4 // not defined: 0xF5 /** Status byte for tune request messages (value 246, 0xF6). */ public static final int TUNE_REQUEST = 0xF6; /** Status byte for end of system excusive messages (value 247, 0xF7). */ public static final int END_OF_EXCLUSIVE = 0xF7; // real-time messages /** Status byte for timing clock messages (value 248, 0xF8). */ public static final int TIMING_CLOCK = 0xF8; // not defined: 0xF9 /** Status byte for start messages (value 250, 0xFA). */ public static final int START = 0xFA; /** Status byte for continue messages (value 251, 0xFB). */ public static final int CONTINUE = 0xFB; /** Status byte for stop messages (value 252, 0xFC). */ public static final int STOP = 0xFC; // not defined: 0xFD /** Status byte for active sensing messages (value 254, 0xFE). */ public static final int ACTIVE_SENSING = 0xFE; /** Status byte for system reset messages (value 255, 0xFF). */ public static final int SYSTEM_RESET = 0xFF; /** Table for length of channel messages. This table is used by {@link #getDataLength() getDataLength()} to calculate the length of messages. The table is indexed with the high nibble of the status byte, if the status byte is < 0xF0. The values in the table are the number of data bytes (not including the status byte) or -1 if the index does not represent a status byte that is usable for ShortMessages. @see #getDataLength() */ private static final int sm_anChannelMessageLength[] = { -1, -1, -1, -1, -1, -1, -1, -1, 2, 2, 2, 2, 1, 1, 2 }; /* 8x 9x Ax Bx Cx Dx Ex */ /** Table for length of system messages. This table is used by {@link #getDataLength() getDataLength()} to calculate the length of messages. The table is indexed with the low nibble of the status byte, if the status byte is >= 0xF0. The values in the table are the number of data bytes (not including the status byte) or -1 if the index does not represent a status byte that is usable for ShortMessages. @see #getDataLength() */ private static final int sm_anSystemMessageLength[] = { -1, 2, 3, 2, -1, -1, 1, 0, 1, -1, 1, 1, 1, -1, 1, 1 }; /* F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF */ // /** Default ShortMessage. // This are the data bytes used to construct a ShortMessage // with the default constructor. // The message is chosen arbitrarily. Actually, it // not so much arbitrary: it is the same as in the Sun // implementation. // */ // private static final byte[] DEFAULT_MESSAGE = new byte[]{(byte) 144, (byte) 255, 0}; /** Create a container for a "short" MIDI message. This constructor does not create an object containing a legal MIDI message. You have to use one of the setMessage() methods. Before calling one of these methods, calling retrieval methods (getLength(), getMessage(), getStatus(), getChannel(), getCommand(), getData1(), getData2()) may have undesired results. @see #setMessage(int) @see #setMessage(int, int, int) @see #setMessage(int, int, int, int) */ public ShortMessage() { this(null); } /** Constructs a ShortMessage with a message set. does not check content of passed array!! TODO: */ protected ShortMessage(byte[] abData) { super(abData); } /** TODO: */ public Object clone() { byte[] abData = getMessage(); ShortMessage message = new ShortMessage(abData); return message; } /** TODO: */ public void setMessage(int nStatus) throws InvalidMidiDataException { if (getDataLength(nStatus) != 0) { throw new InvalidMidiDataException("not a one-byte command"); } byte[] abData = new byte[1]; abData[0] = (byte) nStatus; setMessage(abData, 1); } /** TODO: */ public void setMessage(int nStatus, int nData1, int nData2) throws InvalidMidiDataException { int nDataLength = getDataLength(nStatus); if (nDataLength < 1) { throw new InvalidMidiDataException("not a two- or three-byte command"); } byte[] abData = new byte[nDataLength + 1]; abData[0] = (byte) nStatus; abData[1] = (byte) nData1; if (nDataLength == 2) { abData[2] = (byte) nData2; } else if (nData2 != 0 && CHECK_SECOND_BYTE) { throw new InvalidMidiDataException("data2 should be 0 for a two-byte command"); } setMessage(abData, nDataLength + 1); } /** TODO: */ public void setMessage(int nCommand, int nChannel, int nData1, int nData2) throws InvalidMidiDataException { setMessage((nCommand & 0xF0) | (nChannel & 0x0F), nData1, nData2); } /** TODO: */ public int getChannel() { return (getStatus() & 0xF); } /** TODO: */ public int getCommand() { return getStatus() & 0xF0; } /** TODO: */ public int getData1() { return getDataByte(1); } /** TODO: */ public int getData2() { return getDataByte(2); } /** TODO: */ private int getDataByte(int nNumber) { int nDataByte = 0; if (getLength() >= nNumber) { nDataByte = getMessage()[nNumber]; } return nDataByte; } /** TODO: */ protected final int getDataLength(int nStatus) throws InvalidMidiDataException { int nDataLength = 0; if (nStatus < 0xF0) // channel voice message { nDataLength = sm_anChannelMessageLength[(nStatus >> 4) & 0xF]; } else { nDataLength = sm_anSystemMessageLength[nStatus & 0xF]; } if (nDataLength == -1) { throw new InvalidMidiDataException("status byte not usable for ShortMessage"); } return nDataLength; } } /*** ShortMessage.java ***/