Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/com/sun/media/sound/JavaSoundAudioClip.java
38924 views
/*1* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.media.sound;2627import java.io.IOException;28import java.io.InputStream;29import java.io.BufferedInputStream;30import java.io.ByteArrayOutputStream;31import java.applet.AudioClip;3233import javax.sound.sampled.AudioSystem;34import javax.sound.sampled.Clip;35import javax.sound.sampled.AudioInputStream;36import javax.sound.sampled.AudioFormat;37import javax.sound.sampled.DataLine;38import javax.sound.sampled.SourceDataLine;39import javax.sound.sampled.LineEvent;40import javax.sound.sampled.LineListener;41import javax.sound.sampled.UnsupportedAudioFileException;4243import javax.sound.midi.MidiSystem;44import javax.sound.midi.MidiFileFormat;45import javax.sound.midi.MetaMessage;46import javax.sound.midi.Sequence;47import javax.sound.midi.Sequencer;48import javax.sound.midi.InvalidMidiDataException;49import javax.sound.midi.MidiUnavailableException;50import javax.sound.midi.MetaEventListener;5152/**53* Java Sound audio clip;54*55* @author Arthur van Hoff, Kara Kytle, Jan Borgersen56* @author Florian Bomers57*/5859public final class JavaSoundAudioClip implements AudioClip, MetaEventListener, LineListener {6061private static final boolean DEBUG = false;62private static final int BUFFER_SIZE = 16384; // number of bytes written each time to the source data line6364private long lastPlayCall = 0;65private static final int MINIMUM_PLAY_DELAY = 30;6667private byte loadedAudio[] = null;68private int loadedAudioByteLength = 0;69private AudioFormat loadedAudioFormat = null;7071private AutoClosingClip clip = null;72private boolean clipLooping = false;7374private DataPusher datapusher = null;7576private Sequencer sequencer = null;77private Sequence sequence = null;78private boolean sequencerloop = false;7980/**81* used for determining how many samples is the82* threshhold between playing as a Clip and streaming83* from the file.84*85* $$jb: 11.07.99: the engine has a limit of 1M86* samples to play as a Clip, so compare this number87* with the number of samples in the stream.88*89*/90private final static long CLIP_THRESHOLD = 1048576;91//private final static long CLIP_THRESHOLD = 1;92private final static int STREAM_BUFFER_SIZE = 1024;9394public JavaSoundAudioClip(InputStream in) throws IOException {95if (DEBUG || Printer.debug)Printer.debug("JavaSoundAudioClip.<init>");9697BufferedInputStream bis = new BufferedInputStream(in, STREAM_BUFFER_SIZE);98bis.mark(STREAM_BUFFER_SIZE);99boolean success = false;100try {101AudioInputStream as = AudioSystem.getAudioInputStream(bis);102// load the stream data into memory103success = loadAudioData(as);104105if (success) {106success = false;107if (loadedAudioByteLength < CLIP_THRESHOLD) {108success = createClip();109}110if (!success) {111success = createSourceDataLine();112}113}114} catch (UnsupportedAudioFileException e) {115// not an audio file116try {117MidiFileFormat mff = MidiSystem.getMidiFileFormat(bis);118success = createSequencer(bis);119} catch (InvalidMidiDataException e1) {120success = false;121}122}123if (!success) {124throw new IOException("Unable to create AudioClip from input stream");125}126}127128129public synchronized void play() {130startImpl(false);131}132133134public synchronized void loop() {135startImpl(true);136}137138private synchronized void startImpl(boolean loop) {139// hack for some applets that call the start method very rapidly...140long currentTime = System.currentTimeMillis();141long diff = currentTime - lastPlayCall;142if (diff < MINIMUM_PLAY_DELAY) {143if (DEBUG || Printer.debug) Printer.debug("JavaSoundAudioClip.startImpl(loop="+loop+"): abort - too rapdly");144return;145}146lastPlayCall = currentTime;147148if (DEBUG || Printer.debug) Printer.debug("JavaSoundAudioClip.startImpl(loop="+loop+")");149try {150if (clip != null) {151// We need to disable autoclosing mechanism otherwise the clip152// can be closed after "!clip.isOpen()" check, because of153// previous inactivity.154clip.setAutoClosing(false);155try {156if (!clip.isOpen()) {157clip.open(loadedAudioFormat, loadedAudio, 0,158loadedAudioByteLength);159} else {160clip.flush();161if (loop != clipLooping) {162// need to stop in case the looped status changed163clip.stop();164}165}166clip.setFramePosition(0);167if (loop) {168clip.loop(Clip.LOOP_CONTINUOUSLY);169} else {170clip.start();171}172clipLooping = loop;173} finally {174clip.setAutoClosing(true);175}176} else if (datapusher != null ) {177datapusher.start(loop);178if (DEBUG || Printer.debug)Printer.debug("Stream should be playing/looping");179180} else if (sequencer != null) {181sequencerloop = loop;182if (sequencer.isRunning()) {183sequencer.setMicrosecondPosition(0);184}185if (!sequencer.isOpen()) {186try {187sequencer.open();188sequencer.setSequence(sequence);189190} catch (InvalidMidiDataException e1) {191if (DEBUG || Printer.err)e1.printStackTrace();192} catch (MidiUnavailableException e2) {193if (DEBUG || Printer.err)e2.printStackTrace();194}195}196sequencer.addMetaEventListener(this);197try {198sequencer.start();199} catch (Exception e) {200if (DEBUG || Printer.err) e.printStackTrace();201}202if (DEBUG || Printer.debug)Printer.debug("Sequencer should be playing/looping");203}204} catch (Exception e) {205if (DEBUG || Printer.err)e.printStackTrace();206}207}208209public synchronized void stop() {210211if (DEBUG || Printer.debug)Printer.debug("JavaSoundAudioClip->stop()");212lastPlayCall = 0;213214if (clip != null) {215try {216if (DEBUG || Printer.trace)Printer.trace("JavaSoundAudioClip: clip.flush()");217clip.flush();218} catch (Exception e1) {219if (Printer.err) e1.printStackTrace();220}221try {222if (DEBUG || Printer.trace)Printer.trace("JavaSoundAudioClip: clip.stop()");223clip.stop();224} catch (Exception e2) {225if (Printer.err) e2.printStackTrace();226}227if (DEBUG || Printer.debug)Printer.debug("Clip should be stopped");228229} else if (datapusher != null) {230datapusher.stop();231if (DEBUG || Printer.debug)Printer.debug("Stream should be stopped");232233} else if (sequencer != null) {234try {235sequencerloop = false;236sequencer.addMetaEventListener(this);237sequencer.stop();238} catch (Exception e3) {239if (Printer.err) e3.printStackTrace();240}241try {242sequencer.close();243} catch (Exception e4) {244if (Printer.err) e4.printStackTrace();245}246if (DEBUG || Printer.debug)Printer.debug("Sequencer should be stopped");247}248}249250// Event handlers (for debugging)251252public synchronized void update(LineEvent event) {253if (DEBUG || Printer.debug) Printer.debug("line event received: "+event);254}255256// handle MIDI track end meta events for looping257258public synchronized void meta( MetaMessage message ) {259260if (DEBUG || Printer.debug)Printer.debug("META EVENT RECEIVED!!!!! ");261262if( message.getType() == 47 ) {263if (sequencerloop){264//notifyAll();265sequencer.setMicrosecondPosition(0);266loop();267} else {268stop();269}270}271}272273274public String toString() {275return getClass().toString();276}277278279protected void finalize() {280281if (clip != null) {282if (DEBUG || Printer.trace)Printer.trace("JavaSoundAudioClip.finalize: clip.close()");283clip.close();284}285286//$$fb 2001-09-26: may improve situation related to bug #4302884287if (datapusher != null) {288datapusher.close();289}290291if (sequencer != null) {292sequencer.close();293}294}295296// FILE LOADING METHODS297298private boolean loadAudioData(AudioInputStream as) throws IOException, UnsupportedAudioFileException {299if (DEBUG || Printer.debug)Printer.debug("JavaSoundAudioClip->openAsClip()");300301// first possibly convert this stream to PCM302as = Toolkit.getPCMConvertedAudioInputStream(as);303if (as == null) {304return false;305}306307loadedAudioFormat = as.getFormat();308long frameLen = as.getFrameLength();309int frameSize = loadedAudioFormat.getFrameSize();310long byteLen = AudioSystem.NOT_SPECIFIED;311if (frameLen != AudioSystem.NOT_SPECIFIED312&& frameLen > 0313&& frameSize != AudioSystem.NOT_SPECIFIED314&& frameSize > 0) {315byteLen = frameLen * frameSize;316}317if (byteLen != AudioSystem.NOT_SPECIFIED) {318// if the stream length is known, it can be efficiently loaded into memory319readStream(as, byteLen);320} else {321// otherwise we use a ByteArrayOutputStream to load it into memory322readStream(as);323}324325// if everything went fine, we have now the audio data in326// loadedAudio, and the byte length in loadedAudioByteLength327return true;328}329330331332private void readStream(AudioInputStream as, long byteLen) throws IOException {333// arrays "only" max. 2GB334int intLen;335if (byteLen > 2147483647) {336intLen = 2147483647;337} else {338intLen = (int) byteLen;339}340loadedAudio = new byte[intLen];341loadedAudioByteLength = 0;342343// this loop may throw an IOException344while (true) {345int bytesRead = as.read(loadedAudio, loadedAudioByteLength, intLen - loadedAudioByteLength);346if (bytesRead <= 0) {347as.close();348break;349}350loadedAudioByteLength += bytesRead;351}352}353354private void readStream(AudioInputStream as) throws IOException {355356DirectBAOS baos = new DirectBAOS();357byte buffer[] = new byte[16384];358int bytesRead = 0;359int totalBytesRead = 0;360361// this loop may throw an IOException362while( true ) {363bytesRead = as.read(buffer, 0, buffer.length);364if (bytesRead <= 0) {365as.close();366break;367}368totalBytesRead += bytesRead;369baos.write(buffer, 0, bytesRead);370}371loadedAudio = baos.getInternalBuffer();372loadedAudioByteLength = totalBytesRead;373}374375376// METHODS FOR CREATING THE DEVICE377378private boolean createClip() {379380if (DEBUG || Printer.debug)Printer.debug("JavaSoundAudioClip.createClip()");381382try {383DataLine.Info info = new DataLine.Info(Clip.class, loadedAudioFormat);384if (!(AudioSystem.isLineSupported(info)) ) {385if (DEBUG || Printer.err)Printer.err("Clip not supported: "+loadedAudioFormat);386// fail silently387return false;388}389Object line = AudioSystem.getLine(info);390if (!(line instanceof AutoClosingClip)) {391if (DEBUG || Printer.err)Printer.err("Clip is not auto closing!"+clip);392// fail -> will try with SourceDataLine393return false;394}395clip = (AutoClosingClip) line;396clip.setAutoClosing(true);397if (DEBUG || Printer.debug) clip.addLineListener(this);398} catch (Exception e) {399if (DEBUG || Printer.err)e.printStackTrace();400// fail silently401return false;402}403404if (clip==null) {405// fail silently406return false;407}408409if (DEBUG || Printer.debug)Printer.debug("Loaded clip.");410return true;411}412413private boolean createSourceDataLine() {414if (DEBUG || Printer.debug)Printer.debug("JavaSoundAudioClip.createSourceDataLine()");415try {416DataLine.Info info = new DataLine.Info(SourceDataLine.class, loadedAudioFormat);417if (!(AudioSystem.isLineSupported(info)) ) {418if (DEBUG || Printer.err)Printer.err("Line not supported: "+loadedAudioFormat);419// fail silently420return false;421}422SourceDataLine source = (SourceDataLine) AudioSystem.getLine(info);423datapusher = new DataPusher(source, loadedAudioFormat, loadedAudio, loadedAudioByteLength);424} catch (Exception e) {425if (DEBUG || Printer.err)e.printStackTrace();426// fail silently427return false;428}429430if (datapusher==null) {431// fail silently432return false;433}434435if (DEBUG || Printer.debug)Printer.debug("Created SourceDataLine.");436return true;437}438439private boolean createSequencer(BufferedInputStream in) throws IOException {440441if (DEBUG || Printer.debug)Printer.debug("JavaSoundAudioClip.createSequencer()");442443// get the sequencer444try {445sequencer = MidiSystem.getSequencer( );446} catch(MidiUnavailableException me) {447if (DEBUG || Printer.err)me.printStackTrace();448return false;449}450if (sequencer==null) {451return false;452}453454try {455sequence = MidiSystem.getSequence(in);456if (sequence == null) {457return false;458}459} catch (InvalidMidiDataException e) {460if (DEBUG || Printer.err)e.printStackTrace();461return false;462}463464if (DEBUG || Printer.debug)Printer.debug("Created Sequencer.");465return true;466}467468469/*470* private inner class representing a ByteArrayOutputStream471* which allows retrieval of the internal array472*/473private static class DirectBAOS extends ByteArrayOutputStream {474DirectBAOS() {475super();476}477478public byte[] getInternalBuffer() {479return buf;480}481482} // class DirectBAOS483484}485486487