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;
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 if (!clip.isOpen()) {
153 if (DEBUG || Printer.trace)Printer.trace("JavaSoundAudioClip: clip.open()");
154 clip.open(loadedAudioFormat, loadedAudio, 0, loadedAudioByteLength);
188
189 } catch (InvalidMidiDataException e1) {
190 if (DEBUG || Printer.err)e1.printStackTrace();
191 } catch (MidiUnavailableException e2) {
192 if (DEBUG || Printer.err)e2.printStackTrace();
193 }
194 }
195 sequencer.addMetaEventListener(this);
196 try {
197 sequencer.start();
198 } catch (Exception e) {
199 if (DEBUG || Printer.err) e.printStackTrace();
200 }
201 if (DEBUG || Printer.debug)Printer.debug("Sequencer should be playing/looping");
202 }
203 } catch (Exception e) {
204 if (DEBUG || Printer.err)e.printStackTrace();
205 }
206 }
207
208 public synchronized void stop() {
209
210 if (DEBUG || Printer.debug)Printer.debug("JavaSoundAudioClip->stop()");
211 lastPlayCall = 0;
212
213 if (clip != null) {
214 try {
215 if (DEBUG || Printer.trace)Printer.trace("JavaSoundAudioClip: clip.flush()");
216 clip.flush();
217 } catch (Exception e1) {
218 if (Printer.err) e1.printStackTrace();
219 }
220 try {
221 if (DEBUG || Printer.trace)Printer.trace("JavaSoundAudioClip: clip.stop()");
222 clip.stop();
223 } catch (Exception e2) {
224 if (Printer.err) e2.printStackTrace();
225 }
226 if (DEBUG || Printer.debug)Printer.debug("Clip should be stopped");
227
231
232 } else if (sequencer != null) {
233 try {
234 sequencerloop = false;
235 sequencer.removeMetaEventListener(this);
236 sequencer.stop();
237 } catch (Exception e3) {
238 if (Printer.err) e3.printStackTrace();
239 }
240 try {
241 sequencer.close();
242 } catch (Exception e4) {
243 if (Printer.err) e4.printStackTrace();
244 }
245 if (DEBUG || Printer.debug)Printer.debug("Sequencer should be stopped");
246 }
247 }
248
249 // Event handlers (for debugging)
250
251 public synchronized void update(LineEvent event) {
252 if (DEBUG || Printer.debug) Printer.debug("line event received: "+event);
253 }
254
255 // handle MIDI track end meta events for looping
256
257 public synchronized void meta( MetaMessage message ) {
258
259 if (DEBUG || Printer.debug)Printer.debug("META EVENT RECEIVED!!!!! ");
260
261 if( message.getType() == 47 ) {
262 if (sequencerloop){
263 //notifyAll();
264 sequencer.setMicrosecondPosition(0);
265 loop();
266 } else {
267 stop();
268 }
269 }
270 }
271
272
273 public String toString() {
274 return getClass().toString();
275 }
276
277
278 protected void finalize() {
279
280 if (clip != null) {
281 if (DEBUG || Printer.trace)Printer.trace("JavaSoundAudioClip.finalize: clip.close()");
282 clip.close();
283 }
284
285 //$$fb 2001-09-26: may improve situation related to bug #4302884
286 if (datapusher != null) {
287 datapusher.close();
288 }
289
290 if (sequencer != null) {
291 sequencer.close();
292 }
293 }
294
295 // FILE LOADING METHODS
296
297 private boolean loadAudioData(AudioInputStream as) throws IOException, UnsupportedAudioFileException {
309 long byteLen = AudioSystem.NOT_SPECIFIED;
310 if (frameLen != AudioSystem.NOT_SPECIFIED
311 && frameLen > 0
312 && frameSize != AudioSystem.NOT_SPECIFIED
313 && frameSize > 0) {
314 byteLen = frameLen * frameSize;
315 }
316 if (byteLen != AudioSystem.NOT_SPECIFIED) {
317 // if the stream length is known, it can be efficiently loaded into memory
318 readStream(as, byteLen);
319 } else {
320 // otherwise we use a ByteArrayOutputStream to load it into memory
321 readStream(as);
322 }
323
324 // if everything went fine, we have now the audio data in
325 // loadedAudio, and the byte length in loadedAudioByteLength
326 return true;
327 }
328
329
330
331 private void readStream(AudioInputStream as, long byteLen) throws IOException {
332 // arrays "only" max. 2GB
333 int intLen;
334 if (byteLen > 2147483647) {
335 intLen = 2147483647;
336 } else {
337 intLen = (int) byteLen;
338 }
339 loadedAudio = new byte[intLen];
340 loadedAudioByteLength = 0;
341
342 // this loop may throw an IOException
343 while (true) {
344 int bytesRead = as.read(loadedAudio, loadedAudioByteLength, intLen - loadedAudioByteLength);
345 if (bytesRead <= 0) {
346 as.close();
347 break;
348 }
349 loadedAudioByteLength += bytesRead;
350 }
354
355 DirectBAOS baos = new DirectBAOS();
356 byte buffer[] = new byte[16384];
357 int bytesRead = 0;
358 int totalBytesRead = 0;
359
360 // this loop may throw an IOException
361 while( true ) {
362 bytesRead = as.read(buffer, 0, buffer.length);
363 if (bytesRead <= 0) {
364 as.close();
365 break;
366 }
367 totalBytesRead += bytesRead;
368 baos.write(buffer, 0, bytesRead);
369 }
370 loadedAudio = baos.getInternalBuffer();
371 loadedAudioByteLength = totalBytesRead;
372 }
373
374
375 // METHODS FOR CREATING THE DEVICE
376
377 private boolean createClip() {
378
379 if (DEBUG || Printer.debug)Printer.debug("JavaSoundAudioClip.createClip()");
380
381 try {
382 DataLine.Info info = new DataLine.Info(Clip.class, loadedAudioFormat);
383 if (!(AudioSystem.isLineSupported(info)) ) {
384 if (DEBUG || Printer.err)Printer.err("Clip not supported: "+loadedAudioFormat);
385 // fail silently
386 return false;
387 }
388 Object line = AudioSystem.getLine(info);
389 if (!(line instanceof AutoClosingClip)) {
390 if (DEBUG || Printer.err)Printer.err("Clip is not auto closing!"+clip);
391 // fail -> will try with SourceDataLine
392 return false;
393 }
394 clip = (AutoClosingClip) line;
447 return false;
448 }
449 if (sequencer==null) {
450 return false;
451 }
452
453 try {
454 sequence = MidiSystem.getSequence(in);
455 if (sequence == null) {
456 return false;
457 }
458 } catch (InvalidMidiDataException e) {
459 if (DEBUG || Printer.err)e.printStackTrace();
460 return false;
461 }
462
463 if (DEBUG || Printer.debug)Printer.debug("Created Sequencer.");
464 return true;
465 }
466
467
468 /*
469 * private inner class representing a ByteArrayOutputStream
470 * which allows retrieval of the internal array
471 */
472 private static class DirectBAOS extends ByteArrayOutputStream {
473 DirectBAOS() {
474 super();
475 }
476
477 public byte[] getInternalBuffer() {
478 return buf;
479 }
480
481 } // class DirectBAOS
482
483 }
|
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
34 import javax.sound.midi.InvalidMidiDataException;
35 import javax.sound.midi.MetaEventListener;
36 import javax.sound.midi.MetaMessage;
37 import javax.sound.midi.MidiFileFormat;
38 import javax.sound.midi.MidiSystem;
39 import javax.sound.midi.MidiUnavailableException;
40 import javax.sound.midi.Sequence;
41 import javax.sound.midi.Sequencer;
42 import javax.sound.sampled.AudioFormat;
43 import javax.sound.sampled.AudioInputStream;
44 import javax.sound.sampled.AudioSystem;
45 import javax.sound.sampled.Clip;
46 import javax.sound.sampled.DataLine;
47 import javax.sound.sampled.LineEvent;
48 import javax.sound.sampled.LineListener;
49 import javax.sound.sampled.SourceDataLine;
50 import javax.sound.sampled.UnsupportedAudioFileException;
51
52 /**
53 * Java Sound audio clip;
54 *
55 * @author Arthur van Hoff, Kara Kytle, Jan Borgersen
56 * @author Florian Bomers
57 */
58 public final class JavaSoundAudioClip implements AudioClip, MetaEventListener, LineListener {
59
60 private static final boolean DEBUG = false;
61 private static final int BUFFER_SIZE = 16384; // number of bytes written each time to the source data line
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;
107 success = createClip();
108 }
109 if (!success) {
110 success = createSourceDataLine();
111 }
112 }
113 } catch (UnsupportedAudioFileException e) {
114 // not an audio file
115 try {
116 MidiFileFormat mff = MidiSystem.getMidiFileFormat(bis);
117 success = createSequencer(bis);
118 } catch (InvalidMidiDataException e1) {
119 success = false;
120 }
121 }
122 if (!success) {
123 throw new IOException("Unable to create AudioClip from input stream");
124 }
125 }
126
127 @Override
128 public synchronized void play() {
129 startImpl(false);
130 }
131
132 @Override
133 public synchronized void loop() {
134 startImpl(true);
135 }
136
137 private synchronized void startImpl(boolean loop) {
138 // hack for some applets that call the start method very rapidly...
139 long currentTime = System.currentTimeMillis();
140 long diff = currentTime - lastPlayCall;
141 if (diff < MINIMUM_PLAY_DELAY) {
142 if (DEBUG || Printer.debug) Printer.debug("JavaSoundAudioClip.startImpl(loop="+loop+"): abort - too rapdly");
143 return;
144 }
145 lastPlayCall = currentTime;
146
147 if (DEBUG || Printer.debug) Printer.debug("JavaSoundAudioClip.startImpl(loop="+loop+")");
148 try {
149 if (clip != null) {
150 if (!clip.isOpen()) {
151 if (DEBUG || Printer.trace)Printer.trace("JavaSoundAudioClip: clip.open()");
152 clip.open(loadedAudioFormat, loadedAudio, 0, loadedAudioByteLength);
186
187 } catch (InvalidMidiDataException e1) {
188 if (DEBUG || Printer.err)e1.printStackTrace();
189 } catch (MidiUnavailableException e2) {
190 if (DEBUG || Printer.err)e2.printStackTrace();
191 }
192 }
193 sequencer.addMetaEventListener(this);
194 try {
195 sequencer.start();
196 } catch (Exception e) {
197 if (DEBUG || Printer.err) e.printStackTrace();
198 }
199 if (DEBUG || Printer.debug)Printer.debug("Sequencer should be playing/looping");
200 }
201 } catch (Exception e) {
202 if (DEBUG || Printer.err)e.printStackTrace();
203 }
204 }
205
206 @Override
207 public synchronized void stop() {
208
209 if (DEBUG || Printer.debug)Printer.debug("JavaSoundAudioClip->stop()");
210 lastPlayCall = 0;
211
212 if (clip != null) {
213 try {
214 if (DEBUG || Printer.trace)Printer.trace("JavaSoundAudioClip: clip.flush()");
215 clip.flush();
216 } catch (Exception e1) {
217 if (Printer.err) e1.printStackTrace();
218 }
219 try {
220 if (DEBUG || Printer.trace)Printer.trace("JavaSoundAudioClip: clip.stop()");
221 clip.stop();
222 } catch (Exception e2) {
223 if (Printer.err) e2.printStackTrace();
224 }
225 if (DEBUG || Printer.debug)Printer.debug("Clip should be stopped");
226
230
231 } else if (sequencer != null) {
232 try {
233 sequencerloop = false;
234 sequencer.removeMetaEventListener(this);
235 sequencer.stop();
236 } catch (Exception e3) {
237 if (Printer.err) e3.printStackTrace();
238 }
239 try {
240 sequencer.close();
241 } catch (Exception e4) {
242 if (Printer.err) e4.printStackTrace();
243 }
244 if (DEBUG || Printer.debug)Printer.debug("Sequencer should be stopped");
245 }
246 }
247
248 // Event handlers (for debugging)
249
250 @Override
251 public synchronized void update(LineEvent event) {
252 if (DEBUG || Printer.debug) Printer.debug("line event received: "+event);
253 }
254
255 // handle MIDI track end meta events for looping
256
257 @Override
258 public synchronized void meta(MetaMessage message) {
259
260 if (DEBUG || Printer.debug)Printer.debug("META EVENT RECEIVED!!!!! ");
261
262 if( message.getType() == 47 ) {
263 if (sequencerloop){
264 //notifyAll();
265 sequencer.setMicrosecondPosition(0);
266 loop();
267 } else {
268 stop();
269 }
270 }
271 }
272
273 @Override
274 public String toString() {
275 return getClass().toString();
276 }
277
278 @Override
279 protected void finalize() {
280
281 if (clip != null) {
282 if (DEBUG || Printer.trace)Printer.trace("JavaSoundAudioClip.finalize: clip.close()");
283 clip.close();
284 }
285
286 //$$fb 2001-09-26: may improve situation related to bug #4302884
287 if (datapusher != null) {
288 datapusher.close();
289 }
290
291 if (sequencer != null) {
292 sequencer.close();
293 }
294 }
295
296 // FILE LOADING METHODS
297
298 private boolean loadAudioData(AudioInputStream as) throws IOException, UnsupportedAudioFileException {
310 long byteLen = AudioSystem.NOT_SPECIFIED;
311 if (frameLen != AudioSystem.NOT_SPECIFIED
312 && frameLen > 0
313 && frameSize != AudioSystem.NOT_SPECIFIED
314 && frameSize > 0) {
315 byteLen = frameLen * frameSize;
316 }
317 if (byteLen != AudioSystem.NOT_SPECIFIED) {
318 // if the stream length is known, it can be efficiently loaded into memory
319 readStream(as, byteLen);
320 } else {
321 // otherwise we use a ByteArrayOutputStream to load it into memory
322 readStream(as);
323 }
324
325 // if everything went fine, we have now the audio data in
326 // loadedAudio, and the byte length in loadedAudioByteLength
327 return true;
328 }
329
330 private void readStream(AudioInputStream as, long byteLen) throws IOException {
331 // arrays "only" max. 2GB
332 int intLen;
333 if (byteLen > 2147483647) {
334 intLen = 2147483647;
335 } else {
336 intLen = (int) byteLen;
337 }
338 loadedAudio = new byte[intLen];
339 loadedAudioByteLength = 0;
340
341 // this loop may throw an IOException
342 while (true) {
343 int bytesRead = as.read(loadedAudio, loadedAudioByteLength, intLen - loadedAudioByteLength);
344 if (bytesRead <= 0) {
345 as.close();
346 break;
347 }
348 loadedAudioByteLength += bytesRead;
349 }
353
354 DirectBAOS baos = new DirectBAOS();
355 byte buffer[] = new byte[16384];
356 int bytesRead = 0;
357 int totalBytesRead = 0;
358
359 // this loop may throw an IOException
360 while( true ) {
361 bytesRead = as.read(buffer, 0, buffer.length);
362 if (bytesRead <= 0) {
363 as.close();
364 break;
365 }
366 totalBytesRead += bytesRead;
367 baos.write(buffer, 0, bytesRead);
368 }
369 loadedAudio = baos.getInternalBuffer();
370 loadedAudioByteLength = totalBytesRead;
371 }
372
373 // METHODS FOR CREATING THE DEVICE
374
375 private boolean createClip() {
376
377 if (DEBUG || Printer.debug)Printer.debug("JavaSoundAudioClip.createClip()");
378
379 try {
380 DataLine.Info info = new DataLine.Info(Clip.class, loadedAudioFormat);
381 if (!(AudioSystem.isLineSupported(info)) ) {
382 if (DEBUG || Printer.err)Printer.err("Clip not supported: "+loadedAudioFormat);
383 // fail silently
384 return false;
385 }
386 Object line = AudioSystem.getLine(info);
387 if (!(line instanceof AutoClosingClip)) {
388 if (DEBUG || Printer.err)Printer.err("Clip is not auto closing!"+clip);
389 // fail -> will try with SourceDataLine
390 return false;
391 }
392 clip = (AutoClosingClip) line;
445 return false;
446 }
447 if (sequencer==null) {
448 return false;
449 }
450
451 try {
452 sequence = MidiSystem.getSequence(in);
453 if (sequence == null) {
454 return false;
455 }
456 } catch (InvalidMidiDataException e) {
457 if (DEBUG || Printer.err)e.printStackTrace();
458 return false;
459 }
460
461 if (DEBUG || Printer.debug)Printer.debug("Created Sequencer.");
462 return true;
463 }
464
465 /*
466 * private inner class representing a ByteArrayOutputStream
467 * which allows retrieval of the internal array
468 */
469 private static class DirectBAOS extends ByteArrayOutputStream {
470 DirectBAOS() {
471 super();
472 }
473
474 public byte[] getInternalBuffer() {
475 return buf;
476 }
477
478 } // class DirectBAOS
479 }
|