-2

Can someone please help me understand why this code below doesn't work?

I start the clip by calling method start(). This method creates a new thread for the clip to run. However, no it doesn't seem to play anything.

The code is compiled without any error...

public class Audio
{
    private Clip clip;

    private Thread thread; 

    public Audio (String audioFile)
    {
        AudioInputStream audioStream = null;

        URL audioURL = this.getClass().getClassLoader().getResource(audioFile);

        // Obtain audio input stream from the audio file and load the information
        // into main memory using the URL path retrieved from above.
        try { audioStream = AudioSystem.getAudioInputStream(audioURL); }
        catch (Exception e)
        {
            e.printStackTrace();
            System.exit(1);
        }

        try
        {
            // Retrieve the object of class Clip from the Data Line.
            this.clip = AudioSystem.getClip();

            // Load the audio input stream into memory for future play-back.
            this.clip.open(audioStream);
        }
        catch (LineUnavailableException e)
        {
            e.printStackTrace();
            System.exit(1);
        }
        catch (IOException e)
        {
            e.printStackTrace();
            System.exit(1);
        }
    }

    public void start()
    {
        Runnable r = new Runnable() {
            public void run()
            {
                loop();
            }
        };
        thread = new Thread(r);
        thread.start();
    }

    public void loop ()
    {       
        // Rewind the media to the beginning of the clip.
        this.clip.setFramePosition(0);

        // Continuously play the clip.
        this.clip.loop(Clip.LOOP_CONTINUOUSLY);

        try
        {
            Thread.sleep(5000);
        } catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }

UPDATE

I found the problem! The problem is because of the audio file. I used a different audio file and I can hear the sound with the code above.

It is just really annoying that the code compiled without any error or warning. I detected the problem by getting the audio format, then pass it to an object of class DataLine.Info. Then, retrieve the clip from the Data Line.

So, basically instead of getting the clip by:

this.clip = AudioSystem.getClip();

I would get the clip by:

AudioFormat format = audioStream.getFormat();
DataLine.Info info = new DataLine.Info(Clip.class, format);
this.clip = (Clip) AudioSystem.getLine(info);

When I compiled with this, Java threw below error:

No line matching interface Clip supporting format PCM_SIGNED 48000.0 Hz, 24 bit

So, I replaced the audio file, and it worked !

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
Crazed Hermit
  • 11
  • 1
  • 3
  • API shows inherited start method, have you tried it (as in this.clip.start)? – SleuthEye Jun 10 '14 at 01:54
  • what happens if you try it all in one thread? – Markovian8261 Jun 10 '14 at 02:04
  • Answer your questions: 1. This is not an Applet. I create an object of this class in a different class. 2. I tried clip.start(), but that didn't work. 3. Starting the clip on one thread also doesn't work. I read several articles about this, and they all suggest running clip.loop() on a separate thread. – Crazed Hermit Jun 11 '14 at 00:27
  • I found the problem. Read my new comments above. – Crazed Hermit Jun 14 '14 at 02:33

1 Answers1

1

clip.loop is a non-blocking call. That is, once you call it (and it does what it does), it will return, this means your thread will exit and, unless there is another non-daemon thread running, the JVM will exit.

I had thought you might be able to use Clip#drain to cause it to block until the clip had completed, but technically, the clip won't complete...in the normal sense.

Instead, I set up my own loop...

public void start() {
    Runnable r = new Runnable() {
    public void run() {
        while (true) {
            clip.setFramePosition(0);
            clip.start();
            clip.drain();
        }
    }
    };
    thread = new Thread(r);
    thread.start();
}

Now, this could be an issue, because the Thread is a non-daemon thread and will never end...Instead of while (true) { you should setup some kind volitle flag which you set to false and help terminate the loop...

For example...

import java.io.IOException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineUnavailableException;

public class Audio {

    private Clip clip;

    private Thread thread;
    private volatile boolean keepPlaying = true;

    public static void main(String[] args) {
        Audio audio = new Audio("Kalimba.wav");
        audio.start();

        try {
            Thread.sleep(5000);
        } catch (InterruptedException ex) {
        }
        audio.stop();
    }

    public Audio(String audioFile) {
        AudioInputStream audioStream = null;

        URL audioURL = this.getClass().getClassLoader().getResource(audioFile);

        // Obtain audio input stream from the audio file and load the information
        // into main memory using the URL path retrieved from above.
        try {
            audioStream = AudioSystem.getAudioInputStream(audioURL);
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }

        try {
            // Retrieve the object of class Clip from the Data Line.
            this.clip = AudioSystem.getClip();

            // Load the audio input stream into memory for future play-back.
            this.clip.open(audioStream);
        } catch (LineUnavailableException e) {
            e.printStackTrace();
            System.exit(1);
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }
    }

    public void stop() {
        if (thread != null) {

            keepPlaying = false;
            clip.stop();
            thread.interrupt();

        }
    }

    public void start() {
        Runnable r = new Runnable() {
            public void run() {
                while (keepPlaying) {
                    clip.setFramePosition(0);
                    clip.start();
                    clip.drain();
                }
            }
        };
        thread = new Thread(r);
        thread.start();
    }
}

Updated

There a few things wrong with the above example (IMHO), which can be fixed with a simple object monitor.

So, instead of a volatile flag and a while loop, we can use the Clip#loop functionality and simply use Object#wait and Object#notify instead, for example

import java.io.IOException;
import java.net.URL;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineUnavailableException;

public class Audio {

    private Clip clip;

    private Thread thread;
    private final Object loopLock = new Object();

    public static void main(String[] args) {
        Audio audio = new Audio("Music.wav");
        audio.start();

        try {
            Thread.sleep(5000);
        } catch (InterruptedException ex) {
        }
        audio.stop();
    }

    public Audio(String audioFile) {
        AudioInputStream audioStream = null;

        URL audioURL = this.getClass().getClassLoader().getResource(audioFile);

        // Obtain audio input stream from the audio file and load the information
        // into main memory using the URL path retrieved from above.
        try {
            audioStream = AudioSystem.getAudioInputStream(audioURL);
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }

        try {
            // Retrieve the object of class Clip from the Data Line.
            this.clip = AudioSystem.getClip();

            // Load the audio input stream into memory for future play-back.
            this.clip.open(audioStream);
        } catch (LineUnavailableException e) {
            e.printStackTrace();
            System.exit(1);
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }
    }

    public void stop() {
        synchronized (loopLock) {
            loopLock.notifyAll();
        }
    }

    public void start() {
        Runnable r = new Runnable() {
            public void run() {
                clip.setFramePosition(0);
                clip.loop(Clip.LOOP_CONTINUOUSLY);
                synchronized (loopLock) {
                    try {
                        loopLock.wait();
                    } catch (InterruptedException ex) {
                    }
                }
                clip.stop();
            }
        };
        thread = new Thread(r);
        thread.start();
    }
}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • MadProgrammer, thanks for your reply. I know that clip.loop() starts a daemon thread. That is the reason why I put this command: Thread.sleep(5000); right after calling clip.loop() so that the non-daemon thread stays alive. – Crazed Hermit Jun 11 '14 at 00:30
  • I tried your suggestions of creating a while loop and using clip.drain(). However, that didn't work either. I am starting to suspect this is an environment issue. I am using Eclipse IDE on a MacBook Pro. Does that make any difference? Can you think of anything else I can try? – Crazed Hermit Jun 11 '14 at 00:31
  • If the sound is playing, I can't see any reason why the above code should fail (all be it not the absolutely "best" solution) – MadProgrammer Jun 11 '14 at 00:36
  • I added a "better" solution, which uses a object monitor lock instead of the `while-loop`. I used this to play a 50 second clip and using a 2 minute delay (instead of 5 seconds ;)), was able to hear it play 2 and a bit times – MadProgrammer Jun 11 '14 at 00:50
  • I finally found the problem!!! It is because of the audio file. The code is correct. However, I uses an unsupported audio file. It is just really annoying that the code compiled without any error or warning. Using a different .wav file, I hears the sound. – Crazed Hermit Jun 14 '14 at 02:24