Can anyone point me in the right direction as to why this code will not play this audio clip continuously? It plays it once and stops.
final Clip clip = AudioSystem.getClip();
final AudioInputStream inputStream = AudioSystem.getAudioInputStream(new File("Alarm_Police.wav"));
clip.open(inputStream);
clip.loop(Clip.LOOP_CONTINUOUSLY);
If you are running a bigger application, this answer may not apply. But for a simple test with only that piece of code, this may help:
Clip.loop() starts it's own thread, but that thread will not keep the JVM alive. So to make it work, make sure the clip is not the only thread.
If I leave out Thread.sleep(..) from this snippet, I get the same issue as you;
import java.io.File;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
public class Snippet {
public static void main(String[] args) throws Exception {
AudioInputStream inputStream = AudioSystem.getAudioInputStream(new File("notify.wav"));
Clip clip = AudioSystem.getClip();
clip.open(inputStream);
clip.loop(Clip.LOOP_CONTINUOUSLY);
Thread.sleep(10000); // looping as long as this thread is alive
}
}
In order to play the complete audio file you would need to know the length of time to sleep. An alternative would be to:
while(clip.isRunning())
{
Thread.sleep(100);
}
This keeps sleeping (in 100ms increments) until the isRunning() state has turned to false.
You may need an initial sleep before this loop so that the isRunning() state has time to set.
My audio file contains 20 seconds of an alarm beep. I need it continuously to ring. Instead of using thread, i went on with the piece of code shown below.
while(true){
clip.start();
clip.loop(clip.LOOP_CONTINUOUSLY);
}
Think this would help. Thanks.
Related
I have a game where sound effects are played using this method:
AudioInputStream inputStream = AudioSystem.getAudioInputStream(
TheGame.class.getResource(url));
Clip clip = AudioSystem.getClip();
clip.open(inputStream);
clip.start();
However, not closing the clips obviously builds up memory, and the fixes I've tried cause the game to freeze up occasionally.
I tried :
creating a new LineListener for every single clip that closes the clip when it stops. freezes up game OFTEN.
Having one LineListener that listens to every clip that closes them when they stop. freezes often as well.
Same as #2, but dividing the clips among multiple LineListeners, I've tried 4, 6, and 10, and all freeze up the game at least once.
Never closing the clips, eventually the game will stop playing sounds and freeze up
setting the clips to null as soon as they're opened, same result as #4
I'm not sure why closing clips seems to take so much time that it freezes up the entire game, is there a more time-efficient way?
Here is the code for the LineListener's update method I'm using:
#Override
public void update(LineEvent event) {
if (event.getType() == LineEvent.Type.STOP){
Clip c = (Clip) event.getSource();
c.close();
c.removeLineListener(this);
}
}
As per my comment above, you should reuse the existing Clip object.
Map<String, Clip> clips = new HashMap<String, Clip>();
public synchronized void play(String url) {
Clip clip = clips.get(url);
if(clip == null) {
AudioInputStream inputStream = AudioSystem.getAudioInputStream(
TheGame.class.getResource(url));
Clip clip = AudioSystem.getClip();
clip.open(inputStream);
clips.put(url, clip);
}
if(clip.isRunning())
clip.stop();
clip.setFramePosition(0);
clip.start();
}
You will need to get a bit more creative if you need to overlap the same sound with itself. Note that I have placed this in a synchronized method to protect the Map from concurrent modification if you should happen to be calling this from multiple threads.
I'm quite a newbie in JAVA and I am trying to read a clip. Here is my code :
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
public class TestClipBis {
protected static AudioFormat audioFormat;
public static void main(String[] args) throws Exception {
// Specification of the sound to play
// No control. We assume that the sound can be played on audio system
//File soundFile = new File("chimes.wav");
File soundFile = new File("test.wav");
InputStream is = new FileInputStream(soundFile);
InputStream bufferedIn = new BufferedInputStream(is);
//AudioInputStream sound = AudioSystem.getAudioInputStream(soundFile);
AudioInputStream sound = AudioSystem.getAudioInputStream(bufferedIn);
audioFormat = sound.getFormat();
System.out.println(audioFormat);
// Loading the sound into the memory (a Clip)
DataLine.Info info = new DataLine.Info(Clip.class, sound.getFormat());
System.out.println(info);
//Clip clip = (Clip) AudioSystem.getClip();
Clip clip = (Clip) AudioSystem.getLine(info);
System.out.println("Sound frame lenght : "+sound.getFrameLength());
System.out.println("Clip FrameLength before opening : "+clip.getFrameLength());
System.out.println("Clip will open - "+info);
System.out.println("Info format : "+info.getLineClass());
// Check before this line that everything is in memory
// Yes, but how ?
clip.open(sound);
System.out.println("Clip is open");
System.out.println("Clip FrameLength after opening : "+clip.getFrameLength());
// Due to a bug in Java Sound,
// we explicitly out of the VM when the sounds stop
clip.addLineListener(new LineListener() {
public void update(LineEvent event) {
if (event.getType() == LineEvent.Type.STOP) {
System.out.println("Methode de sortie");
event.getLine().close();
System.exit(0);
}
}
});
// Playing the clip
clip.start();
System.out.println("IsActive : "+clip.isActive());
//clip.close();
}
}
My problem is how to be sure that the clip is loaded in memory before opening and playing it ? With the above code, when I open and play the sound file, I have few seconds of playing but never the same length, randomly, and never the full song.
Or should I use something else than a clip to play a song? But I want to "move" into the song and not only streaming it from the start to the end.
Edit:
Ok, I tried few things. First, I tried to see if the "ByteArrayOuptputStream" was written. I had a "println" in the loop and yes, all is written but it don't fix the problem.
Then, when I open the clip, I tried to add the parameters : audioformat, bytearray, startpoint, bufferlength. Nothing better.
Then, I noticed that when the sounds stop, the method to exit was used. So, I tried to "mute" that method (with comment signs). The result is different : it read the file but the sound is jerky. And when I check the CPU use, it's around 100%. Is it a first clue to guess what's the problem ?
I tried to make a loop that indicates the frameposition after the start : all the frames are read but the sound is still jerky.
I also tried the thead.sleep before and after the start method : nothing better.
So, here is the code I use. Many code parts are between "comment quotes" because they are try, unsuccessfull...
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
public class TestBufferedClip {
protected static AudioFormat audioFormat;
public static ByteBuffer buffer;
public static void main(String[] args) throws Exception {
// Specification of the sound to play
// No control. We assume that the sound can be played on audio system
//File soundFile = new File("chimes.wav");
File soundFile = new File("test.wav");
FileInputStream fis = new FileInputStream(soundFile);
ByteArrayOutputStream baos = new ByteArrayOutputStream((int)soundFile.length());
byte[] buf = new byte[1024];
int n = 0;
int loop = 0;
while ((n=fis.read(buf))>=0) {
baos.write(buf);
buf=new byte[1024];
System.out.println("Loop = "+loop);
loop+=1;
}
byte[] ba = baos.toByteArray();
System.out.println("ByteArray size : "+ba.length);
ByteArrayInputStream bais = new ByteArrayInputStream(ba);
//AudioInputStream sound = AudioSystem.getAudioInputStream(soundFile);
AudioInputStream sound = AudioSystem.getAudioInputStream(bais);
audioFormat = sound.getFormat();
System.out.println(audioFormat);
// Loading the sound into the memory (a Clip)
DataLine.Info info = new DataLine.Info(Clip.class, sound.getFormat());
System.out.println("Info :"+info);
//Clip clip = (Clip) AudioSystem.getClip();
Clip clip = (Clip) AudioSystem.getLine(info);
System.out.println("Sound frame lenght : "+sound.getFrameLength());
System.out.println("Info format : "+info.getLineClass());
// Check before this line that everything is in memory
// Yes, but how ?
clip.open(audioFormat, ba, 0, ba.length);
//clip.open(sound);
System.out.println("Clip is open");
System.out.println("Clip FrameLength after opening : "+clip.getFrameLength());
// Due to a bug in Java Sound,
// we explicitly out of the VM when the sounds stop
/*
clip.addLineListener(new LineListener() {
public void update(LineEvent event) {
if (event.getType() == LineEvent.Type.STOP) {
System.out.println("Methode de sortie");
event.getLine().close();
System.exit(0);
}
}
});
*/
// Playing the clip
System.out.println("Before thread sleep");
try {
Thread.sleep(31000);
} catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("After thread sleep");
clip.start();
System.out.println("IsActive : "+clip.isActive());
/*
while (clip.isActive()==true) {
System.out.println("Position = "+clip.getFramePosition());
}
*/
//clip.close();
}
}
#Phil Freihofner :
I thought about your solution to read and discards data until I reach my "start" point. You wrote "In order to start at a point within the audio file, using a SourceDataLine, you would have to read and discard data from the audio input line until you got to the desired starting spot". How do you do that ? When I use the "SourceDataLine" method, my start method is a loop with a line.write(bytes, 0, bytesreads); to point the sound on the speakers.
So, how do you just read and discard ? I didn't find any "read" method with the line.
javax.sound.sampled supports two objects for playing back audio. The Clip, as you are using, has to be loaded completely into memory before one can play it back. On the plus side, it is also easy to position the playback to start from within the Clip, either using microseconds or frame position.
I see no benefit from first loading the sound into a byte buffer. That is a redundant level of buffering for Clips. I'd only do it if you were trying to do DSP or something else that requires getting to the data, something beyond the Clip's built in ability to set a start point.
If you are able to preload the possible audio choices as Clips before they are selected, that might be the best solution, as long as you don't run out of RAM.
The other option for playback is a SourceDataLine. It reads and plays back the file on a per-buffer basis. Thus, it can start up quicker than an unloaded Clip (no need to load the entire file into memory first). However, once the Clip is preloaded, the Clip will play back without having to do repeated file loads.
In order to start at a point within the audio file, using a SourceDataLine, you would have to read and discard data from the audio input line until you got to the desired starting spot. You can do this by counting frames (the format will tell you the number of bytes per frame). This reading and discarding would disrupt timing a bit, but my experience has been that reading and discarding audio data is a couple of orders of magnitude faster than playback, since playback involves blocking queues to keep the output at the required frame rate.
Check the Java Sound Tutorials for more info, which includes links to the Clip and SourceDataLine APIs.
Here is an example of the loading of a Clip:
File soundFile = new File("test.wav");
AudioInputStream sound = AudioSystem.getAudioInputStream(soundFile);
DataLine.Info info = new DataLine.Info(Clip.class, sound.getFormat());
Clip clip = (Clip) AudioSystem.getLine(info);
clip.open(sound);
The data from test.wav should now be in RAM. All that other stuff you have using byte buffers and buffered lines is unnecessary.
When you are ready to play, use clip.setMicrosecondPosition(long milliseconds) to set your sound to start at the desired location, (not needed if you are starting from the beginning, unless you've already played the Clip, in which case the position will be where it was when you stopped). Then use clip.start() to commence playing.
IMPORTANT NOTE: playback will end prematurely if the program running it exits. One way to test this is to put a Thread.sleep(long milliseconds) command after the clip.start(), where the value of milliseconds is longer than the length of the clip. But that is not a real solution, just a diagnostic to prevent the program from closing the clip playback thread. You should be handling keeping the program running from the main threads, not the thread with the audio playback.
First read whole into a byte buffer. Do this by copying all content from file to ByteArrayOutputStream. This way you will have whole media content in memory. Now you can wrap array from ByteArrayOutputStream.toByteArray() into ByteArrayInputStream and provide that pure in-memory stream as audio input stream.
I wrote a program in Java using the pi4j lib to make sound whenever a (physical) button is clicked. This program works, but it now plays all the sounds interchangeably. I want that when you click on 2,3,4 or more buttons you only hear one sound.
This is the code I hope you can help.
public class ButtonSoundsProject{
public static void main(String args[]) throws InterruptedException {
System.out.println("Toy has been started!");
// create gpio controller
final GpioController gpio = GpioFactory.getInstance();
// provision gpio pin #02 as an input pin with its internal pull down resistor enabled
GpioPinDigitalInput[] pins = {
gpio.provisionDigitalInputPin(RaspiPin.GPIO_00, PinPullResistance.PULL_DOWN),
gpio.provisionDigitalInputPin(RaspiPin.GPIO_01, PinPullResistance.PULL_DOWN),
gpio.provisionDigitalInputPin(RaspiPin.GPIO_02, PinPullResistance.PULL_DOWN),
gpio.provisionDigitalInputPin(RaspiPin.GPIO_03, PinPullResistance.PULL_DOWN),
gpio.provisionDigitalInputPin(RaspiPin.GPIO_04, PinPullResistance.PULL_DOWN),
gpio.provisionDigitalInputPin(RaspiPin.GPIO_05, PinPullResistance.PULL_DOWN),};
final ArrayList<String> soundList = new ArrayList<String>();
soundList.add("/home/pi/Sounds/Sound1.wav");
soundList.add("/home/pi/Sounds/Sound2.wav");
soundList.add("/home/pi/Sounds/Sound3.wav");
soundList.add("/home/pi/Sounds/Sound4.wav");
soundList.add("/home/pi/Sounds/Sound5.wav");
soundList.add("/home/pi/Sounds/Sound6.wav");
soundList.add("/home/pi/Sounds/Sound7.wav");
soundList.add("/home/pi/Sounds/Sound8.wav");
soundList.add("/home/pi/Sounds/Sound9.wav");
soundList.add("/home/pi/Sounds/Sound10.wav");
soundList.add("/home/pi/Sounds/Sound11.wav");
soundList.add("/home/pi/Sounds/Sound12.wav");
// create and register gpio pin listener
GpioPinListenerDigital listener = new GpioPinListenerDigital() {
#Override
public void handleGpioPinDigitalStateChangeEvent(GpioPinDigitalStateChangeEvent event) {
// display pin state on console
final int randomNum = 0 + (int) (Math.random() * 12);
System.out.println(randomNum);
System.out.println(" --> GPIO PIN STATE CHANGE: " + event.getPin() + " = " + event.getState());
InputStream in;
try {
System.out.println(soundList.get(randomNum).toString());
String filepath = soundList.get(randomNum).toString();
in = new FileInputStream(new File(filepath));
AudioStream as = new AudioStream(in);
AudioPlayer.player.start(as);
} catch (Exception ex) {
ex.printStackTrace();
}
}
};
gpio.addListener(listener, pins);
for (;;) {
Thread.sleep(500);
}
}
}
As stated in the comments, I can't give you advise regarding the AudioStream and AudioPlayer classes because I don't seem to have those in my JDK. Since my method is similar, I'll give you what I have, and you can hopefully take it from there.
Basically, the solution is to stop and/or "mute" that audio clip. This is how I accomplish it using the javax.sound package.:
private Clip currentAudioClip; // Keep a reference to the current clip being played
public void handleGpioPinDigitalStateChangeEvent(GpioPinDigitalStateChangeEvent event) {
// Call this every time regardless.
// If nothing is playing, this will do nothing.
stopAudio();
String filepath = soundList.get(randomNum)
URL soundFileUrl = new File(filePath).toURI().toURL();
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(soundFileUrl);
Line.Info lineInfo = new Line.Info(Clip.class);
Line line = AudioSystem.getLine(lineInfo);
currentAudioClip = (Clip) line;
currentAudioClip.open(audioInputStream);
audioClip.start();
// Alternative if you want to loop continuously. Comment out the `.start` line to use this.
// audioClip.loop(Clip.LOOP_CONTINUOUSLY);
}
public void stopAudio(){
if(audioClip != null){
muteLine(); // A gotcha I discovered (see explanation below)
audioClip.stop();
// audioClip.loop(0); // if you chose to loop, use this instead of `.stop()`
audioClip.flush();
audioClip = null;
}
}
public void muteLine(){
BooleanControl muteControl = (BooleanControl) audioClip.getControl(BooleanControl.Type.MUTE);
if(muteControl != null){
muteControl.setValue(true); // True to mute the line, false to unmute
}
}
In short, every time a pin state change event is fired, the previous audio clip will be ceased, and a new one should play. You shouldn't get any sound overlapping with this.
Also note that this is a slight modification of my original code, so let me know if there are any issues
Note about the GOTCHA
I wrote a question over on the Raspberry PI Stackexchange about an odd problem I encountered. The problem was that I discovered my audio clip would not cease playing on command. It would continue playing for a seemingly arbitrary amount of time. The stranger thing is that I only observed this while testing the app on the raspberry; it worked perfectly fine on my local machine (and even on several other machines).
It is possible my issue is related to the "looping" of my clip; if that is the case, and you simply want the clip to play for its length and no further, you may not encounter that issue, and you can probably dispense with the "muting" code I included. However, if you do observe the same issue, at least you have a solution.
Hope this helps, let me know if you have any questions.
So i'm writing this sort of game in Java where you play like Guitar Hero. I can play both parts of the song (the song and the guitar) and so far so good.
Next, i needed to mute the guitar part when someone missed a key. The problem is, there's a 1sec or so delay from the moment i mute the Clip until it actually mutes.
How can this delay be fixed?
try {
audioIn = AudioSystem.getAudioInputStream(new File("guitar.wav"));
guitar = AudioSystem.getClip();
guitar.open(audioIn);
} catch (Exception e) {}
guitar.start();
volume = (BooleanControl) guitar.getControl(BooleanControl.Type.MUTE);
An in the game loop:
if (missedKey()) {
volume.setValue(true);
} else {
volume.setValue(false);
}
Ended up following Radiodef's answer and use byte streams. I have a buffer filled with 0's and i either output that or the normal audio to the stream.
I am playing of a sound-file in Java, and is looking for a simple way to determine when the sound-file has finished playing so I can kill the thread. Is there a simple way to accomplish this?
Sorry this is a little late, but I just ran into an issue today that sounds suspiciously familiar to this one. In some game code, Im using javax.sound.sampled.Clip to play various sounds, I found that if I didn't explicitly call line.close() once it was finished, the count of native threads in the profiler would just sky-rocket until I got an OutOfMemory error.
// this just opens a line to play the sample
Clip clip = AudioSystem.getClip();
clip.open( audioFormat, sounddata, 0, sounddata.length);
clip.start();
// at this point, there is a native thread created 'behind the scenes'
// unless I added this, it never goes away:
clip.addLineListener( new LineListener() {
public void update(LineEvent evt) {
if (evt.getType() == LineEvent.Type.STOP) {
evt.getLine().close();
}
}
});
My presumption is that the clip creates a thread to meter out the sample bytes into the line, but the thread hangs around after that in case you want to re-use the clip again. My second presumption is that somewhere something in my code must have a reference to the clip, or vice-versa, but at any rate, the snippet above duct-taped the problem.
Hope this is useful to someone.
Comments
You play sound either synchronously or asynchronously.
In case you play it synchronously (blocking the calling thread), you know when the sound ends playing — your code gain control then.
In case you play it asynchronously (allowing a different thread to be created), the helper thread will terminate itself right after finishing playing.
P.S.
Please share your observations on memory leaking and reasons underneath the question.
Why do you want to kill the thread? It will go away on its own once the it terminates.
If you insist, set up a synchronized method you can call to set a "die now" flag; check that flag periodically.
I dont use clips, it takes more time to load in memory, depending the size of the file you are reading.
I preefer reading the bytes, and use this method I created:
public void play(File file) throws UnsupportedAudioFileException, IOException, LineUnavailableException, InterruptedException
{
AudioInputStream encoded = AudioSystem.getAudioInputStream(file);
AudioFormat encodedFormat = encoded.getFormat();
AudioFormat decodedFormat = this.getDecodedFormat(encodedFormat);
line = AudioSystem.getSourceDataLine(decodedFormat);
currentDecoded = AudioSystem.getAudioInputStream(decodedFormat, encoded);
line.open(decodedFormat);
line.start();
byte[] b = new byte[this.bufferSize];
int i = 0;
synchronized(lock){
while(true)
{
i = currentDecoded.read(b, 0, b.length);
if(i == -1)
break;
line.write(b, 0, i);
if(paused == true)
{
line.stop();
lock.wait();
line.start();
}
}
}
line.drain();
line.stop();
line.close();
currentDecoded.close();
encoded.close();
}
it uses this method:
protected AudioFormat getDecodedFormat(AudioFormat format)
{
AudioFormat decodedFormat = new AudioFormat(
AudioFormat.Encoding.PCM_SIGNED, // Encoding to use
format.getSampleRate(), // sample rate (same as base format)
16, // sample size in bits (thx to Javazoom)
format.getChannels(), // # of Channels
format.getChannels()*2, // Frame Size
format.getSampleRate(), // Frame Rate
false // Big Endian
);
return decodedFormat;
}