/**
* MediaFrame is an Open Source streaming media platform in Java
* which provides a fast, easy to implement and extremely small applet
* that enables to view your audio/video content without having
* to rely on external player applications or bulky plug-ins.
*
* Copyright (C) 2004/5 MediaFrame (http://www.mediaframe.org).
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
package mediaframe.mpeg4.audio;
import java.io.InterruptedIOException;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.BooleanControl;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
/**
* The <code>Java2AudioDevice</code> class realizes the sound device (implements
* the <code>AudioDevice</code> interface) through Java2 Sound API.
*/
public final class Java2AudioDevice implements AudioDevice {
/** Constant, the size of the inner buffer with audio samples. */
public final int BUFFER_SIZE = 102400; // 100K
/** The audio line to output the audio data. */
private SourceDataLine sourceDataLine = null;
/** The current volume of the player. */
private int volume = 100;
/** <tt>True</tt>, if the audio device plays the audio. */
private boolean playing = false;
/** The mute status of the audio device (equals <tt>true</tt> if no audio). */
private boolean mute = false;
/** <tt>True</tt>, if the audio device is opened. */
private boolean opened = false;
/**
* Constructs an <code>Java2AudioDevice</code> object and checks if a free
* audio channel is exist.
*
* @throws Exception
* raises if there is an error occurs (in most cases if no free
* audio channels have been found).
*/
public Java2AudioDevice() throws Exception {
super();
SourceDataLine sourceDataLine = getSourceDataLine(new AudioFormat(
44100, 16, 2, true, false));
sourceDataLine.open();
sourceDataLine.close();
}
/**
* Gets an available audio channel (<code>SourceDataLine</code>) for
* specified audio stream's format.
*
* @param format
* the format of the audio stream.
* @return the available audio channel.
* @throws LineUnavailableException
* raises if no available audio channels have been found for the
* specified format.
*/
private SourceDataLine getSourceDataLine(AudioFormat format)
throws LineUnavailableException {
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
if (!AudioSystem.isLineSupported(info)) {
throw new LineUnavailableException(
"Unable to find the Java 2 audio channel");
}
return (SourceDataLine) AudioSystem.getLine(info);
}
/**
* Starts the playback of the audio stream.
*/
public void play() {
playing = true;
if (sourceDataLine != null) {
sourceDataLine.start();
}
synchronized (this) {
notifyAll();
}
}
/**
* Opens the output audio channel and initializes it with the specified
* sample frequency and the number of channels of the audio stream.
*
* @param sampleFrequency
* the sample frequency of the audio stream.
* @param channelCount
* the number of channels of the audio stream.
*/
public void open(int sampleFrequency, int channelCount) {
try {
if (sourceDataLine == null) {
AudioFormat format = new AudioFormat(sampleFrequency, 16,
channelCount, true, false);
sourceDataLine = getSourceDataLine(format);
sourceDataLine.open(format, BUFFER_SIZE);
opened = true;
setMute(mute);
setVolume(volume);
if (playing) {
sourceDataLine.start();
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
/**
* Writes the next portion of audio samples into the audio device.
*
* @param buffer
* the array with the audio samples' data.
* @param size
* the size of the audio samples' data.
* @throws InterruptedIOException
* raises if the current thread has been interrupted.
*/
public void write(byte[] buffer, int size) throws InterruptedIOException {
try {
sourceDataLine.write(buffer, 0, size);
synchronized (this) {
while (playing == false) {
wait();
}
}
} catch (InterruptedException ex) {
throw new InterruptedIOException(ex.getMessage());
} catch (Exception ex) {
ex.printStackTrace();
}
}
/**
* Pauses the playback of the audio stream.
*/
public void pause() {
playing = false;
if (sourceDataLine != null) {
sourceDataLine.stop();
}
}
/**
* Closes the audio device.
*/
public void close() {
if (sourceDataLine != null) {
sourceDataLine.close();
sourceDataLine = null;
opened = false;
}
}
/**
* Sets the mute state of the audio device.
*
* @param mute
* the mute state to set.
*/
public void setMute(boolean mute) {
this.mute = mute;
if (sourceDataLine != null) {
BooleanControl muteCtrl = (BooleanControl) sourceDataLine
.getControl(BooleanControl.Type.MUTE);
muteCtrl.setValue(mute);
if (!mute) {
setVolume(volume);
}
}
}
/**
* Sets the volume of the audio stream.
*
* @param volume
* the volume to set.
*/
public void setVolume(int volume) {
this.volume = volume;
if ((sourceDataLine != null) && !mute) {
FloatControl volumeCtrl = (FloatControl) sourceDataLine
.getControl(FloatControl.Type.MASTER_GAIN);
float GainDb = (float) (20.d * Math
.log(volume == 0 ? Double.MIN_VALUE
: ((double) volume / 100.d)) / Math.log(10));
volumeCtrl.setValue(GainDb);
}
}
/**
* Returns <tt>true</tt>, if the audio device is opened, <tt>false</tt>
* otherwise.
*/
public boolean isOpened() {
return opened;
}
/**
* Returns <tt>true</tt>, if the audio device is ready to play the audio
* stream, <tt>false</tt> otherwise.
*/
public boolean isReady() {
return opened;
}
}