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