Save This Page
Home » openjdk-7 » com.sun.media » sound » [javadoc | source]
    1   /*
    2    * Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   
   26   package com.sun.media.sound;
   27   
   28   import java.util.EventObject;
   29   import java.util.ArrayList;
   30   import java.util.List;
   31   
   32   import javax.sound.sampled.Clip;
   33   import javax.sound.sampled.Line;
   34   import javax.sound.sampled.LineEvent;
   35   import javax.sound.sampled.LineListener;
   36   
   37   import javax.sound.midi.MetaMessage;
   38   import javax.sound.midi.ShortMessage;
   39   import javax.sound.midi.MetaEventListener;
   40   import javax.sound.midi.ControllerEventListener;
   41   
   42   
   43   
   44   /**
   45    * EventDispatcher.  Used by various classes in the Java Sound implementation
   46    * to send events.
   47    *
   48    * @author David Rivas
   49    * @author Kara Kytle
   50    * @author Florian Bomers
   51    */
   52   class EventDispatcher implements Runnable {
   53   
   54       /**
   55        * time of inactivity until the auto closing clips
   56        * are closed
   57        */
   58       private static final int AUTO_CLOSE_TIME = 5000;
   59   
   60   
   61       /**
   62        * List of events
   63        */
   64       private ArrayList eventQueue = new ArrayList();
   65   
   66   
   67       /**
   68        * Thread object for this EventDispatcher instance
   69        */
   70       private Thread thread = null;
   71   
   72   
   73       /*
   74        * support for auto-closing Clips
   75        */
   76       private ArrayList<ClipInfo> autoClosingClips = new ArrayList<ClipInfo>();
   77   
   78       /*
   79        * support for monitoring data lines
   80        */
   81       private ArrayList<LineMonitor> lineMonitors = new ArrayList<LineMonitor>();
   82   
   83       /**
   84        * Approximate interval between calls to LineMonitor.checkLine
   85        */
   86       static final int LINE_MONITOR_TIME = 400;
   87   
   88   
   89       /**
   90        * This start() method starts an event thread if one is not already active.
   91        */
   92       synchronized void start() {
   93   
   94           if(thread == null) {
   95               thread = JSSecurityManager.createThread(this,
   96                                                       "Java Sound Event Dispatcher",   // name
   97                                                       true,  // daemon
   98                                                       -1,    // priority
   99                                                       true); // doStart
  100           }
  101       }
  102   
  103   
  104       /**
  105        * Invoked when there is at least one event in the queue.
  106        * Implement this as a callback to process one event.
  107        */
  108       protected void processEvent(EventInfo eventInfo) {
  109           int count = eventInfo.getListenerCount();
  110   
  111           // process an LineEvent
  112           if (eventInfo.getEvent() instanceof LineEvent) {
  113               LineEvent event = (LineEvent) eventInfo.getEvent();
  114               if (Printer.debug) Printer.debug("Sending "+event+" to "+count+" listeners");
  115               for (int i = 0; i < count; i++) {
  116                   try {
  117                       ((LineListener) eventInfo.getListener(i)).update(event);
  118                   } catch (Throwable t) {
  119                       if (Printer.err) t.printStackTrace();
  120                   }
  121               }
  122               return;
  123           }
  124   
  125           // process a MetaMessage
  126           if (eventInfo.getEvent() instanceof MetaMessage) {
  127               MetaMessage event = (MetaMessage)eventInfo.getEvent();
  128               for (int i = 0; i < count; i++) {
  129                   try {
  130                       ((MetaEventListener) eventInfo.getListener(i)).meta(event);
  131                   } catch (Throwable t) {
  132                       if (Printer.err) t.printStackTrace();
  133                   }
  134               }
  135               return;
  136           }
  137   
  138           // process a Controller or Mode Event
  139           if (eventInfo.getEvent() instanceof ShortMessage) {
  140               ShortMessage event = (ShortMessage)eventInfo.getEvent();
  141               int status = event.getStatus();
  142   
  143               // Controller and Mode events have status byte 0xBc, where
  144               // c is the channel they are sent on.
  145               if ((status & 0xF0) == 0xB0) {
  146                   for (int i = 0; i < count; i++) {
  147                       try {
  148                           ((ControllerEventListener) eventInfo.getListener(i)).controlChange(event);
  149                       } catch (Throwable t) {
  150                           if (Printer.err) t.printStackTrace();
  151                       }
  152                   }
  153               }
  154               return;
  155           }
  156   
  157           Printer.err("Unknown event type: " + eventInfo.getEvent());
  158       }
  159   
  160   
  161       /**
  162        * Wait until there is something in the event queue to process.  Then
  163        * dispatch the event to the listeners.The entire method does not
  164        * need to be synchronized since this includes taking the event out
  165        * from the queue and processing the event. We only need to provide
  166        * exclusive access over the code where an event is removed from the
  167        *queue.
  168        */
  169       protected void dispatchEvents() {
  170   
  171           EventInfo eventInfo = null;
  172   
  173           synchronized (this) {
  174   
  175               // Wait till there is an event in the event queue.
  176               try {
  177   
  178                   if (eventQueue.size() == 0) {
  179                       if (autoClosingClips.size() > 0 || lineMonitors.size() > 0) {
  180                           int waitTime = AUTO_CLOSE_TIME;
  181                           if (lineMonitors.size() > 0) {
  182                               waitTime = LINE_MONITOR_TIME;
  183                           }
  184                           wait(waitTime);
  185                       } else {
  186                           wait();
  187                       }
  188                   }
  189               } catch (InterruptedException e) {
  190               }
  191               if (eventQueue.size() > 0) {
  192                   // Remove the event from the queue and dispatch it to the listeners.
  193                   eventInfo = (EventInfo) eventQueue.remove(0);
  194               }
  195   
  196           } // end of synchronized
  197           if (eventInfo != null) {
  198               processEvent(eventInfo);
  199           } else {
  200               if (autoClosingClips.size() > 0) {
  201                   closeAutoClosingClips();
  202               }
  203               if (lineMonitors.size() > 0) {
  204                   monitorLines();
  205               }
  206           }
  207       }
  208   
  209   
  210       /**
  211        * Queue the given event in the event queue.
  212        */
  213       private synchronized void postEvent(EventInfo eventInfo) {
  214           eventQueue.add(eventInfo);
  215           notifyAll();
  216       }
  217   
  218   
  219       /**
  220        * A loop to dispatch events.
  221        */
  222       public void run() {
  223   
  224           while (true) {
  225               try {
  226                   dispatchEvents();
  227               } catch (Throwable t) {
  228                   if (Printer.err) t.printStackTrace();
  229               }
  230           }
  231       }
  232   
  233   
  234       /**
  235        * Send audio and MIDI events.
  236        */
  237       void sendAudioEvents(Object event, List listeners) {
  238           if ((listeners == null)
  239               || (listeners.size() == 0)) {
  240               // nothing to do
  241               return;
  242           }
  243   
  244           start();
  245   
  246           EventInfo eventInfo = new EventInfo(event, listeners);
  247           postEvent(eventInfo);
  248       }
  249   
  250   
  251       /*
  252        * go through the list of registered auto-closing
  253        * Clip instances and close them, if appropriate
  254        *
  255        * This method is called in regular intervals
  256        */
  257       private void closeAutoClosingClips() {
  258           synchronized(autoClosingClips) {
  259               if (Printer.debug)Printer.debug("> EventDispatcher.closeAutoClosingClips ("+autoClosingClips.size()+" clips)");
  260               long currTime = System.currentTimeMillis();
  261               for (int i = autoClosingClips.size()-1; i >= 0 ; i--) {
  262                   ClipInfo info = autoClosingClips.get(i);
  263                   if (info.isExpired(currTime)) {
  264                       AutoClosingClip clip = info.getClip();
  265                       // sanity check
  266                       if (!clip.isOpen() || !clip.isAutoClosing()) {
  267                           if (Printer.debug)Printer.debug("EventDispatcher: removing clip "+clip+"  isOpen:"+clip.isOpen());
  268                           autoClosingClips.remove(i);
  269                       }
  270                       else if (!clip.isRunning() && !clip.isActive() && clip.isAutoClosing()) {
  271                           if (Printer.debug)Printer.debug("EventDispatcher: closing clip "+clip);
  272                           clip.close();
  273                       } else {
  274                           if (Printer.debug)Printer.debug("Doing nothing with clip "+clip+":");
  275                           if (Printer.debug)Printer.debug("  open="+clip.isOpen()+", autoclosing="+clip.isAutoClosing());
  276                           if (Printer.debug)Printer.debug("  isRunning="+clip.isRunning()+", isActive="+clip.isActive());
  277                       }
  278                   } else {
  279                       if (Printer.debug)Printer.debug("EventDispatcher: clip "+info.getClip()+" not yet expired");
  280                   }
  281               }
  282           }
  283           if (Printer.debug)Printer.debug("< EventDispatcher.closeAutoClosingClips ("+autoClosingClips.size()+" clips)");
  284       }
  285   
  286       private int getAutoClosingClipIndex(AutoClosingClip clip) {
  287           synchronized(autoClosingClips) {
  288               for (int i = autoClosingClips.size()-1; i >= 0; i--) {
  289                   if (clip.equals(autoClosingClips.get(i).getClip())) {
  290                       return i;
  291                   }
  292               }
  293           }
  294           return -1;
  295       }
  296   
  297       /**
  298        * called from auto-closing clips when one of their open() method is called
  299        */
  300       void autoClosingClipOpened(AutoClosingClip clip) {
  301           if (Printer.debug)Printer.debug("> EventDispatcher.autoClosingClipOpened ");
  302           int index = 0;
  303           synchronized(autoClosingClips) {
  304               index = getAutoClosingClipIndex(clip);
  305               if (index == -1) {
  306                   if (Printer.debug)Printer.debug("EventDispatcher: adding auto-closing clip "+clip);
  307                   autoClosingClips.add(new ClipInfo(clip));
  308               }
  309           }
  310           if (index == -1) {
  311               synchronized (this) {
  312                   // this is only for the case that the first clip is set to autoclosing,
  313                   // and it is already open, and nothing is done with it.
  314                   // EventDispatcher.process() method would block in wait() and
  315                   // never close this first clip, keeping the device open.
  316                   notifyAll();
  317               }
  318           }
  319           if (Printer.debug)Printer.debug("< EventDispatcher.autoClosingClipOpened finished("+autoClosingClips.size()+" clips)");
  320       }
  321   
  322       /**
  323        * called from auto-closing clips when their closed() method is called
  324        */
  325       void autoClosingClipClosed(AutoClosingClip clip) {
  326           // nothing to do -- is removed from arraylist above
  327       }
  328   
  329   
  330       // ////////////////////////// Line Monitoring Support /////////////////// //
  331       /*
  332        * go through the list of registered line monitors
  333        * and call their checkLine method
  334        *
  335        * This method is called in regular intervals
  336        */
  337       private void monitorLines() {
  338           synchronized(lineMonitors) {
  339               if (Printer.debug)Printer.debug("> EventDispatcher.monitorLines ("+lineMonitors.size()+" monitors)");
  340               for (int i = 0; i < lineMonitors.size(); i++) {
  341                   lineMonitors.get(i).checkLine();
  342               }
  343           }
  344           if (Printer.debug)Printer.debug("< EventDispatcher.monitorLines("+lineMonitors.size()+" monitors)");
  345       }
  346   
  347   
  348       /**
  349        * Add this LineMonitor instance to the list of monitors
  350        */
  351       void addLineMonitor(LineMonitor lm) {
  352           if (Printer.trace)Printer.trace("> EventDispatcher.addLineMonitor("+lm+")");
  353           synchronized(lineMonitors) {
  354               if (lineMonitors.indexOf(lm) >= 0) {
  355                   if (Printer.trace)Printer.trace("< EventDispatcher.addLineMonitor finished -- this monitor already exists!");
  356                   return;
  357               }
  358               if (Printer.debug)Printer.debug("EventDispatcher: adding line monitor "+lm);
  359               lineMonitors.add(lm);
  360           }
  361           synchronized (this) {
  362               // need to interrupt the infinite wait()
  363               notifyAll();
  364           }
  365           if (Printer.debug)Printer.debug("< EventDispatcher.addLineMonitor finished -- now ("+lineMonitors.size()+" monitors)");
  366       }
  367   
  368       /**
  369        * Remove this LineMonitor instance from the list of monitors
  370        */
  371       void removeLineMonitor(LineMonitor lm) {
  372           if (Printer.trace)Printer.trace("> EventDispatcher.removeLineMonitor("+lm+")");
  373           synchronized(lineMonitors) {
  374               if (lineMonitors.indexOf(lm) < 0) {
  375                   if (Printer.trace)Printer.trace("< EventDispatcher.removeLineMonitor finished -- this monitor does not exist!");
  376                   return;
  377               }
  378               if (Printer.debug)Printer.debug("EventDispatcher: removing line monitor "+lm);
  379               lineMonitors.remove(lm);
  380           }
  381           if (Printer.debug)Printer.debug("< EventDispatcher.removeLineMonitor finished -- now ("+lineMonitors.size()+" monitors)");
  382       }
  383   
  384       // /////////////////////////////////// INNER CLASSES ////////////////////////////////////////// //
  385   
  386       /**
  387        * Container for an event and a set of listeners to deliver it to.
  388        */
  389       private class EventInfo {
  390   
  391           private Object event;
  392           private Object[] listeners;
  393   
  394           /**
  395            * Create a new instance of this event Info class
  396            * @param event the event to be dispatched
  397            * @param listeners listener list; will be copied
  398            */
  399           EventInfo(Object event, List listeners) {
  400               this.event = event;
  401               this.listeners = listeners.toArray();
  402           }
  403   
  404           Object getEvent() {
  405               return event;
  406           }
  407   
  408           int getListenerCount() {
  409               return listeners.length;
  410           }
  411   
  412           Object getListener(int index) {
  413               return listeners[index];
  414           }
  415   
  416       } // class EventInfo
  417   
  418   
  419       /**
  420        * Container for a clip with its expiration time
  421        */
  422       private class ClipInfo {
  423   
  424           private AutoClosingClip clip;
  425           private long expiration;
  426   
  427           /**
  428            * Create a new instance of this clip Info class
  429            */
  430           ClipInfo(AutoClosingClip clip) {
  431               this.clip = clip;
  432               this.expiration = System.currentTimeMillis() + AUTO_CLOSE_TIME;
  433           }
  434   
  435           AutoClosingClip getClip() {
  436               return clip;
  437           }
  438   
  439           boolean isExpired(long currTime) {
  440               return currTime > expiration;
  441           }
  442       } // class ClipInfo
  443   
  444   
  445       /**
  446        * Interface that a class that wants to get regular
  447        * line monitor events implements
  448        */
  449       interface LineMonitor {
  450           /**
  451            * Called by event dispatcher in regular intervals
  452            */
  453           public void checkLine();
  454       }
  455   
  456   } // class EventDispatcher

Save This Page
Home » openjdk-7 » com.sun.media » sound » [javadoc | source]