1 /* 2 * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.sound.midi; 27 28 /** 29 * A {@code SysexMessage} object represents a MIDI system exclusive message. 30 * <p> 31 * When a system exclusive message is read from a MIDI file, it always has a 32 * defined length. Data from a system exclusive message from a MIDI file should 33 * be stored in the data array of a {@code SysexMessage} as follows: the system 34 * exclusive message status byte (0xF0 or 0xF7), all message data bytes, and 35 * finally the end-of-exclusive flag (0xF7). The length reported by the 36 * {@code SysexMessage} object is therefore the length of the system exclusive 37 * data plus two: one byte for the status byte and one for the end-of-exclusive 38 * flag. 39 * <p> 40 * As dictated by the Standard MIDI Files specification, two status byte values 41 * are legal for a {@code SysexMessage} read from a MIDI file: 42 * <ul> 43 * <li>0xF0: System Exclusive message (same as in MIDI wire protocol)</li> 44 * <li>0xF7: Special System Exclusive message</li> 45 * </ul> 46 * When Java Sound is used to handle system exclusive data that is being 47 * received using MIDI wire protocol, it should place the data in one or more 48 * {@code SysexMessages}. In this case, the length of the system exclusive data 49 * is not known in advance; the end of the system exclusive data is marked by an 50 * end-of-exclusive flag (0xF7) in the MIDI wire byte stream. 51 * <ul> 52 * <li>0xF0: System Exclusive message (same as in MIDI wire protocol)</li> 53 * <li>0xF7: End of Exclusive (EOX)</li> 54 * </ul> 55 * The first {@code SysexMessage} object containing data for a particular system 56 * exclusive message should have the status value 0xF0. If this message contains 57 * all the system exclusive data for the message, it should end with the status 58 * byte 0xF7 (EOX). Otherwise, additional system exclusive data should be sent 59 * in one or more {@code SysexMessages} with a status value of 0xF7. The 60 * {@code SysexMessage} containing the last of the data for the system exclusive 61 * message should end with the value 0xF7 (EOX) to mark the end of the system 62 * exclusive message. 63 * <p> 64 * If system exclusive data from {@code SysexMessages} objects is being 65 * transmitted using MIDI wire protocol, only the initial 0xF0 status byte, the 66 * system exclusive data itself, and the final 0xF7 (EOX) byte should be 67 * propagated; any 0xF7 status bytes used to indicate that a 68 * {@code SysexMessage} contains continuing system exclusive data should not be 69 * propagated via MIDI wire protocol. 70 * 71 * @author David Rivas 72 * @author Kara Kytle 73 * @author Florian Bomers 74 */ 75 public class SysexMessage extends MidiMessage { 76 77 // Status byte defines 78 79 /** 80 * Status byte for System Exclusive message (0xF0, or 240). 81 * 82 * @see MidiMessage#getStatus 83 */ 84 public static final int SYSTEM_EXCLUSIVE = 0xF0; // 240 85 86 /** 87 * Status byte for Special System Exclusive message (0xF7, or 247), which is 88 * used in MIDI files. It has the same value as END_OF_EXCLUSIVE, which is 89 * used in the real-time "MIDI wire" protocol. 90 * 91 * @see MidiMessage#getStatus 92 */ 93 public static final int SPECIAL_SYSTEM_EXCLUSIVE = 0xF7; // 247 94 95 /** 96 * The data bytes for this system exclusive message. These are initialized 97 * to {@code null} and are set explicitly by 98 * {@link #setMessage(int, byte[], int, long) setMessage}. 99 */ 100 //protected byte[] data = null; 101 102 /** 103 * Constructs a new {@code SysexMessage}. The contents of the new message 104 * are guaranteed to specify a valid MIDI message. Subsequently, you may set 105 * the contents of the message using one of the {@code setMessage} methods. 106 * 107 * @see #setMessage 108 */ 109 public SysexMessage() { 110 this(new byte[2]); 111 // Default sysex message data: SOX followed by EOX 112 data[0] = (byte) (SYSTEM_EXCLUSIVE & 0xFF); 113 data[1] = (byte) (ShortMessage.END_OF_EXCLUSIVE & 0xFF); 114 } 115 116 /** 117 * Constructs a new {@code SysexMessage} and sets the data for the message. 118 * The first byte of the data array must be a valid system exclusive status 119 * byte (0xF0 or 0xF7). The contents of the message can be changed by using 120 * one of the {@code setMessage} methods. 121 * 122 * @param data the system exclusive message data including the status byte 123 * @param length the length of the valid message data in the array, 124 * including the status byte; it should be non-negative and less 125 * than or equal to {@code data.length} 126 * @throws InvalidMidiDataException if the parameter values do not specify a 127 * valid MIDI meta message. 128 * @see #setMessage(byte[], int) 129 * @see #setMessage(int, byte[], int) 130 * @see #getData() 131 * @since 1.7 132 */ 133 public SysexMessage(byte[] data, int length) 134 throws InvalidMidiDataException { 135 super(null); 136 setMessage(data, length); 137 } 138 139 /** 140 * Constructs a new {@code SysexMessage} and sets the data for the message. 141 * The contents of the message can be changed by using one of the 142 * {@code setMessage} methods. 143 * 144 * @param status the status byte for the message; it must be a valid system 145 * exclusive status byte (0xF0 or 0xF7) 146 * @param data the system exclusive message data (without the status byte) 147 * @param length the length of the valid message data in the array; it 148 * should be non-negative and less than or equal to 149 * {@code data.length} 150 * @throws InvalidMidiDataException if the parameter values do not specify a 151 * valid MIDI meta message 152 * @see #setMessage(byte[], int) 153 * @see #setMessage(int, byte[], int) 154 * @see #getData() 155 * @since 1.7 156 */ 157 public SysexMessage(int status, byte[] data, int length) 158 throws InvalidMidiDataException { 159 super(null); 160 setMessage(status, data, length); 161 } 162 163 /** 164 * Constructs a new {@code SysexMessage}. 165 * 166 * @param data an array of bytes containing the complete message. The 167 * message data may be changed using the {@code setMessage} method. 168 * @see #setMessage 169 */ 170 protected SysexMessage(byte[] data) { 171 super(data); 172 } 173 174 /** 175 * Sets the data for the system exclusive message. The first byte of the 176 * data array must be a valid system exclusive status byte (0xF0 or 0xF7). 177 * 178 * @param data the system exclusive message data 179 * @param length the length of the valid message data in the array, 180 * including the status byte 181 */ 182 public void setMessage(byte[] data, int length) throws InvalidMidiDataException { 183 int status = (data[0] & 0xFF); 184 if ((status != 0xF0) && (status != 0xF7)) { 185 throw new InvalidMidiDataException("Invalid status byte for sysex message: 0x" + Integer.toHexString(status)); 186 } 187 super.setMessage(data, length); 188 } 189 190 /** 191 * Sets the data for the system exclusive message. 192 * 193 * @param status the status byte for the message (0xF0 or 0xF7) 194 * @param data the system exclusive message data 195 * @param length the length of the valid message data in the array 196 * @throws InvalidMidiDataException if the status byte is invalid for a 197 * sysex message 198 */ 199 public void setMessage(int status, byte[] data, int length) throws InvalidMidiDataException { 200 if ( (status != 0xF0) && (status != 0xF7) ) { 201 throw new InvalidMidiDataException("Invalid status byte for sysex message: 0x" + Integer.toHexString(status)); 202 } 203 if (length < 0 || length > data.length) { 204 throw new IndexOutOfBoundsException("length out of bounds: "+length); 205 } 206 this.length = length + 1; 207 208 if (this.data==null || this.data.length < this.length) { 209 this.data = new byte[this.length]; 210 } 211 212 this.data[0] = (byte) (status & 0xFF); 213 if (length > 0) { 214 System.arraycopy(data, 0, this.data, 1, length); 215 } 216 } 217 218 /** 219 * Obtains a copy of the data for the system exclusive message. The returned 220 * array of bytes does not include the status byte. 221 * 222 * @return array containing the system exclusive message data 223 */ 224 public byte[] getData() { 225 byte[] returnedArray = new byte[length - 1]; 226 System.arraycopy(data, 1, returnedArray, 0, (length - 1)); 227 return returnedArray; 228 } 229 230 /** 231 * Creates a new object of the same class and with the same contents as this 232 * object. 233 * 234 * @return a clone of this instance 235 */ 236 public Object clone() { 237 byte[] newData = new byte[length]; 238 System.arraycopy(data, 0, newData, 0, newData.length); 239 SysexMessage event = new SysexMessage(newData); 240 return event; 241 } 242 }