1 /*
   2  * Copyright (c) 1998, 2013, 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</code> object represents a MIDI system exclusive message.
  30  * <p>
  31  * When a system exclusive message is read from a MIDI file, it always has
  32  * a defined length.  Data from a system exclusive message from a MIDI file
  33  * should be stored in the data array of a <code>SysexMessage</code> as
  34  * follows: the system exclusive message status byte (0xF0 or 0xF7), all
  35  * message data bytes, and finally the end-of-exclusive flag (0xF7).
  36  * The length reported by the <code>SysexMessage</code> object is therefore
  37  * the length of the system exclusive data plus two: one byte for the status
  38  * byte and one for the end-of-exclusive flag.
  39  * <p>
  40  * As dictated by the Standard MIDI Files specification, two status byte values are legal
  41  * for a <code>SysexMessage</code> 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  * <p>
  47  * When Java Sound is used to handle system exclusive data that is being received
  48  * using MIDI wire protocol, it should place the data in one or more
  49  * <code>SysexMessages</code>.  In this case, the length of the system exclusive data
  50  * is not known in advance; the end of the system exclusive data is marked by an
  51  * end-of-exclusive flag (0xF7) in the MIDI wire byte stream.
  52  * <ul>
  53  * <li>0xF0: System Exclusive message (same as in MIDI wire protocol)</li>
  54  * <li>0xF7: End of Exclusive (EOX)</li>
  55  * </ul>
  56  * The first <code>SysexMessage</code> object containing data for a particular system
  57  * exclusive message should have the status value 0xF0.  If this message contains all
  58  * the system exclusive data
  59  * for the message, it should end with the status byte 0xF7 (EOX).
  60  * Otherwise, additional system exclusive data should be sent in one or more
  61  * <code>SysexMessages</code> with a status value of 0xF7.  The <code>SysexMessage</code>
  62  * containing the last of the data for the system exclusive message should end with the
  63  * value 0xF7 (EOX) to mark the end of the system exclusive message.
  64  * <p>
  65  * If system exclusive data from <code>SysexMessages</code> objects is being transmitted
  66  * using MIDI wire protocol, only the initial 0xF0 status byte, the system exclusive
  67  * data itself, and the final 0xF7 (EOX) byte should be propagated; any 0xF7 status
  68  * bytes used to indicate that a <code>SysexMessage</code> contains continuing system
  69  * exclusive data should not be 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 
  78     // Status byte defines
  79 
  80 
  81     /**
  82      * Status byte for System Exclusive message (0xF0, or 240).
  83      * @see MidiMessage#getStatus
  84      */
  85     public static final int SYSTEM_EXCLUSIVE                    = 0xF0; // 240
  86 
  87 
  88     /**
  89      * Status byte for Special System Exclusive message (0xF7, or 247), which is used
  90      * in MIDI files.  It has the same value as END_OF_EXCLUSIVE, which
  91      * is used in the real-time "MIDI wire" protocol.
  92      * @see MidiMessage#getStatus
  93      */
  94     public static final int SPECIAL_SYSTEM_EXCLUSIVE    = 0xF7; // 247
  95 
  96 
  97     // Instance variables
  98 
  99 
 100     /*
 101      * The data bytes for this system exclusive message.  These are
 102      * initialized to <code>null</code> and are set explicitly
 103      * by {@link #setMessage(int, byte[], int, long) setMessage}.
 104      */
 105     //protected byte[] data = null;
 106 
 107 
 108     /**
 109      * Constructs a new <code>SysexMessage</code>. The
 110      * contents of the new message are guaranteed to specify
 111      * a valid MIDI message.  Subsequently, you may set the
 112      * contents of the message using one of the <code>setMessage</code>
 113      * methods.
 114      * @see #setMessage
 115      */
 116     public SysexMessage() {
 117         this(new byte[2]);
 118         // Default sysex message data: SOX followed by EOX
 119         data[0] = (byte) (SYSTEM_EXCLUSIVE & 0xFF);
 120         data[1] = (byte) (ShortMessage.END_OF_EXCLUSIVE & 0xFF);
 121     }
 122 
 123     /**
 124      * Constructs a new {@code SysexMessage} and sets the data for
 125      * the message. The first byte of the data array must be a valid system
 126      * exclusive status byte (0xF0 or 0xF7).
 127      * The contents of the message can be changed by using one of
 128      * the {@code setMessage} methods.
 129      *
 130      * @param data the system exclusive message data including the status byte
 131      * @param length the length of the valid message data in the array,
 132      *     including the status byte; it should be non-negative and less than
 133      *     or equal to {@code data.length}
 134      * @throws InvalidMidiDataException if the parameter values
 135      *     do not specify a valid MIDI meta message.
 136      * @see #setMessage(byte[], int)
 137      * @see #setMessage(int, byte[], int)
 138      * @see #getData()
 139      * @since 1.7
 140      */
 141     public SysexMessage(byte[] data, int length)
 142             throws InvalidMidiDataException {
 143         super(null);
 144         setMessage(data, length);
 145     }
 146 
 147     /**
 148      * Constructs a new {@code SysexMessage} and sets the data for the message.
 149      * The contents of the message can be changed by using one of
 150      * the {@code setMessage} methods.
 151      *
 152      * @param status the status byte for the message; it must be a valid system
 153      *     exclusive status byte (0xF0 or 0xF7)
 154      * @param data the system exclusive message data (without the status byte)
 155      * @param length the length of the valid message data in the array;
 156      *     it should be non-negative and less than or equal to
 157      *     {@code data.length}
 158      * @throws InvalidMidiDataException if the parameter values
 159      *     do not specify a valid MIDI meta message.
 160      * @see #setMessage(byte[], int)
 161      * @see #setMessage(int, byte[], int)
 162      * @see #getData()
 163      * @since 1.7
 164      */
 165     public SysexMessage(int status, byte[] data, int length)
 166             throws InvalidMidiDataException {
 167         super(null);
 168         setMessage(status, data, length);
 169     }
 170 
 171 
 172     /**
 173      * Constructs a new <code>SysexMessage</code>.
 174      * @param data an array of bytes containing the complete message.
 175      * The message data may be changed using the <code>setMessage</code>
 176      * method.
 177      * @see #setMessage
 178      */
 179     protected SysexMessage(byte[] data) {
 180         super(data);
 181     }
 182 
 183 
 184     /**
 185      * Sets the data for the system exclusive message.   The
 186      * first byte of the data array must be a valid system
 187      * exclusive status byte (0xF0 or 0xF7).
 188      * @param data the system exclusive message data
 189      * @param length the length of the valid message data in
 190      * the array, including the status byte.
 191      */
 192     public void setMessage(byte[] data, int length) throws InvalidMidiDataException {
 193         int status = (data[0] & 0xFF);
 194         if ((status != 0xF0) && (status != 0xF7)) {
 195             throw new InvalidMidiDataException("Invalid status byte for sysex message: 0x" + Integer.toHexString(status));
 196         }
 197         super.setMessage(data, length);
 198     }
 199 
 200 
 201     /**
 202      * Sets the data for the system exclusive message.
 203      * @param status the status byte for the message (0xF0 or 0xF7)
 204      * @param data the system exclusive message data
 205      * @param length the length of the valid message data in
 206      * the array
 207      * @throws InvalidMidiDataException if the status byte is invalid for a sysex message
 208      */
 209     public void setMessage(int status, byte[] data, int length) throws InvalidMidiDataException {
 210         if ( (status != 0xF0) && (status != 0xF7) ) {
 211             throw new InvalidMidiDataException("Invalid status byte for sysex message: 0x" + Integer.toHexString(status));
 212         }
 213         if (length < 0 || length > data.length) {
 214             throw new IndexOutOfBoundsException("length out of bounds: "+length);
 215         }
 216         this.length = length + 1;
 217 
 218         if (this.data==null || this.data.length < this.length) {
 219             this.data = new byte[this.length];
 220         }
 221 
 222         this.data[0] = (byte) (status & 0xFF);
 223         if (length > 0) {
 224             System.arraycopy(data, 0, this.data, 1, length);
 225         }
 226     }
 227 
 228 
 229     /**
 230      * Obtains a copy of the data for the system exclusive message.
 231      * The returned array of bytes does not include the status byte.
 232      * @return array containing the system exclusive message data.
 233      */
 234     public byte[] getData() {
 235         byte[] returnedArray = new byte[length - 1];
 236         System.arraycopy(data, 1, returnedArray, 0, (length - 1));
 237         return returnedArray;
 238     }
 239 
 240 
 241     /**
 242      * Creates a new object of the same class and with the same contents
 243      * as this object.
 244      * @return a clone of this instance
 245      */
 246     public Object clone() {
 247         byte[] newData = new byte[length];
 248         System.arraycopy(data, 0, newData, 0, newData.length);
 249         SysexMessage event = new SysexMessage(newData);
 250         return event;
 251     }
 252 }