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/AbstractMidiDevice.java
38924 views
1
/*
2
* Copyright (c) 1999, 2016, 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
import java.util.Collections;
31
32
import javax.sound.midi.*;
33
34
35
/**
36
* Abstract AbstractMidiDevice class representing functionality shared by
37
* MidiInDevice and MidiOutDevice objects.
38
*
39
* @author David Rivas
40
* @author Kara Kytle
41
* @author Matthias Pfisterer
42
* @author Florian Bomers
43
*/
44
abstract class AbstractMidiDevice implements MidiDevice, ReferenceCountingDevice {
45
46
// STATIC VARIABLES
47
private static final boolean TRACE_TRANSMITTER = false;
48
49
// INSTANCE VARIABLES
50
51
private ArrayList<Receiver> receiverList;
52
53
private TransmitterList transmitterList;
54
55
// lock to protect receiverList and transmitterList
56
// from simultaneous creation and destruction
57
// reduces possibility of deadlock, compared to
58
// synchronizing to the class instance
59
private final Object traRecLock = new Object();
60
61
// DEVICE ATTRIBUTES
62
63
private final MidiDevice.Info info;
64
65
66
// DEVICE STATE
67
68
private volatile boolean open;
69
private int openRefCount;
70
71
/** List of Receivers and Transmitters that opened the device implicitely.
72
*/
73
private List openKeepingObjects;
74
75
/**
76
* This is the device handle returned from native code
77
*/
78
protected volatile long id;
79
80
81
82
// CONSTRUCTOR
83
84
85
/**
86
* Constructs an AbstractMidiDevice with the specified info object.
87
* @param info the description of the device
88
*/
89
/*
90
* The initial mode and and only supported mode default to OMNI_ON_POLY.
91
*/
92
protected AbstractMidiDevice(MidiDevice.Info info) {
93
94
if(Printer.trace) Printer.trace(">> AbstractMidiDevice CONSTRUCTOR");
95
96
this.info = info;
97
openRefCount = 0;
98
99
if(Printer.trace) Printer.trace("<< AbstractMidiDevice CONSTRUCTOR completed");
100
}
101
102
103
// MIDI DEVICE METHODS
104
105
public final MidiDevice.Info getDeviceInfo() {
106
return info;
107
}
108
109
/** Open the device from an application program.
110
* Setting the open reference count to -1 here prevents Transmitters and Receivers that
111
* opened the the device implicitly from closing it. The only way to close the device after
112
* this call is a call to close().
113
*/
114
public final void open() throws MidiUnavailableException {
115
if (Printer.trace) Printer.trace("> AbstractMidiDevice: open()");
116
synchronized(this) {
117
openRefCount = -1;
118
doOpen();
119
}
120
if (Printer.trace) Printer.trace("< AbstractMidiDevice: open() completed");
121
}
122
123
124
125
/** Open the device implicitly.
126
* This method is intended to be used by AbstractReceiver
127
* and BasicTransmitter. Actually, it is called by getReceiverReferenceCounting() and
128
* getTransmitterReferenceCounting(). These, in turn, are called by MidiSytem on calls to
129
* getReceiver() and getTransmitter(). The former methods should pass the Receiver or
130
* Transmitter just created as the object parameter to this method. Storing references to
131
* these objects is necessary to be able to decide later (when it comes to closing) if
132
* R/T's are ones that opened the device implicitly.
133
*
134
* @object The Receiver or Transmitter instance that triggered this implicit open.
135
*/
136
private void openInternal(Object object) throws MidiUnavailableException {
137
if (Printer.trace) Printer.trace("> AbstractMidiDevice: openInternal()");
138
synchronized(this) {
139
if (openRefCount != -1) {
140
openRefCount++;
141
getOpenKeepingObjects().add(object);
142
}
143
// double calls to doOpens() will be catched by the open flag.
144
doOpen();
145
}
146
if (Printer.trace) Printer.trace("< AbstractMidiDevice: openInternal() completed");
147
}
148
149
150
private void doOpen() throws MidiUnavailableException {
151
if (Printer.trace) Printer.trace("> AbstractMidiDevice: doOpen()");
152
synchronized(this) {
153
if (! isOpen()) {
154
implOpen();
155
open = true;
156
}
157
}
158
if (Printer.trace) Printer.trace("< AbstractMidiDevice: doOpen() completed");
159
}
160
161
162
public final void close() {
163
if (Printer.trace) Printer.trace("> AbstractMidiDevice: close()");
164
synchronized (this) {
165
doClose();
166
openRefCount = 0;
167
}
168
if (Printer.trace) Printer.trace("< AbstractMidiDevice: close() completed");
169
}
170
171
172
/** Close the device for an object that implicitely opened it.
173
* This method is intended to be used by Transmitter.close() and Receiver.close().
174
* Those methods should pass this for the object parameter. Since Transmitters or Receivers
175
* do not know if their device has been opened implicitely because of them, they call this
176
* method in any case. This method now is able to seperate Receivers/Transmitters that opened
177
* the device implicitely from those that didn't by looking up the R/T in the
178
* openKeepingObjects list. Only if the R/T is contained there, the reference count is
179
* reduced.
180
*
181
* @param object The object that might have been opening the device implicitely (for now,
182
* this may be a Transmitter or receiver).
183
*/
184
public final void closeInternal(Object object) {
185
if (Printer.trace) Printer.trace("> AbstractMidiDevice: closeInternal()");
186
synchronized(this) {
187
if (getOpenKeepingObjects().remove(object)) {
188
if (openRefCount > 0) {
189
openRefCount--;
190
if (openRefCount == 0) {
191
doClose();
192
}
193
}
194
}
195
}
196
if (Printer.trace) Printer.trace("< AbstractMidiDevice: closeInternal() completed");
197
}
198
199
200
public final void doClose() {
201
if (Printer.trace) Printer.trace("> AbstractMidiDevice: doClose()");
202
synchronized(this) {
203
if (isOpen()) {
204
implClose();
205
open = false;
206
}
207
}
208
if (Printer.trace) Printer.trace("< AbstractMidiDevice: doClose() completed");
209
}
210
211
212
public final boolean isOpen() {
213
return open;
214
}
215
216
217
protected void implClose() {
218
synchronized (traRecLock) {
219
if (receiverList != null) {
220
// close all receivers
221
for(int i = 0; i < receiverList.size(); i++) {
222
receiverList.get(i).close();
223
}
224
receiverList.clear();
225
}
226
if (transmitterList != null) {
227
// close all transmitters
228
transmitterList.close();
229
}
230
}
231
}
232
233
234
/**
235
* This implementation always returns -1.
236
* Devices that actually provide this should over-ride
237
* this method.
238
*/
239
public long getMicrosecondPosition() {
240
return -1;
241
}
242
243
244
/** Return the maximum number of Receivers supported by this device.
245
Depending on the return value of hasReceivers(), this method returns either 0 or -1.
246
Subclasses should rather override hasReceivers() than override this method.
247
*/
248
public final int getMaxReceivers() {
249
if (hasReceivers()) {
250
return -1;
251
} else {
252
return 0;
253
}
254
}
255
256
257
/** Return the maximum number of Transmitters supported by this device.
258
Depending on the return value of hasTransmitters(), this method returns either 0 or -1.
259
Subclasses should override hasTransmitters().
260
*/
261
public final int getMaxTransmitters() {
262
if (hasTransmitters()) {
263
return -1;
264
} else {
265
return 0;
266
}
267
}
268
269
270
/** Retrieve a Receiver for this device.
271
This method returns the value returned by createReceiver(), if it doesn't throw
272
an exception. Subclasses should rather override createReceiver() than override
273
this method.
274
If createReceiver returns a Receiver, it is added to the internal list
275
of Receivers (see getReceiversList)
276
*/
277
public final Receiver getReceiver() throws MidiUnavailableException {
278
Receiver receiver;
279
synchronized (traRecLock) {
280
receiver = createReceiver(); // may throw MidiUnavailableException
281
getReceiverList().add(receiver);
282
}
283
return receiver;
284
}
285
286
287
public final List<Receiver> getReceivers() {
288
List<Receiver> recs;
289
synchronized (traRecLock) {
290
if (receiverList == null) {
291
recs = Collections.unmodifiableList(new ArrayList<Receiver>(0));
292
} else {
293
recs = Collections.unmodifiableList
294
((List<Receiver>) (receiverList.clone()));
295
}
296
}
297
return recs;
298
}
299
300
301
/**
302
* This implementation uses createTransmitter, which may throw an exception.
303
* If a transmitter is returned in createTransmitter, it is added to the internal
304
* TransmitterList
305
*/
306
public final Transmitter getTransmitter() throws MidiUnavailableException {
307
Transmitter transmitter;
308
synchronized (traRecLock) {
309
transmitter = createTransmitter(); // may throw MidiUnavailableException
310
getTransmitterList().add(transmitter);
311
}
312
return transmitter;
313
}
314
315
316
public final List<Transmitter> getTransmitters() {
317
List<Transmitter> tras;
318
synchronized (traRecLock) {
319
if (transmitterList == null
320
|| transmitterList.transmitters.size() == 0) {
321
tras = Collections.unmodifiableList(new ArrayList<Transmitter>(0));
322
} else {
323
tras = Collections.unmodifiableList((List<Transmitter>) (transmitterList.transmitters.clone()));
324
}
325
}
326
return tras;
327
}
328
329
330
// HELPER METHODS
331
332
final long getId() {
333
return id;
334
}
335
336
337
// REFERENCE COUNTING
338
339
/** Retrieve a Receiver and open the device implicitly.
340
This method is called by MidiSystem.getReceiver().
341
*/
342
public final Receiver getReceiverReferenceCounting()
343
throws MidiUnavailableException {
344
/* Keep this order of commands! If getReceiver() throws an exception,
345
openInternal() should not be called!
346
*/
347
Receiver receiver;
348
synchronized (traRecLock) {
349
receiver = getReceiver();
350
AbstractMidiDevice.this.openInternal(receiver);
351
}
352
return receiver;
353
}
354
355
356
/** Retrieve a Transmitter and open the device implicitly.
357
This method is called by MidiSystem.getTransmitter().
358
*/
359
public final Transmitter getTransmitterReferenceCounting()
360
throws MidiUnavailableException {
361
/* Keep this order of commands! If getTransmitter() throws an exception,
362
openInternal() should not be called!
363
*/
364
Transmitter transmitter;
365
synchronized (traRecLock) {
366
transmitter = getTransmitter();
367
AbstractMidiDevice.this.openInternal(transmitter);
368
}
369
return transmitter;
370
}
371
372
373
/** Return the list of objects that have opened the device implicitely.
374
*/
375
private synchronized List getOpenKeepingObjects() {
376
if (openKeepingObjects == null) {
377
openKeepingObjects = new ArrayList();
378
}
379
return openKeepingObjects;
380
}
381
382
383
384
// RECEIVER HANDLING METHODS
385
386
387
/** Return the internal list of Receivers, possibly creating it first.
388
*/
389
private List<Receiver> getReceiverList() {
390
synchronized (traRecLock) {
391
if (receiverList == null) {
392
receiverList = new ArrayList<Receiver>();
393
}
394
}
395
return receiverList;
396
}
397
398
399
/** Returns if this device supports Receivers.
400
Subclasses that use Receivers should override this method to
401
return true. They also should override createReceiver().
402
403
@return true, if the device supports Receivers, false otherwise.
404
*/
405
protected boolean hasReceivers() {
406
return false;
407
}
408
409
410
/** Create a Receiver object.
411
throwing an exception here means that Receivers aren't enabled.
412
Subclasses that use Receivers should override this method with
413
one that returns objects implementing Receiver.
414
Classes overriding this method should also override hasReceivers()
415
to return true.
416
*/
417
protected Receiver createReceiver() throws MidiUnavailableException {
418
throw new MidiUnavailableException("MIDI IN receiver not available");
419
}
420
421
422
423
// TRANSMITTER HANDLING
424
425
/** Return the internal list of Transmitters, possibly creating it first.
426
*/
427
final TransmitterList getTransmitterList() {
428
synchronized (traRecLock) {
429
if (transmitterList == null) {
430
transmitterList = new TransmitterList();
431
}
432
}
433
return transmitterList;
434
}
435
436
437
/** Returns if this device supports Transmitters.
438
Subclasses that use Transmitters should override this method to
439
return true. They also should override createTransmitter().
440
441
@return true, if the device supports Transmitters, false otherwise.
442
*/
443
protected boolean hasTransmitters() {
444
return false;
445
}
446
447
448
/** Create a Transmitter object.
449
throwing an exception here means that Transmitters aren't enabled.
450
Subclasses that use Transmitters should override this method with
451
one that returns objects implementing Transmitters.
452
Classes overriding this method should also override hasTransmitters()
453
to return true.
454
*/
455
protected Transmitter createTransmitter() throws MidiUnavailableException {
456
throw new MidiUnavailableException("MIDI OUT transmitter not available");
457
}
458
459
// ABSTRACT METHODS
460
461
protected abstract void implOpen() throws MidiUnavailableException;
462
463
464
/**
465
* close this device if discarded by the garbage collector
466
*/
467
protected final void finalize() {
468
close();
469
}
470
471
// INNER CLASSES
472
473
/** Base class for Receivers.
474
Subclasses that use Receivers must use this base class, since it
475
contains magic necessary to manage implicit closing the device.
476
This is necessary for Receivers retrieved via MidiSystem.getReceiver()
477
(which opens the device implicitely).
478
*/
479
abstract class AbstractReceiver implements MidiDeviceReceiver {
480
private volatile boolean open = true;
481
482
483
/** Deliver a MidiMessage.
484
This method contains magic related to the closed state of a
485
Receiver. Therefore, subclasses should not override this method.
486
Instead, they should implement implSend().
487
*/
488
@Override
489
public final synchronized void send(final MidiMessage message,
490
final long timeStamp) {
491
if (!open) {
492
throw new IllegalStateException("Receiver is not open");
493
}
494
implSend(message, timeStamp);
495
}
496
497
abstract void implSend(MidiMessage message, long timeStamp);
498
499
/** Close the Receiver.
500
* Here, the call to the magic method closeInternal() takes place.
501
* Therefore, subclasses that override this method must call
502
* 'super.close()'.
503
*/
504
@Override
505
public final void close() {
506
open = false;
507
synchronized (AbstractMidiDevice.this.traRecLock) {
508
AbstractMidiDevice.this.getReceiverList().remove(this);
509
}
510
AbstractMidiDevice.this.closeInternal(this);
511
}
512
513
@Override
514
public final MidiDevice getMidiDevice() {
515
return AbstractMidiDevice.this;
516
}
517
518
final boolean isOpen() {
519
return open;
520
}
521
522
//$$fb is that a good idea?
523
//protected void finalize() {
524
// close();
525
//}
526
527
} // class AbstractReceiver
528
529
530
/**
531
* Transmitter base class.
532
* This class especially makes sure the device is closed if it
533
* has been opened implicitly by a call to MidiSystem.getTransmitter().
534
* The logic of doing so is actually in closeInternal().
535
*
536
* Also, it has some optimizations regarding sending to the Receivers,
537
* for known Receivers, and managing itself in the TransmitterList.
538
*/
539
class BasicTransmitter implements MidiDeviceTransmitter {
540
541
private Receiver receiver = null;
542
TransmitterList tlist = null;
543
544
protected BasicTransmitter() {
545
}
546
547
private void setTransmitterList(TransmitterList tlist) {
548
this.tlist = tlist;
549
}
550
551
public final void setReceiver(Receiver receiver) {
552
if (tlist != null && this.receiver != receiver) {
553
if (Printer.debug) Printer.debug("Transmitter "+toString()+": set receiver "+receiver);
554
tlist.receiverChanged(this, this.receiver, receiver);
555
this.receiver = receiver;
556
}
557
}
558
559
public final Receiver getReceiver() {
560
return receiver;
561
}
562
563
564
/** Close the Transmitter.
565
* Here, the call to the magic method closeInternal() takes place.
566
* Therefore, subclasses that override this method must call
567
* 'super.close()'.
568
*/
569
public final void close() {
570
AbstractMidiDevice.this.closeInternal(this);
571
if (tlist != null) {
572
tlist.receiverChanged(this, this.receiver, null);
573
tlist.remove(this);
574
tlist = null;
575
}
576
}
577
578
public final MidiDevice getMidiDevice() {
579
return AbstractMidiDevice.this;
580
}
581
582
} // class BasicTransmitter
583
584
585
/**
586
* a class to manage a list of transmitters
587
*/
588
final class TransmitterList {
589
590
private final ArrayList<Transmitter> transmitters = new ArrayList<Transmitter>();
591
private MidiOutDevice.MidiOutReceiver midiOutReceiver;
592
593
// how many transmitters must be present for optimized
594
// handling
595
private int optimizedReceiverCount = 0;
596
597
598
private void add(Transmitter t) {
599
synchronized(transmitters) {
600
transmitters.add(t);
601
}
602
if (t instanceof BasicTransmitter) {
603
((BasicTransmitter) t).setTransmitterList(this);
604
}
605
if (Printer.debug) Printer.debug("--added transmitter "+t);
606
}
607
608
private void remove(Transmitter t) {
609
synchronized(transmitters) {
610
int index = transmitters.indexOf(t);
611
if (index >= 0) {
612
transmitters.remove(index);
613
if (Printer.debug) Printer.debug("--removed transmitter "+t);
614
}
615
}
616
}
617
618
private void receiverChanged(BasicTransmitter t,
619
Receiver oldR,
620
Receiver newR) {
621
synchronized(transmitters) {
622
// some optimization
623
if (midiOutReceiver == oldR) {
624
midiOutReceiver = null;
625
}
626
if (newR != null) {
627
if ((newR instanceof MidiOutDevice.MidiOutReceiver)
628
&& (midiOutReceiver == null)) {
629
midiOutReceiver = ((MidiOutDevice.MidiOutReceiver) newR);
630
}
631
}
632
optimizedReceiverCount =
633
((midiOutReceiver!=null)?1:0);
634
}
635
// more potential for optimization here
636
}
637
638
639
/** closes all transmitters and empties the list */
640
void close() {
641
synchronized (transmitters) {
642
for(int i = 0; i < transmitters.size(); i++) {
643
transmitters.get(i).close();
644
}
645
transmitters.clear();
646
}
647
if (Printer.trace) Printer.trace("TransmitterList.close() succeeded");
648
}
649
650
651
652
/**
653
* Send this message to all receivers
654
* status = packedMessage & 0xFF
655
* data1 = (packedMessage & 0xFF00) >> 8;
656
* data1 = (packedMessage & 0xFF0000) >> 16;
657
*/
658
void sendMessage(int packedMessage, long timeStamp) {
659
try {
660
synchronized(transmitters) {
661
int size = transmitters.size();
662
if (optimizedReceiverCount == size) {
663
if (midiOutReceiver != null) {
664
if (TRACE_TRANSMITTER) Printer.println("Sending packed message to MidiOutReceiver");
665
midiOutReceiver.sendPackedMidiMessage(packedMessage, timeStamp);
666
}
667
} else {
668
if (TRACE_TRANSMITTER) Printer.println("Sending packed message to "+size+" transmitter's receivers");
669
for (int i = 0; i < size; i++) {
670
Receiver receiver = ((Transmitter)transmitters.get(i)).getReceiver();
671
if (receiver != null) {
672
if (optimizedReceiverCount > 0) {
673
if (receiver instanceof MidiOutDevice.MidiOutReceiver) {
674
((MidiOutDevice.MidiOutReceiver) receiver).sendPackedMidiMessage(packedMessage, timeStamp);
675
} else {
676
receiver.send(new FastShortMessage(packedMessage), timeStamp);
677
}
678
} else {
679
receiver.send(new FastShortMessage(packedMessage), timeStamp);
680
}
681
}
682
}
683
}
684
}
685
} catch (InvalidMidiDataException e) {
686
// this happens when invalid data comes over the wire. Ignore it.
687
}
688
}
689
690
void sendMessage(byte[] data, long timeStamp) {
691
try {
692
synchronized(transmitters) {
693
int size = transmitters.size();
694
if (TRACE_TRANSMITTER) Printer.println("Sending long message to "+size+" transmitter's receivers");
695
for (int i = 0; i < size; i++) {
696
Receiver receiver = ((Transmitter)transmitters.get(i)).getReceiver();
697
if (receiver != null) {
698
//$$fb 2002-04-02: SysexMessages are mutable, so
699
// an application could change the contents of this object,
700
// or try to use the object later. So we can't get around object creation
701
// But the array need not be unique for each FastSysexMessage object,
702
// because it cannot be modified.
703
receiver.send(new FastSysexMessage(data), timeStamp);
704
}
705
}
706
}
707
} catch (InvalidMidiDataException e) {
708
// this happens when invalid data comes over the wire. Ignore it.
709
return;
710
}
711
}
712
713
714
/**
715
* Send this message to all transmitters
716
*/
717
void sendMessage(MidiMessage message, long timeStamp) {
718
if (message instanceof FastShortMessage) {
719
sendMessage(((FastShortMessage) message).getPackedMsg(), timeStamp);
720
return;
721
}
722
synchronized(transmitters) {
723
int size = transmitters.size();
724
if (optimizedReceiverCount == size) {
725
if (midiOutReceiver != null) {
726
if (TRACE_TRANSMITTER) Printer.println("Sending MIDI message to MidiOutReceiver");
727
midiOutReceiver.send(message, timeStamp);
728
}
729
} else {
730
if (TRACE_TRANSMITTER) Printer.println("Sending MIDI message to "+size+" transmitter's receivers");
731
for (int i = 0; i < size; i++) {
732
Receiver receiver = ((Transmitter)transmitters.get(i)).getReceiver();
733
if (receiver != null) {
734
//$$fb 2002-04-02: ShortMessages are mutable, so
735
// an application could change the contents of this object,
736
// or try to use the object later.
737
// We violate this spec here, to avoid costly (and gc-intensive)
738
// object creation for potentially hundred of messages per second.
739
// The spec should be changed to allow Immutable MidiMessages
740
// (i.e. throws InvalidStateException or so in setMessage)
741
receiver.send(message, timeStamp);
742
}
743
}
744
}
745
}
746
}
747
748
749
} // TransmitterList
750
751
}
752
753