1 /* 2 * Copyright (c) 1998, 2019, 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 import com.sun.media.sound.MidiUtils; 29 30 /** 31 * A {@code SysexMessage} object represents a MIDI system exclusive message. 32 * <p> 33 * When a system exclusive message is read from a MIDI file, it always has a 34 * defined length. Data from a system exclusive message from a MIDI file should 35 * be stored in the data array of a {@code SysexMessage} as follows: the system 36 * exclusive message status byte (0xF0 or 0xF7), all message data bytes, and 37 * finally the end-of-exclusive flag (0xF7). The length reported by the 38 * {@code SysexMessage} object is therefore the length of the system exclusive 39 * data plus two: one byte for the status byte and one for the end-of-exclusive 40 * flag. 41 * <p> 42 * As dictated by the Standard MIDI Files specification, two status byte values 43 * are legal for a {@code SysexMessage} read from a MIDI file: 44 * <ul> 45 * <li>0xF0: System Exclusive message (same as in MIDI wire protocol) 46 * <li>0xF7: Special System Exclusive message 47 * </ul> 48 * When Java Sound is used to handle system exclusive data that is being 49 * received using MIDI wire protocol, it should place the data in one or more 50 * {@code SysexMessages}. In this case, the length of the system exclusive data 51 * is not known in advance; the end of the system exclusive data is marked by an 52 * end-of-exclusive flag (0xF7) in the MIDI wire byte stream. 53 * <ul> 54 * <li>0xF0: System Exclusive message (same as in MIDI wire protocol) 55 * <li>0xF7: End of Exclusive (EOX) 56 * </ul> 57 * The first {@code SysexMessage} object containing data for a particular system 58 * exclusive message should have the status value 0xF0. If this message contains 59 * all the system exclusive data for the message, it should end with the status 60 * byte 0xF7 (EOX). Otherwise, additional system exclusive data should be sent 61 * in one or more {@code SysexMessages} with a status value of 0xF7. The 62 * {@code SysexMessage} containing the last of the data for the system exclusive 63 * message should end with the value 0xF7 (EOX) to mark the end of the system 64 * exclusive message. 65 * <p> 66 * If system exclusive data from {@code SysexMessages} objects is being 67 * transmitted using MIDI wire protocol, only the initial 0xF0 status byte, the 68 * system exclusive data itself, and the final 0xF7 (EOX) byte should be 69 * propagated; any 0xF7 status bytes used to indicate that a 70 * {@code SysexMessage} contains continuing system exclusive data should not be 71 * propagated via MIDI wire protocol. 72 * 73 * @author David Rivas 74 * @author Kara Kytle 75 * @author Florian Bomers 76 */ 77 public class SysexMessage extends MidiMessage { 78 79 // Status byte defines 80 81 /** 82 * Status byte for System Exclusive message (0xF0, or 240). 83 * 84 * @see MidiMessage#getStatus 85 */ 86 public static final int SYSTEM_EXCLUSIVE = 0xF0; // 240 87 88 /** 89 * Status byte for Special System Exclusive message (0xF7, or 247), which is 90 * used in MIDI files. It has the same value as END_OF_EXCLUSIVE, which is 91 * used in the real-time "MIDI wire" protocol. 92 * 93 * @see MidiMessage#getStatus 94 */ 95 public static final int SPECIAL_SYSTEM_EXCLUSIVE = 0xF7; // 247 96 97 /** 98 * The data bytes for this system exclusive message. These are initialized 99 * to {@code null} and are set explicitly by 100 * {@link #setMessage(int, byte[], int, long) setMessage}. 101 */ 102 //protected byte[] data = null; 103 104 /** 105 * Constructs a new {@code SysexMessage}. The contents of the new message 106 * are guaranteed to specify a valid MIDI message. Subsequently, you may set 107 * the contents of the message using one of the {@code setMessage} methods. 108 * 109 * @see #setMessage 110 */ SysexMessage()111 public SysexMessage() { 112 this(new byte[2]); 113 // Default sysex message data: SOX followed by EOX 114 data[0] = (byte) (SYSTEM_EXCLUSIVE & 0xFF); 115 data[1] = (byte) (ShortMessage.END_OF_EXCLUSIVE & 0xFF); 116 } 117 118 /** 119 * Constructs a new {@code SysexMessage} and sets the data for the message. 120 * The first byte of the data array must be a valid system exclusive status 121 * byte (0xF0 or 0xF7). The contents of the message can be changed by using 122 * one of the {@code setMessage} methods. 123 * 124 * @param data the system exclusive message data including the status byte 125 * @param length the length of the valid message data in the array, 126 * including the status byte; it should be non-negative and less 127 * than or equal to {@code data.length} 128 * @throws InvalidMidiDataException if the parameter values do not specify a 129 * valid MIDI meta message 130 * @see #setMessage(byte[], int) 131 * @see #setMessage(int, byte[], int) 132 * @see #getData() 133 * @since 1.7 134 */ SysexMessage(byte[] data, int length)135 public SysexMessage(byte[] data, int length) 136 throws InvalidMidiDataException { 137 super(null); 138 setMessage(data, length); 139 } 140 141 /** 142 * Constructs a new {@code SysexMessage} and sets the data for the message. 143 * The contents of the message can be changed by using one of the 144 * {@code setMessage} methods. 145 * 146 * @param status the status byte for the message; it must be a valid system 147 * exclusive status byte (0xF0 or 0xF7) 148 * @param data the system exclusive message data (without the status byte) 149 * @param length the length of the valid message data in the array; it 150 * should be non-negative and less than or equal to 151 * {@code data.length} 152 * @throws InvalidMidiDataException if the parameter values do not specify a 153 * valid MIDI system exclusive message 154 * @see #setMessage(byte[], int) 155 * @see #setMessage(int, byte[], int) 156 * @see #getData() 157 * @since 1.7 158 */ SysexMessage(int status, byte[] data, int length)159 public SysexMessage(int status, byte[] data, int length) 160 throws InvalidMidiDataException { 161 super(null); 162 setMessage(status, data, length); 163 } 164 165 /** 166 * Constructs a new {@code SysexMessage}. 167 * 168 * @param data an array of bytes containing the complete message. The 169 * message data may be changed using the {@code setMessage} method. 170 * @see #setMessage 171 */ SysexMessage(byte[] data)172 protected SysexMessage(byte[] data) { 173 super(data); 174 } 175 176 /** 177 * Sets the data for the system exclusive message. The first byte of the 178 * data array must be a valid system exclusive status byte (0xF0 or 0xF7). 179 * 180 * @param data the system exclusive message data 181 * @param length the length of the valid message data in the array, 182 * including the status byte 183 * @throws InvalidMidiDataException if the parameter values do not specify a 184 * valid MIDI system exclusive message 185 */ 186 @Override setMessage(byte[] data, int length)187 public void setMessage(byte[] data, int length) throws InvalidMidiDataException { 188 MidiUtils.checkSysexStatus(data, length); 189 super.setMessage(data, length); 190 } 191 192 /** 193 * Sets the data for the system exclusive message. 194 * 195 * @param status the status byte for the message (0xF0 or 0xF7) 196 * @param data the system exclusive message data 197 * @param length the length of the valid message data in the array 198 * @throws InvalidMidiDataException if the status byte is invalid for a 199 * system exclusive message 200 */ setMessage(int status, byte[] data, int length)201 public void setMessage(int status, byte[] data, int length) throws InvalidMidiDataException { 202 MidiUtils.checkSysexStatus(status); 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 */ getData()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 @Override clone()237 public Object clone() { 238 byte[] newData = new byte[length]; 239 System.arraycopy(data, 0, newData, 0, newData.length); 240 return new SysexMessage(newData); 241 } 242 } 243