History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/com/sun/media/sound/JavaSoundAudioClip.java (1 of 18352 files). Show all

Unified · Split

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