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 @Override 183 public void setMessage(byte[] data, int length) throws InvalidMidiDataException { 184 int status = (data[0] & 0xFF); 185 if ((status != 0xF0) && (status != 0xF7)) { 186 throw new InvalidMidiDataException("Invalid status byte for sysex message: 0x" + Integer.toHexString(status)); 187 } 188 super.setMessage(data, length); 189 } 190 191 /** 192 * Sets the data for the system exclusive message. 193 * 194 * @param status the status byte for the message (0xF0 or 0xF7) 195 * @param data the system exclusive message data 196 * @param length the length of the valid message data in the array 197 * @throws InvalidMidiDataException if the status byte is invalid for a 198 * sysex message 199 */ 200 public void setMessage(int status, byte[] data, int length) throws InvalidMidiDataException { 201 if ( (status != 0xF0) && (status != 0xF7) ) { 202 throw new InvalidMidiDataException("Invalid status byte for sysex message: 0x" + Integer.toHexString(status)); 203 } 204 if (length < 0 || length > data.length) { 205 throw new IndexOutOfBoundsException("length out of bounds: "+length); 206 } 207 this.length = length + 1; 208 209 if (this.data==null || this.data.length < this.length) { 210 this.data = new byte[this.length]; 211 } 212 213 this.data[0] = (byte) (status & 0xFF); 214 if (length > 0) { 215 System.arraycopy(data, 0, this.data, 1, length); 216 } 217 } 218 219 /** 220 * Obtains a copy of the data for the system exclusive message. The returned 221 * array of bytes does not include the status byte. 222 * 223 * @return array containing the system exclusive message data 224 */ 225 public byte[] getData() { 226 byte[] returnedArray = new byte[length - 1]; 227 System.arraycopy(data, 1, returnedArray, 0, (length - 1)); 228 return returnedArray; 229 } 230 231 /** 232 * Creates a new object of the same class and with the same contents as this 233 * object. 234 * 235 * @return a clone of this instance 236 */ 237 @Override 238 public Object clone() { 239 byte[] newData = new byte[length]; 240 System.arraycopy(data, 0, newData, 0, newData.length); 241 SysexMessage event = new SysexMessage(newData); 242 return event; 243 } 244 }