/*
*
* Copyright 1990-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program 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 version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
package javax.microedition.media.control;
import javax.microedition.media.MediaException;
/**
* <code>MIDIControl</code> provides access to MIDI rendering
* and transmitting devices.<p>
*
* Typical devices that are controlled with <code>MIDIControl</code>
* are internal synthesizers (software/hardware) or external
* hardware ports. Devices are virtual, i.e. even if there is only
* one physical synthesizer, all instances of <code>MIDIControl</code> seem
* to operate on its own synthesizer.<p>
*
* General functionality of this control is:
* <ol>
* <li>Querying current state of the device:
* <ul>
* <li>The programs that are currently assigned to each of the 16 channels</li>
* <li>Volume of each channel</li>
* </ul></li>
* <li>Querying the banks of the synthesizer:
* <ul>
* <li>Get a list of internal sound banks</li>
* <li>Get a list of custom sound banks</li>
* <li>Get the list of programs of a sound bank</li>
* <li>Get the name of a specific program</li>
* </ul></li>
* <li>Set the volume assigned to a channel</li>
* <li>Set the bank/program assigned to a channel</li>
* <li>Send short MIDI messages to the device</li>
* <li>Send long MIDI messages (system exclusive)</li>
* </ol>
*
* In Java Sound terms, <code>MIDIControl</code> combines
* methods and concepts of the interfaces Transmitter,
* Receiver, Synthesizer, MidiChannel, Soundbank, and Patch.<p>
*
* In this context, the following naming conventions are used:
* <ul>
* <li>A <i>program</i> refers to a single instrument. This is
* also known as a patch.</li>
* <li>A <i>bank</i> is short for sound bank. It contains up
* to 128 programs, numbered in the range from 0..127.</li>
* <li>An <i>internal bank</i> is provided by the software
* implementation or the hardware of the device.</li>
* <li>A <i>custom bank</i> is installed by an application,
* e.g. by loading an XMF meta file with an embedded bank.</li>
* </ul>
* <p>
* The conception of <code>MIDIControl</code> is based on scope and
* abstraction level:
* <ul>
* <li><code>MIDIControl</code> has methods that are specific
* to the device or renderer, and do not directly relate to a specific
* MIDI file or sequence to be played. However, as devices are virtual,
* MIDIControl's methods only operate on this virtual device.
* On the other hand, it is also
* possible to get an instance of <code>MIDIControl</code>
* without providing a sequence or MIDI file; this is done by
* specifying a magic Locator:<br>
* <br><code>
* try{
* <br> Player
* p = Manager.createPlayer(Manager.MIDI_DEVICE_LOCATOR);
* <br> MIDIControl
* synth = (MIDIControl)p.getControls("javax.microedition.media.control.MIDIControl");
* <br> } catch (MediaException e) {
* <br> }
* </code></li>
*
* <li><code>MIDIControl</code>'s methods can be considered
* advanced, low level functionality. This has 2 implications:
* <ol>
* <li><code>MIDIControl</code> is optional, i.e. no Player
* instance is required to provide an implementation of
* it</li>
* <li>Basic media or MIDI player applications will not need
* <code>MIDIControl</code>; {@link VolumeControl VolumeControl},
* {@link TempoControl TempoControl}, and {@link PitchControl PitchControl}
* are sufficient for basic needs.
* </li>
* </ol></li>
* </ul>
* <p>
* A useful function is "Panic": immediately turn off all
* sounds and notes. It can be implemented using the following code fragment:<br>
* <code>
* int CONTROL_ALL_SOUND_OFF = 0x78;<br>
* for (int channel = 0; channel < 16; channel++) {<br>
* shortMidiEvent(CONTROL_CHANGE | channel, CONTROL_ALL_SOUND_OFF, 0);<br>
* }<br>
* </code>
* <p>
* The implementation need not support the various query methods.
* This is a technical limitation, as the MIDI standard does not
* provide a standardized means to query the current program or
* the installed
* soundbanks. This especially applies to external MIDI ports.
* Optional methods must not be called if {@link #isBankQuerySupported isBankQuerySupported}
* returns false.
*
* @see javax.microedition.media.Player
* @see javax.microedition.media.control.RateControl
* @see javax.microedition.media.control.TempoControl
* @see javax.microedition.media.control.PitchControl
*/
public interface MIDIControl extends javax.microedition.media.Control {
// constants for MIDI status (upper nibble of first byte)
/**
* Command value for Note On message (0x90, or 144).
* To turn a note off, send a NOTE_ON message with 0
* velocity. Alternatively, a Note Off message (0x80)
* can be sent.
*
* @see #shortMidiEvent(int, int, int)
*/
int NOTE_ON = 0x90; // 144
/**
* Command value for Control Change message (0xB0, or 176).
* @see #shortMidiEvent(int, int, int)
*/
int CONTROL_CHANGE = 0xB0; // 176
// query device state
/**
* Returns whether banks of the synthesizer can be queried.
* <p>
* If this functions returns true,
* then the following methods can be used to query banks:
* <ul>
* <li>{@link #getProgram(int) getProgram(int)}</li>
* <li>{@link #getBankList(boolean) getBankList(boolean)}</li>
* <li>{@link #getProgramList(int) getProgramList(int)}</li>
* <li>{@link #getProgramName(int, int) getProgramName(int, int)}</li>
* <li>{@link #getKeyName(int, int, int) getKeyName(int, int, int)}</li>
* </ul>
*
* @return true if this device supports querying of banks
*/
boolean isBankQuerySupported();
// send a Program Change short MIDI message, or
/**
* Returns program assigned to channel. It represents the current
* state of the channel. During playback of a MIDI file, the program
* may change due to program change events in the MIDI file.<p>
* To set a program for a channel,
* use setProgram(int, int, int).<p>
*
* The returned array is represented by an array {bank,program}.<p>
* If the device has not been initialized with a MIDI file, or the MIDI file
* does not contain a program change for this channel, an implementation
* specific default value is returned.<p>
*
* As there is no MIDI equivalent to this method, this method is
* optional, indicated by {@link #isBankQuerySupported isBankQuerySupported}.
* If it returns false, this function is not supported and throws an exception.
*
* @param channel 0-15
* @return program assigned to channel, represented by array {bank,program}.
* @exception IllegalArgumentException Thrown if <code>channel</code>
* is out of range.
* @exception IllegalStateException Thrown if the player has not been prefetched.
* @exception MediaException Thrown if querying of banks is not supported.
* @see #isBankQuerySupported
* @see #setProgram
*/
int[] getProgram(int channel)
throws MediaException;
/**
* Get volume for the given channel. The return value is
* independent of the master volume, which is set and retrieved
* with {@link VolumeControl VolumeControl}.<p>
*
* As there is no MIDI equivalent to this method, the implementation
* may not always know the current volume for a given channel. In
* this case the return value is -1.
*
* @param channel 0-15
* @return channel volume, 0-127, or -1 if not known
* @exception IllegalArgumentException Thrown if <code>channel</code>
* is out of range.
* @exception IllegalStateException Thrown if the player has not been prefetched.
* @see #setChannelVolume(int, int)
*/
int getChannelVolume(int channel);
// set device state
/**
* Set program of a channel. This sets the current program for the
* channel and may be overwritten during playback by events in a MIDI sequence.<p>
* It is a high level convenience function. Internally, these method calls are
* executed:<p>
* <code>
* shortMidiEvent(CONTROL_CHANGE | channel, CONTROL_BANK_CHANGE_MSB, bank >> 7);<br>
* shortMidiEvent(CONTROL_CHANGE | channel, CONTROL_BANK_CHANGE_LSB, bank & 0x7F);<br>
* shortMidiEvent(PROGRAM_CHANGE | channel, program, 0);
* </code><p>
*
* In order to use the default bank (the initial bank), set the bank parameter to -1.
* <p>
*
* In order to set a program without explicitly setting the bank,
* use the following call: <p>
* <code>
* shortMidiEvent(PROGRAM_CHANGE | channel, program, 0);
* </code><p>
*
* In both examples, the following constants are used:<p>
* <code>
* int PROGRAM_CHANGE = 0xC0;<br>
* int CONTROL_BANK_CHANGE_MSB = 0x00;<br>
* int CONTROL_BANK_CHANGE_LSB = 0x20;
* </code><p>
*
* @param channel 0-15
* @param bank 0-16383, or -1 for default bank
* @param program 0-127
* @exception IllegalArgumentException Thrown if any of the given
* parameters is out of range.
* @exception IllegalStateException Thrown if the player has not been prefetched.
* @see #getProgram
*/
void setProgram(int channel, int bank, int program);
/**
* Set volume for the given channel. To mute, set to 0.
* This sets the current volume for the
* channel and may be overwritten during playback by events in a MIDI sequence.<p>
* It is a high level convenience function. Internally, the following command
* is executed:<p>
* <code>
* shortMidiEvent(CONTROL_CHANGE | channel, CONTROL_MAIN_VOLUME, 0);
* </code><p>
* where this constant is used:<p>
* <code> int CONTROL_MAIN_VOLUME = 0x07</code><p>
*
* The channel volume is independent of the master volume, which
* is accessed with {@link VolumeControl VolumeControl}.
* Setting the channel volume does not modify the value of the master
* volume - and vice versa: changing the value of master volume does not
* change any channel's volume value.<br>
* The synthesizer
* mixes the output of up to 16 channels, each channel with its own
* channel volume. The master volume then controls the volume of the mix.
* Consequently, the effective output volume of a channel is the product
* of master volume and channel volume. <p>
*
* Setting the channel volume does not generate a
* {@link javax.microedition.media.PlayerListener#VOLUME_CHANGED VOLUME_CHANGED event}.
*
* @param channel 0-15
* @param volume 0-127
* @exception IllegalArgumentException Thrown if
* <code>channel</code> or <code>volume</code> is out of range.
* @exception IllegalStateException Thrown if the player has not been prefetched.
* @see #getChannelVolume
*/
void setChannelVolume(int channel, int volume);
// banks
/**
* Returns list of installed banks.
* If the <code>custom</code> parameter is true, a list of custom banks is returned.
* Otherwise, a list of all banks (custom and internal) is returned.
* <p>
* As there is no MIDI equivalent to this method, this method is
* optional, indicated by {@link #isBankQuerySupported isBankQuerySupported}.
* If it returns false, this function is not supported and throws an exception.
*
* @param custom if set to true, returns list of custom banks.
* @return an array of all installed bank numbers.
* Each bank number is in the range of 0..16383
* @exception MediaException if this device does not support retrieval of
* banks
* @exception IllegalStateException Thrown if the player has not been prefetched.
* @see #isBankQuerySupported
*/
int[] getBankList(boolean custom)
throws MediaException;
/**
* Given bank, get list of program numbers. If and only if
* this bank is not installed, an empty array is returned.<p>
*
* As there is no MIDI equivalent to this method, this method is
* optional, indicated by {@link #isBankQuerySupported isBankQuerySupported}.
* If it returns false, this function is not supported and throws an exception.
*
* @param bank 0..16383
* @return an array of programs defined in the given bank.
* Each program number is from 0..127.
* @exception IllegalArgumentException Thrown if <code>bank</code>
* is out of range.
* @exception MediaException Thrown if the device does not support
* retrieval of programs.
* @exception IllegalStateException Thrown if the player has not been prefetched.
* @see #setProgram
* @see #isBankQuerySupported
*/
int[] getProgramList(int bank)
throws MediaException;
/**
* Given bank and program, get name of program.
* For space-saving reasons, an implementation may return an empty string.
* <p>
* As there is no MIDI equivalent to this method, this method is
* optional, indicated by {@link #isBankQuerySupported isBankQuerySupported}.
* If it returns false, this function is not supported and throws an exception.
*
* @param bank 0-16383
* @param prog 0-127
* @exception IllegalArgumentException Thrown if <code>bank</code>
* or <code>prog</code> is out of range.
* @exception MediaException Thrown if the bank or program is
* not installed (internal or custom), or if this device does not
* support retrieval of program names
* @exception IllegalStateException Thrown if the player has not been prefetched.
* @return name of the specified program, or empty string.
* @see #isBankQuerySupported
*/
String getProgramName(int bank, int prog)
throws MediaException;
/**
* Given bank, program and key, get name of key.
* This method applies to key-mapped banks (i.e. percussive banks
* or effect banks) only.
* A return value of <code>null</code> means that the specified key
* is not mapped to a sound. For melodic banks,
* where each key (=note) produces the same sound at different pitch, this method
* always returns <code>null</code>.
* For space-saving reasons, an implementation may return an empty string
* instead of the key name. To find out which keys in a specific program
* are mapped to a sound, iterate through all keys (0-127) and compare
* the return value of <code>getKeyName</code> to non-<code>null</code>.
* <p>
* As there is no MIDI equivalent to this method, this method is
* optional, indicated by {@link #isBankQuerySupported isBankQuerySupported}.
* If it returns false, this function is not supported and throws an exception.
*
* @param bank 0-16383
* @param prog 0-127
* @param key 0-127
* @exception IllegalArgumentException Thrown if <code>bank</code>,
* <code>prog</code> or <code>key</code> is out of range.
* @exception MediaException Thrown if the bank or program is
* not installed (internal or custom), or if this device does not
* support retrieval of key names
* @exception IllegalStateException Thrown if the player has not been prefetched.
* @return name of the specified key, empty string, or <code>null</code> if
* the key is not mapped to a sound.
* @see #isBankQuerySupported
*/
String getKeyName(int bank, int prog, int key)
throws MediaException;
/**
* Sends a short MIDI event to the device.
* Short MIDI events consist of 1, 2, or 3 unsigned bytes.
* For non-realtime events, the first byte is split up into
* status (upper nibble, 0x80-0xF0) and channel (0x00-0x0F).
* For example, to send a <code>Note On</code> event on a given channel,
* use this line:<p>
* <code> shortMidiEvent(NOTE_ON | channel, note, velocity);</code><p>
* For events with less than 3 bytes, set the remaining data bytes to 0.<p>
*
* There is no guarantee that a specific
* implementation of a MIDI device supports all event types.
* Also, the MIDI protocol does not implement flow control and it is not
* guaranteed that an event reaches the destination.
* In both these cases, this method fails silently. <p>
*
* Static error checking is performed on the passed parameters. They have to
* specify a valid, complete MIDI event. Events with <code>type</code> < 0x80 are
* not valid MIDI events (-> running status). When an invalid event
* is encountered, an IllegalArgumentException is thrown.
*
* @param type 0x80..0xFF, excluding 0xF0 and 0xF7, which are reserved for system exclusive
* @param data1 for 2 and 3-byte events: first data byte, 0..127
* @param data2 for 3-byte events: second data byte, 0..127
* @exception IllegalArgumentException Thrown if one of the parameters
* is out of range.
* @exception IllegalStateException Thrown if the player has not been prefetched.
*/
void shortMidiEvent(int type, int data1, int data2);
/**
* Sends a long MIDI event to the device, typically a system exclusive message.
* This method passes the data directly to the receiving device.
* The data array's contents are not checked for validity.<p>
* It is possible to send short events, or even a series of short events
* with this method.<p>
*
* @param data array of the bytes to send
* @param offset start offset in data array
* @param length number of bytes to be sent
* @exception IllegalArgumentException Thrown if any one of the given
* parameters is not valid.
* @exception IllegalStateException Thrown if the player has not been prefetched.
* @return the number of bytes actually sent to the device or
* -1 if an error occurred
*/
int longMidiEvent(byte[] data, int offset, int length);
}