Package com.pcmsolutions.comms

Source Code of com.pcmsolutions.comms.MidiDeviceFacade$Impl_PausingOutlet$TimerThread

/*
* 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);
            }
        }
    }
}
TOP

Related Classes of com.pcmsolutions.comms.MidiDeviceFacade$Impl_PausingOutlet$TimerThread

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.