Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/com/sun/media/sound/EventDispatcher.java
38924 views
/*1* Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.media.sound;2627import java.util.ArrayList;28import java.util.List;2930import javax.sound.midi.ControllerEventListener;31import javax.sound.midi.MetaEventListener;32import javax.sound.midi.MetaMessage;33import javax.sound.midi.ShortMessage;34import javax.sound.sampled.LineEvent;35import javax.sound.sampled.LineListener;36373839/**40* EventDispatcher. Used by various classes in the Java Sound implementation41* to send events.42*43* @author David Rivas44* @author Kara Kytle45* @author Florian Bomers46*/47final class EventDispatcher implements Runnable {4849/**50* time of inactivity until the auto closing clips51* are closed52*/53private static final int AUTO_CLOSE_TIME = 5000;545556/**57* List of events58*/59private final ArrayList eventQueue = new ArrayList();606162/**63* Thread object for this EventDispatcher instance64*/65private Thread thread = null;666768/*69* support for auto-closing Clips70*/71private final ArrayList<ClipInfo> autoClosingClips = new ArrayList<ClipInfo>();7273/*74* support for monitoring data lines75*/76private final ArrayList<LineMonitor> lineMonitors = new ArrayList<LineMonitor>();7778/**79* Approximate interval between calls to LineMonitor.checkLine80*/81static final int LINE_MONITOR_TIME = 400;828384/**85* This start() method starts an event thread if one is not already active.86*/87synchronized void start() {8889if(thread == null) {90thread = JSSecurityManager.createThread(this,91"Java Sound Event Dispatcher", // name92true, // daemon93-1, // priority94true); // doStart95}96}979899/**100* Invoked when there is at least one event in the queue.101* Implement this as a callback to process one event.102*/103void processEvent(EventInfo eventInfo) {104int count = eventInfo.getListenerCount();105106// process an LineEvent107if (eventInfo.getEvent() instanceof LineEvent) {108LineEvent event = (LineEvent) eventInfo.getEvent();109if (Printer.debug) Printer.debug("Sending "+event+" to "+count+" listeners");110for (int i = 0; i < count; i++) {111try {112((LineListener) eventInfo.getListener(i)).update(event);113} catch (Throwable t) {114if (Printer.err) t.printStackTrace();115}116}117return;118}119120// process a MetaMessage121if (eventInfo.getEvent() instanceof MetaMessage) {122MetaMessage event = (MetaMessage)eventInfo.getEvent();123for (int i = 0; i < count; i++) {124try {125((MetaEventListener) eventInfo.getListener(i)).meta(event);126} catch (Throwable t) {127if (Printer.err) t.printStackTrace();128}129}130return;131}132133// process a Controller or Mode Event134if (eventInfo.getEvent() instanceof ShortMessage) {135ShortMessage event = (ShortMessage)eventInfo.getEvent();136int status = event.getStatus();137138// Controller and Mode events have status byte 0xBc, where139// c is the channel they are sent on.140if ((status & 0xF0) == 0xB0) {141for (int i = 0; i < count; i++) {142try {143((ControllerEventListener) eventInfo.getListener(i)).controlChange(event);144} catch (Throwable t) {145if (Printer.err) t.printStackTrace();146}147}148}149return;150}151152Printer.err("Unknown event type: " + eventInfo.getEvent());153}154155156/**157* Wait until there is something in the event queue to process. Then158* dispatch the event to the listeners.The entire method does not159* need to be synchronized since this includes taking the event out160* from the queue and processing the event. We only need to provide161* exclusive access over the code where an event is removed from the162*queue.163*/164void dispatchEvents() {165166EventInfo eventInfo = null;167168synchronized (this) {169170// Wait till there is an event in the event queue.171try {172173if (eventQueue.size() == 0) {174if (autoClosingClips.size() > 0 || lineMonitors.size() > 0) {175int waitTime = AUTO_CLOSE_TIME;176if (lineMonitors.size() > 0) {177waitTime = LINE_MONITOR_TIME;178}179wait(waitTime);180} else {181wait();182}183}184} catch (InterruptedException e) {185}186if (eventQueue.size() > 0) {187// Remove the event from the queue and dispatch it to the listeners.188eventInfo = (EventInfo) eventQueue.remove(0);189}190191} // end of synchronized192if (eventInfo != null) {193processEvent(eventInfo);194} else {195if (autoClosingClips.size() > 0) {196closeAutoClosingClips();197}198if (lineMonitors.size() > 0) {199monitorLines();200}201}202}203204205/**206* Queue the given event in the event queue.207*/208private synchronized void postEvent(EventInfo eventInfo) {209eventQueue.add(eventInfo);210notifyAll();211}212213214/**215* A loop to dispatch events.216*/217public void run() {218219while (true) {220try {221dispatchEvents();222} catch (Throwable t) {223if (Printer.err) t.printStackTrace();224}225}226}227228229/**230* Send audio and MIDI events.231*/232void sendAudioEvents(Object event, List listeners) {233if ((listeners == null)234|| (listeners.size() == 0)) {235// nothing to do236return;237}238239start();240241EventInfo eventInfo = new EventInfo(event, listeners);242postEvent(eventInfo);243}244245246/*247* go through the list of registered auto-closing248* Clip instances and close them, if appropriate249*250* This method is called in regular intervals251*/252private void closeAutoClosingClips() {253synchronized(autoClosingClips) {254if (Printer.debug)Printer.debug("> EventDispatcher.closeAutoClosingClips ("+autoClosingClips.size()+" clips)");255long currTime = System.currentTimeMillis();256for (int i = autoClosingClips.size()-1; i >= 0 ; i--) {257ClipInfo info = autoClosingClips.get(i);258if (info.isExpired(currTime)) {259AutoClosingClip clip = info.getClip();260// sanity check261if (!clip.isOpen() || !clip.isAutoClosing()) {262if (Printer.debug)Printer.debug("EventDispatcher: removing clip "+clip+" isOpen:"+clip.isOpen());263autoClosingClips.remove(i);264}265else if (!clip.isRunning() && !clip.isActive() && clip.isAutoClosing()) {266if (Printer.debug)Printer.debug("EventDispatcher: closing clip "+clip);267clip.close();268} else {269if (Printer.debug)Printer.debug("Doing nothing with clip "+clip+":");270if (Printer.debug)Printer.debug(" open="+clip.isOpen()+", autoclosing="+clip.isAutoClosing());271if (Printer.debug)Printer.debug(" isRunning="+clip.isRunning()+", isActive="+clip.isActive());272}273} else {274if (Printer.debug)Printer.debug("EventDispatcher: clip "+info.getClip()+" not yet expired");275}276}277}278if (Printer.debug)Printer.debug("< EventDispatcher.closeAutoClosingClips ("+autoClosingClips.size()+" clips)");279}280281private int getAutoClosingClipIndex(AutoClosingClip clip) {282synchronized(autoClosingClips) {283for (int i = autoClosingClips.size()-1; i >= 0; i--) {284if (clip.equals(autoClosingClips.get(i).getClip())) {285return i;286}287}288}289return -1;290}291292/**293* called from auto-closing clips when one of their open() method is called294*/295void autoClosingClipOpened(AutoClosingClip clip) {296if (Printer.debug)Printer.debug("> EventDispatcher.autoClosingClipOpened ");297int index = 0;298synchronized(autoClosingClips) {299index = getAutoClosingClipIndex(clip);300if (index == -1) {301if (Printer.debug)Printer.debug("EventDispatcher: adding auto-closing clip "+clip);302autoClosingClips.add(new ClipInfo(clip));303}304}305if (index == -1) {306synchronized (this) {307// this is only for the case that the first clip is set to autoclosing,308// and it is already open, and nothing is done with it.309// EventDispatcher.process() method would block in wait() and310// never close this first clip, keeping the device open.311notifyAll();312}313}314if (Printer.debug)Printer.debug("< EventDispatcher.autoClosingClipOpened finished("+autoClosingClips.size()+" clips)");315}316317/**318* called from auto-closing clips when their closed() method is called319*/320void autoClosingClipClosed(AutoClosingClip clip) {321synchronized(autoClosingClips) {322int index = getAutoClosingClipIndex(clip);323if (index != -1) {324autoClosingClips.remove(index);325}326}327}328329330// ////////////////////////// Line Monitoring Support /////////////////// //331/*332* go through the list of registered line monitors333* and call their checkLine method334*335* This method is called in regular intervals336*/337private void monitorLines() {338synchronized(lineMonitors) {339if (Printer.debug)Printer.debug("> EventDispatcher.monitorLines ("+lineMonitors.size()+" monitors)");340for (int i = 0; i < lineMonitors.size(); i++) {341lineMonitors.get(i).checkLine();342}343}344if (Printer.debug)Printer.debug("< EventDispatcher.monitorLines("+lineMonitors.size()+" monitors)");345}346347348/**349* Add this LineMonitor instance to the list of monitors350*/351void addLineMonitor(LineMonitor lm) {352if (Printer.trace)Printer.trace("> EventDispatcher.addLineMonitor("+lm+")");353synchronized(lineMonitors) {354if (lineMonitors.indexOf(lm) >= 0) {355if (Printer.trace)Printer.trace("< EventDispatcher.addLineMonitor finished -- this monitor already exists!");356return;357}358if (Printer.debug)Printer.debug("EventDispatcher: adding line monitor "+lm);359lineMonitors.add(lm);360}361synchronized (this) {362// need to interrupt the infinite wait()363notifyAll();364}365if (Printer.debug)Printer.debug("< EventDispatcher.addLineMonitor finished -- now ("+lineMonitors.size()+" monitors)");366}367368/**369* Remove this LineMonitor instance from the list of monitors370*/371void removeLineMonitor(LineMonitor lm) {372if (Printer.trace)Printer.trace("> EventDispatcher.removeLineMonitor("+lm+")");373synchronized(lineMonitors) {374if (lineMonitors.indexOf(lm) < 0) {375if (Printer.trace)Printer.trace("< EventDispatcher.removeLineMonitor finished -- this monitor does not exist!");376return;377}378if (Printer.debug)Printer.debug("EventDispatcher: removing line monitor "+lm);379lineMonitors.remove(lm);380}381if (Printer.debug)Printer.debug("< EventDispatcher.removeLineMonitor finished -- now ("+lineMonitors.size()+" monitors)");382}383384// /////////////////////////////////// INNER CLASSES ////////////////////////////////////////// //385386/**387* Container for an event and a set of listeners to deliver it to.388*/389private class EventInfo {390391private final Object event;392private final Object[] listeners;393394/**395* Create a new instance of this event Info class396* @param event the event to be dispatched397* @param listeners listener list; will be copied398*/399EventInfo(Object event, List listeners) {400this.event = event;401this.listeners = listeners.toArray();402}403404Object getEvent() {405return event;406}407408int getListenerCount() {409return listeners.length;410}411412Object getListener(int index) {413return listeners[index];414}415416} // class EventInfo417418419/**420* Container for a clip with its expiration time421*/422private class ClipInfo {423424private final AutoClosingClip clip;425private final long expiration;426427/**428* Create a new instance of this clip Info class429*/430ClipInfo(AutoClosingClip clip) {431this.clip = clip;432this.expiration = System.currentTimeMillis() + AUTO_CLOSE_TIME;433}434435AutoClosingClip getClip() {436return clip;437}438439boolean isExpired(long currTime) {440return currTime > expiration;441}442} // class ClipInfo443444445/**446* Interface that a class that wants to get regular447* line monitor events implements448*/449interface LineMonitor {450/**451* Called by event dispatcher in regular intervals452*/453public void checkLine();454}455456} // class EventDispatcher457458459