/**
* Copyright 2011 The ForPlay Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package forplay.java;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineEvent.Type;
import javax.sound.sampled.LineListener;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import forplay.core.Asserts;
import forplay.core.ForPlay;
import forplay.core.Sound;
class JavaSound implements Sound {
private Clip clip;
private boolean looping;
private boolean playing;
private InputStream inputStream;
private final File file;
public JavaSound(File file) {
this.file = file;
try {
clip = AudioSystem.getClip();
} catch (LineUnavailableException e) {
ForPlay.log().warn("Unable to create clip for " + file);
// give up
return;
} catch (IllegalArgumentException e) {
/*
* OpenJDK on Linux may throw java.lang.IllegalArgumentException: No line matching interface
* Clip supporting format PCM_SIGNED unknown sample rate, 16 bit, stereo, 4 bytes/frame,
* big-endian is supported.
*/
ForPlay.log().info("Failed to load sound " + file + " due to " + e.toString());
// give up
return;
}
clip.addLineListener(new LineListener() {
@Override
public void update(LineEvent event) {
Type type = event.getType();
if (LineEvent.Type.STOP == type) {
// ForPlay.log().info("STOP EVENT");
try {
clip.close();
} finally {
playing = false;
}
}
}
});
}
@Override
public boolean play() {
// ForPlay.log().info("play()");
if (playing) {
// we have not yet received LineEvent.Type.STOP
return false;
}
if (clip == null) {
// no audio clip to play
return false;
}
if (clip.isActive()) {
// already playing
return false;
}
try {
inputStream = new FileInputStream(file);
} catch (FileNotFoundException e) {
ForPlay.log().warn("Sound file not found " + file);
// give up
return false;
}
AudioInputStream ais;
try {
// ForPlay.log().info("calling AudioSystem.getAudioInputStream()");
ais = AudioSystem.getAudioInputStream(inputStream);
} catch (UnsupportedAudioFileException e) {
ForPlay.log().warn(
"Failed to play sound " + file + " due to failure to get audio stream caused by "
+ e.toString(), e);
return false;
} catch (IOException e) {
ForPlay.log().warn(
"Failed to play sound " + file + " due to failure to get audio stream caused by "
+ e.toString(), e);
return false;
}
try {
// ForPlay.log().info("calling clip.open()");
clip.open(ais);
} catch (IOException e) {
ForPlay.log().warn(
"Failed to play sound " + file + " due to failure to open clip caused by " + e.toString(),
e);
return false;
} catch (LineUnavailableException e) {
ForPlay.log().info(
"Not playing sound " + file + " due to failure to open clip caused by " + e.toString());
} catch (IllegalArgumentException e) {
ForPlay.log().info(
"Not playing sound " + file + " due to failure to open clip caused by " + e.toString());
return false;
} catch (IllegalStateException e) {
// Line may already be open
// TODO(fredsa): figure out why this happens
ForPlay.log().info(
"Not playing sound " + file + " due to failure to open clip caused by " + e.toString());
return false;
}
if (looping) {
clip.loop(Clip.LOOP_CONTINUOUSLY);
}
// ForPlay.log().info("calling clip.start()");
clip.start();
playing = true;
return true;
}
@Override
public void stop() {
// ForPlay.log().info("stop()");
if (clip == null) {
// no audio clip to stop
return;
}
if (!clip.isActive()) {
// clip is not playing
return;
}
// ForPlay.log().info("Calling clip.stop()");
clip.stop();
// ForPlay.log().info("Calling clip.flush()");
clip.flush();
}
@Override
public void setLooping(boolean looping) {
this.looping = looping;
}
@Override
public void setVolume(float volume) {
Asserts.checkArgument(0f <= volume && volume <= 1f, "Must ensure 0f <= volume <= 1f");
// TODO implement
}
@Override
public boolean isPlaying() {
return playing;
}
}