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/JavaSoundAudioClip.java
41161 views
1
/*
2
* Copyright (c) 1999, 2021, 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.applet.AudioClip;
29
import java.io.BufferedInputStream;
30
import java.io.ByteArrayOutputStream;
31
import java.io.IOException;
32
import java.io.InputStream;
33
import java.net.URL;
34
import java.net.URLConnection;
35
36
import javax.sound.midi.InvalidMidiDataException;
37
import javax.sound.midi.MetaEventListener;
38
import javax.sound.midi.MetaMessage;
39
import javax.sound.midi.MidiFileFormat;
40
import javax.sound.midi.MidiSystem;
41
import javax.sound.midi.MidiUnavailableException;
42
import javax.sound.midi.Sequence;
43
import javax.sound.midi.Sequencer;
44
import javax.sound.sampled.AudioFormat;
45
import javax.sound.sampled.AudioInputStream;
46
import javax.sound.sampled.AudioSystem;
47
import javax.sound.sampled.Clip;
48
import javax.sound.sampled.DataLine;
49
import javax.sound.sampled.LineEvent;
50
import javax.sound.sampled.LineListener;
51
import javax.sound.sampled.SourceDataLine;
52
import javax.sound.sampled.UnsupportedAudioFileException;
53
54
/**
55
* Java Sound audio clip;
56
*
57
* @author Arthur van Hoff, Kara Kytle, Jan Borgersen
58
* @author Florian Bomers
59
*/
60
@SuppressWarnings({"deprecation", "removal"})
61
public final class JavaSoundAudioClip implements AudioClip, MetaEventListener, LineListener {
62
63
private long lastPlayCall = 0;
64
private static final int MINIMUM_PLAY_DELAY = 30;
65
66
private byte[] loadedAudio = null;
67
private int loadedAudioByteLength = 0;
68
private AudioFormat loadedAudioFormat = null;
69
70
private AutoClosingClip clip = null;
71
private boolean clipLooping = false;
72
73
private DataPusher datapusher = null;
74
75
private Sequencer sequencer = null;
76
private Sequence sequence = null;
77
private boolean sequencerloop = false;
78
private volatile boolean success;
79
80
/**
81
* used for determining how many samples is the
82
* threshhold between playing as a Clip and streaming
83
* from the file.
84
*
85
* $$jb: 11.07.99: the engine has a limit of 1M
86
* samples to play as a Clip, so compare this number
87
* with the number of samples in the stream.
88
*
89
*/
90
private static final long CLIP_THRESHOLD = 1048576;
91
private static final int STREAM_BUFFER_SIZE = 1024;
92
93
public static JavaSoundAudioClip create(final URLConnection uc) {
94
JavaSoundAudioClip clip = new JavaSoundAudioClip();
95
try {
96
clip.init(uc.getInputStream());
97
} catch (final Exception ignored) {
98
// AudioClip will be no-op if some exception will occurred
99
}
100
return clip;
101
}
102
103
public static JavaSoundAudioClip create(final URL url) {
104
JavaSoundAudioClip clip = new JavaSoundAudioClip();
105
try {
106
clip.init(url.openStream());
107
} catch (final Exception ignored) {
108
// AudioClip will be no-op if some exception will occurred
109
}
110
return clip;
111
}
112
113
private void init(InputStream in) throws IOException {
114
BufferedInputStream bis = new BufferedInputStream(in, STREAM_BUFFER_SIZE);
115
bis.mark(STREAM_BUFFER_SIZE);
116
try {
117
AudioInputStream as = AudioSystem.getAudioInputStream(bis);
118
// load the stream data into memory
119
success = loadAudioData(as);
120
121
if (success) {
122
success = false;
123
if (loadedAudioByteLength < CLIP_THRESHOLD) {
124
success = createClip();
125
}
126
if (!success) {
127
success = createSourceDataLine();
128
}
129
}
130
} catch (UnsupportedAudioFileException e) {
131
// not an audio file
132
try {
133
MidiFileFormat mff = MidiSystem.getMidiFileFormat(bis);
134
success = createSequencer(bis);
135
} catch (InvalidMidiDataException e1) {
136
success = false;
137
}
138
}
139
}
140
141
@Override
142
public synchronized void play() {
143
if (!success) {
144
return;
145
}
146
startImpl(false);
147
}
148
149
@Override
150
public synchronized void loop() {
151
if (!success) {
152
return;
153
}
154
startImpl(true);
155
}
156
157
private synchronized void startImpl(boolean loop) {
158
// hack for some applets that call the start method very rapidly...
159
long currentTime = System.currentTimeMillis();
160
long diff = currentTime - lastPlayCall;
161
if (diff < MINIMUM_PLAY_DELAY) {
162
return;
163
}
164
lastPlayCall = currentTime;
165
try {
166
if (clip != null) {
167
// We need to disable autoclosing mechanism otherwise the clip
168
// can be closed after "!clip.isOpen()" check, because of
169
// previous inactivity.
170
clip.setAutoClosing(false);
171
try {
172
if (!clip.isOpen()) {
173
clip.open(loadedAudioFormat, loadedAudio, 0,
174
loadedAudioByteLength);
175
} else {
176
clip.flush();
177
if (loop != clipLooping) {
178
// need to stop in case the looped status changed
179
clip.stop();
180
}
181
}
182
clip.setFramePosition(0);
183
if (loop) {
184
clip.loop(Clip.LOOP_CONTINUOUSLY);
185
} else {
186
clip.start();
187
}
188
clipLooping = loop;
189
} finally {
190
clip.setAutoClosing(true);
191
}
192
} else if (datapusher != null ) {
193
datapusher.start(loop);
194
195
} else if (sequencer != null) {
196
sequencerloop = loop;
197
if (sequencer.isRunning()) {
198
sequencer.setMicrosecondPosition(0);
199
}
200
if (!sequencer.isOpen()) {
201
try {
202
sequencer.open();
203
sequencer.setSequence(sequence);
204
205
} catch (InvalidMidiDataException e1) {
206
if (Printer.err) e1.printStackTrace();
207
} catch (MidiUnavailableException e2) {
208
if (Printer.err) e2.printStackTrace();
209
}
210
}
211
sequencer.addMetaEventListener(this);
212
try {
213
sequencer.start();
214
} catch (Exception e) {
215
if (Printer.err) e.printStackTrace();
216
}
217
}
218
} catch (Exception e) {
219
if (Printer.err) e.printStackTrace();
220
}
221
}
222
223
@Override
224
public synchronized void stop() {
225
if (!success) {
226
return;
227
}
228
lastPlayCall = 0;
229
230
if (clip != null) {
231
try {
232
clip.flush();
233
} catch (Exception e1) {
234
if (Printer.err) e1.printStackTrace();
235
}
236
try {
237
clip.stop();
238
} catch (Exception e2) {
239
if (Printer.err) e2.printStackTrace();
240
}
241
} else if (datapusher != null) {
242
datapusher.stop();
243
} else if (sequencer != null) {
244
try {
245
sequencerloop = false;
246
sequencer.removeMetaEventListener(this);
247
sequencer.stop();
248
} catch (Exception e3) {
249
if (Printer.err) e3.printStackTrace();
250
}
251
try {
252
sequencer.close();
253
} catch (Exception e4) {
254
if (Printer.err) e4.printStackTrace();
255
}
256
}
257
}
258
259
// Event handlers (for debugging)
260
261
@Override
262
public synchronized void update(LineEvent event) {
263
}
264
265
// handle MIDI track end meta events for looping
266
267
@Override
268
public synchronized void meta(MetaMessage message) {
269
if( message.getType() == 47 ) {
270
if (sequencerloop){
271
//notifyAll();
272
sequencer.setMicrosecondPosition(0);
273
loop();
274
} else {
275
stop();
276
}
277
}
278
}
279
280
@Override
281
public String toString() {
282
return getClass().toString();
283
}
284
285
@Override
286
protected void finalize() {
287
288
if (clip != null) {
289
clip.close();
290
}
291
292
//$$fb 2001-09-26: may improve situation related to bug #4302884
293
if (datapusher != null) {
294
datapusher.close();
295
}
296
297
if (sequencer != null) {
298
sequencer.close();
299
}
300
}
301
302
// FILE LOADING METHODS
303
304
private boolean loadAudioData(AudioInputStream as) throws IOException, UnsupportedAudioFileException {
305
// first possibly convert this stream to PCM
306
as = Toolkit.getPCMConvertedAudioInputStream(as);
307
if (as == null) {
308
return false;
309
}
310
311
loadedAudioFormat = as.getFormat();
312
long frameLen = as.getFrameLength();
313
int frameSize = loadedAudioFormat.getFrameSize();
314
long byteLen = AudioSystem.NOT_SPECIFIED;
315
if (frameLen != AudioSystem.NOT_SPECIFIED
316
&& frameLen > 0
317
&& frameSize != AudioSystem.NOT_SPECIFIED
318
&& frameSize > 0) {
319
byteLen = frameLen * frameSize;
320
}
321
if (byteLen != AudioSystem.NOT_SPECIFIED) {
322
// if the stream length is known, it can be efficiently loaded into memory
323
readStream(as, byteLen);
324
} else {
325
// otherwise we use a ByteArrayOutputStream to load it into memory
326
readStream(as);
327
}
328
329
// if everything went fine, we have now the audio data in
330
// loadedAudio, and the byte length in loadedAudioByteLength
331
return true;
332
}
333
334
private void readStream(AudioInputStream as, long byteLen) throws IOException {
335
// arrays "only" max. 2GB
336
int intLen;
337
if (byteLen > 2147483647) {
338
intLen = 2147483647;
339
} else {
340
intLen = (int) byteLen;
341
}
342
loadedAudio = new byte[intLen];
343
loadedAudioByteLength = 0;
344
345
// this loop may throw an IOException
346
while (true) {
347
int bytesRead = as.read(loadedAudio, loadedAudioByteLength, intLen - loadedAudioByteLength);
348
if (bytesRead <= 0) {
349
as.close();
350
break;
351
}
352
loadedAudioByteLength += bytesRead;
353
}
354
}
355
356
private void readStream(AudioInputStream as) throws IOException {
357
358
DirectBAOS baos = new DirectBAOS();
359
int totalBytesRead;
360
try (as) {
361
totalBytesRead = (int) as.transferTo(baos);
362
}
363
loadedAudio = baos.getInternalBuffer();
364
loadedAudioByteLength = totalBytesRead;
365
}
366
367
// METHODS FOR CREATING THE DEVICE
368
369
private boolean createClip() {
370
try {
371
DataLine.Info info = new DataLine.Info(Clip.class, loadedAudioFormat);
372
if (!(AudioSystem.isLineSupported(info)) ) {
373
if (Printer.err) Printer.err("Clip not supported: "+loadedAudioFormat);
374
// fail silently
375
return false;
376
}
377
Object line = AudioSystem.getLine(info);
378
if (!(line instanceof AutoClosingClip)) {
379
if (Printer.err) Printer.err("Clip is not auto closing!"+clip);
380
// fail -> will try with SourceDataLine
381
return false;
382
}
383
clip = (AutoClosingClip) line;
384
clip.setAutoClosing(true);
385
} catch (Exception e) {
386
if (Printer.err) e.printStackTrace();
387
// fail silently
388
return false;
389
}
390
391
if (clip==null) {
392
// fail silently
393
return false;
394
}
395
return true;
396
}
397
398
private boolean createSourceDataLine() {
399
try {
400
DataLine.Info info = new DataLine.Info(SourceDataLine.class, loadedAudioFormat);
401
if (!(AudioSystem.isLineSupported(info)) ) {
402
if (Printer.err) Printer.err("Line not supported: "+loadedAudioFormat);
403
// fail silently
404
return false;
405
}
406
SourceDataLine source = (SourceDataLine) AudioSystem.getLine(info);
407
datapusher = new DataPusher(source, loadedAudioFormat, loadedAudio, loadedAudioByteLength);
408
} catch (Exception e) {
409
if (Printer.err) e.printStackTrace();
410
// fail silently
411
return false;
412
}
413
414
if (datapusher==null) {
415
// fail silently
416
return false;
417
}
418
return true;
419
}
420
421
private boolean createSequencer(BufferedInputStream in) throws IOException {
422
// get the sequencer
423
try {
424
sequencer = MidiSystem.getSequencer( );
425
} catch(MidiUnavailableException me) {
426
if (Printer.err) me.printStackTrace();
427
return false;
428
}
429
if (sequencer==null) {
430
return false;
431
}
432
433
try {
434
sequence = MidiSystem.getSequence(in);
435
if (sequence == null) {
436
return false;
437
}
438
} catch (InvalidMidiDataException e) {
439
if (Printer.err) e.printStackTrace();
440
return false;
441
}
442
return true;
443
}
444
445
/*
446
* private inner class representing a ByteArrayOutputStream
447
* which allows retrieval of the internal array
448
*/
449
private static class DirectBAOS extends ByteArrayOutputStream {
450
DirectBAOS() {
451
super();
452
}
453
454
public byte[] getInternalBuffer() {
455
return buf;
456
}
457
458
} // class DirectBAOS
459
}
460
461