/*
Mjdj MIDI Morph - an extensible MIDI processor and translator.
Copyright (C) 2010 Confusionists, LLC (www.confusionists.com)
This program 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 3 of the License, or (at your option) any later version. 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 for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
You may contact the author at mjdj_midi_morph [at] confusionists.com
*/
package com.confusionists.mjdj.midi;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.sound.midi.*;
import com.confusionists.mjdj.Universe;
import com.confusionists.mjdj.fileIO.DeviceLoaderJava;
import com.confusionists.mjdj.midi.time.InternalClock;
import com.confusionists.mjdj.settings.MorphAdaptor;
import com.confusionists.mjdj.settings.Settings;
import com.confusionists.mjdj.ui.Logger;
import com.confusionists.mjdjApi.midi.MessageWrapper;
import com.confusionists.mjdjApi.midiDevice.DeviceUnavailableException;
import com.confusionists.mjdjApi.midiDevice.DeviceWrapper;
import com.confusionists.mjdjApi.midiDevice.ReceiverDeviceWrapper;
import com.confusionists.mjdjApi.midiDevice.TransmitterDeviceWrapper;
import com.confusionists.mjdjApi.morph.Morph;
public class MidiDriverManager {
public List<ReceiverDeviceWrapper> outputReceivers;
public List<TransmitterDeviceWrapper> inputTransmitters;
private boolean comSunAvailable;
public MidiDriverManager() {
}
public void init() {
outputReceivers = new ArrayList<ReceiverDeviceWrapper>();
inputTransmitters = new ArrayList<TransmitterDeviceWrapper>();
inputTransmitters.add(new InternalClock());
new DeviceLoaderJava().load(inputTransmitters, outputReceivers);
open(outputReceivers);
open(inputTransmitters);
// open the real midi devices
MidiDevice.Info[] mdi = MidiSystem.getMidiDeviceInfo();
comSunAvailable = isComSunAvailable(mdi);
for (int i = 0; i < mdi.length; i++) {
MidiDevice device;
try {
device = MidiSystem.getMidiDevice(mdi[i]);
if ( useDevice(device)) {
if (device.getMaxTransmitters() != 0) {
TransmitterWrapperImpl transmitter = new TransmitterWrapperImpl(device);
openTransmitter(transmitter);
}
if (device.getMaxReceivers() != 0) {
ReceiverWrapperImpl receiver = new ReceiverWrapperImpl(device);
openReceiver(receiver);
}
}
} catch (MidiUnavailableException mex) {
// it's fine Logger.log(e.getMessage());
} catch (DeviceUnavailableException e) {
Logger.log("Device unavailable", e);
}
}
}
private boolean isComSunAvailable(MidiDevice.Info[] mdiInfos) {
for (int i = 0; i < mdiInfos.length; i++) {
try {
MidiDevice device = MidiSystem.getMidiDevice(mdiInfos[i]);
if (isDeviceFromSunProviders(device))
return true;
} catch (MidiUnavailableException e) {
// hide this
}
}
return false;
}
private boolean isDeviceFromSunProviders(MidiDevice device) {
if (device instanceof Sequencer || device instanceof Synthesizer)
return false; // it's no problem to get a few false negatives with this method
String providerPackagePrefix = device.getClass().getName();
int secondPeriod = providerPackagePrefix.indexOf('.', providerPackagePrefix.indexOf('.')+1);
return providerPackagePrefix.substring(0, secondPeriod).equals("com.sun");
}
static boolean warned = false;
private boolean useDevice(MidiDevice device) {
boolean retVal;
if (device instanceof Sequencer || device instanceof Synthesizer)
return false;
if (Settings.getInstance().doNotUseComSunDrivers) {
retVal = !isDeviceFromSunProviders(device);
if (!retVal && !warned) {
Logger.log("Skipping Sun drivers (per settings file)");
warned = true;
}
return retVal;
}
else {
if (!comSunAvailable)
return true;
else {
if (isDeviceFromSunProviders(device))
return true;
else {
if (!warned) {
Logger.log("Non-default drivers skipped as Sun drivers were found (per settings file)");
warned = true;
}
return false;
}
}
}
}
private void openTransmitter(TransmitterDeviceWrapper transmitter) throws DeviceUnavailableException {
while (nameExists(transmitter.toString(), true)) {
transmitter.makeNewId(getExistingIds(true));
// Logger.log("Cannot use device " + receiver.toString() + ", name is already used.");
}
transmitter.open();
transmitter.setService(MjdjServiceImpl.instance);
inputTransmitters.add(transmitter);
}
private ArrayList<String> getExistingIds(boolean transmitters) {
List<? extends DeviceWrapper> things = transmitters ? this.inputTransmitters : this.outputReceivers;
ArrayList<String> retVal = new ArrayList<String>();
for (DeviceWrapper midiDeviceWrapper : things) {
retVal.add(midiDeviceWrapper.toString());
}
return retVal;
}
private void openReceiver(ReceiverDeviceWrapper receiver) throws DeviceUnavailableException {
while (nameExists(receiver.toString(), false)) {
receiver.makeNewId(getExistingIds(false));
// Logger.log("Cannot use device " + receiver.toString() + ", name is already used.");
}
receiver.open();
receiver.setService(MjdjServiceImpl.instance);
outputReceivers.add(receiver);
}
private boolean nameExists(String name, boolean transmitter) {
if (transmitter) {
for (DeviceWrapper midiDeviceWrapper : inputTransmitters) {
if (midiDeviceWrapper.toString().equals(name)) {
return true;
}
}
} else {
for (DeviceWrapper midiDeviceWrapper : outputReceivers) {
if (midiDeviceWrapper.toString().equals(name)) {
return true;
}
}
}
return false;
}
private void open(List<? extends DeviceWrapper> adHocDevices) {
// open all the ad-hoc devices and
// eliminate duplicate names
ArrayList<String> names = new ArrayList<String>();
for (Iterator<? extends DeviceWrapper> i = adHocDevices.iterator(); i.hasNext();) {
try {
DeviceWrapper receiver = i.next();
if (names.contains(receiver.toString())) {
Logger.log("Removing device " + receiver.toString() + " because it's a duplicate name");
i.remove();
} else {
receiver.setService(MjdjServiceImpl.instance);
receiver.open();
names.add(receiver.toString());
}
} catch (Exception e) {
Logger.log("Couldn't open MIDI receiver for output", e);
i.remove();
}
}
}
public void close() {
if (inputTransmitters != null) {
for (DeviceWrapper wrapper : inputTransmitters) {
wrapper.close();
}
}
}
public void sendToMorphs(MessageWrapper message, String from) {
sendToMorphs(message, from, null);
}
public void sendToMorphs(MessageWrapper message, String from, Morph afterMorph) {
for (MorphAdaptor morph : Universe.instance.main.morphCheckboxList.getMorphs(afterMorph)) {
if (morph.isActive()) {
try {
boolean result = morph.getMorph().process(message, from);
if (result)
return;
} catch (Throwable e) {
Logger.log("Error occurred processing translator " + morph.getMorph().getName());
e.printStackTrace();
}
}
}
}
public void onBeat() {
for (MorphAdaptor morph : Universe.instance.main.morphCheckboxList.getMorphs()) {
if (morph.isActive()) {
try {
morph.getMorph().onBeat();
} catch (Throwable e) {
Logger.log("Error occurred processing onBeat for translator " + morph.getMorph().getName());
e.printStackTrace();
}
}
}
}
}