Path: blob/master/src/java.desktop/share/classes/com/sun/media/sound/EventDispatcher.java
41161 views
/*1* Copyright (c) 1998, 2019, 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;3637/**38* EventDispatcher. Used by various classes in the Java Sound implementation39* to send events.40*41* @author David Rivas42* @author Kara Kytle43* @author Florian Bomers44*/45final class EventDispatcher implements Runnable {4647/**48* time of inactivity until the auto closing clips49* are closed.50*/51private static final int AUTO_CLOSE_TIME = 5000;5253/**54* List of events.55*/56private final ArrayList<EventInfo> eventQueue = new ArrayList<>();5758/**59* Thread object for this EventDispatcher instance.60*/61private Thread thread = null;6263/*64* support for auto-closing Clips65*/66private final ArrayList<ClipInfo> autoClosingClips = new ArrayList<>();6768/*69* support for monitoring data lines70*/71private final ArrayList<LineMonitor> lineMonitors = new ArrayList<>();7273/**74* Approximate interval between calls to LineMonitor.checkLine75*/76static final int LINE_MONITOR_TIME = 400;7778/**79* This start() method starts an event thread if one is not already active.80*/81synchronized void start() {8283if(thread == null) {84thread = JSSecurityManager.createThread(this,85"Java Sound Event Dispatcher", // name86true, // daemon87-1, // priority88true); // doStart89}90}9192/**93* Invoked when there is at least one event in the queue.94* Implement this as a callback to process one event.95*/96void processEvent(EventInfo eventInfo) {97int count = eventInfo.getListenerCount();9899// process an LineEvent100if (eventInfo.getEvent() instanceof LineEvent) {101LineEvent event = (LineEvent) eventInfo.getEvent();102for (int i = 0; i < count; i++) {103try {104((LineListener) eventInfo.getListener(i)).update(event);105} catch (Throwable t) {106if (Printer.err) t.printStackTrace();107}108}109return;110}111112// process a MetaMessage113if (eventInfo.getEvent() instanceof MetaMessage) {114MetaMessage event = (MetaMessage)eventInfo.getEvent();115for (int i = 0; i < count; i++) {116try {117((MetaEventListener) eventInfo.getListener(i)).meta(event);118} catch (Throwable t) {119if (Printer.err) t.printStackTrace();120}121}122return;123}124125// process a Controller or Mode Event126if (eventInfo.getEvent() instanceof ShortMessage) {127ShortMessage event = (ShortMessage)eventInfo.getEvent();128int status = event.getStatus();129130// Controller and Mode events have status byte 0xBc, where131// c is the channel they are sent on.132if ((status & 0xF0) == 0xB0) {133for (int i = 0; i < count; i++) {134try {135((ControllerEventListener) eventInfo.getListener(i)).controlChange(event);136} catch (Throwable t) {137if (Printer.err) t.printStackTrace();138}139}140}141return;142}143144Printer.err("Unknown event type: " + eventInfo.getEvent());145}146147/**148* Wait until there is something in the event queue to process. Then149* dispatch the event to the listeners.The entire method does not150* need to be synchronized since this includes taking the event out151* from the queue and processing the event. We only need to provide152* exclusive access over the code where an event is removed from the153*queue.154*/155void dispatchEvents() {156157EventInfo eventInfo = null;158159synchronized (this) {160161// Wait till there is an event in the event queue.162try {163164if (eventQueue.size() == 0) {165if (autoClosingClips.size() > 0 || lineMonitors.size() > 0) {166int waitTime = AUTO_CLOSE_TIME;167if (lineMonitors.size() > 0) {168waitTime = LINE_MONITOR_TIME;169}170wait(waitTime);171} else {172wait();173}174}175} catch (InterruptedException e) {176}177if (eventQueue.size() > 0) {178// Remove the event from the queue and dispatch it to the listeners.179eventInfo = eventQueue.remove(0);180}181182} // end of synchronized183if (eventInfo != null) {184processEvent(eventInfo);185} else {186if (autoClosingClips.size() > 0) {187closeAutoClosingClips();188}189if (lineMonitors.size() > 0) {190monitorLines();191}192}193}194195/**196* Queue the given event in the event queue.197*/198private synchronized void postEvent(EventInfo eventInfo) {199eventQueue.add(eventInfo);200notifyAll();201}202203/**204* A loop to dispatch events.205*/206@Override207public void run() {208209while (true) {210try {211dispatchEvents();212} catch (Throwable t) {213if (Printer.err) t.printStackTrace();214}215}216}217218/**219* Send audio and MIDI events.220*/221void sendAudioEvents(Object event, List<Object> listeners) {222if ((listeners == null)223|| (listeners.size() == 0)) {224// nothing to do225return;226}227228start();229230EventInfo eventInfo = new EventInfo(event, listeners);231postEvent(eventInfo);232}233234/*235* go through the list of registered auto-closing236* Clip instances and close them, if appropriate237*238* This method is called in regular intervals239*/240private void closeAutoClosingClips() {241synchronized(autoClosingClips) {242long currTime = System.currentTimeMillis();243for (int i = autoClosingClips.size()-1; i >= 0 ; i--) {244ClipInfo info = autoClosingClips.get(i);245if (info.isExpired(currTime)) {246AutoClosingClip clip = info.getClip();247// sanity check248if (!clip.isOpen() || !clip.isAutoClosing()) {249autoClosingClips.remove(i);250}251else if (!clip.isRunning() && !clip.isActive() && clip.isAutoClosing()) {252clip.close();253} else {254}255}256}257}258}259260private int getAutoClosingClipIndex(AutoClosingClip clip) {261synchronized(autoClosingClips) {262for (int i = autoClosingClips.size()-1; i >= 0; i--) {263if (clip.equals(autoClosingClips.get(i).getClip())) {264return i;265}266}267}268return -1;269}270271/**272* called from auto-closing clips when one of their open() method is called.273*/274void autoClosingClipOpened(AutoClosingClip clip) {275int index = 0;276synchronized(autoClosingClips) {277index = getAutoClosingClipIndex(clip);278if (index == -1) {279autoClosingClips.add(new ClipInfo(clip));280}281}282if (index == -1) {283synchronized (this) {284// this is only for the case that the first clip is set to autoclosing,285// and it is already open, and nothing is done with it.286// EventDispatcher.process() method would block in wait() and287// never close this first clip, keeping the device open.288notifyAll();289}290}291}292293/**294* called from auto-closing clips when their closed() method is called.295*/296void autoClosingClipClosed(AutoClosingClip clip) {297synchronized(autoClosingClips) {298int index = getAutoClosingClipIndex(clip);299if (index != -1) {300autoClosingClips.remove(index);301}302}303}304305306// ////////////////////////// Line Monitoring Support /////////////////// //307/*308* go through the list of registered line monitors309* and call their checkLine method310*311* This method is called in regular intervals312*/313private void monitorLines() {314synchronized(lineMonitors) {315for (int i = 0; i < lineMonitors.size(); i++) {316lineMonitors.get(i).checkLine();317}318}319}320321/**322* Add this LineMonitor instance to the list of monitors.323*/324void addLineMonitor(LineMonitor lm) {325synchronized(lineMonitors) {326if (lineMonitors.indexOf(lm) >= 0) {327return;328}329lineMonitors.add(lm);330}331synchronized (this) {332// need to interrupt the infinite wait()333notifyAll();334}335}336337/**338* Remove this LineMonitor instance from the list of monitors.339*/340void removeLineMonitor(LineMonitor lm) {341synchronized(lineMonitors) {342if (lineMonitors.indexOf(lm) < 0) {343return;344}345lineMonitors.remove(lm);346}347}348349/**350* Container for an event and a set of listeners to deliver it to.351*/352private class EventInfo {353354private final Object event;355private final Object[] listeners;356357/**358* Create a new instance of this event Info class359* @param event the event to be dispatched360* @param listeners listener list; will be copied361*/362EventInfo(Object event, List<Object> listeners) {363this.event = event;364this.listeners = listeners.toArray();365}366367Object getEvent() {368return event;369}370371int getListenerCount() {372return listeners.length;373}374375Object getListener(int index) {376return listeners[index];377}378379} // class EventInfo380381382/**383* Container for a clip with its expiration time.384*/385private class ClipInfo {386387private final AutoClosingClip clip;388private final long expiration;389390/**391* Create a new instance of this clip Info class.392*/393ClipInfo(AutoClosingClip clip) {394this.clip = clip;395this.expiration = System.currentTimeMillis() + AUTO_CLOSE_TIME;396}397398AutoClosingClip getClip() {399return clip;400}401402boolean isExpired(long currTime) {403return currTime > expiration;404}405} // class ClipInfo406407408/**409* Interface that a class that wants to get regular410* line monitor events implements.411*/412interface LineMonitor {413/**414* Called by event dispatcher in regular intervals.415*/416void checkLine();417}418419} // class EventDispatcher420421422