package moppydesk.midputs;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sound.midi.*;
import moppydesk.Constants;
/**
* This class will re-map notes from specified incoming channels to a pool of
* output channels based on one of several strategies.
*/
public class DrivePooler implements Receiver, Transmitter {
public enum PoolingStrategy {
STRAIGHT_THROUGH, ROUND_ROBIN, STACK
};
Receiver receiver = null;
private int startInput = 0;
private int endInput = 0;
private int startOutput = 0;
private int endOutput = 7;
private PoolingStrategy currentStrategy = PoolingStrategy.STRAIGHT_THROUGH;
private int[] currentNotes = new int[Constants.NUM_MIDI_CHANNELS];
private int rrNextNote = 0;
public void setInputRange(int start, int end) {
startInput = start - 1;
endInput = end - 1;
}
public void setOutputRange(int start, int end) {
startOutput = start - 1;
endOutput = end - 1;
}
public void setStrategy(PoolingStrategy newStrat) {
allOff();
currentStrategy = newStrat;
}
public void send(MidiMessage message, long timeStamp) {
if (receiver != null) {
if (message instanceof ShortMessage
&& (message.getMessage()[0] & 0xFF) > 127 && (message.getMessage()[1] & 0xFF) < 160) {
switch (currentStrategy) {
case STRAIGHT_THROUGH:
receiver.send(message, timeStamp); //No modifications
break;
case ROUND_ROBIN:
receiver.send(roundRobinMap(message), timeStamp);
break;
case STACK:
receiver.send(stackMap(message), timeStamp);
}
}
}
}
private MidiMessage roundRobinMap(MidiMessage message) {
if (rrNextNote < startOutput || rrNextNote > endOutput) {
rrNextNote = startOutput;
}
ShortMessage mappedMessage = (ShortMessage) message;
try {
if (mappedMessage.getChannel() >= startInput && mappedMessage.getChannel() <= endInput) {
if (mappedMessage.getCommand() == ShortMessage.NOTE_OFF || (mappedMessage.getCommand() == ShortMessage.NOTE_ON && mappedMessage.getData2() == 0)) {
int noteNumber = mappedMessage.getData1();
for (int n = startOutput; n <= endOutput; n++) {
if (currentNotes[n] == noteNumber) {
currentNotes[n] = -1;
mappedMessage.setMessage(mappedMessage.getCommand(), n, mappedMessage.getData1(), mappedMessage.getData2());
break; // Only turn off one of the notes
}
}
} else if (mappedMessage.getCommand() == ShortMessage.NOTE_ON) {
int targetNote = rrNextNote;
while (targetNote <= endOutput) {
if (currentNotes[targetNote] < 0) {
break; // If that one's free, we're good.
} else if (targetNote == endOutput) {
targetNote = startOutput;
} else {
targetNote++;
}
if (targetNote == rrNextNote) {
break; //We've gone around once, stop!
}
}
currentNotes[targetNote] = mappedMessage.getData1();
mappedMessage.setMessage(mappedMessage.getCommand(), targetNote, mappedMessage.getData1(), mappedMessage.getData2());
rrNextNote++;
}
} else {
return message; //It's not in the mapping range, let it pass through...
}
} catch (InvalidMidiDataException ex) {
Logger.getLogger(DrivePooler.class.getName()).log(Level.SEVERE, null, ex);
}
return mappedMessage;
}
private MidiMessage stackMap(MidiMessage message) {
ShortMessage mappedMessage = (ShortMessage) message;
try {
//If it's in the chosen range:
if (mappedMessage.getChannel() >= startInput && mappedMessage.getChannel() <= endInput) {
if (mappedMessage.getCommand() == ShortMessage.NOTE_OFF || (mappedMessage.getCommand() == ShortMessage.NOTE_ON && mappedMessage.getData2() == 0)) {
int noteNumber = mappedMessage.getData1();
for (int n = startOutput; n <= endOutput; n++) {
if (currentNotes[n] == noteNumber) {
currentNotes[n] = -1;
mappedMessage.setMessage(mappedMessage.getCommand(), n, mappedMessage.getData1(), mappedMessage.getData2());
}
}
} else if (mappedMessage.getCommand() == ShortMessage.NOTE_ON) {
int targetChannel = 0;
for (int n = startOutput; n <= endOutput; n++) {
if (currentNotes[n] < 0) {
targetChannel = n;
break;
}
}
currentNotes[targetChannel] = mappedMessage.getData1();
mappedMessage.setMessage(mappedMessage.getCommand(), targetChannel, mappedMessage.getData1(), mappedMessage.getData2());
}
} else {
return message; //It's not in the mapping range, let it pass through...
}
} catch (InvalidMidiDataException ex) {
Logger.getLogger(DrivePooler.class.getName()).log(Level.SEVERE, null, ex);
}
return mappedMessage;
}
private void allOff() {
Arrays.fill(currentNotes, -1);
}
//
//// Transmitter-methods
//
public void close() {
if (receiver != null) {
receiver.close();
receiver = null;
}
}
public void setReceiver(Receiver newReceiver) {
if (this.receiver != null) {
this.receiver.close();
}
this.receiver = newReceiver;
}
public Receiver getReceiver() {
return receiver;
}
}