001    /* ShortMessage.java -- A MIDI message no longer than 3 bytes
002       Copyright (C) 2005 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.sound.midi;
040    
041    /**
042     * A short MIDI message that is no longer than 3 bytes long.
043     *
044     * @author Anthony Green (green@redhat.com)
045     * @since 1.3
046     *
047     */
048    public class ShortMessage extends MidiMessage
049    {
050      /**
051       * Status byte for Time Code message.
052       */
053      public static final int MIDI_TIME_CODE = 0xF1;
054    
055      /**
056       * Status byte for Song Position Pointer message.
057       */
058      public static final int SONG_POSITION_POINTER = 0xF2;
059    
060      /**
061       * Status byte for Song Select message.
062       */
063      public static final int SONG_SELECT = 0xF3;
064    
065      /**
066       * Status byte for Tune Request message.
067       */
068      public static final int TUNE_REQUEST = 0xF6;
069    
070      /**
071       * Status byte for End Of Exclusive message.
072       */
073      public static final int END_OF_EXCLUSIVE = 0xF7;
074    
075      /**
076       * Status byte for Timing Clock message.
077       */
078      public static final int TIMING_CLOCK = 0xF8;
079    
080      /**
081       * Status byte for Start message.
082       */
083      public static final int START = 0xFA;
084    
085      /**
086       * Status byte for Continue message.
087       */
088      public static final int CONTINUE = 0xFB;
089    
090      /**
091       * Status byte for Stop message.
092       */
093      public static final int STOP = 0xFC;
094    
095      /**
096       * Status byte for Active Sensing message.
097       */
098      public static final int ACTIVE_SENSING = 0xFE;
099    
100      /**
101       * Status byte for System Reset message.
102       */
103      public static final int SYSTEM_RESET = 0xFF;
104    
105      /**
106       * Status nibble for Note Off message.
107       */
108      public static final int NOTE_OFF = 0x80;
109    
110      /**
111       * Status nibble for Note On message.
112       */
113      public static final int NOTE_ON = 0x90;
114    
115      /**
116       * Status nibble for Poly Pressure message.
117       */
118      public static final int POLY_PRESSURE = 0xA0;
119    
120      /**
121       * Status nibble for Control Change message.
122       */
123      public static final int CONTROL_CHANGE = 0xB0;
124    
125      /**
126       * Status nibble for Program Change message.
127       */
128      public static final int PROGRAM_CHANGE = 0xC0;
129    
130      /**
131       * Statue nibble for Channel Pressure message.
132       */
133      public static final int CHANNEL_PRESSURE = 0xD0;
134    
135      /**
136       * Status nibble for Pitch Bend message.
137       */
138      public static final int PITCH_BEND = 0xE0;
139    
140      // Create and initialize a default, arbitrary message.
141      private static byte[] defaultMessage;
142      static
143      {
144        defaultMessage = new byte[1];
145        defaultMessage[0] = (byte) STOP;
146      }
147    
148      /**
149       * Create a short MIDI message.
150       *
151       * The spec requires that this represent a valid MIDI message, but doesn't
152       * specify what it should be.  We've chosen the STOP message for our
153       * implementation.
154       */
155      public ShortMessage()
156      {
157        this(defaultMessage);
158      }
159    
160      /**
161       * Create a short MIDI message.
162       *
163       * The data argument should be a valid MIDI message.  Unfortunately the spec
164       * does not allow us to throw an InvalidMidiDataException if data is invalid.
165       *
166       * @param data the message data
167       */
168      protected ShortMessage(byte[] data)
169      {
170        super(data);
171      }
172    
173      /**
174       * Set the MIDI message.
175       *
176       * @param status the status byte for this message
177       * @param data1 the first data byte for this message
178       * @param data2 the second data byte for this message
179       * @throws InvalidMidiDataException if status is bad, or data is out of range
180       */
181      public void setMessage(int status, int data1, int data2)
182        throws InvalidMidiDataException
183      {
184        length = getDataLength(status);
185        length++;
186        if (data == null || data.length < length)
187          data = new byte[length];
188        data[0] = (byte) status;
189        if (length > 1)
190        {
191          if (data1 < 0 || data1 > 127)
192            throw new InvalidMidiDataException("data1 (" + data1
193                                               + ") must be between 0 and 127.");
194          data[1] = (byte) data1;
195          if (length > 2)
196          {
197            if (data2 < 0 || data2 > 127)
198              throw new InvalidMidiDataException("data2 (" + data2
199                                                 + ") must be between 0 and 127.");
200            data[2] = (byte) data2;
201          }
202        }
203      }
204    
205      public void setMessage(int command, int channel, int data1, int data2)
206        throws InvalidMidiDataException
207      {
208        // TODO: This could probably stand some error checking.
209        // It currently assumes command and channel are valid values.
210        setMessage(command + channel, data1, data2);
211      }
212    
213      /**
214       * Set the MIDI message to one that requires no data bytes.
215       *
216       * @param status the status byte for this message
217       * @throws InvalidMidiDataException if status is bad, or requires data
218       */
219      public void setMessage(int status) throws InvalidMidiDataException
220      {
221        int length = getDataLength(status);
222        if (length != 0)
223          throw new InvalidMidiDataException("Status byte 0x"
224                                             + Integer.toHexString(status)
225                                             + " requires "
226                                             + length + " bytes of data.");
227        setMessage(status, 0, 0);
228      }
229    
230    
231      /**
232       * Return the number of data bytes needed for a given MIDI status byte.
233       *
234       * @param status the status byte for a short MIDI message
235       * @return the number of data bytes needed for this status byte
236       * @throws InvalidMidiDataException if status is an invalid status byte
237       */
238      protected final int getDataLength(int status) throws InvalidMidiDataException
239      {
240        int originalStatus = status;
241    
242        if ((status & 0xF0) != 0xF0)
243          status &= 0xF0;
244    
245        switch (status)
246        {
247        case NOTE_OFF:
248        case NOTE_ON:
249        case POLY_PRESSURE:
250        case CONTROL_CHANGE:
251        case PITCH_BEND:
252        case SONG_POSITION_POINTER:
253          return 2;
254    
255        case PROGRAM_CHANGE:
256        case CHANNEL_PRESSURE:
257        case SONG_SELECT:
258        case 0xF5:  // FIXME: unofficial bus select.  Not in spec??
259          return 1;
260    
261        case TUNE_REQUEST:
262        case END_OF_EXCLUSIVE:
263        case TIMING_CLOCK:
264        case START:
265        case CONTINUE:
266        case STOP:
267        case ACTIVE_SENSING:
268        case SYSTEM_RESET:
269          return 0;
270    
271        default:
272          throw new InvalidMidiDataException("Invalid status: 0x"
273                                             + Integer.toHexString(originalStatus));
274        }
275      }
276    
277      /**
278       * Get the channel information from this MIDI message, assuming it is a
279       * MIDI channel message.
280       *
281       * @return the MIDI channel for this message
282       */
283      public int getChannel()
284      {
285        return data[0] & 0x0F;
286      }
287    
288      /**
289       * Get the command nibble from this MIDI message, assuming it is a MIDI
290       * channel message.
291       *
292       * @return the MIDI command for this message
293       */
294      public int getCommand()
295      {
296        return data[0] & 0xF0;
297      }
298    
299      /**
300       * Get the first data byte from this message, assuming it exists, and
301       * zero otherwise.
302       *
303       * @return the first data byte or zero if none exists.
304       */
305      public int getData1()
306      {
307        if (length > 1)
308          return data[1];
309        else
310          return 0;
311      }
312    
313      /**
314       * Get the second data byte from this message, assuming it exists, and
315       * zero otherwise.
316       *
317       * @return the second date byte or zero if none exists.
318       */
319      public int getData2()
320      {
321        if (length > 2)
322          return data[2];
323        else
324          return 0;
325      }
326    
327      /* Create a deep-copy clone of this object.
328       * @see java.lang.Object#clone()
329       */
330      public Object clone()
331      {
332        byte message[] = new byte[length];
333        System.arraycopy(data, 0, message, 0, length);
334        return new ShortMessage(message);
335      }
336    }