3

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);
    }
}
Dihre
  • 43
  • 4
  • I'd place all the audio files into a `List`, I'd then use `LineListener` to monitor for when the line stops, pop off the next audio and play it, till you run out of audio to play - [This example](https://stackoverflow.com/questions/34758718/panel-fails-to-show-up-concurrently-with-sound-file-in-java-swing/34758778#34758778) demonstrates at least the use of the `LineListener` – MadProgrammer Jun 06 '17 at 02:12
  • I'd also be VERY weary about using `Thread.sleep` from within the context of any GUIs main thread – MadProgrammer Jun 06 '17 at 02:15

1 Answers1

1

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

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • for some reason the program can't find the audio file. It says there is no such file in the directory but it is there and it the same directory worked with the old code. – Dihre Jun 06 '17 at 17:42
  • Well, I tested the code by placing the audio in the current working directory. Try using `File#exists` to check that the file is where you think it is – MadProgrammer Jun 06 '17 at 19:49
  • It works now! The audio file needs to be in .wav format, .mp3 does not seem to work. Thanks a lot for the help @MadProgrammer – Dihre Jun 07 '17 at 19:04