456 lines · 14.9 KB · Java
RawDownload
/* * Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.media.sound; import java.util.EventObject;import java.util.ArrayList;import java.util.List; import javax.sound.sampled.Clip;import javax.sound.sampled.Line;import javax.sound.sampled.LineEvent;import javax.sound.sampled.LineListener; import javax.sound.midi.MetaMessage;import javax.sound.midi.ShortMessage;import javax.sound.midi.MetaEventListener;import javax.sound.midi.ControllerEventListener;   /** * EventDispatcher.  Used by various classes in the Java Sound implementation * to send events. * * @author David Rivas * @author Kara Kytle * @author Florian Bomers */class EventDispatcher implements Runnable {     /**     * time of inactivity until the auto closing clips     * are closed     */    private static final int AUTO_CLOSE_TIME = 5000;      /**     * List of events     */    private ArrayList eventQueue = new ArrayList();      /**     * Thread object for this EventDispatcher instance     */    private Thread thread = null;      /*     * support for auto-closing Clips     */    private ArrayList<ClipInfo> autoClosingClips = new ArrayList<ClipInfo>();     /*     * support for monitoring data lines     */    private ArrayList<LineMonitor> lineMonitors = new ArrayList<LineMonitor>();     /**     * Approximate interval between calls to LineMonitor.checkLine     */    static final int LINE_MONITOR_TIME = 400;      /**     * This start() method starts an event thread if one is not already active.     */    synchronized void start() {         if(thread == null) {            thread = JSSecurityManager.createThread(this,                                                    "Java Sound Event Dispatcher",   // name                                                    true,  // daemon                                                    -1,    // priority                                                    true); // doStart        }    }      /**     * Invoked when there is at least one event in the queue.     * Implement this as a callback to process one event.     */    protected void processEvent(EventInfo eventInfo) {        int count = eventInfo.getListenerCount();         // process an LineEvent        if (eventInfo.getEvent() instanceof LineEvent) {            LineEvent event = (LineEvent) eventInfo.getEvent();            if (Printer.debug) Printer.debug("Sending "+event+" to "+count+" listeners");            for (int i = 0; i < count; i++) {                try {                    ((LineListener) eventInfo.getListener(i)).update(event);                } catch (Throwable t) {                    if (Printer.err) t.printStackTrace();                }            }            return;        }         // process a MetaMessage        if (eventInfo.getEvent() instanceof MetaMessage) {            MetaMessage event = (MetaMessage)eventInfo.getEvent();            for (int i = 0; i < count; i++) {                try {                    ((MetaEventListener) eventInfo.getListener(i)).meta(event);                } catch (Throwable t) {                    if (Printer.err) t.printStackTrace();                }            }            return;        }         // process a Controller or Mode Event        if (eventInfo.getEvent() instanceof ShortMessage) {            ShortMessage event = (ShortMessage)eventInfo.getEvent();            int status = event.getStatus();             // Controller and Mode events have status byte 0xBc, where            // c is the channel they are sent on.            if ((status & 0xF0) == 0xB0) {                for (int i = 0; i < count; i++) {                    try {                        ((ControllerEventListener) eventInfo.getListener(i)).controlChange(event);                    } catch (Throwable t) {                        if (Printer.err) t.printStackTrace();                    }                }            }            return;        }         Printer.err("Unknown event type: " + eventInfo.getEvent());    }      /**     * Wait until there is something in the event queue to process.  Then     * dispatch the event to the listeners.The entire method does not     * need to be synchronized since this includes taking the event out     * from the queue and processing the event. We only need to provide     * exclusive access over the code where an event is removed from the     *queue.     */    protected void dispatchEvents() {         EventInfo eventInfo = null;         synchronized (this) {             // Wait till there is an event in the event queue.            try {                 if (eventQueue.size() == 0) {                    if (autoClosingClips.size() > 0 || lineMonitors.size() > 0) {                        int waitTime = AUTO_CLOSE_TIME;                        if (lineMonitors.size() > 0) {                            waitTime = LINE_MONITOR_TIME;                        }                        wait(waitTime);                    } else {                        wait();                    }                }            } catch (InterruptedException e) {            }            if (eventQueue.size() > 0) {                // Remove the event from the queue and dispatch it to the listeners.                eventInfo = (EventInfo) eventQueue.remove(0);            }         } // end of synchronized        if (eventInfo != null) {            processEvent(eventInfo);        } else {            if (autoClosingClips.size() > 0) {                closeAutoClosingClips();            }            if (lineMonitors.size() > 0) {                monitorLines();            }        }    }      /**     * Queue the given event in the event queue.     */    private synchronized void postEvent(EventInfo eventInfo) {        eventQueue.add(eventInfo);        notifyAll();    }      /**     * A loop to dispatch events.     */    public void run() {         while (true) {            try {                dispatchEvents();            } catch (Throwable t) {                if (Printer.err) t.printStackTrace();            }        }    }      /**     * Send audio and MIDI events.     */    void sendAudioEvents(Object event, List listeners) {        if ((listeners == null)            || (listeners.size() == 0)) {            // nothing to do            return;        }         start();         EventInfo eventInfo = new EventInfo(event, listeners);        postEvent(eventInfo);    }      /*     * go through the list of registered auto-closing     * Clip instances and close them, if appropriate     *     * This method is called in regular intervals     */    private void closeAutoClosingClips() {        synchronized(autoClosingClips) {            if (Printer.debug)Printer.debug("> EventDispatcher.closeAutoClosingClips ("+autoClosingClips.size()+" clips)");            long currTime = System.currentTimeMillis();            for (int i = autoClosingClips.size()-1; i >= 0 ; i--) {                ClipInfo info = autoClosingClips.get(i);                if (info.isExpired(currTime)) {                    AutoClosingClip clip = info.getClip();                    // sanity check                    if (!clip.isOpen() || !clip.isAutoClosing()) {                        if (Printer.debug)Printer.debug("EventDispatcher: removing clip "+clip+"  isOpen:"+clip.isOpen());                        autoClosingClips.remove(i);                    }                    else if (!clip.isRunning() && !clip.isActive() && clip.isAutoClosing()) {                        if (Printer.debug)Printer.debug("EventDispatcher: closing clip "+clip);                        clip.close();                    } else {                        if (Printer.debug)Printer.debug("Doing nothing with clip "+clip+":");                        if (Printer.debug)Printer.debug("  open="+clip.isOpen()+", autoclosing="+clip.isAutoClosing());                        if (Printer.debug)Printer.debug("  isRunning="+clip.isRunning()+", isActive="+clip.isActive());                    }                } else {                    if (Printer.debug)Printer.debug("EventDispatcher: clip "+info.getClip()+" not yet expired");                }            }        }        if (Printer.debug)Printer.debug("< EventDispatcher.closeAutoClosingClips ("+autoClosingClips.size()+" clips)");    }     private int getAutoClosingClipIndex(AutoClosingClip clip) {        synchronized(autoClosingClips) {            for (int i = autoClosingClips.size()-1; i >= 0; i--) {                if (clip.equals(autoClosingClips.get(i).getClip())) {                    return i;                }            }        }        return -1;    }     /**     * called from auto-closing clips when one of their open() method is called     */    void autoClosingClipOpened(AutoClosingClip clip) {        if (Printer.debug)Printer.debug("> EventDispatcher.autoClosingClipOpened ");        int index = 0;        synchronized(autoClosingClips) {            index = getAutoClosingClipIndex(clip);            if (index == -1) {                if (Printer.debug)Printer.debug("EventDispatcher: adding auto-closing clip "+clip);                autoClosingClips.add(new ClipInfo(clip));            }        }        if (index == -1) {            synchronized (this) {                // this is only for the case that the first clip is set to autoclosing,                // and it is already open, and nothing is done with it.                // EventDispatcher.process() method would block in wait() and                // never close this first clip, keeping the device open.                notifyAll();            }        }        if (Printer.debug)Printer.debug("< EventDispatcher.autoClosingClipOpened finished("+autoClosingClips.size()+" clips)");    }     /**     * called from auto-closing clips when their closed() method is called     */    void autoClosingClipClosed(AutoClosingClip clip) {        // nothing to do -- is removed from arraylist above    }      // ////////////////////////// Line Monitoring Support /////////////////// //    /*     * go through the list of registered line monitors     * and call their checkLine method     *     * This method is called in regular intervals     */    private void monitorLines() {        synchronized(lineMonitors) {            if (Printer.debug)Printer.debug("> EventDispatcher.monitorLines ("+lineMonitors.size()+" monitors)");            for (int i = 0; i < lineMonitors.size(); i++) {                lineMonitors.get(i).checkLine();            }        }        if (Printer.debug)Printer.debug("< EventDispatcher.monitorLines("+lineMonitors.size()+" monitors)");    }      /**     * Add this LineMonitor instance to the list of monitors     */    void addLineMonitor(LineMonitor lm) {        if (Printer.trace)Printer.trace("> EventDispatcher.addLineMonitor("+lm+")");        synchronized(lineMonitors) {            if (lineMonitors.indexOf(lm) >= 0) {                if (Printer.trace)Printer.trace("< EventDispatcher.addLineMonitor finished -- this monitor already exists!");                return;            }            if (Printer.debug)Printer.debug("EventDispatcher: adding line monitor "+lm);            lineMonitors.add(lm);        }        synchronized (this) {            // need to interrupt the infinite wait()            notifyAll();        }        if (Printer.debug)Printer.debug("< EventDispatcher.addLineMonitor finished -- now ("+lineMonitors.size()+" monitors)");    }     /**     * Remove this LineMonitor instance from the list of monitors     */    void removeLineMonitor(LineMonitor lm) {        if (Printer.trace)Printer.trace("> EventDispatcher.removeLineMonitor("+lm+")");        synchronized(lineMonitors) {            if (lineMonitors.indexOf(lm) < 0) {                if (Printer.trace)Printer.trace("< EventDispatcher.removeLineMonitor finished -- this monitor does not exist!");                return;            }            if (Printer.debug)Printer.debug("EventDispatcher: removing line monitor "+lm);            lineMonitors.remove(lm);        }        if (Printer.debug)Printer.debug("< EventDispatcher.removeLineMonitor finished -- now ("+lineMonitors.size()+" monitors)");    }     // /////////////////////////////////// INNER CLASSES ////////////////////////////////////////// //     /**     * Container for an event and a set of listeners to deliver it to.     */    private class EventInfo {         private Object event;        private Object[] listeners;         /**         * Create a new instance of this event Info class         * @param event the event to be dispatched         * @param listeners listener list; will be copied         */        EventInfo(Object event, List listeners) {            this.event = event;            this.listeners = listeners.toArray();        }         Object getEvent() {            return event;        }         int getListenerCount() {            return listeners.length;        }         Object getListener(int index) {            return listeners[index];        }     } // class EventInfo      /**     * Container for a clip with its expiration time     */    private class ClipInfo {         private AutoClosingClip clip;        private long expiration;         /**         * Create a new instance of this clip Info class         */        ClipInfo(AutoClosingClip clip) {            this.clip = clip;            this.expiration = System.currentTimeMillis() + AUTO_CLOSE_TIME;        }         AutoClosingClip getClip() {            return clip;        }         boolean isExpired(long currTime) {            return currTime > expiration;        }    } // class ClipInfo      /**     * Interface that a class that wants to get regular     * line monitor events implements     */    interface LineMonitor {        /**         * Called by event dispatcher in regular intervals         */        public void checkLine();    } } // class EventDispatcher