< prev index next >

src/java.desktop/share/classes/com/sun/media/sound/JavaSoundAudioClip.java

Print this page




   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 }
< prev index next >