/*
* MidiSystemFacade.java
*
* Created on November 12, 2002, 12:22 AM
*/
package com.pcmsolutions.comms;
import com.pcmsolutions.device.EMU.E4.RemoteUnreachableException;
import com.pcmsolutions.system.ZDisposable;
import com.pcmsolutions.system.ZUtilities;
import com.pcmsolutions.system.Zoeos;
import com.pcmsolutions.system.threads.ZDefaultThread;
import com.pcmsolutions.system.threads.ZWaitThread;
import com.pcmsolutions.util.CALock;
import com.pcmsolutions.util.RWLock;
import javax.sound.midi.*;
import javax.swing.*;
import java.util.*;
import java.util.prefs.Preferences;
/**
*
* @author pmeehan
*/
public class MidiSystemFacade implements ZDisposable {
// private static volatile int TRANSACTION_PRIORITY = 5;
public static final CALock midiLock = new CALock();
private final Hashtable deviceMap = new Hashtable(); // Mididevice.info-->MidiDeviceFacade
private final Vector ignoreTokens = new Vector();
private static final Vector listeners = new Vector();
private static final boolean DEF_ONLY_AT_ZERO = false;
// PREFERENCE STUFF
public static final int CONCURRENCY_LOW = 0;
public static final int CONCURRENCY_MEDIUM = 1;
public static final int CONCURRENCY_HIGH = 2;
private static final String PREF_sysexConcurrencyLevel = "sysexConcurrencyLevel";
public final static String PREF_midiIgnoreTokens = "midiPortIgnoreTokens";
public static final Preferences prefs = Preferences.userNodeForPackage(MidiSystemFacade.class).node("MidiSystemFacade");
// CONCURRENCY
private static volatile int concurrencyLevel;
static {
getSysexConcurrencyLevel();
}
private static final Vector midiIn_q = new Vector(100);
private static FinalMidiMessage finalizeMidiMessage(MidiMessage m, MidiDevice.Info source) {
if (m instanceof SysexMessage)
return new Sysex(m, source);
if (m instanceof ShortMessage)
return new Short(m, source);
return null;
}
private static volatile int staleDaemonSleepTime = 625;
private static ZDefaultThread staleDaemon = new ZDefaultThread("Midi stale message daemon") {
public void run() {
int nextStaleIndex = -1;
while (alive) {
try {
try {
Thread.sleep(staleDaemonSleepTime);
} catch (InterruptedException e) {
}
synchronized (midiIn_q) {
try {
if (nextStaleIndex != -1) {
List rem = new ArrayList(midiIn_q.subList(nextStaleIndex, midiIn_q.size()));
midiIn_q.clear();
midiIn_q.addAll(rem);
}
//System.out.println("midi in q size: " + midiIn_q.size());
} finally {
nextStaleIndex = midiIn_q.size();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
static {
staleDaemon.start();
}
private static List filterMidiIn_q(Filterable filter, MidiDevice.Info source, long timeStamp, int maxMatchCount, boolean rmv) {
Object filt_msg;
FinalMidiMessage m;
ArrayList matches = new ArrayList();
if (filter != null) {
synchronized (midiIn_q) {
for (int n = 0, p = midiIn_q.size(); n < p; n++) {
m = (FinalMidiMessage) midiIn_q.get(n);
if (m != null && m.getSource().equals(source) && (timeStamp == -1 || m.getTimeStamp() >= timeStamp) && m.getMatchRef() <= maxMatchCount) {
filt_msg = filter.filter(m);
if (filt_msg != null) {
matches.add(filt_msg);
m.addMatchRef();
if (rmv)
midiIn_q.set(n, null);
}
}
}
}
}
return matches;
}
private static final RWLock globalSysexLock = new RWLock();
private static final RWLock secondaryGlobalSysexLock = new RWLock();
public static void setSysexConcurrencyLevel(int level) {
midiLock.configure();
try {
concurrencyLevel = level;
if (concurrencyLevel < CONCURRENCY_LOW)
concurrencyLevel = CONCURRENCY_LOW;
else if (concurrencyLevel > CONCURRENCY_HIGH)
concurrencyLevel = CONCURRENCY_HIGH;
prefs.putInt(PREF_sysexConcurrencyLevel, concurrencyLevel);
getInstance().fireMidiSystemChanged();
} finally {
midiLock.unlock();
}
}
public static int getSysexConcurrencyLevel() {
return (concurrencyLevel = prefs.getInt(PREF_sysexConcurrencyLevel, CONCURRENCY_LOW));
}
private static void beginShortSysexTransaction() throws IllegalArgumentException {
midiLock.access();
try {
switch (concurrencyLevel) {
case CONCURRENCY_LOW:
globalSysexLock.write();
return;
case CONCURRENCY_MEDIUM:
case CONCURRENCY_HIGH:
globalSysexLock.read();
return;
default:
throw new IllegalArgumentException("invalid concurrency level");
}
} catch (IllegalArgumentException e) {
midiLock.unlock();
throw e;
}
}
private static void endShortSysexTransaction() {
try {
midiLock.unlock();
} finally {
globalSysexLock.unlock();
}
}
private static void beginLongSysexTransaction() throws IllegalArgumentException {
midiLock.access();
try {
switch (concurrencyLevel) {
case CONCURRENCY_LOW:
globalSysexLock.write();
try {
secondaryGlobalSysexLock.write();
} catch (IllegalArgumentException e) {
globalSysexLock.unlock();
throw e;
}
return;
case CONCURRENCY_MEDIUM:
globalSysexLock.read();
try {
secondaryGlobalSysexLock.write();
} catch (IllegalArgumentException e) {
globalSysexLock.unlock();
}
return;
case CONCURRENCY_HIGH:
globalSysexLock.read();
try {
secondaryGlobalSysexLock.read();
} catch (IllegalArgumentException e) {
globalSysexLock.unlock();
}
return;
default:
throw new IllegalArgumentException("invalid concurrency level");
}
} catch (IllegalArgumentException e) {
midiLock.unlock();
throw e;
}
}
private static void endLongSysexTransaction() {
try {
midiLock.unlock();
} finally {
try {
globalSysexLock.unlock();
} finally {
secondaryGlobalSysexLock.unlock();
}
}
}
/** Creates a new instance of MidiSystemFacade */
private MidiSystemFacade() {
getIgnoreTokensPreference();
refreshMidiDeviceInfo(true);
//ZoeosFrame.getInstance().addWindowListener(this);
}
public interface MidiSystemListener {
public void midiSystemChanged(MidiSystemFacade msf);
}
public static void addMidiSystemListener(MidiSystemListener msl) {
listeners.add(msl);
}
public static void removeMidiSystemListener(MidiSystemListener msl) {
listeners.remove(msl);
}
private void fireMidiSystemChanged() {
final Vector listeners_clone = (Vector) listeners.clone();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
for (Iterator i = listeners_clone.iterator(); i.hasNext();)
((MidiSystemListener) i.next()).midiSystemChanged(MidiSystemFacade.this);
}
});
}
public void zDispose() {
try {
synchronized (deviceMap) {
for (Iterator i = deviceMap.values().iterator(); i.hasNext();) {
Object d = i.next();
if (d instanceof MidiDeviceFacade)
((MidiDeviceFacade) d).close();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void setIgnoreTokensPreference() {
Preferences.userNodeForPackage(this.getClass()).put(PREF_midiIgnoreTokens, makeIgnoreTokensPreferenceString());
}
private void getIgnoreTokensPreference() {
synchronized (ignoreTokens) {
String toks = Preferences.userNodeForPackage(this.getClass()).get(PREF_midiIgnoreTokens, "Broadcast;Control;Sync;Creative;Wavetable;Microsoft;Java Sound;Wire;");
StringTokenizer t = new StringTokenizer(toks, Zoeos.preferenceFieldSeperator, false);
while (t.hasMoreTokens())
ignoreTokens.add(t.nextToken());
}
}
private String makeIgnoreTokensPreferenceString() {
synchronized (ignoreTokens) {
StringBuffer sb = new StringBuffer();
int num = ignoreTokens.size();
for (int n = 0; n < num; n++)
sb.append(ignoreTokens.get(n).toString() + Zoeos.preferenceFieldSeperator);
return sb.toString();
}
}
public void refresh(boolean visualFeedback) {
refreshMidiDeviceInfo(visualFeedback);
}
private boolean shouldIgnorePort(MidiDevice.Info devInfo) {
return shouldIgnorePort(devInfo, DEF_ONLY_AT_ZERO);
}
private boolean shouldIgnorePort(MidiDevice.Info devInfo, boolean onlyAtZero) {
synchronized (ignoreTokens) {
int numIgnores = ignoreTokens.size();
for (int n = 0; n < numIgnores; n++) {
if (onlyAtZero) {
if (devInfo.getName().indexOf(ignoreTokens.get(n).toString()) == 0)
return true;
} else if (devInfo.getName().indexOf(ignoreTokens.get(n).toString()) >= 0)
return true;
}
return false;
}
}
private void assertDevice(MidiDevice.Info dev) throws UnknownMidiDeviceException {
if (!deviceMap.containsKey(dev)) throw new UnknownMidiDeviceException(dev);
}
public boolean isPortPermitted(MidiDevice.Info devInfo) throws UnknownMidiDeviceException {
assertDevice(devInfo);
return !shouldIgnorePort(devInfo);
}
public boolean ignoreTokenExists(String tok) {
return ignoreTokens.contains(tok);
}
public boolean ignoreTokenExists(MidiDevice.Info devInfo) {
return ignoreTokens.contains(devInfo.getName());
}
public void addIgnoreToken(final Object token) {
synchronized (ignoreTokens) {
if (!ignoreTokens.contains(token)) {
ignoreTokens.add(token);
setIgnoreTokensPreference();
}
}
fireMidiSystemChanged();
}
public String getIgnoreTokensFormattedForDisplay() {
String[] toks = getIgnoreTokens();
String fs = "";
for (int i = 0,j = toks.length; i < j; i++)
fs += toks[i] + ";";
return fs;
}
public String[] getIgnoreTokens() {
String[] toks;
final Vector ignoreTokens_clone = (Vector) ignoreTokens.clone();
toks = (String[]) ignoreTokens_clone.toArray(new String[ignoreTokens_clone.size()]);
return toks;
}
public void setIgnoreTokens(final String[] toks) {
synchronized (ignoreTokens) {
ignoreTokens.clear();
ignoreTokens.addAll(Arrays.asList(toks));
setIgnoreTokensPreference();
}
fireMidiSystemChanged();
}
public void addIgnoreToken(final MidiDevice.Info devInfo) {
synchronized (ignoreTokens) {
if (!ignoreTokens.contains(devInfo.getName())) {
ignoreTokens.add(devInfo.getName());
setIgnoreTokensPreference();
}
}
fireMidiSystemChanged();
}
public void removeIgnoreToken(final Object token) {
synchronized (ignoreTokens) {
ignoreTokens.remove(token);
setIgnoreTokensPreference();
}
fireMidiSystemChanged();
}
public void removeIgnoreToken(final MidiDevice.Info devInfo) {
synchronized (ignoreTokens) {
ignoreTokens.remove(devInfo.getName());
setIgnoreTokensPreference();
}
fireMidiSystemChanged();
}
public void clearIgnoreTokens() {
synchronized (ignoreTokens) {
ignoreTokens.clear();
setIgnoreTokensPreference();
}
fireMidiSystemChanged();
}
private void refreshMidiDeviceInfo(boolean visualFeedback) {
midiLock.access();
try {
synchronized (deviceMap) {
MidiDevice.Info[] devices;
devices = MidiSystem.getMidiDeviceInfo();
Map dm_clone = (Map) deviceMap.clone();
deviceMap.clear();
Zoeos z = Zoeos.getInstance();
if (visualFeedback)
z.beginProgressElement(this, "Initializing Midi System", devices.length);
MidiDevice.Info info;
try {
for (int n = 0; n < devices.length; n++) {
try {
info = devices[n];
if (!dm_clone.containsKey(info)) {
deviceMap.put(info, new MidiDeviceFacade(info));
} else
deviceMap.put(info, dm_clone.get(info));
} finally {
if (visualFeedback)
z.updateProgressElement(this);
}
}
} finally {
if (visualFeedback)
z.endProgressElement(this);
}
}
} finally {
midiLock.unlock();
}
this.fireMidiSystemChanged();
}
private static MidiSystemFacade instance;
public static MidiSystemFacade getInstance() {
if (instance == null)
instance = new MidiSystemFacade();
return instance;
}
public MidiDevice.Info[] getPermittedDevices() {
midiLock.access();
try {
ArrayList permitted = new ArrayList();
MidiDevice.Info dev;
for (Iterator i = deviceMap.keySet().iterator(); i.hasNext();) {
dev = (MidiDevice.Info) i.next();
if (!this.shouldIgnorePort(dev))
permitted.add(dev);
}
return (MidiDevice.Info[]) permitted.toArray(new MidiDevice.Info[permitted.size()]);
} finally {
midiLock.unlock();
}
}
public MidiDevice.Info[] getAllDevices() {
Hashtable dmc = (Hashtable) deviceMap.clone();
return (MidiDevice.Info[]) dmc.keySet().toArray(new MidiDevice.Info[dmc.keySet().size()]);
}
public Inlet getInlet(MidiDevice.Info i, Object owner, String name) throws MidiUnavailableException, UnknownMidiDeviceException, MidiDeviceNotPermittedException {
assertDevice(i);
midiLock.access();
try {
synchronized (deviceMap) {
if (shouldIgnorePort(i))
throw new MidiDeviceNotPermittedException(i);
MidiDeviceFacade fac = (MidiDeviceFacade) deviceMap.get(i);
return fac.getInlet(owner, name);
}
} finally {
midiLock.unlock();
}
}
public Outlet getOutlet(MidiDevice.Info i, Object owner, String name) throws MidiUnavailableException, UnknownMidiDeviceException, MidiDeviceNotPermittedException {
midiLock.access();
try {
synchronized (deviceMap) {
assertDevice(i);
if (shouldIgnorePort(i))
throw new MidiDeviceNotPermittedException(i);
MidiDeviceFacade fac = (MidiDeviceFacade) deviceMap.get(i);
return fac.getOutlet(owner, name);
}
} finally {
midiLock.unlock();
}
}
public PausingOutlet getPausingOutlet(MidiDevice.Info i, Object owner, String name) throws MidiUnavailableException, UnknownMidiDeviceException, MidiDeviceNotPermittedException {
midiLock.access();
try {
synchronized (deviceMap) {
assertDevice(i);
if (shouldIgnorePort(i))
throw new MidiDeviceNotPermittedException(i);
MidiDeviceFacade fac = (MidiDeviceFacade) deviceMap.get(i);
return fac.getPausingOutlet(owner, name);
}
} finally {
midiLock.unlock();
}
}
public BufferedInlet getBufferedInlet(MidiDevice.Info i, Object owner, String name) throws MidiUnavailableException, UnknownMidiDeviceException, MidiDeviceNotPermittedException {
midiLock.access();
try {
synchronized (deviceMap) {
assertDevice(i);
if (shouldIgnorePort(i))
throw new MidiDeviceNotPermittedException(i);
MidiDeviceFacade fac = (MidiDeviceFacade) deviceMap.get(i);
return fac.getBufferedInlet(owner, name);
}
} finally {
midiLock.unlock();
}
}
public static DeviceHunter getDeviceHunter() {
return Impl_DeviceHunter.getInstance();
}
public int getRefCount(MidiDevice.Info devInfo) throws UnknownMidiDeviceException {
midiLock.access();
try {
synchronized (deviceMap) {
assertDevice(devInfo);
MidiDeviceFacade df = (MidiDeviceFacade) deviceMap.get(devInfo);
if (df != null)
return df.numRefs();
return 0;
}
} finally {
midiLock.unlock();
}
}
private class MidiDeviceFacade {
private MidiDevice realDevice;
private MidiDevice.Info devInfo;
private int refs = 0;
private Transmitter xmit;
private Receiver recv;
private final Receiver deviceReceiver = new Receiver() {
public void send(javax.sound.midi.MidiMessage midiMessage, long param) {
try {
final FinalMidiMessage fm = finalizeMidiMessage(midiMessage, devInfo);
if (fm != null)
synchronized (midiIn_q) {
midiIn_q.add(fm);
midiIn_q.notifyAll();
}
else
System.out.println("Couldn't finalize a midi message!");
// NOTE: MIDI THREAD SEEMS TO HAVE PRIORITY OF 6
} catch (Exception e) {
e.printStackTrace();
}
}
public void close() {
System.out.println("Call to close application receiver");
}
};
private MidiDeviceFacade(MidiDevice.Info di) {
devInfo = di;
//getRealDevice();
}
private MidiDevice getRealDevice() throws MidiUnavailableException {
if (realDevice == null) {
realDevice = MidiSystem.getMidiDevice(devInfo);
System.out.println("Midi device retrieved: " + realDevice.getDeviceInfo().getName());
}
return realDevice;
}
private void openRealDevice() throws MidiUnavailableException {
if (!getRealDevice().isOpen()) {
getRealDevice().open();
if (getRealDevice().getMaxTransmitters() != 0)
try {
xmit = getRealDevice().getTransmitter();
xmit.setReceiver(deviceReceiver);
} catch (MidiUnavailableException e) {
xmit = null;
}
if (getRealDevice().getMaxReceivers() != 0)
try {
recv = getRealDevice().getReceiver();
} catch (MidiUnavailableException e) {
recv = null;
}
System.out.println("Midi device opened: " + realDevice.getDeviceInfo().getName());
}
}
private void closeRealDevice() {
if (realDevice != null) {
if (xmit != null) {
xmit.close();
xmit = null;
}
if (recv != null) {
recv.close();
recv = null;
}
if (realDevice.isOpen()) {
realDevice.close();
System.out.println("Midi device closed: " + realDevice.getDeviceInfo().getName());
}
realDevice = null;
}
}
public void close() {
midiLock.access();
try {
closeRealDevice();
} finally {
midiLock.unlock();
}
}
private int numRefs() {
return refs;
}
private void release(Object o) {
midiLock.access();
try {
synchronized (deviceMap) {
if (o instanceof Impl_AbstractInlet) {
refs--;
System.out.println(Zoeos.getZoeosTime() + "Inlet released");
} else if (o instanceof Impl_Outlet) {
refs--;
System.out.println(Zoeos.getZoeosTime() + "Outlet released");
}
try {
if (refs == 0 && getRealDevice().isOpen()) {
System.out.println(Zoeos.getZoeosTime() + ": " + "Midi Device released: " + realDevice.getDeviceInfo());
closeRealDevice();
}
} catch (MidiUnavailableException e) {
e.printStackTrace();
}
}
} finally {
midiLock.unlock();
}
fireMidiSystemChanged();
}
private Inlet getInlet(Object owner, String name) throws MidiUnavailableException {
midiLock.access();
try {
synchronized (deviceMap) {
if (!getRealDevice().isOpen())
openRealDevice();
try {
if (xmit != null) {
refs++;
return new Impl_Inlet(owner, name);
} else
throw new MidiUnavailableException("not an in device");
} finally {
if (refs == 0 && getRealDevice().isOpen())
closeRealDevice();
}
}
} finally {
midiLock.unlock();
}
}
private BufferedInlet getBufferedInlet(Object owner, String name) throws MidiUnavailableException {
midiLock.access();
try {
synchronized (deviceMap) {
if (!getRealDevice().isOpen())
openRealDevice();
try {
if (xmit != null) {
refs++;
return new Impl_BufferedInlet(owner, name);
} else
throw new MidiUnavailableException("not an in device");
} finally {
if (refs == 0 && getRealDevice().isOpen())
closeRealDevice();
}
}
} finally {
midiLock.unlock();
}
}
private Outlet getOutlet(Object owner, String name) throws MidiUnavailableException {
midiLock.access();
try {
synchronized (deviceMap) {
if (!getRealDevice().isOpen())
openRealDevice();
try {
if (recv != null) {
refs++;
return new Impl_Outlet(owner, name);
} else
throw new MidiUnavailableException("not an out device");
} finally {
if (refs == 0 && getRealDevice().isOpen())
closeRealDevice();
}
}
} finally {
midiLock.unlock();
}
}
private PausingOutlet getPausingOutlet(Object owner, String name) throws MidiUnavailableException {
midiLock.access();
try {
synchronized (deviceMap) {
if (!getRealDevice().isOpen())
openRealDevice();
try {
if (recv != null) {
refs++;
return new Impl_PausingOutlet(owner, name);
} else
throw new MidiUnavailableException("not an out device");
} finally {
if (refs == 0 && getRealDevice().isOpen())
closeRealDevice();
}
}
} finally {
midiLock.unlock();
}
}
/*private boolean isOpen() {
return realDevice.isOpen();
}
private long getMicrosecondPosition() {
return realDevice.getMicrosecondPosition();
}
*/
private abstract class Impl_AbstractInlet /*implements MidiInletStateMachine*/ {
protected Object owner;
protected String name;
public Impl_AbstractInlet(Object owner, String name) throws MidiUnavailableException {
this.owner = owner;
this.name = name;
System.out.println(Zoeos.getZoeosTime() + ": " + "INLET Created on Midi Device : " + realDevice.getDeviceInfo());
}
private boolean discarded = false;
public void discard() {
if (!discarded) {
release(this);
discarded = true;
}
}
public MidiDevice.Info getDeviceInfo() {
return realDevice.getDeviceInfo();
}
public MidiDeviceFacade getDevice() {
return MidiDeviceFacade.this;
}
public Object getOwner() {
return owner;
}
}
private class Impl_Inlet extends Impl_AbstractInlet implements Inlet {
private static final int defTimeout = 1000; // ms
private volatile int timeout = defTimeout;
private final List client_q = new ArrayList();
private Impl_Inlet(Object owner, String name) throws MidiUnavailableException {
super(owner, name);
}
public void setTimeout(int val) {
timeout = val;
}
public Object[] dispatchAndWaitForLongReply(Outlet o, MidiMessage m, Filterable filter) throws com.pcmsolutions.device.EMU.E4.RemoteUnreachableException, RemoteDeviceDidNotRespondException, RemoteMessagingException {
Object[] replies = dispatchAndWaitForLongReplies(o, m, filter, 1);
if (replies != null && replies.length > 0)
return replies;
return null;
}
public Object dispatchAndWaitForReply(Outlet o, MidiMessage m, Filterable filter) throws RemoteMessagingException, RemoteDeviceDidNotRespondException, RemoteUnreachableException {
Object[] replies = new Object[0];
replies = dispatchAndWaitForReplies(o, m, filter, 1);
if (replies != null && replies.length > 0)
return replies[0];
return null;
}
public Object[] dispatchAndWaitForReplies(Outlet o, MidiMessage m, Filterable filter, int numReplies) throws com.pcmsolutions.device.EMU.E4.RemoteUnreachableException, RemoteDeviceDidNotRespondException, RemoteMessagingException {
return dispatchAndWaitForReplies(o, m, filter, numReplies, false);
}
public Object[] dispatchAndWaitForLongReplies(Outlet o, MidiMessage m, Filterable filter, int numReplies) throws com.pcmsolutions.device.EMU.E4.RemoteUnreachableException, RemoteDeviceDidNotRespondException, RemoteMessagingException {
return dispatchAndWaitForReplies(o, m, filter, numReplies, true);
}
private Object[] dispatchAndWaitForReplies(Outlet o, MidiMessage m, Filterable filter, int numReplies, final boolean longReplies) throws com.pcmsolutions.device.EMU.E4.RemoteUnreachableException, RemoteDeviceDidNotRespondException, RemoteMessagingException {
try {
return taskDispatchAndWaitForReplies(o, m, filter, numReplies, longReplies, 1);
} catch (MidiUnavailableException e) {
e.printStackTrace();
throw new RemoteUnreachableException("midi unavailable");
}
}
public Object[] taskDispatchAndWaitForReplies(Outlet o, MidiMessage m, Filterable filter, final int reqdReplies, final boolean longReplies, final int retries) throws MidiUnavailableException, RemoteUnreachableException, RemoteMessagingException, RemoteDeviceDidNotRespondException {
//if (scheme1)
return dispatchAndWaitForRepliesScheme1(o, m, filter, reqdReplies, longReplies);
// else
// return dispatchAndWaitForRepliesScheme2(o, m, filter, reqdReplies, longReplies);
}
private Object[] dispatchAndWaitForRepliesScheme1(final Outlet o, final MidiMessage m, final Filterable filter, final int reqdReplies, final boolean longReplies) throws com.pcmsolutions.device.EMU.E4.RemoteUnreachableException, RemoteDeviceDidNotRespondException, RemoteMessagingException {
if (reqdReplies < 1)
throw new IllegalArgumentException("Request for zero replies in inlet");
//final int priority = Thread.currentThread().getPriority();
//Thread.currentThread().setPriority(TRANSACTION_PRIORITY);
try {
if (longReplies)
MidiSystemFacade.beginLongSysexTransaction();
else
MidiSystemFacade.beginShortSysexTransaction();
try {
if (xmit == null)
throw new com.pcmsolutions.device.EMU.E4.RemoteUnreachableException("Midi transmitter not available");
client_q.clear();
final long startTicks = Zoeos.getZoeosTicks();
final long startTime = Zoeos.getZoeosTimeForZoeosTicks(startTicks);
o.dispatch(m, -1);
synchronized (midiIn_q) {
while (true) {
client_q.addAll(filterMidiIn_q(filter, devInfo, startTicks, 0, false));
if ((Zoeos.getZoeosTime() - startTime < timeout) && client_q.size() < reqdReplies)
try {
midiIn_q.wait(timeout);
} catch (InterruptedException e) {
}
else
break;
}
}
/* do {
try {
midiIn_q.wait(timeout);
} catch (InterruptedException e) {
}
client_q.addAll(filterMidiIn_q(filter, devInfo, startTime, true));
} while ((Zoeos.getZoeosTime() - startTime < timeout) && client_q.size() < reqdReplies);
*/
/* try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
return analyzeQueues(m, reqdReplies);
} finally {
if (longReplies)
MidiSystemFacade.endLongSysexTransaction();
else
MidiSystemFacade.endShortSysexTransaction();
}
} catch (IllegalArgumentException e) {
throw new com.pcmsolutions.device.EMU.E4.RemoteUnreachableException("Midi locking error");
} finally {
// Thread.currentThread().setPriority(priority);
}
}
private Object[] analyzeQueues(MidiMessage m, int reqdReplies) throws RemoteDeviceDidNotRespondException, RemoteMessagingException {
if (client_q.size() >= reqdReplies) {
if (client_q.size() > reqdReplies)
System.out.println("more than requested");
return client_q.toArray();
} else if (client_q.size() == 0)
throw new RemoteDeviceDidNotRespondException("No response(s) to message: " + ZUtilities.getByteString(m.getMessage()) + Zoeos.lineSeperator);
else
throw new RemoteMessagingException("Insufficient number of responses to message: " + ZUtilities.getByteString(m.getMessage()) + Zoeos.lineSeperator);
}
/* private Object[] dispatchAndWaitForRepliesScheme2(Outlet o, MidiMessage m, Filterable filter, int reqdReplies, final boolean longReplies) throws com.pcmsolutions.device.EMU.E4.RemoteUnreachableException, RemoteDeviceDidNotRespondException, RemoteMessagingException {
if (reqdReplies < 1)
throw new IllegalArgumentException("Request for zero replies in inlet");
Object[] replies = null;
int last_q_size;
final int priority = Thread.currentThread().getPriority();
Thread.currentThread().setPriority(TRANSACTION_PRIORITY);
try {
if (longReplies)
MidiSystemFacade.beginLongSysexTransaction();
else
MidiSystemFacade.beginShortSysexTransaction();
try {
if (xmit == null)
throw new com.pcmsolutions.device.EMU.E4.RemoteUnreachableException("Midi transmitter not available");
synchronized (midi_q) {
client_q.clear();
dropped_q.clear();
midi_q.clear();
o.dispatch(m, -1);
//for (int i = 0; i < reqdReplies; i++) {
while (true) {
last_q_size = client_q.size();
try {
midi_q.wait(timeout);
} catch (InterruptedException e) {
continue;
}
filterMidi_q(filter);
if (client_q.size() >= reqdReplies) {
replies = client_q.toArray();
break;
}
if (!(client_q.size() > last_q_size))
break;
}
}
if (replies == null)
if (client_q.size() > 0)
throw new RemoteMessagingException("Insufficient number of responses to message: " + ZUtilities.getByteString(m.getMessage()) + Zoeos.lineSeperator);
else if (dropped_q.size() == 0)
throw new RemoteDeviceDidNotRespondException("No response to message: " + ZUtilities.getByteString(m.getMessage()) + Zoeos.lineSeperator);
else
throw new RemoteMessagingException("Invalid response to message: " + ZUtilities.getByteString(m.getMessage()) + Zoeos.lineSeperator + "Response was: " + Zoeos.lineSeperator + qToString(dropped_q));
return replies;
} finally {
if (longReplies)
MidiSystemFacade.endLongSysexTransaction();
else
MidiSystemFacade.endShortSysexTransaction();
}
} catch (IllegalArgumentException e) {
throw new com.pcmsolutions.device.EMU.E4.RemoteUnreachableException("Midi locking error");
} finally {
Thread.currentThread().setPriority(priority);
}
}
*/
/* private String qToString(List q) {
String s = "";
for (int i = 0, j = q.size(); i < j; i++)
try {
s += q.get(i).toString() + Zoeos.lineSeperator;
} catch (Exception e) {
}
return s;
}*/
// returns num of unfiltered messages
// make sure this is called under midi_q synchronization
/* private int filterMidi_q(Filterable filter) {
int nuf = 0;
Object filt_msg;
FinalMidiMessage m;
if (filter != null) {
for (int n = 0, p = midi_q.size(); n < p; n++) {
m = (FinalMidiMessage) midi_q.get(n);
filt_msg = filter.filter(m);
if (filt_msg != null)
client_q.addDesktopElement(filt_msg);
else {
dropped_q.addDesktopElement(m);
nuf++;
}
}
}
midi_q.clear();
return nuf;
}
*/
}
private class Impl_BufferedInlet extends Impl_AbstractInlet implements BufferedInlet {
protected final Vector filters = new Vector();
protected long lastTimeStamp = Zoeos.getZoeosTicks();
private Impl_BufferedInlet(Object owner, String name) throws MidiUnavailableException {
super(owner, name);
}
public List clearBuffer() {
ArrayList buffer = new ArrayList();
int fsz = filters.size();
if (fsz == 0)
return buffer;
Filterable filter;
for (int n = 0; n < fsz; n++) {
filter = (Filterable) filters.get(n);
buffer.addAll(filterMidiIn_q(filter, devInfo, lastTimeStamp, Integer.MAX_VALUE, false));
}
lastTimeStamp = Zoeos.getZoeosTicks();
return buffer;
}
public void addFilter(Filterable o) {
filters.add(o);
}
public void removeFilter(Filterable o) {
filters.remove(o);
}
public List getFilters() {
return (List) filters.clone();
}
}
private class Impl_Outlet implements Outlet {
protected Object owner;
protected String name;
private Impl_Outlet(Object owner, String name) {
this.owner = owner;
this.name = name;
System.out.println(Zoeos.getZoeosTime() + ": " + "OUTLET Created on Midi Device : " + realDevice.getDeviceInfo());
}
public Object getOwner() {
return owner;
}
public MidiDevice.Info getDeviceInfo() {
return realDevice.getDeviceInfo();
}
public void dispatch(MidiMessage midiMessage, long param) throws com.pcmsolutions.device.EMU.E4.RemoteUnreachableException {
midiLock.access();
try {
if (recv == null)
throw new com.pcmsolutions.device.EMU.E4.RemoteUnreachableException("Midi receiver not available");
try {
recv.send(midiMessage, param);
} catch (Exception e) {
System.out.println(e);
}
} finally {
midiLock.unlock();
}
}
private boolean discarded = false;
public void discard() {
if (!discarded) {
release(this);
discarded = true;
}
}
}
private class Impl_PausingOutlet extends Impl_Outlet implements PausingOutlet {
private ZWaitThread timer;
private volatile long pause = 0;
private boolean isPaused = false;
// passed pause ignored for now
private Impl_PausingOutlet(Object owner, String name) {
super(owner, name);
timer = new TimerThread();
timer.start();
}
public void setPause(long pause) {
this.pause = pause;
}
private class TimerThread extends ZWaitThread {
private Object start_mon = new Object();
private boolean running = false;
public void start() {
synchronized (start_mon) {
super.start();
while (running == false)
try {
start_mon.wait();
} catch (InterruptedException e) {
}
}
}
public void run() {
setName("Pausing Outlet Timer Thread");
synchronized (start_mon) {
running = true;
start_mon.notify(); //notify starter of thread that it is now running
}
synchronized (this) {
while (alive) {
try {
wait();
Thread.sleep(pause);
synchronized (Impl_PausingOutlet.this) {
isPaused = false;
Impl_PausingOutlet.this.notifyAll();
}
} catch (InterruptedException e) {
}
}
}
}
}
public void discard() {
try {
super.discard();
} finally {
timer.stopThread();
}
}
public void dispatch(MidiMessage midiMessage, long param, long pause) throws com.pcmsolutions.device.EMU.E4.RemoteUnreachableException {
midiLock.access();
this.pause = pause;
try {
if (recv == null)
throw new com.pcmsolutions.device.EMU.E4.RemoteUnreachableException("Midi receiver not available");
synchronized (this) {
while (isPaused) {
try {
wait();
} catch (Exception e) {
}
}
try {
recv.send(midiMessage, param);
if (pause != 0) {
isPaused = true;
synchronized (timer) {
timer.notify();
}
}
} catch (Exception e) {
System.out.println(e);
}
}
} finally {
midiLock.unlock();
}
}
public void dispatch(MidiMessage midiMessage, long param) throws com.pcmsolutions.device.EMU.E4.RemoteUnreachableException {
dispatch(midiMessage, param, 0);
}
}
}
public interface Inlet {
public Object getOwner();
public void discard();
public MidiDevice.Info getDeviceInfo();
public MidiDeviceFacade getDevice();
public void setTimeout(int val);
public Object dispatchAndWaitForReply(Outlet o, MidiMessage m, Filterable filter) throws RemoteMessagingException, RemoteDeviceDidNotRespondException, RemoteUnreachableException;
public Object[] dispatchAndWaitForLongReply(Outlet o, MidiMessage m, Filterable filter) throws com.pcmsolutions.device.EMU.E4.RemoteUnreachableException, RemoteDeviceDidNotRespondException, RemoteMessagingException;
public Object[] dispatchAndWaitForReplies(Outlet o, MidiMessage m, Filterable filter, int numReplies) throws com.pcmsolutions.device.EMU.E4.RemoteUnreachableException, RemoteDeviceDidNotRespondException, RemoteMessagingException;
public Object[] dispatchAndWaitForLongReplies(Outlet o, MidiMessage m, Filterable filter, int numReplies) throws com.pcmsolutions.device.EMU.E4.RemoteUnreachableException, RemoteDeviceDidNotRespondException, RemoteMessagingException;
}
public interface BufferedInlet {
public Object getOwner();
public void discard();
public MidiDevice.Info getDeviceInfo();
public List clearBuffer();
public void addFilter(Filterable o);
public void removeFilter(Filterable o);
public List getFilters();
}
public interface Outlet {
public Object getOwner();
public MidiDevice.Info getDeviceInfo();
public void dispatch(MidiMessage midiMessage, long param) throws com.pcmsolutions.device.EMU.E4.RemoteUnreachableException;
public void discard();
}
public interface PausingOutlet extends MidiSystemFacade.Outlet {
public void dispatch(MidiMessage midiMessage, long param, long pause) throws com.pcmsolutions.device.EMU.E4.RemoteUnreachableException;
}
/**
* @author pmeehan
* @version
*/
public static interface DeviceHunter {
public List hunt(int id, MidiDevice.Info[] devices, int timeout);
public List hunt(int id, MidiDevice.Info[] devices);
public List hunt(int id, int timeout);
public List hunt(int id);
}
private static class Impl_DeviceHunter implements DeviceHunter {
private static final Impl_DeviceHunter INSTANCE = new Impl_DeviceHunter();
private static final int defTimeout = 250;
private int timeout = defTimeout;
private List huntOutlets = new ArrayList();
private List huntInlets = new ArrayList();
private Outlet currOutlet = null;
private Impl_DeviceHunter() {
}
public String toString() {
return "Device Hunter";
}
public static DeviceHunter getInstance() {
return INSTANCE;
}
public List hunt(int id, MidiDevice.Info[] devInfos, int timeout) {
MidiSystemFacade.midiLock.configure();
try {
prepare(devInfos, timeout);
List replies = performHunt(id);
cleanup();
return replies;
} finally {
MidiSystemFacade.midiLock.unlock();
}
}
public List hunt(int id, MidiDevice.Info[] devInfos) {
MidiSystemFacade.midiLock.configure();
try {
prepare(devInfos, defTimeout);
List replies = performHunt(id);
cleanup();
return replies;
} finally {
MidiSystemFacade.midiLock.unlock();
}
}
public List hunt(int id, int timeout) {
MidiSystemFacade ms = MidiSystemFacade.getInstance();
MidiSystemFacade.midiLock.configure();
try {
prepare(ms.getPermittedDevices(), timeout);
List replies = performHunt(id);
cleanup();
return replies;
} finally {
MidiSystemFacade.midiLock.unlock();
}
}
public List hunt(int id) {
MidiSystemFacade ms = MidiSystemFacade.getInstance();
MidiSystemFacade.midiLock.configure();
try {
prepare(ms.getPermittedDevices(), defTimeout);
List replies = performHunt(id);
cleanup();
return replies;
} finally {
MidiSystemFacade.midiLock.unlock();
}
}
private void prepare(MidiDevice.Info[] devInfos, int timeout) {
MidiSystemFacade ms = MidiSystemFacade.getInstance();
MidiDevice.Info d;
for (int i = 0,j = devInfos.length; i < j; i++) {
d = devInfos[i];
try {
BufferedInlet inlet = ms.getBufferedInlet(d, this, "Hunt Port");
inlet.addFilter(new IdentityReply());
huntInlets.add(inlet);
//System.out.println(Zoeos.getZoeosTime() + ": " + "Found in port:" + inlet.getDeviceInfo().toString());
} catch (MidiUnavailableException e) {
// e.printStackTrace();
} catch (UnknownMidiDeviceException e) {
e.printStackTrace();
} catch (MidiDeviceNotPermittedException e) {
e.printStackTrace();
}
try {
Outlet o = ms.getOutlet(d, this, "Hunt Port");
huntOutlets.add(o);
//System.out.println(Zoeos.getZoeosTime() + ": " + "Found out port:" + o.getDeviceInfo().toString());
} catch (MidiUnavailableException e) {
// e.printStackTrace();
} catch (UnknownMidiDeviceException e) {
e.printStackTrace();
} catch (MidiDeviceNotPermittedException e) {
e.printStackTrace();
}
}
this.timeout = timeout;
}
private List performHunt(int id) {
Zoeos z = Zoeos.getInstance();
List replies = new ArrayList();
z.beginProgressElement(this, "Hunting for Devices...", huntOutlets.size());
try {
IdentityRequest msg = new IdentityRequest((byte) id);
for (Iterator i = huntOutlets.iterator(); i.hasNext();) {
currOutlet = (Outlet) i.next();
currOutlet.dispatch(msg, -1);
System.out.println(Zoeos.getZoeosTime() + ": " + "Sending Identity Request on port:" + currOutlet.getDeviceInfo().toString() + " ; while listening on " + huntInlets.size() + " ports");
try {
Thread.sleep(timeout);
} catch (IllegalStateException e) {
System.out.println(e);
} catch (InterruptedException e) {
System.out.println(e);
}
for (Iterator j = huntInlets.iterator(); j.hasNext();) {
final BufferedInlet inlet = (BufferedInlet) j.next();
final List buf = inlet.clearBuffer();
for (Iterator k = buf.iterator(); k.hasNext();) {
Object o = k.next();
final IdentityReply irm = (IdentityReply) o;
final IdentityRequest reqMsg = (IdentityRequest) msg.clone();
final MidiDevice.Info inInfo = inlet.getDeviceInfo();
final MidiDevice.Info outInfo = currOutlet.getDeviceInfo();
System.out.println("REPLY:");
System.out.println(irm);
System.out.println("OUT PORT->" + outInfo.toString());
System.out.println("IN PORT->" + inInfo.toString());
replies.add(new SysexTransactionRecord() {
public MidiDevice.Info getOutDeviceInfo() {
return outInfo;
}
public MidiDevice.Info getInDeviceInfo() {
return inInfo;
}
public FinalMidiMessage getReply() {
return irm;
}
public IdentityRequest getRequest() {
return reqMsg;
}
});
}
}
z.updateProgressElement(this);
}
} catch (Exception e) {
System.out.println(e);
} finally {
z.endProgressElement(this);
}
return replies;
}
private void cleanup() {
for (Iterator i = huntInlets.iterator(); i.hasNext();)
((BufferedInlet) i.next()).discard();
huntInlets.clear();
try {
for (Iterator i = huntOutlets.iterator(); i.hasNext();)
((Outlet) i.next()).discard();
huntOutlets.clear();
} catch (Exception e) {
System.out.println(e);
}
}
}
}