/*
* Copyright (C) 2014, David Verhaak
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package edu.atlas.games.media;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sound.sampled.*;
import edu.atlas.games.Game;
/**
*
* @todo Write documentation
* @author David Verhaak <david@forcez.nl>
* @since 0.2
*/
public class AudioPlayer
{
private int bufferSize = 4096;
private volatile boolean paused = false;
private final Object lock = new Object();
private SourceDataLine line;
/**
*
* @todo Write documentation
* @since 0.2
*/
public void stop()
{
if (line != null)
{
line.stop();
line.close();
}
}
/**
*
* @todo Write documentation
* @return
* @since 0.2
*/
public boolean isPaused()
{
return this.paused;
}
/**
*
* @todo Write documentation
* @since 0.2
*/
public void pause()
{
if (!this.isPaused())
{
paused = true;
}
}
/**
*
* @todo Write documentation
* @since 0.2
*/
public void resume()
{
if (this.isPaused())
{
synchronized (lock)
{
lock.notifyAll();
paused = false;
}
}
}
/**
*
* @todo Write documentation
* @param song
* @since 0.2
*/
public void play(Song song)
{
new Thread(Game.THREAD_GROUP, new AudioExecutor(song), song.getName()).start();
}
private void playFile(Song song) throws UnsupportedAudioFileException, IOException, LineUnavailableException, InterruptedException
{
AudioInputStream encoded = AudioSystem.getAudioInputStream(song.getSource());
AudioFormat encodedFormat = encoded.getFormat();
AudioFormat decodedFormat = this.getDecodedFormat(encodedFormat);
Long duration = null;
AudioInputStream currentDecoded = AudioSystem.getAudioInputStream(decodedFormat, encoded);
line = AudioSystem.getSourceDataLine(decodedFormat);
line.open(decodedFormat);
line.start();
byte[] b = new byte[this.bufferSize];
int i = 0;
duration = song.getDuration().getTotalMilliseconds();
synchronized (lock)
{
while (true)
{
if (paused == true)
{
line.stop();
lock.wait();
line.start();
}
i = currentDecoded.read(b, 0, b.length);
if (i == -1)
{
break;
}
line.write(b, 0, i);
}
}
line.drain();
line.stop();
line.close();
currentDecoded.close();
encoded.close();
}
/**
*
* @todo Write documentation
* @param value
* @since 0.2
*/
public void setVolume(double value)
{
if (!line.isOpen())
{
return;
}
value = (value <= 0.0) ? 0.0001 : ((value > 1.0) ? 1.0 : value);
try
{
float dB = (float) (Math.log(value) / Math.log(10.0) * 20.0);
((FloatControl) line.getControl(FloatControl.Type.MASTER_GAIN)).setValue(dB);
} catch (Exception exception)
{
Logger.getLogger(AudioPlayer.class.getName()).log(Level.SEVERE, null, exception);
}
}
/**
*
* @todo Write documentation
* @return
* @since 0.2
*/
public boolean isPlaying()
{
return (line != null && line.isOpen());
}
/**
*
* @todo Write documentation
* @param format
* @return
* @since 0.2
*/
protected AudioFormat getDecodedFormat(AudioFormat format)
{
return new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, format.getSampleRate(), 16, format.getChannels(), format.getChannels() * 2, format.getSampleRate(), false);
}
/**
*
* @todo Write documentation
* @return
* @since 0.2
*/
public int getBufferSize()
{
return bufferSize;
}
/**
*
* @todo Write documentation
* @param bufferSize
* @since 0.2
*/
public void setBufferSize(int bufferSize)
{
if (bufferSize <= 0)
{
return;
}
this.bufferSize = bufferSize;
}
/**
*
* @todo Write documentation
*/
private class AudioExecutor implements Runnable
{
private Song song;
/**
*
* @todo Write documentation
* @param song
* @since 0.2
*/
public AudioExecutor(Song song)
{
super();
this.song = song;
}
/**
*
* @todo Write documentation
* @since 0.2
*/
@Override
public void run()
{
try
{
new AudioPlayer().playFile(song);
} catch (UnsupportedAudioFileException | IOException | LineUnavailableException | InterruptedException ex)
{
Logger.getLogger(AudioPlayer.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}