K-Gater, Part 12


Ok, so that’s the K-Gater app. Time for a little recap.

The entire point of this exercise was to create a kind of midi mapper to drive the Korg Kaossilator Pro, which doesn’t support standard NOTE_ON, NOTE_OFF MIDI messages. And because I couldn’t find example code for talking to external hardware. All the tutorials and documentation start out discussing both external hardware and the default software synthesizer, but right at the most crucial point, they switch over to just talking about the software synth exclusively. I had to do a lot of trial and error to make that leap to a working external hardware connection.

Both start out the same way, by using the MidiSystem object. For external hardware, we need to know what’s plugged in and running, which will include the Windows synth support files if you’re on a Windows PC. We do this by getting an array of info objects. Note that it doesn’t matter to us if the devices are software-only, connected through MIDI cables or through the USB port.

MidiDevice.Info[] infos = MidiSystem.getMidiDeviceInfo();

MidiDevice just lets us display the vendor name, device name, description and version. If we want the instrument names or channel numbers, we need to open a Synthesizer object, if possible.

Next, loop through the info array and check if the vendor name or description matches the device we want. For the K-Pro, the name is “Kaossilator Pro 1 Sound”, where the “1” is the port number, if we enabled the “include port number in driver name” option. For the Roland A-300 Pro midi keyboard controller, there are actually 5 ports, for MIDI IN: “A-PRO 1”, “A-PRO 2” and “A-PRO MIDI IN”; and for MIDI OUT: “A-Pro” and “A-PRO MIDI OUT”. So, we need to check for all three MIDI IN ports in order to connect transmitters to them. (The following code fragment is for the K-Pro only.)

int kProDevice = -1;
for (int i = 0; i < infos.length; i++) {
if(debug) {
jTextArea1.append(infos[i].getDescription() + “\n” + infos[i].getName() + “\n” + infos[i].getVendor() + “\n” + infos[i].getVersion() + “\n\n”);
}
if(infos[i].getName().startsWith(“KAOSSILATOR PRO”)) {
kProDeviceNo = i;
}
}

If kProDeviceNo is -1, it hadn’t been plugged in or turned on at the time the app started. Since MidiSystem doesn’t seem to be refreshing properly on my computer, the only choice is to exit the app and try running it again in a few seconds, after making sure the K-Pro is on and connected. Otherwise, the next step is to create a MidiDevice object with the K-Pro info.

MidiDevice kProDevice = null;
if(kProDeviceNo > -1) {
try {
kProDevice = MidiSystem.getMidiDevice(infos[kProDeviceNo]);
} catch (MidiUnavailableException e) {
JOptionPane.showMessageDialog(this, “Couldn’t Get Device:” + kProDeviceNo, “Device Open Error”, JOptionPane.PLAIN_MESSAGE);
}
if (!(kProDevice.isOpen())) {
try {
kProDevice.open();
} catch (MidiUnavailableException e) {
JOptionPane.showMessageDialog(this, “Couldn’t Open Device:” + kProDeviceNo, “Device Open Error”, JOptionPane.PLAIN_MESSAGE);
}
}
initkProInstrumentList();
} else {
jComboBox1.setEnabled(false); // Deactivate K-Pro related buttons if no K-Pro
for(int i=0; i<11; i++) {
jBList.get(i).setEnabled(false);
}
}

Lots of exceptions being thrown here. Netbeans forces us to handle all of them, or it won’t run right. I think that some of the Java documentation assumes that the user is hand-coding Java and that the exceptions can be thrown without being caught. This is another issue that I have with the official documentation, which doesn’t always include exception handling.

Once we do the .getMidiDevice(info), we need to .open() the device. When we exit the app, we need to make sure we .close() it, or the link to the K-Pro will remain open after the app stops running. Same holds true for the A-300 Pro support.

To determine if the external device is a synthesizer or sequencer, we can query it with “instance of”. We can do this before doing .open();

if (kProDevice instanceof Synthesizer) {
JOptionPane.showMessageDialog(this, “K-Pro is a synthesizer!”, “Whee!”, JOptionPane.PLAIN_MESSAGE);
}

The K-Pro doesn’t support the synthesizer class, so I can’t get the soundbank from it. If it did, I could make a Synthesizer object, and use something like .getSoundBank(). Instead, I hand-typed the instrument names into an array, and I use my initProInstrumentList() method to copy the names from the array to a combo box to display to the user. The remaining code is just used to disable my combo box and K-Pro voice preset buttons if kProDevice is null.

The next part is what all of the official documentation skips. To actually talk to the external hardware, we need to open a receiver and/or a transmitter to it. If we’re sending MIDI messages to the device, we need a receiver. Which of course can throw another exception we have to catch. Note that I initially opened the receiver every time I wanted to send data to the K-Pro because that’s the way it’s shown in one of the examples. It’s inefficient, but the idea is that if the hardware can only support a limited number of receivers, you’ll allow another app to talk to the K-Pro in sequence this way. In fact, the number of receivers the K-Pro supports is unlimited, so I’m just going to open one receiver at the beginning and leave it open in the next version.

