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