/* * Copyright (c) 1999, 2003, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.audio; import java.util.Hashtable;import java.util.Vector;import java.util.Enumeration;import java.io.IOException;import java.io.InputStream;import java.io.BufferedInputStream;import java.io.OutputStream;import java.io.ByteArrayInputStream; import javax.sound.sampled.*;import javax.sound.midi.*;import com.sun.media.sound.DataPusher;import com.sun.media.sound.Toolkit; /** * This class provides an interface to the Headspace Audio engine through * the Java Sound API. * * This class emulates systems with multiple audio channels, mixing * multiple streams for the workstation's single-channel device. * * @see AudioData * @see AudioDataStream * @see AudioStream * @see AudioStreamSequence * @see ContinuousAudioDataStream * @author David Rivas * @author Kara Kytle * @author Jan Borgersen * @author Florian Bomers */ public class AudioDevice { private boolean DEBUG = false /*true*/ ; /** Hashtable of audio clips / input streams. */ private Hashtable clipStreams; private Vector infos; /** Are we currently playing audio? */ private boolean playing = false; /** Handle to the JS audio mixer. */ private Mixer mixer = null; /** * The default audio player. This audio player is initialized * automatically. */ public static final AudioDevice device = new AudioDevice(); /** * Create an AudioDevice instance. */ private AudioDevice() { clipStreams = new Hashtable(); infos = new Vector(); } private synchronized void startSampled( AudioInputStream as, InputStream in ) throws UnsupportedAudioFileException, LineUnavailableException { Info info = null; DataPusher datapusher = null; DataLine.Info lineinfo = null; SourceDataLine sourcedataline = null; // if ALAW or ULAW, we must convert.... as = Toolkit.getPCMConvertedAudioInputStream(as); if( as==null ) { // could not convert return; } lineinfo = new DataLine.Info(SourceDataLine.class, as.getFormat()); if( !(AudioSystem.isLineSupported(lineinfo))) { return; } sourcedataline = (SourceDataLine)AudioSystem.getLine(lineinfo); datapusher = new DataPusher(sourcedataline, as); info = new Info( null, in, datapusher ); infos.addElement( info ); datapusher.start(); } private synchronized void startMidi( InputStream bis, InputStream in ) throws InvalidMidiDataException, MidiUnavailableException { Sequencer sequencer = null; Info info = null; sequencer = MidiSystem.getSequencer( ); sequencer.open(); try { sequencer.setSequence( bis ); } catch( IOException e ) { throw new InvalidMidiDataException( e.getMessage() ); } info = new Info( sequencer, in, null ); infos.addElement( info ); // fix for bug 4302884: Audio device is not released when AudioClip stops sequencer.addMetaEventListener(info); sequencer.start(); } /** * Open an audio channel. */ public synchronized void openChannel(InputStream in) { if(DEBUG) { System.out.println("AudioDevice: openChannel"); System.out.println("input stream =" + in); } Info info = null; // is this already playing? if so, then just return for(int i=0; i<infos.size(); i++) { info = (AudioDevice.Info)infos.elementAt(i); if( info.in == in ) { return; } } AudioInputStream as = null; if( in instanceof AudioStream ) { if ( ((AudioStream)in).midiformat != null ) { // it's a midi file try { startMidi( ((AudioStream)in).stream, in ); } catch (Exception e) { return; } } else if( ((AudioStream)in).ais != null ) { // it's sampled audio try { startSampled( ((AudioStream)in).ais, in ); } catch (Exception e) { return; } } } else if (in instanceof AudioDataStream ) { if (in instanceof ContinuousAudioDataStream) { try { AudioInputStream ais = new AudioInputStream(in, ((AudioDataStream)in).getAudioData().format, AudioSystem.NOT_SPECIFIED); startSampled(ais, in ); } catch (Exception e) { return; } } else { try { AudioInputStream ais = new AudioInputStream(in, ((AudioDataStream)in).getAudioData().format, ((AudioDataStream)in).getAudioData().buffer.length); startSampled(ais, in ); } catch (Exception e) { return; } } } else { BufferedInputStream bis = new BufferedInputStream( in, 1024 ); try { try { as = AudioSystem.getAudioInputStream(bis); } catch(IOException ioe) { return; } startSampled( as, in ); } catch( UnsupportedAudioFileException e ) { try { try { MidiFileFormat mff = MidiSystem.getMidiFileFormat( bis ); } catch(IOException ioe1) { return; } startMidi( bis, in ); } catch( InvalidMidiDataException e1 ) { // $$jb:08.01.99: adding this section to make some of our other // legacy classes work..... // not MIDI either, special case handling for all others AudioFormat defformat = new AudioFormat( AudioFormat.Encoding.ULAW, 8000, 8, 1, 1, 8000, true ); try { AudioInputStream defaif = new AudioInputStream( bis, defformat, AudioSystem.NOT_SPECIFIED); startSampled( defaif, in ); } catch (UnsupportedAudioFileException es) { return; } catch (LineUnavailableException es2) { return; } } catch( MidiUnavailableException e2 ) { // could not open sequence return; } } catch( LineUnavailableException e ) { return; } } // don't forget adjust for a new stream. notify(); } /** * Close an audio channel. */ public synchronized void closeChannel(InputStream in) { if(DEBUG) { System.out.println("AudioDevice.closeChannel"); } if (in == null) return; // can't go anywhere here! Info info; for(int i=0; i<infos.size(); i++) { info = (AudioDevice.Info)infos.elementAt(i); if( info.in == in ) { if( info.sequencer != null ) { info.sequencer.stop(); //info.sequencer.close(); infos.removeElement( info ); } else if( info.datapusher != null ) { info.datapusher.stop(); infos.removeElement( info ); } } } notify(); } /** * Open the device (done automatically) */ public synchronized void open() { // $$jb: 06.24.99: This is done on a per-stream // basis using the new JS API now. } /** * Close the device (done automatically) */ public synchronized void close() { // $$jb: 06.24.99: This is done on a per-stream // basis using the new JS API now. } /** * Play open audio stream(s) */ public void play() { // $$jb: 06.24.99: Holdover from old architechture ... // we now open/close the devices as needed on a per-stream // basis using the JavaSound API. if (DEBUG) { System.out.println("exiting play()"); } } /** * Close streams */ public synchronized void closeStreams() { Info info; for(int i=0; i<infos.size(); i++) { info = (AudioDevice.Info)infos.elementAt(i); if( info.sequencer != null ) { info.sequencer.stop(); info.sequencer.close(); infos.removeElement( info ); } else if( info.datapusher != null ) { info.datapusher.stop(); infos.removeElement( info ); } } if (DEBUG) { System.err.println("Audio Device: Streams all closed."); } // Empty the hash table. clipStreams = new Hashtable(); infos = new Vector(); } /** * Number of channels currently open. */ public int openChannels() { return infos.size(); } /** * Make the debug info print out. */ void setVerbose(boolean v) { DEBUG = v; } // INFO CLASS class Info implements MetaEventListener { Sequencer sequencer; InputStream in; DataPusher datapusher; Info( Sequencer sequencer, InputStream in, DataPusher datapusher ) { this.sequencer = sequencer; this.in = in; this.datapusher = datapusher; } public void meta(MetaMessage event) { if (event.getType() == 47 && sequencer != null) { sequencer.close(); } } } }
/*
* Copyright (c) 1999, 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.audio;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Enumeration;
import java.io.IOException;
import java.io.InputStream;
import java.io.BufferedInputStream;
import java.io.OutputStream;
import java.io.ByteArrayInputStream;
import javax.sound.sampled.*;
import javax.sound.midi.*;
import com.sun.media.sound.DataPusher;
import com.sun.media.sound.Toolkit;
/**
* This class provides an interface to the Headspace Audio engine through
* the Java Sound API.
*
* This class emulates systems with multiple audio channels, mixing
* multiple streams for the workstation's single-channel device.
*
* @see AudioData
* @see AudioDataStream
* @see AudioStream
* @see AudioStreamSequence
* @see ContinuousAudioDataStream
* @author David Rivas
* @author Kara Kytle
* @author Jan Borgersen
* @author Florian Bomers
*/
public class
AudioDevice {
private boolean DEBUG = false /*true*/ ;
/** Hashtable of audio clips / input streams. */
private Hashtable clipStreams;
private Vector infos;
/** Are we currently playing audio? */
private boolean playing = false;
/** Handle to the JS audio mixer. */
private Mixer mixer = null;
/**
* The default audio player. This audio player is initialized
* automatically.
*/
public static final AudioDevice device = new AudioDevice();
/**
* Create an AudioDevice instance.
*/
private AudioDevice() {
clipStreams = new Hashtable();
infos = new Vector();
}
private synchronized void startSampled( AudioInputStream as,
InputStream in ) throws UnsupportedAudioFileException,
LineUnavailableException {
Info info = null;
DataPusher datapusher = null;
DataLine.Info lineinfo = null;
SourceDataLine sourcedataline = null;
// if ALAW or ULAW, we must convert....
as = Toolkit.getPCMConvertedAudioInputStream(as);
if( as==null ) {
// could not convert
return;
}
lineinfo = new DataLine.Info(SourceDataLine.class,
as.getFormat());
if( !(AudioSystem.isLineSupported(lineinfo))) {
return;
}
sourcedataline = (SourceDataLine)AudioSystem.getLine(lineinfo);
datapusher = new DataPusher(sourcedataline, as);
info = new Info( null, in, datapusher );
infos.addElement( info );
datapusher.start();
}
private synchronized void startMidi( InputStream bis,
InputStream in ) throws InvalidMidiDataException,
MidiUnavailableException {
Sequencer sequencer = null;
Info info = null;
sequencer = MidiSystem.getSequencer( );
sequencer.open();
try {
sequencer.setSequence( bis );
} catch( IOException e ) {
throw new InvalidMidiDataException( e.getMessage() );
}
info = new Info( sequencer, in, null );
infos.addElement( info );
// fix for bug 4302884: Audio device is not released when AudioClip stops
sequencer.addMetaEventListener(info);
sequencer.start();
}
/**
* Open an audio channel.
*/
public synchronized void openChannel(InputStream in) {
if(DEBUG) {
System.out.println("AudioDevice: openChannel");
System.out.println("input stream =" + in);
}
Info info = null;
// is this already playing? if so, then just return
for(int i=0; i<infos.size(); i++) {
info = (AudioDevice.Info)infos.elementAt(i);
if( info.in == in ) {
return;
}
}
AudioInputStream as = null;
if( in instanceof AudioStream ) {
if ( ((AudioStream)in).midiformat != null ) {
// it's a midi file
try {
startMidi( ((AudioStream)in).stream, in );
} catch (Exception e) {
return;
}
} else if( ((AudioStream)in).ais != null ) {
// it's sampled audio
try {
startSampled( ((AudioStream)in).ais, in );
} catch (Exception e) {
return;
}
}
} else if (in instanceof AudioDataStream ) {
if (in instanceof ContinuousAudioDataStream) {
try {
AudioInputStream ais = new AudioInputStream(in,
((AudioDataStream)in).getAudioData().format,
AudioSystem.NOT_SPECIFIED);
startSampled(ais, in );
} catch (Exception e) {
return;
}
}
else {
try {
AudioInputStream ais = new AudioInputStream(in,
((AudioDataStream)in).getAudioData().format,
((AudioDataStream)in).getAudioData().buffer.length);
startSampled(ais, in );
} catch (Exception e) {
return;
}
}
} else {
BufferedInputStream bis = new BufferedInputStream( in, 1024 );
try {
try {
as = AudioSystem.getAudioInputStream(bis);
} catch(IOException ioe) {
return;
}
startSampled( as, in );
} catch( UnsupportedAudioFileException e ) {
try {
try {
MidiFileFormat mff =
MidiSystem.getMidiFileFormat( bis );
} catch(IOException ioe1) {
return;
}
startMidi( bis, in );
} catch( InvalidMidiDataException e1 ) {
// $$jb:08.01.99: adding this section to make some of our other
// legacy classes work.....
// not MIDI either, special case handling for all others
AudioFormat defformat = new AudioFormat( AudioFormat.Encoding.ULAW,
8000, 8, 1, 1, 8000, true );
try {
AudioInputStream defaif = new AudioInputStream( bis,
defformat, AudioSystem.NOT_SPECIFIED);
startSampled( defaif, in );
} catch (UnsupportedAudioFileException es) {
return;
} catch (LineUnavailableException es2) {
return;
}
} catch( MidiUnavailableException e2 ) {
// could not open sequence
return;
}
} catch( LineUnavailableException e ) {
return;
}
}
// don't forget adjust for a new stream.
notify();
}
/**
* Close an audio channel.
*/
public synchronized void closeChannel(InputStream in) {
if(DEBUG) {
System.out.println("AudioDevice.closeChannel");
}
if (in == null) return; // can't go anywhere here!
Info info;
for(int i=0; i<infos.size(); i++) {
info = (AudioDevice.Info)infos.elementAt(i);
if( info.in == in ) {
if( info.sequencer != null ) {
info.sequencer.stop();
//info.sequencer.close();
infos.removeElement( info );
} else if( info.datapusher != null ) {
info.datapusher.stop();
infos.removeElement( info );
}
}
}
notify();
}
/**
* Open the device (done automatically)
*/
public synchronized void open() {
// $$jb: 06.24.99: This is done on a per-stream
// basis using the new JS API now.
}
/**
* Close the device (done automatically)
*/
public synchronized void close() {
// $$jb: 06.24.99: This is done on a per-stream
// basis using the new JS API now.
}
/**
* Play open audio stream(s)
*/
public void play() {
// $$jb: 06.24.99: Holdover from old architechture ...
// we now open/close the devices as needed on a per-stream
// basis using the JavaSound API.
if (DEBUG) {
System.out.println("exiting play()");
}
}
/**
* Close streams
*/
public synchronized void closeStreams() {
Info info;
for(int i=0; i<infos.size(); i++) {
info = (AudioDevice.Info)infos.elementAt(i);
if( info.sequencer != null ) {
info.sequencer.stop();
info.sequencer.close();
infos.removeElement( info );
} else if( info.datapusher != null ) {
info.datapusher.stop();
infos.removeElement( info );
}
}
if (DEBUG) {
System.err.println("Audio Device: Streams all closed.");
}
// Empty the hash table.
clipStreams = new Hashtable();
infos = new Vector();
}
/**
* Number of channels currently open.
*/
public int openChannels() {
return infos.size();
}
/**
* Make the debug info print out.
*/
void setVerbose(boolean v) {
DEBUG = v;
}
// INFO CLASS
class Info implements MetaEventListener {
Sequencer sequencer;
InputStream in;
DataPusher datapusher;
Info( Sequencer sequencer, InputStream in, DataPusher datapusher ) {
this.sequencer = sequencer;
this.in = in;
this.datapusher = datapusher;
}
public void meta(MetaMessage event) {
if (event.getType() == 47 && sequencer != null) {
sequencer.close();
}
}
}
}