package tim.prune.function;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import tim.prune.App;
import tim.prune.GenericFunction;
import tim.prune.data.AudioClip;
/**
* Class to play the current audio clip
*/
public class PlayAudioFunction extends GenericFunction implements Runnable
{
/** Audio clip used for playing within java */
private Clip _clip = null;
/**
* Constructor
* @param inApp app object
*/
public PlayAudioFunction(App inApp) {
super(inApp);
}
/**
* @return name key
*/
public String getNameKey() {
return "function.playaudio";
}
/**
* Perform function
*/
public void begin()
{
// Launch new thread if clip isn't currently playing
if (_clip == null) {
new Thread(this).start();
}
}
/**
* Play the audio in a new thread
*/
public void run()
{
AudioClip audio = _app.getTrackInfo().getCurrentAudio();
File audioFile = audio.getFile();
boolean played = false;
if (audioFile != null && audioFile.exists() && audioFile.isFile() && audioFile.canRead())
{
// First choice is to play using java
played = playClip(audio);
// If this didn't work, then try to play the file another way
if (!played) {
played = playAudioFile(audioFile);
}
}
else if (audioFile == null && audio.getByteData() != null)
{
// Try to play audio clip using byte array
played = playClip(audio);
// If this didn't work, then need to copy the byte data to a file and play it from there
if (!played)
{
try
{
String suffix = getSuffix(audio.getName());
File tempFile = File.createTempFile("gpsaudio", suffix);
tempFile.deleteOnExit();
// Copy byte data to this file
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(tempFile));
bos.write(audio.getByteData(), 0, audio.getByteData().length);
bos.close();
played = playAudioFile(tempFile);
}
catch (IOException ignore) {
System.err.println("Error: " + ignore.getClass().getName() + " - " + ignore.getMessage());
}
}
}
if (!played)
{
// If still not worked, show error message
_app.showErrorMessage(getNameKey(), "error.playaudiofailed");
}
}
/**
* Try to play the sound file using built-in java libraries
* @param inAudio audio clip to play
* @return true if play was successful
*/
private boolean playClip(AudioClip inClip)
{
boolean success = false;
AudioInputStream audioInputStream = null;
_clip = null;
try
{
if (inClip.getFile() != null)
audioInputStream = AudioSystem.getAudioInputStream(inClip.getFile());
else if (inClip.getByteData() != null)
audioInputStream = AudioSystem.getAudioInputStream(new ByteArrayInputStream(inClip.getByteData()));
else return false;
_clip = AudioSystem.getClip();
_clip.open(audioInputStream);
// play the clip
_clip.start();
_clip.drain();
success = true;
} catch (Exception e) {
System.err.println(e.getClass().getName() + " - " + e.getMessage());
} finally {
// close the stream to clean up
try {
_clip.close();
audioInputStream.close();
} catch (Exception e) {}
_clip = null;
}
return success;
}
/**
* Try to play the specified audio file
* @param inFile file to play
* @return true if play was successful
*/
private boolean playAudioFile(File inFile)
{
boolean played = false;
// Try the Desktop library from java 6, if available
if (!played)
{
try
{
Class<?> d = Class.forName("java.awt.Desktop");
d.getDeclaredMethod("open", new Class[] {File.class}).invoke(
d.getDeclaredMethod("getDesktop").invoke(null), new Object[] {inFile});
//above code mimics: Desktop.getDesktop().open(audioFile);
played = true;
}
catch (InvocationTargetException e) {
System.err.println("ITE: " + e.getCause().getClass().getName() + " - " + e.getCause().getMessage());
played = false;
}
catch (Exception ignore) {
System.err.println(ignore.getClass().getName() + " - " + ignore.getMessage());
played = false;
}
}
// If the Desktop call failed, need to try backup methods
if (!played)
{
// If system looks like a Mac, try the open command
String osName = System.getProperty("os.name").toLowerCase();
boolean isMacOsx = osName.indexOf("mac os") >= 0 || osName.indexOf("darwin") >= 0;
if (isMacOsx)
{
String[] command = new String[] {"open", inFile.getAbsolutePath()};
try {
Runtime.getRuntime().exec(command);
played = true;
}
catch (IOException ioe) {}
}
}
return played;
}
/**
* Try to stop a currently playing clip
*/
public void stopClip()
{
if (_clip != null && _clip.isActive()) {
try {
_clip.stop();
_clip.flush();
}
catch (Exception e) {}
}
}
/**
* @return percentage of clip currently played, or -1 if not playing
*/
public int getPercentage()
{
int percent = -1;
if (_clip != null && _clip.isActive())
{
long clipLen = _clip.getMicrosecondLength();
if (clipLen > 0) {
percent = (int) (_clip.getMicrosecondPosition() * 100.0 / clipLen);
}
}
return percent;
}
/**
* @param inName name of audio file
* @return suffix (rest of name after the dot) - expect mp3, wav, ogg
*/
private static final String getSuffix(String inName)
{
if (inName == null || inName.equals("")) {return ".tmp";}
final int dotPos = inName.lastIndexOf('.');
if (dotPos < 0) {return inName;} // no dot found
return inName.substring(dotPos);
}
}