Frames | No Frames |
1: /* ShortMessage.java -- A MIDI message no longer than 3 bytes 2: Copyright (C) 2005 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package javax.sound.midi; 40: 41: /** 42: * A short MIDI message that is no longer than 3 bytes long. 43: * 44: * @author Anthony Green (green@redhat.com) 45: * @since 1.3 46: * 47: */ 48: public class ShortMessage extends MidiMessage 49: { 50: /** 51: * Status byte for Time Code message. 52: */ 53: public static final int MIDI_TIME_CODE = 0xF1; 54: 55: /** 56: * Status byte for Song Position Pointer message. 57: */ 58: public static final int SONG_POSITION_POINTER = 0xF2; 59: 60: /** 61: * Status byte for Song Select message. 62: */ 63: public static final int SONG_SELECT = 0xF3; 64: 65: /** 66: * Status byte for Tune Request message. 67: */ 68: public static final int TUNE_REQUEST = 0xF6; 69: 70: /** 71: * Status byte for End Of Exclusive message. 72: */ 73: public static final int END_OF_EXCLUSIVE = 0xF7; 74: 75: /** 76: * Status byte for Timing Clock message. 77: */ 78: public static final int TIMING_CLOCK = 0xF8; 79: 80: /** 81: * Status byte for Start message. 82: */ 83: public static final int START = 0xFA; 84: 85: /** 86: * Status byte for Continue message. 87: */ 88: public static final int CONTINUE = 0xFB; 89: 90: /** 91: * Status byte for Stop message. 92: */ 93: public static final int STOP = 0xFC; 94: 95: /** 96: * Status byte for Active Sensing message. 97: */ 98: public static final int ACTIVE_SENSING = 0xFE; 99: 100: /** 101: * Status byte for System Reset message. 102: */ 103: public static final int SYSTEM_RESET = 0xFF; 104: 105: /** 106: * Status nibble for Note Off message. 107: */ 108: public static final int NOTE_OFF = 0x80; 109: 110: /** 111: * Status nibble for Note On message. 112: */ 113: public static final int NOTE_ON = 0x90; 114: 115: /** 116: * Status nibble for Poly Pressure message. 117: */ 118: public static final int POLY_PRESSURE = 0xA0; 119: 120: /** 121: * Status nibble for Control Change message. 122: */ 123: public static final int CONTROL_CHANGE = 0xB0; 124: 125: /** 126: * Status nibble for Program Change message. 127: */ 128: public static final int PROGRAM_CHANGE = 0xC0; 129: 130: /** 131: * Statue nibble for Channel Pressure message. 132: */ 133: public static final int CHANNEL_PRESSURE = 0xD0; 134: 135: /** 136: * Status nibble for Pitch Bend message. 137: */ 138: public static final int PITCH_BEND = 0xE0; 139: 140: // Create and initialize a default, arbitrary message. 141: private static byte[] defaultMessage; 142: static 143: { 144: defaultMessage = new byte[1]; 145: defaultMessage[0] = (byte) STOP; 146: } 147: 148: /** 149: * Create a short MIDI message. 150: * 151: * The spec requires that this represent a valid MIDI message, but doesn't 152: * specify what it should be. We've chosen the STOP message for our 153: * implementation. 154: */ 155: public ShortMessage() 156: { 157: this(defaultMessage); 158: } 159: 160: /** 161: * Create a short MIDI message. 162: * 163: * The data argument should be a valid MIDI message. Unfortunately the spec 164: * does not allow us to throw an InvalidMidiDataException if data is invalid. 165: * 166: * @param data the message data 167: */ 168: protected ShortMessage(byte[] data) 169: { 170: super(data); 171: } 172: 173: /** 174: * Set the MIDI message. 175: * 176: * @param status the status byte for this message 177: * @param data1 the first data byte for this message 178: * @param data2 the second data byte for this message 179: * @throws InvalidMidiDataException if status is bad, or data is out of range 180: */ 181: public void setMessage(int status, int data1, int data2) 182: throws InvalidMidiDataException 183: { 184: length = getDataLength(status); 185: length++; 186: if (data == null || data.length < length) 187: data = new byte[length]; 188: data[0] = (byte) status; 189: if (length > 1) 190: { 191: if (data1 < 0 || data1 > 127) 192: throw new InvalidMidiDataException("data1 (" + data1 193: + ") must be between 0 and 127."); 194: data[1] = (byte) data1; 195: if (length > 2) 196: { 197: if (data2 < 0 || data2 > 127) 198: throw new InvalidMidiDataException("data2 (" + data2 199: + ") must be between 0 and 127."); 200: data[2] = (byte) data2; 201: } 202: } 203: } 204: 205: public void setMessage(int command, int channel, int data1, int data2) 206: throws InvalidMidiDataException 207: { 208: // TODO: This could probably stand some error checking. 209: // It currently assumes command and channel are valid values. 210: setMessage(command + channel, data1, data2); 211: } 212: 213: /** 214: * Set the MIDI message to one that requires no data bytes. 215: * 216: * @param status the status byte for this message 217: * @throws InvalidMidiDataException if status is bad, or requires data 218: */ 219: public void setMessage(int status) throws InvalidMidiDataException 220: { 221: int length = getDataLength(status); 222: if (length != 0) 223: throw new InvalidMidiDataException("Status byte 0x" 224: + Integer.toHexString(status) 225: + " requires " 226: + length + " bytes of data."); 227: setMessage(status, 0, 0); 228: } 229: 230: 231: /** 232: * Return the number of data bytes needed for a given MIDI status byte. 233: * 234: * @param status the status byte for a short MIDI message 235: * @return the number of data bytes needed for this status byte 236: * @throws InvalidMidiDataException if status is an invalid status byte 237: */ 238: protected final int getDataLength(int status) throws InvalidMidiDataException 239: { 240: int originalStatus = status; 241: 242: if ((status & 0xF0) != 0xF0) 243: status &= 0xF0; 244: 245: switch (status) 246: { 247: case NOTE_OFF: 248: case NOTE_ON: 249: case POLY_PRESSURE: 250: case CONTROL_CHANGE: 251: case PITCH_BEND: 252: case SONG_POSITION_POINTER: 253: return 2; 254: 255: case PROGRAM_CHANGE: 256: case CHANNEL_PRESSURE: 257: case SONG_SELECT: 258: case 0xF5: // FIXME: unofficial bus select. Not in spec?? 259: return 1; 260: 261: case TUNE_REQUEST: 262: case END_OF_EXCLUSIVE: 263: case TIMING_CLOCK: 264: case START: 265: case CONTINUE: 266: case STOP: 267: case ACTIVE_SENSING: 268: case SYSTEM_RESET: 269: return 0; 270: 271: default: 272: throw new InvalidMidiDataException("Invalid status: 0x" 273: + Integer.toHexString(originalStatus)); 274: } 275: } 276: 277: /** 278: * Get the channel information from this MIDI message, assuming it is a 279: * MIDI channel message. 280: * 281: * @return the MIDI channel for this message 282: */ 283: public int getChannel() 284: { 285: return data[0] & 0x0F; 286: } 287: 288: /** 289: * Get the command nibble from this MIDI message, assuming it is a MIDI 290: * channel message. 291: * 292: * @return the MIDI command for this message 293: */ 294: public int getCommand() 295: { 296: return data[0] & 0xF0; 297: } 298: 299: /** 300: * Get the first data byte from this message, assuming it exists, and 301: * zero otherwise. 302: * 303: * @return the first data byte or zero if none exists. 304: */ 305: public int getData1() 306: { 307: if (length > 1) 308: return data[1]; 309: else 310: return 0; 311: } 312: 313: /** 314: * Get the second data byte from this message, assuming it exists, and 315: * zero otherwise. 316: * 317: * @return the second date byte or zero if none exists. 318: */ 319: public int getData2() 320: { 321: if (length > 2) 322: return data[2]; 323: else 324: return 0; 325: } 326: 327: /* Create a deep-copy clone of this object. 328: * @see java.lang.Object#clone() 329: */ 330: public Object clone() 331: { 332: byte message[] = new byte[length]; 333: System.arraycopy(data, 0, message, 0, length); 334: return new ShortMessage(message); 335: } 336: }