Whole document tree Chapter 10Transmitting and Receiving MIDI MessagesUnderstanding Devices, Transmitters, and ReceiversThe JavaTM Sound API specifies a message-routing architecture for MIDI data that's flexible and easy to use, once you understand how it works. The system is based on a module-connection design: distinct modules, each of which performs a specific task, can be interconnected (networked), enabling data to flow from one module to another.
The base module in the Java Sound API's messaging system is
A
The
The We'll now examine how to use transmitters and receivers. Before getting to the typical case of connecting two devices (such as hooking a sequencer to a synthesizer), we'll examine the simpler case where you send a MIDI message directly from your application program to a device. Studying this simple scenario should make it easier to understand how the Java Sound API arranges for sending MIDI messages between two devices. Sending a Message to a Receiver without Using a Transmitter
Let's say you want to create a MIDI message from scratch and then send it to some receiver. You can create a new, blank void setMessage(int command, int channel, int data1, int data2)
Once you have a message ready to send, you can send it to a void send(MidiMessage message, long timeStamp) The time-stamp argument will be explained momentarily. For now, we'll just mention that its value can be set to -1 if you don't care about specifying a precise time. In this case, the device receiving the message will try to respond to the message as soon as possible.
An application program can obtain a receiver for a
As a concrete simple example of sending a message without using a transmitter, let's send a Note On message to the default receiver, which is typically associated with a device such as the MIDI output port or a synthesizer. We do this by creating a suitable ShortMessage myMsg = new ShortMessage(); // Start playing the note Middle C (60), // moderately loud (velocity = 93). myMsg.setMessage(ShortMessage.NOTE_ON, 0, 60, 93); long timeStamp = -1; Receiver rcvr = MidiSystem.getReceiver(); rcvr.send(myMsg, timeStamp);
This code uses a static integer field of Understanding Time StampsChapter 8, "Overview of the MIDI Package," explained that the MIDI specification has different parts. One part describes MIDI "wire" protocol (messages sent between devices in real time), and another part describes Standard MIDI Files (messages stored as events in "sequences"). In the latter part of the specification, each event stored in a standard MIDI file is tagged with a timing value that indicates when that event should be played. By contrast, messages in MIDI wire protocol are always supposed to be processed immediately, as soon as they're received by a device, so they have no accompanying timing values.
The Java Sound API adds an additional twist. It comes as no surprise that timing values are present in the Time Stamps on Messages Sent to Devices
The time stamp that can optionally accompany messages sent between devices in the Java Sound API is quite different from the timing values in a standard MIDI file. The timing values in a MIDI file are often based on musical concepts such as beats and tempo, and each event's timing measures the time elapsed since the previous event. In contrast, the time stamp on a message sent to a device's
This kind of time stamp is designed to help compensate for latencies introduced by the operating system or by the application program. It's important to realize that these time stamps are used for minor adjustments to timing, not to implement complex queues that can schedule events at completely arbitrary times (as
The time stamp on a message sent to a device (through a Even if a device supports time stamps, it might not schedule the event for exactly the time that you requested. You can't expect to send a message whose time stamp is very far in the future and have the device handle it as you intended, and you certainly can't expect a device to correctly schedule a message whose time stamp is in the past! It's up to the device to decide how to handle time stamps that are too far off in the future or the past. The sender doesn't know what the device considers to be too far off, or whether the device had any problem with the time stamp. This ignorance mimics the behavior of external MIDI hardware devices, which send messages without ever knowing whether they were received correctly. (MIDI wire protocol is unidirectional.)
Some devices send time-stamped messages (via a
To learn whether a device supports time stamps, invoke the following method of long getMicrosecondPosition()
This method returns -1 if the device ignores time stamps. Otherwise, it returns the device's current notion of time, which you as the sender can use as an offset when determining the time stamps for messages you subsequently send. For example, if you want to send a message with a time stamp for five milliseconds in the future, you can get the device's current position in microseconds, add 5000 microseconds, and use that as the time stamp. Keep in mind that the
Now, with all that explanation of time stamps as a background, let's return to the void send(MidiMessage message, long timeStamp)
The Connecting Transmitters to ReceiversWe've seen how you can send a MIDI message directly to a receiver, without using a transmitter. Now let's look at the more common case, where you aren't creating MIDI messages from scratch, but are simply connecting devices together so that one of them can send MIDI messages to the other. Connecting to a Single DeviceThe specific case we'll take as our first example is connecting a sequencer to a synthesizer. After this connection is made, starting the sequencer running will cause the synthesizer to generate audio from the events in the sequencer's current sequence. For now, we'll ignore the process of loading a sequence from a MIDI file into the sequencer. Also, we won't go into the mechanism of playing the sequence. Loading and playing sequences is discussed in detail in Chapter 11, "Playing, Recording, and Editing MIDI Sequences." Loading instruments into the synthesizer is discussed in Chapter 12, "Synthesizing Sound." For now, all we're interested in is how to make the connection between the sequencer and the synthesizer. This will serve as an illustration of the more general process of connecting one device's transmitter to another device's receiver. For simplicity, we'll use the default sequencer and the default synthesizer. (See Chapter 9, "Accessing MIDI System Resources," for more about default devices and how to access non-default devices.) Sequencer seq; Transmitter seqTrans; Synthesizer synth; Receiver synthRcvr; try { seq = MidiSystem.getSequencer(); seqTrans = seq.getTransmitter(); synth = MidiSystem.getSynthesizer(); synthRcvr = synth.getReceiver(); seqTrans.setReceiver(synthRcvr); } catch (MidiUnavailableException e) { // handle or throw exception }
An implementation might actually have a single object that serves as both the default sequencer and the default synthesizer. In other words, the implementation might use a class that implements both the if (seq instanceof Synthesizer) although the explicit connection above should work in any case. Connecting to More than One Device
The previous code example illustrated a one-to-one connection between a transmitter and a receiver. But, what if you need to send the same MIDI message to multiple receivers? For example, suppose you want to capture MIDI data from an external device to drive the internal synthesizer while simultaneously recording the data to a sequence. This form of connection, sometimes referred to as "fan out" or as a "splitter," is straightforward. The following statements show how to create a fan-out connection, through which the MIDI messages arriving at the MIDI input port are sent to both a Synthesizer synth; Sequencer seq; MidiDevice inputPort; // [obtain and open the three devices...] Transmitter inPortTrans1, inPortTrans2; Receiver synthRcvr; Receiver seqRcvr; try { inPortTrans1 = inputPort.getTransmitter(); synthRcvr = synth.getReceiver(); inPortTrans1.setReceiver(synthRcvr); inPortTrans2 = inputPort.getTransmitter(); seqRcvr = seq.getReceiver(); inPortTrans2.setReceiver(seqRcvr); } catch (MidiUnavailableException e) { // handle or throw exception }
This code introduces a dual invocation of the
To learn how many transmitters and receivers a device supports, you can use the following int getMaxTransmitters() int These methods return the total number owned by the device, not the number currently available.
A transmitter can transmit MIDI messages to only one receiver at a time. (Every time you call Similarly, a device can use its multiple receivers to receive from more than one device at a time. The multiple-receiver code that's required is straightforward, being directly analogous to the multiple-transmitter code above. It's also possible for a single receiver to receive messages from more than one transmitter at a time. Closing Connections
Once you're done with a connection, you can free up its resources by invoking the
If you're also done with the devices, you can similarly make them available to other application programs by invoking [Top] [Prev] [Next] [Bottom] Copyright © 2000, Sun Microsystems Inc. All rights reserved. |