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+
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.ByteArrayInputStream;
32+import java.io.ByteArrayOutputStream;
33+import java.applet.AudioClip;
34+import java.lang.InterruptedException;
35+
36+import javax.sound.sampled.AudioSystem;
37+import javax.sound.sampled.Mixer;
38+import javax.sound.sampled.Clip;
39+import javax.sound.sampled.Control;
40+import javax.sound.sampled.AudioInputStream;
41+import javax.sound.sampled.AudioFormat;
42+import javax.sound.sampled.DataLine;
43+import javax.sound.sampled.SourceDataLine;
44+import javax.sound.sampled.LineEvent;
45+import javax.sound.sampled.LineListener;
46+import javax.sound.sampled.LineUnavailableException;
47+import javax.sound.sampled.UnsupportedAudioFileException;
48+
49+
50+import javax.sound.midi.MidiSystem;
51+import javax.sound.midi.MidiFileFormat;
52+import javax.sound.midi.MetaMessage;
53+import javax.sound.midi.Sequence;
54+import javax.sound.midi.Sequencer;
55+import javax.sound.midi.InvalidMidiDataException;
56+import javax.sound.midi.MidiUnavailableException;
57+import 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+
66+public 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+}