337 lines
8.9 KiB
Java
337 lines
8.9 KiB
Java
/* ShortMessage.java -- A MIDI message no longer than 3 bytes
|
|
Copyright (C) 2005 Free Software Foundation, Inc.
|
|
|
|
This file is part of GNU Classpath.
|
|
|
|
GNU Classpath is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
any later version.
|
|
|
|
GNU Classpath is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GNU Classpath; see the file COPYING. If not, write to the
|
|
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
02110-1301 USA.
|
|
|
|
Linking this library statically or dynamically with other modules is
|
|
making a combined work based on this library. Thus, the terms and
|
|
conditions of the GNU General Public License cover the whole
|
|
combination.
|
|
|
|
As a special exception, the copyright holders of this library give you
|
|
permission to link this library with independent modules to produce an
|
|
executable, regardless of the license terms of these independent
|
|
modules, and to copy and distribute the resulting executable under
|
|
terms of your choice, provided that you also meet, for each linked
|
|
independent module, the terms and conditions of the license of that
|
|
module. An independent module is a module which is not derived from
|
|
or based on this library. If you modify this library, you may extend
|
|
this exception to your version of the library, but you are not
|
|
obligated to do so. If you do not wish to do so, delete this
|
|
exception statement from your version. */
|
|
|
|
|
|
package javax.sound.midi;
|
|
|
|
/**
|
|
* A short MIDI message that is no longer than 3 bytes long.
|
|
*
|
|
* @author Anthony Green (green@redhat.com)
|
|
* @since 1.3
|
|
*
|
|
*/
|
|
public class ShortMessage extends MidiMessage
|
|
{
|
|
/**
|
|
* Status byte for Time Code message.
|
|
*/
|
|
public static final int MIDI_TIME_CODE = 0xF1;
|
|
|
|
/**
|
|
* Status byte for Song Position Pointer message.
|
|
*/
|
|
public static final int SONG_POSITION_POINTER = 0xF2;
|
|
|
|
/**
|
|
* Status byte for Song Select message.
|
|
*/
|
|
public static final int SONG_SELECT = 0xF3;
|
|
|
|
/**
|
|
* Status byte for Tune Request message.
|
|
*/
|
|
public static final int TUNE_REQUEST = 0xF6;
|
|
|
|
/**
|
|
* Status byte for End Of Exclusive message.
|
|
*/
|
|
public static final int END_OF_EXCLUSIVE = 0xF7;
|
|
|
|
/**
|
|
* Status byte for Timing Clock message.
|
|
*/
|
|
public static final int TIMING_CLOCK = 0xF8;
|
|
|
|
/**
|
|
* Status byte for Start message.
|
|
*/
|
|
public static final int START = 0xFA;
|
|
|
|
/**
|
|
* Status byte for Continue message.
|
|
*/
|
|
public static final int CONTINUE = 0xFB;
|
|
|
|
/**
|
|
* Status byte for Stop message.
|
|
*/
|
|
public static final int STOP = 0xFC;
|
|
|
|
/**
|
|
* Status byte for Active Sensing message.
|
|
*/
|
|
public static final int ACTIVE_SENSING = 0xFE;
|
|
|
|
/**
|
|
* Status byte for System Reset message.
|
|
*/
|
|
public static final int SYSTEM_RESET = 0xFF;
|
|
|
|
/**
|
|
* Status nibble for Note Off message.
|
|
*/
|
|
public static final int NOTE_OFF = 0x80;
|
|
|
|
/**
|
|
* Status nibble for Note On message.
|
|
*/
|
|
public static final int NOTE_ON = 0x90;
|
|
|
|
/**
|
|
* Status nibble for Poly Pressure message.
|
|
*/
|
|
public static final int POLY_PRESSURE = 0xA0;
|
|
|
|
/**
|
|
* Status nibble for Control Change message.
|
|
*/
|
|
public static final int CONTROL_CHANGE = 0xB0;
|
|
|
|
/**
|
|
* Status nibble for Program Change message.
|
|
*/
|
|
public static final int PROGRAM_CHANGE = 0xC0;
|
|
|
|
/**
|
|
* Statue nibble for Channel Pressure message.
|
|
*/
|
|
public static final int CHANNEL_PRESSURE = 0xD0;
|
|
|
|
/**
|
|
* Status nibble for Pitch Bend message.
|
|
*/
|
|
public static final int PITCH_BEND = 0xE0;
|
|
|
|
// Create and initialize a default, arbitrary message.
|
|
private static byte[] defaultMessage;
|
|
static
|
|
{
|
|
defaultMessage = new byte[1];
|
|
defaultMessage[0] = (byte) STOP;
|
|
}
|
|
|
|
/**
|
|
* Create a short MIDI message.
|
|
*
|
|
* The spec requires that this represent a valid MIDI message, but doesn't
|
|
* specify what it should be. We've chosen the STOP message for our
|
|
* implementation.
|
|
*/
|
|
public ShortMessage()
|
|
{
|
|
this(defaultMessage);
|
|
}
|
|
|
|
/**
|
|
* Create a short MIDI message.
|
|
*
|
|
* The data argument should be a valid MIDI message. Unfortunately the spec
|
|
* does not allow us to throw an InvalidMidiDataException if data is invalid.
|
|
*
|
|
* @param data the message data
|
|
*/
|
|
protected ShortMessage(byte[] data)
|
|
{
|
|
super(data);
|
|
}
|
|
|
|
/**
|
|
* Set the MIDI message.
|
|
*
|
|
* @param status the status byte for this message
|
|
* @param data1 the first data byte for this message
|
|
* @param data2 the second data byte for this message
|
|
* @throws InvalidMidiDataException if status is bad, or data is out of range
|
|
*/
|
|
public void setMessage(int status, int data1, int data2)
|
|
throws InvalidMidiDataException
|
|
{
|
|
length = getDataLength(status);
|
|
length++;
|
|
if (data == null || data.length < length)
|
|
data = new byte[length];
|
|
data[0] = (byte) status;
|
|
if (length > 1)
|
|
{
|
|
if (data1 < 0 || data1 > 127)
|
|
throw new InvalidMidiDataException("data1 (" + data1
|
|
+ ") must be between 0 and 127.");
|
|
data[1] = (byte) data1;
|
|
if (length > 2)
|
|
{
|
|
if (data2 < 0 || data2 > 127)
|
|
throw new InvalidMidiDataException("data2 (" + data2
|
|
+ ") must be between 0 and 127.");
|
|
data[2] = (byte) data2;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void setMessage(int command, int channel, int data1, int data2)
|
|
throws InvalidMidiDataException
|
|
{
|
|
// TODO: This could probably stand some error checking.
|
|
// It currently assumes command and channel are valid values.
|
|
setMessage(command + channel, data1, data2);
|
|
}
|
|
|
|
/**
|
|
* Set the MIDI message to one that requires no data bytes.
|
|
*
|
|
* @param status the status byte for this message
|
|
* @throws InvalidMidiDataException if status is bad, or requires data
|
|
*/
|
|
public void setMessage(int status) throws InvalidMidiDataException
|
|
{
|
|
int length = getDataLength(status);
|
|
if (length != 0)
|
|
throw new InvalidMidiDataException("Status byte 0x"
|
|
+ Integer.toHexString(status)
|
|
+ " requires "
|
|
+ length + " bytes of data.");
|
|
setMessage(status, 0, 0);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the number of data bytes needed for a given MIDI status byte.
|
|
*
|
|
* @param status the status byte for a short MIDI message
|
|
* @return the number of data bytes needed for this status byte
|
|
* @throws InvalidMidiDataException if status is an invalid status byte
|
|
*/
|
|
protected final int getDataLength(int status) throws InvalidMidiDataException
|
|
{
|
|
int originalStatus = status;
|
|
|
|
if ((status & 0xF0) != 0xF0)
|
|
status &= 0xF0;
|
|
|
|
switch (status)
|
|
{
|
|
case NOTE_OFF:
|
|
case NOTE_ON:
|
|
case POLY_PRESSURE:
|
|
case CONTROL_CHANGE:
|
|
case PITCH_BEND:
|
|
case SONG_POSITION_POINTER:
|
|
return 2;
|
|
|
|
case PROGRAM_CHANGE:
|
|
case CHANNEL_PRESSURE:
|
|
case SONG_SELECT:
|
|
case 0xF5: // FIXME: unofficial bus select. Not in spec??
|
|
return 1;
|
|
|
|
case TUNE_REQUEST:
|
|
case END_OF_EXCLUSIVE:
|
|
case TIMING_CLOCK:
|
|
case START:
|
|
case CONTINUE:
|
|
case STOP:
|
|
case ACTIVE_SENSING:
|
|
case SYSTEM_RESET:
|
|
return 0;
|
|
|
|
default:
|
|
throw new InvalidMidiDataException("Invalid status: 0x"
|
|
+ Integer.toHexString(originalStatus));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the channel information from this MIDI message, assuming it is a
|
|
* MIDI channel message.
|
|
*
|
|
* @return the MIDI channel for this message
|
|
*/
|
|
public int getChannel()
|
|
{
|
|
return data[0] & 0x0F;
|
|
}
|
|
|
|
/**
|
|
* Get the command nibble from this MIDI message, assuming it is a MIDI
|
|
* channel message.
|
|
*
|
|
* @return the MIDI command for this message
|
|
*/
|
|
public int getCommand()
|
|
{
|
|
return data[0] & 0xF0;
|
|
}
|
|
|
|
/**
|
|
* Get the first data byte from this message, assuming it exists, and
|
|
* zero otherwise.
|
|
*
|
|
* @return the first data byte or zero if none exists.
|
|
*/
|
|
public int getData1()
|
|
{
|
|
if (length > 1)
|
|
return data[1];
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Get the second data byte from this message, assuming it exists, and
|
|
* zero otherwise.
|
|
*
|
|
* @return the second date byte or zero if none exists.
|
|
*/
|
|
public int getData2()
|
|
{
|
|
if (length > 2)
|
|
return data[2];
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/* Create a deep-copy clone of this object.
|
|
* @see java.lang.Object#clone()
|
|
*/
|
|
public Object clone()
|
|
{
|
|
byte message[] = new byte[length];
|
|
System.arraycopy(data, 0, message, 0, length);
|
|
return new ShortMessage(message);
|
|
}
|
|
}
|