private void kProProgram(int no) { // Change K-Pro instrument
int bank = 0;
int nbank = 0;
int ch = 0;
ShortMessage myMsg = new ShortMessage();
Receiver rcvr = null;
long timeStamp = -1;
if(kProDevice != null) {
try {
rcvr = kProDevice.getReceiver();
} catch (MidiUnavailableException e) {
}
try {
bank = (no < 127) ? 0 : 1;
nbank = (bank == 1) ? 0 : 1;
ch = (no < 127) ? no : no – 128;
myMsg.setMessage(ShortMessage.CONTROL_CHANGE, midiPorts.kProChannelNo, 0, nbank);
rcvr.send(myMsg, timeStamp);
myMsg.setMessage(ShortMessage.CONTROL_CHANGE, midiPorts.kProChannelNo, 32, bank);
rcvr.send(myMsg, timeStamp);
myMsg.setMessage(ShortMessage.PROGRAM_CHANGE, midiPorts.kProChannelNo, ch, 0);
rcvr.send(myMsg, timeStamp);
} catch (javax.sound.midi.InvalidMidiDataException e) {
}
}
}

private void kProNote(int ch, int col, int row, int onOff) { // Change K-Pro note (x-y pad)
ShortMessage myMsg = new ShortMessage();
Receiver rcvr = null;
long timeStamp = -1;
if(kProDevice != null) {
try {
rcvr = kProDevice.getReceiver();
} catch (MidiUnavailableException e) {
}
try {
myMsg.setMessage(ShortMessage.CONTROL_CHANGE, midiPorts.kProChannelNo, 12, row);
rcvr.send(myMsg, timeStamp);
myMsg.setMessage(ShortMessage.CONTROL_CHANGE, midiPorts.kProChannelNo, 13, col);
rcvr.send(myMsg, timeStamp);
myMsg.setMessage(ShortMessage.CONTROL_CHANGE, midiPorts.kProChannelNo, 92, onOff);
rcvr.send(myMsg, timeStamp);
} catch (javax.sound.midi.InvalidMidiDataException e) {
}
}
}

Talking to the external device is just a matter of building up a ShortMessage and using receiver.send(). The type of message you send depends a large part on whether it’s the software synthesizer or external hardware. For the software synth, messages follow the MIDI spec, with ShortMessage.NOTE_ON, ShortMessage.NOTE_OFF and ShortMessage.PROGRAM_CHANGE being the primary ones. For the Kaossilator Pro, most messages are ShortMessage.CONTROL_CHANGE messages. For more typical external instruments, the messages will be the same as for the software synth. It’s important to remember here that the K-Pro doesn’t implement the Synthesizer class and therefore we have to send the messages the hard way. In the section below, we get to see what happens if a device DOES implement the Synthesizer class.

And, that’s it for external devices.

——————————————

In contrast, let’s look at the one part that all of the documentation agrees on – using the built-in software synth. This starts out the same, using the MidiSystem. .getSynthesizer() returns a pointer to the default synth. That’s it, it’s just that easy. You can then get the default soundbank, or load one that you want from a file or from a URL. The soundbank contains bank numbers and voice numbers, which need to be loaded to an array for later reference. And, because the software synth can be assigned to all 16 channels, we need an array for the channel-to-voice relationship.

Soundbank soundbank = null;
Synthesizer synth = null;
MidiChannel [] channels = null;
Instrument[] aInstruments = null;

private void readSoundBank() { // Load the default Java software synth
try {
synth = MidiSystem.getSynthesizer();
synth.open();
soundbank = synth.getDefaultSoundbank();
synth.loadAllInstruments(soundbank);
channels = synth.getChannels();
} catch (Exception ex) {
JOptionPane.showMessageDialog(this, “Couldn’t Open Soundbank:\n” + ex, “Soundbank Open Error”, JOptionPane.PLAIN_MESSAGE);
}
if (soundbank == null){
jTextArea1.setText(“no soundbank”);
} else {
aInstruments = soundbank.getInstruments();
for (int i = 0; i < aInstruments.length; i++) {
jComboBox3.addItem(aInstruments[i].getName());
}
}
}

Changing instruments takes place with the MidiChannel object .programChange() method:

channels[channelNo].programChange(aInstruments[voiceNum].getPatch().getBank(), aInstruments[voiceNum].getPatch().getProgram());

Turning notes on and off is just a matter of calling the appropriate Channel method. We just need to give the note number (0-127) and the keyboard velocity (i.e. – volume; 0-127) for note on.

channels[channelNo].noteOn(arp.currentNote, keyboardVolume);
channels[channelNo].noteOff(arp.currentNote);

The main point regarding this comparison is that if the external hardware implements the Synthesizer class, we can use these Synthesizer methods on it just as we do for the default synth. The difference being that we’d open a receiver to the hardware rather than using MidiSystem.getSynthesizer(). (I would give working example code for using an external hardware synthesizer, but I don’t own one.)

And that’s it for the Kaossilator Pro support.

Advertisements
Previous Post
Next Post
Leave a comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: