1 /* |
1 /* |
2 * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. |
2 * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. |
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 * |
4 * |
5 * This code is free software; you can redistribute it and/or modify it |
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 |
6 * under the terms of the GNU General Public License version 2 only, as |
7 * published by the Free Software Foundation. Oracle designates this |
7 * published by the Free Software Foundation. Oracle designates this |
58 * @author Florian Bomers |
58 * @author Florian Bomers |
59 */ |
59 */ |
60 @SuppressWarnings("deprecation") |
60 @SuppressWarnings("deprecation") |
61 public final class JavaSoundAudioClip implements AudioClip, MetaEventListener, LineListener { |
61 public final class JavaSoundAudioClip implements AudioClip, MetaEventListener, LineListener { |
62 |
62 |
63 private static final boolean DEBUG = false; |
|
64 private static final int BUFFER_SIZE = 16384; // number of bytes written each time to the source data line |
63 private static final int BUFFER_SIZE = 16384; // number of bytes written each time to the source data line |
65 |
64 |
66 private long lastPlayCall = 0; |
65 private long lastPlayCall = 0; |
67 private static final int MINIMUM_PLAY_DELAY = 30; |
66 private static final int MINIMUM_PLAY_DELAY = 30; |
68 |
67 |
163 private synchronized void startImpl(boolean loop) { |
160 private synchronized void startImpl(boolean loop) { |
164 // hack for some applets that call the start method very rapidly... |
161 // hack for some applets that call the start method very rapidly... |
165 long currentTime = System.currentTimeMillis(); |
162 long currentTime = System.currentTimeMillis(); |
166 long diff = currentTime - lastPlayCall; |
163 long diff = currentTime - lastPlayCall; |
167 if (diff < MINIMUM_PLAY_DELAY) { |
164 if (diff < MINIMUM_PLAY_DELAY) { |
168 if (DEBUG || Printer.debug) Printer.debug("JavaSoundAudioClip.startImpl(loop="+loop+"): abort - too rapdly"); |
|
169 return; |
165 return; |
170 } |
166 } |
171 lastPlayCall = currentTime; |
167 lastPlayCall = currentTime; |
172 |
|
173 if (DEBUG || Printer.debug) Printer.debug("JavaSoundAudioClip.startImpl(loop="+loop+")"); |
|
174 try { |
168 try { |
175 if (clip != null) { |
169 if (clip != null) { |
176 // We need to disable autoclosing mechanism otherwise the clip |
170 // We need to disable autoclosing mechanism otherwise the clip |
177 // can be closed after "!clip.isOpen()" check, because of |
171 // can be closed after "!clip.isOpen()" check, because of |
178 // previous inactivity. |
172 // previous inactivity. |
198 } finally { |
192 } finally { |
199 clip.setAutoClosing(true); |
193 clip.setAutoClosing(true); |
200 } |
194 } |
201 } else if (datapusher != null ) { |
195 } else if (datapusher != null ) { |
202 datapusher.start(loop); |
196 datapusher.start(loop); |
203 if (DEBUG || Printer.debug)Printer.debug("Stream should be playing/looping"); |
|
204 |
197 |
205 } else if (sequencer != null) { |
198 } else if (sequencer != null) { |
206 sequencerloop = loop; |
199 sequencerloop = loop; |
207 if (sequencer.isRunning()) { |
200 if (sequencer.isRunning()) { |
208 sequencer.setMicrosecondPosition(0); |
201 sequencer.setMicrosecondPosition(0); |
211 try { |
204 try { |
212 sequencer.open(); |
205 sequencer.open(); |
213 sequencer.setSequence(sequence); |
206 sequencer.setSequence(sequence); |
214 |
207 |
215 } catch (InvalidMidiDataException e1) { |
208 } catch (InvalidMidiDataException e1) { |
216 if (DEBUG || Printer.err)e1.printStackTrace(); |
209 if (Printer.err) e1.printStackTrace(); |
217 } catch (MidiUnavailableException e2) { |
210 } catch (MidiUnavailableException e2) { |
218 if (DEBUG || Printer.err)e2.printStackTrace(); |
211 if (Printer.err) e2.printStackTrace(); |
219 } |
212 } |
220 } |
213 } |
221 sequencer.addMetaEventListener(this); |
214 sequencer.addMetaEventListener(this); |
222 try { |
215 try { |
223 sequencer.start(); |
216 sequencer.start(); |
224 } catch (Exception e) { |
217 } catch (Exception e) { |
225 if (DEBUG || Printer.err) e.printStackTrace(); |
218 if (Printer.err) e.printStackTrace(); |
226 } |
219 } |
227 if (DEBUG || Printer.debug)Printer.debug("Sequencer should be playing/looping"); |
|
228 } |
220 } |
229 } catch (Exception e) { |
221 } catch (Exception e) { |
230 if (DEBUG || Printer.err)e.printStackTrace(); |
222 if (Printer.err) e.printStackTrace(); |
231 } |
223 } |
232 } |
224 } |
233 |
225 |
234 @Override |
226 @Override |
235 public synchronized void stop() { |
227 public synchronized void stop() { |
236 if (!success) { |
228 if (!success) { |
237 return; |
229 return; |
238 } |
230 } |
239 |
|
240 if (DEBUG || Printer.debug)Printer.debug("JavaSoundAudioClip->stop()"); |
|
241 lastPlayCall = 0; |
231 lastPlayCall = 0; |
242 |
232 |
243 if (clip != null) { |
233 if (clip != null) { |
244 try { |
234 try { |
245 if (DEBUG || Printer.trace)Printer.trace("JavaSoundAudioClip: clip.flush()"); |
|
246 clip.flush(); |
235 clip.flush(); |
247 } catch (Exception e1) { |
236 } catch (Exception e1) { |
248 if (Printer.err) e1.printStackTrace(); |
237 if (Printer.err) e1.printStackTrace(); |
249 } |
238 } |
250 try { |
239 try { |
251 if (DEBUG || Printer.trace)Printer.trace("JavaSoundAudioClip: clip.stop()"); |
|
252 clip.stop(); |
240 clip.stop(); |
253 } catch (Exception e2) { |
241 } catch (Exception e2) { |
254 if (Printer.err) e2.printStackTrace(); |
242 if (Printer.err) e2.printStackTrace(); |
255 } |
243 } |
256 if (DEBUG || Printer.debug)Printer.debug("Clip should be stopped"); |
|
257 |
|
258 } else if (datapusher != null) { |
244 } else if (datapusher != null) { |
259 datapusher.stop(); |
245 datapusher.stop(); |
260 if (DEBUG || Printer.debug)Printer.debug("Stream should be stopped"); |
|
261 |
|
262 } else if (sequencer != null) { |
246 } else if (sequencer != null) { |
263 try { |
247 try { |
264 sequencerloop = false; |
248 sequencerloop = false; |
265 sequencer.removeMetaEventListener(this); |
249 sequencer.removeMetaEventListener(this); |
266 sequencer.stop(); |
250 sequencer.stop(); |
270 try { |
254 try { |
271 sequencer.close(); |
255 sequencer.close(); |
272 } catch (Exception e4) { |
256 } catch (Exception e4) { |
273 if (Printer.err) e4.printStackTrace(); |
257 if (Printer.err) e4.printStackTrace(); |
274 } |
258 } |
275 if (DEBUG || Printer.debug)Printer.debug("Sequencer should be stopped"); |
|
276 } |
259 } |
277 } |
260 } |
278 |
261 |
279 // Event handlers (for debugging) |
262 // Event handlers (for debugging) |
280 |
263 |
281 @Override |
264 @Override |
282 public synchronized void update(LineEvent event) { |
265 public synchronized void update(LineEvent event) { |
283 if (DEBUG || Printer.debug) Printer.debug("line event received: "+event); |
|
284 } |
266 } |
285 |
267 |
286 // handle MIDI track end meta events for looping |
268 // handle MIDI track end meta events for looping |
287 |
269 |
288 @Override |
270 @Override |
289 public synchronized void meta(MetaMessage message) { |
271 public synchronized void meta(MetaMessage message) { |
290 |
|
291 if (DEBUG || Printer.debug)Printer.debug("META EVENT RECEIVED!!!!! "); |
|
292 |
|
293 if( message.getType() == 47 ) { |
272 if( message.getType() == 47 ) { |
294 if (sequencerloop){ |
273 if (sequencerloop){ |
295 //notifyAll(); |
274 //notifyAll(); |
296 sequencer.setMicrosecondPosition(0); |
275 sequencer.setMicrosecondPosition(0); |
297 loop(); |
276 loop(); |
402 } |
378 } |
403 |
379 |
404 // METHODS FOR CREATING THE DEVICE |
380 // METHODS FOR CREATING THE DEVICE |
405 |
381 |
406 private boolean createClip() { |
382 private boolean createClip() { |
407 |
|
408 if (DEBUG || Printer.debug)Printer.debug("JavaSoundAudioClip.createClip()"); |
|
409 |
|
410 try { |
383 try { |
411 DataLine.Info info = new DataLine.Info(Clip.class, loadedAudioFormat); |
384 DataLine.Info info = new DataLine.Info(Clip.class, loadedAudioFormat); |
412 if (!(AudioSystem.isLineSupported(info)) ) { |
385 if (!(AudioSystem.isLineSupported(info)) ) { |
413 if (DEBUG || Printer.err)Printer.err("Clip not supported: "+loadedAudioFormat); |
386 if (Printer.err) Printer.err("Clip not supported: "+loadedAudioFormat); |
414 // fail silently |
387 // fail silently |
415 return false; |
388 return false; |
416 } |
389 } |
417 Object line = AudioSystem.getLine(info); |
390 Object line = AudioSystem.getLine(info); |
418 if (!(line instanceof AutoClosingClip)) { |
391 if (!(line instanceof AutoClosingClip)) { |
419 if (DEBUG || Printer.err)Printer.err("Clip is not auto closing!"+clip); |
392 if (Printer.err) Printer.err("Clip is not auto closing!"+clip); |
420 // fail -> will try with SourceDataLine |
393 // fail -> will try with SourceDataLine |
421 return false; |
394 return false; |
422 } |
395 } |
423 clip = (AutoClosingClip) line; |
396 clip = (AutoClosingClip) line; |
424 clip.setAutoClosing(true); |
397 clip.setAutoClosing(true); |
425 if (DEBUG || Printer.debug) clip.addLineListener(this); |
|
426 } catch (Exception e) { |
398 } catch (Exception e) { |
427 if (DEBUG || Printer.err)e.printStackTrace(); |
399 if (Printer.err) e.printStackTrace(); |
428 // fail silently |
400 // fail silently |
429 return false; |
401 return false; |
430 } |
402 } |
431 |
403 |
432 if (clip==null) { |
404 if (clip==null) { |
433 // fail silently |
405 // fail silently |
434 return false; |
406 return false; |
435 } |
407 } |
436 |
|
437 if (DEBUG || Printer.debug)Printer.debug("Loaded clip."); |
|
438 return true; |
408 return true; |
439 } |
409 } |
440 |
410 |
441 private boolean createSourceDataLine() { |
411 private boolean createSourceDataLine() { |
442 if (DEBUG || Printer.debug)Printer.debug("JavaSoundAudioClip.createSourceDataLine()"); |
|
443 try { |
412 try { |
444 DataLine.Info info = new DataLine.Info(SourceDataLine.class, loadedAudioFormat); |
413 DataLine.Info info = new DataLine.Info(SourceDataLine.class, loadedAudioFormat); |
445 if (!(AudioSystem.isLineSupported(info)) ) { |
414 if (!(AudioSystem.isLineSupported(info)) ) { |
446 if (DEBUG || Printer.err)Printer.err("Line not supported: "+loadedAudioFormat); |
415 if (Printer.err) Printer.err("Line not supported: "+loadedAudioFormat); |
447 // fail silently |
416 // fail silently |
448 return false; |
417 return false; |
449 } |
418 } |
450 SourceDataLine source = (SourceDataLine) AudioSystem.getLine(info); |
419 SourceDataLine source = (SourceDataLine) AudioSystem.getLine(info); |
451 datapusher = new DataPusher(source, loadedAudioFormat, loadedAudio, loadedAudioByteLength); |
420 datapusher = new DataPusher(source, loadedAudioFormat, loadedAudio, loadedAudioByteLength); |
452 } catch (Exception e) { |
421 } catch (Exception e) { |
453 if (DEBUG || Printer.err)e.printStackTrace(); |
422 if (Printer.err) e.printStackTrace(); |
454 // fail silently |
423 // fail silently |
455 return false; |
424 return false; |
456 } |
425 } |
457 |
426 |
458 if (datapusher==null) { |
427 if (datapusher==null) { |
459 // fail silently |
428 // fail silently |
460 return false; |
429 return false; |
461 } |
430 } |
462 |
|
463 if (DEBUG || Printer.debug)Printer.debug("Created SourceDataLine."); |
|
464 return true; |
431 return true; |
465 } |
432 } |
466 |
433 |
467 private boolean createSequencer(BufferedInputStream in) throws IOException { |
434 private boolean createSequencer(BufferedInputStream in) throws IOException { |
468 |
|
469 if (DEBUG || Printer.debug)Printer.debug("JavaSoundAudioClip.createSequencer()"); |
|
470 |
|
471 // get the sequencer |
435 // get the sequencer |
472 try { |
436 try { |
473 sequencer = MidiSystem.getSequencer( ); |
437 sequencer = MidiSystem.getSequencer( ); |
474 } catch(MidiUnavailableException me) { |
438 } catch(MidiUnavailableException me) { |
475 if (DEBUG || Printer.err)me.printStackTrace(); |
439 if (Printer.err) me.printStackTrace(); |
476 return false; |
440 return false; |
477 } |
441 } |
478 if (sequencer==null) { |
442 if (sequencer==null) { |
479 return false; |
443 return false; |
480 } |
444 } |