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 }