I am currently doing a simple morse code translator and I want my code to play a long beep on '-' and a short beep on '.', and of course this is to be done after one another. But my program is currently playing it all at once. What am I doing wrong?
I have tried adding an onCompletionListener but Netbeans 8.1 gives an error and cant resolve the problem.
//Play the morse code
public void playButton(ActionEvent event){
String morse = mcTextField.getText();
char[] charArray = morse.toCharArray();
for(int x = 0; x<charArray.length; x++){
if(charArray[x] == '.'){
playBeep(shortBeep);
}
else if(charArray[x] == '-'){
playBeep(longBeep);
}
else{
sleep(500);
}
sleep(1000);
}
}
private void playBeep(String name){
String musicFile = name;
Media sound = new Media(new File(musicFile).toURI().toString());
MediaPlayer mediaPlayer = new MediaPlayer(sound);
mediaPlayer.play();
sleep(1000);
}
private void sleep(int duration){
try{
Thread.sleep(duration);
}catch(Exception e){
System.out.println("Did not work."+e);
}
}
This make use of the Java Sound API, but the basic idea should be feasible for most audio libraries, so long as the provide "end of play back" notification.
All this does is, places the sequence you want to play into a queue, it plays the next tone, wait till it completes and plays the next, until there are no more tones in the queue.
Because AudioClip
plays in it's own thread, this uses another Thread
to allow the example to "wait for" the sequence to play out fully. If you're doing this from a UI framework, then you could get away without the additional overhead.
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
public class Test {
public static void main(String[] args) throws InterruptedException {
try {
new Test();
} catch (UnsupportedAudioFileException | IOException | LineUnavailableException ex) {
ex.printStackTrace();
}
}
public Test() throws UnsupportedAudioFileException, IOException, LineUnavailableException {
Sound longBeep = new Sound(new File(...));
Sound shortBeep = new Sound(new File(...));
List<Sound> message = new ArrayList<Sound>(9);
message.add(shortBeep);
message.add(shortBeep);
message.add(shortBeep);
message.add(longBeep);
message.add(longBeep);
message.add(longBeep);
message.add(shortBeep);
message.add(shortBeep);
message.add(shortBeep);
play(message);
}
public void play(List<Sound> message) {
try {
List<Sound> queue = new ArrayList<>(message);
Transmit transmit = new Transmit(message);
Thread thread = new Thread(transmit);
thread.start();
transmit.waitFor();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
public class Transmit implements Runnable {
private List<Sound> queue;
private ReentrantLock lock;
private Condition playCondition;
public Transmit(List<Sound> message) {
this.queue = new ArrayList<>(message);
lock = new ReentrantLock();
playCondition = lock.newCondition();
}
@Override
public void run() {
playNext();
}
public void waitFor() throws InterruptedException {
lock.lock();
if (!queue.isEmpty()) {
try {
playCondition.await();
} finally {
lock.unlock();
}
} else {
lock.unlock();
}
}
protected void playNext() {
if (queue.size() > 0) {
lock.lock();
try {
System.out.println("Play Next");
Sound sound = queue.remove(0);
sound.addLineListener(new LineListener() {
@Override
public void update(LineEvent event) {
if (event.getType().equals(LineEvent.Type.STOP)) {
sound.removeLineListener(this);
System.out.println("Audio Completed");
playNext();
}
}
});
sound.play();
} finally {
lock.unlock();
}
} else {
lock.lock();
try {
playCondition.signalAll();
} finally {
lock.unlock();
}
}
}
}
public class Sound {
private Clip audio;
public Sound(AudioInputStream audioInputStream) throws LineUnavailableException, IOException {
audio = AudioSystem.getClip();
audio.open(audioInputStream);
}
public Sound(File file) throws UnsupportedAudioFileException, IOException, LineUnavailableException {
this(AudioSystem.getAudioInputStream(file));
}
public Sound(URL url) throws UnsupportedAudioFileException, IOException, LineUnavailableException {
this(AudioSystem.getAudioInputStream(url));
}
public Sound(InputStream stream) throws UnsupportedAudioFileException, IOException, LineUnavailableException {
this(AudioSystem.getAudioInputStream(stream));
}
public void close() {
Objects.requireNonNull(audio, "Audio Clip has not been initalised");
audio.close();
}
public void play() {
Objects.requireNonNull(audio, "Audio Clip has not been initalised");
audio.setFramePosition(0);
audio.start();
}
public void addLineListener(LineListener listener) {
Objects.requireNonNull(audio, "Audio Clip has not been initalised");
audio.addLineListener(listener);
}
public void removeLineListener(LineListener listener) {
Objects.requireNonNull(audio, "Audio Clip has not been initalised");
audio.removeLineListener(listener);
}
}
}
If you want to know when the sequence completes, without going to trouble of using the waitFor
method, you wouldn't be a difficult thing to add an observer into for the Transmit
class which generate a "completed sequence" event, but I'll leave that up to you to implement