package com.studiofortress.sf.audio;
import com.studiofortress.sf.util.Repeater;
import java.io.IOException;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
/**
*
* @author Joseph Lenton - JosephLenton@StudioFortress.com
*/
class InnerAudioPlayer extends Repeater
{
private static final int DEFAULT_VOLUME = 90;
private static final int DEFAULT_CHUNK_SIZE = 512*4;
private static SourceDataLine newSourceDataLine(AudioFormat format)
{
try {
final DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
final SourceDataLine newDataLine = (SourceDataLine) AudioSystem.getLine(info);
newDataLine.open( format );
return newDataLine;
} catch (LineUnavailableException ex) {
throw new RuntimeException( ex );
}
}
private boolean paused;
private boolean playing;
private boolean repeat;
private int volume;
private final AudioStreamFactory factory;
private final byte[] tempBuffer;
private AudioInputStream stream;
private SourceDataLine sourceDataLine;
public InnerAudioPlayer(AudioStreamFactory factory)
{
this.factory = factory;
this.tempBuffer = new byte[DEFAULT_CHUNK_SIZE];
this.stream = null;
this.sourceDataLine = null;
this.playing = false;
this.paused = false;
this.repeat = false;
this.volume = DEFAULT_VOLUME;
}
public boolean isPaused()
{
return paused;
}
public boolean isPlaying()
{
return playing;
}
public synchronized void pause()
{
if ( isPlaying() ) {
paused = true;
sourceDataLine.stop();
}
}
public synchronized void play()
{
if ( !isPlaying() ) {
this.notify();
if (sourceDataLine == null) {
stream = factory.newStream();
sourceDataLine = newSourceDataLine( stream.getFormat() );
setVolume( volume );
}
sourceDataLine.start();
paused = false;
playing = true;
}
}
public synchronized void stopPlaying()
{
if ( isPlaying() || isPaused() ) {
sourceDataLine.drain();
sourceDataLine.close();
sourceDataLine = null;
try {
stream.close();
stream = null;
} catch (IOException ex) { }
paused = false;
playing = false;
}
}
public void setAudioRepeating(boolean repeat)
{
this.repeat = repeat;
}
public boolean isAudioRepeating()
{
return repeat;
}
public synchronized void setVolume(int volume)
{
this.volume = volume;
final FloatControl volumeControl = getVolumeControl();
if (volumeControl != null) {
final float minimum = volumeControl.getMinimum();
final float diff = volumeControl.getMaximum() - minimum;
volume = Math.round(((volume / 100.0f) * diff) + minimum);
volumeControl.setValue( volume );
}
}
public int getVolume()
{
return volume;
}
/**
* @return The current volume this is really playing at, according to the data line.
*/
private int getDataLineVolume()
{
final FloatControl volumeControl = getVolumeControl();
final float minimum = volumeControl.getMinimum();
final float diff = volumeControl.getMaximum() - minimum;
return Math.round(((volumeControl.getValue() - minimum) / diff) * 100.0f);
}
/**
*
* @return A FloatControl that can be used for volume usage.
*/
private FloatControl getVolumeControl()
{
if (sourceDataLine != null) {
if (sourceDataLine.isControlSupported(FloatControl.Type.VOLUME)) {
return (FloatControl)sourceDataLine.getControl(FloatControl.Type.VOLUME);
} else if (sourceDataLine.isControlSupported(FloatControl.Type.MASTER_GAIN)) {
return (FloatControl)sourceDataLine.getControl(FloatControl.Type.MASTER_GAIN);
}
}
return null;
}
@Override
public synchronized void repeat()
{
try {
if (isPlaying()) {
final int read = stream.read( tempBuffer );
if (read < 0) {
stopPlaying();
if (isAudioRepeating()) {
play();
}
} else if (read > 0) {
sourceDataLine.write( tempBuffer, 0, read );
}
} else {
this.wait();
}
} catch (IOException ex) {
ex.printStackTrace();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
|