Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/com/sun/media/sound/EventDispatcher.java
38924 views
1
/*
2
* Copyright (c) 1998, 2018, 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.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 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
}
120
121
// process a MetaMessage
122
if (eventInfo.getEvent() instanceof MetaMessage) {
123
MetaMessage event = (MetaMessage)eventInfo.getEvent();
124
for (int i = 0; i < count; i++) {
125
try {
126
((MetaEventListener) eventInfo.getListener(i)).meta(event);
127
} catch (Throwable t) {
128
if (Printer.err) t.printStackTrace();
129
}
130
}
131
return;
132
}
133
134
// process a Controller or Mode Event
135
if (eventInfo.getEvent() instanceof ShortMessage) {
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;
177
if (lineMonitors.size() > 0) {
178
waitTime = LINE_MONITOR_TIME;
179
}
180
wait(waitTime);
181
} else {
182
wait();
183
}
184
}
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 = (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 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()) {
267
if (Printer.debug)Printer.debug("EventDispatcher: closing clip "+clip);
268
clip.close();
269
} else {
270
if (Printer.debug)Printer.debug("Doing nothing with clip "+clip+":");
271
if (Printer.debug)Printer.debug(" open="+clip.isOpen()+", autoclosing="+clip.isAutoClosing());
272
if (Printer.debug)Printer.debug(" isRunning="+clip.isRunning()+", isActive="+clip.isActive());
273
}
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
synchronized(autoClosingClips) {
323
int index = getAutoClosingClipIndex(clip);
324
if (index != -1) {
325
autoClosingClips.remove(index);
326
}
327
}
328
}
329
330
331
// ////////////////////////// Line Monitoring Support /////////////////// //
332
/*
333
* go through the list of registered line monitors
334
* and call their checkLine method
335
*
336
* This method is called in regular intervals
337
*/
338
private void monitorLines() {
339
synchronized(lineMonitors) {
340
if (Printer.debug)Printer.debug("> EventDispatcher.monitorLines ("+lineMonitors.size()+" monitors)");
341
for (int i = 0; i < lineMonitors.size(); i++) {
342
lineMonitors.get(i).checkLine();
343
}
344
}
345
if (Printer.debug)Printer.debug("< EventDispatcher.monitorLines("+lineMonitors.size()+" monitors)");
346
}
347
348
349
/**
350
* Add this LineMonitor instance to the list of monitors
351
*/
352
void addLineMonitor(LineMonitor lm) {
353
if (Printer.trace)Printer.trace("> EventDispatcher.addLineMonitor("+lm+")");
354
synchronized(lineMonitors) {
355
if (lineMonitors.indexOf(lm) >= 0) {
356
if (Printer.trace)Printer.trace("< EventDispatcher.addLineMonitor finished -- this monitor already exists!");
357
return;
358
}
359
if (Printer.debug)Printer.debug("EventDispatcher: adding line monitor "+lm);
360
lineMonitors.add(lm);
361
}
362
synchronized (this) {
363
// need to interrupt the infinite wait()
364
notifyAll();
365
}
366
if (Printer.debug)Printer.debug("< EventDispatcher.addLineMonitor finished -- now ("+lineMonitors.size()+" monitors)");
367
}
368
369
/**
370
* Remove this LineMonitor instance from the list of monitors
371
*/
372
void removeLineMonitor(LineMonitor lm) {
373
if (Printer.trace)Printer.trace("> EventDispatcher.removeLineMonitor("+lm+")");
374
synchronized(lineMonitors) {
375
if (lineMonitors.indexOf(lm) < 0) {
376
if (Printer.trace)Printer.trace("< EventDispatcher.removeLineMonitor finished -- this monitor does not exist!");
377
return;
378
}
379
if (Printer.debug)Printer.debug("EventDispatcher: removing line monitor "+lm);
380
lineMonitors.remove(lm);
381
}
382
if (Printer.debug)Printer.debug("< EventDispatcher.removeLineMonitor finished -- now ("+lineMonitors.size()+" monitors)");
383
}
384
385
// /////////////////////////////////// INNER CLASSES ////////////////////////////////////////// //
386
387
/**
388
* Container for an event and a set of listeners to deliver it to.
389
*/
390
private class EventInfo {
391
392
private final Object event;
393
private final Object[] listeners;
394
395
/**
396
* Create a new instance of this event Info class
397
* @param event the event to be dispatched
398
* @param listeners listener list; will be copied
399
*/
400
EventInfo(Object event, List listeners) {
401
this.event = event;
402
this.listeners = listeners.toArray();
403
}
404
405
Object getEvent() {
406
return event;
407
}
408
409
int getListenerCount() {
410
return listeners.length;
411
}
412
413
Object getListener(int index) {
414
return listeners[index];
415
}
416
417
} // class EventInfo
418
419
420
/**
421
* Container for a clip with its expiration time
422
*/
423
private class ClipInfo {
424
425
private final AutoClosingClip clip;
426
private final long expiration;
427
428
/**
429
* Create a new instance of this clip Info class
430
*/
431
ClipInfo(AutoClosingClip clip) {
432
this.clip = clip;
433
this.expiration = System.currentTimeMillis() + AUTO_CLOSE_TIME;
434
}
435
436
AutoClosingClip getClip() {
437
return clip;
438
}
439
440
boolean isExpired(long currTime) {
441
return currTime > expiration;
442
}
443
} // class ClipInfo
444
445
446
/**
447
* Interface that a class that wants to get regular
448
* line monitor events implements
449
*/
450
interface LineMonitor {
451
/**
452
* Called by event dispatcher in regular intervals
453
*/
454
public void checkLine();
455
}
456
457
} // class EventDispatcher
458
459