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