I am trying to use Javax.sound to play a .wav file.
Everything works fine, the file plays as expected and in the end I close the Clip and I close the AudioInputStream. However, the file remains locked (in use) after that and I cannot touch it without getting an exception: java.nio.file.FileSystemException: alerting.wav: The process cannot access the file because it is being used by another process.
A sample of code is below:
static private class SoundThread extends Thread implements LineListener {
private boolean playCompleted;
private int cycles;
public SoundThread(int repeats) {
cycles = repeats;
}
#Override
public void run() {
Clip clip;
AudioInputStream inputStream;
File soundFile = new File("alerting.wav");
try {
inputStream = AudioSystem.getAudioInputStream(soundFile);
try {
clip = AudioSystem.getClip();
clip.addLineListener(this);
clip.open(inputStream);
while(cycles > 0) {
playCompleted = false;
clip.setFramePosition(0);
clip.start();
while(!playCompleted) {
Thread.sleep(1000);
}
Thread.sleep(audioRepeatTime * 1000);
cycles--;
}
//clip.drain();
clip.close();
inputStream.close();
System.out.println("All closed");
try {
this.finalize();
} catch (Throwable ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
} catch (Exception ex) {
Main.syslog(Level.WARNING, "E1001 could not play alert sound", ex);
} finally {
inputStream.close();
}
} catch (UnsupportedAudioFileException ex) {
Main.syslog(Level.WARNING, "E1001 could not play alert sound", ex);
} catch (IOException ex) {
Main.syslog(Level.WARNING, "E1001 could not play alert sound", ex);
}
}
#Override
public void update(LineEvent event) {
LineEvent.Type type = event.getType();
System.out.println("Event: " + type);
if(type == LineEvent.Type.STOP) {
playCompleted = true;
} else if (type == LineEvent.Type.CLOSE) {
System.out.println("listener closed");
}
}
}
public static void PlayAlertSound() {
if(enableAudio) {
SoundThread st = new SoundThread(audioLoops);
st.start();
}
}
public static void PlayAlertSound(int repeats) {
if(enableAudio) {
SoundThread st = new SoundThread(repeats);
st.start();
}
}
In the Java threads list I see "Java Sound Event Dispatcher" running. I think this is what keeps the file locked.
Any idea how can I fix this? Thanks
The API for Clip states:
Note that some lines, once closed, cannot be reopened. Attempts to
reopen such a line will always result in a LineUnavailableException.
I'm going to make a couple additional suggestions.
Instead of using File, a better way to load audio resources is with the class.getResource method. This method returns a URL which you can then pass as your argument to the AudioSystem.getAudioInputStream method.
I'm not clear what you are trying to do, but I also recommend some further changes to your code. Initializing and playing a Clip in the same method is not generally done, as it goes against the intended use of the Clip. A Clip is meant for sounds that can be held in memory. So, make your Clip an instance variable. Then, place the code that loads and opens the Clip in its own method. And put the code that calls start or loop in a separate method or methods, and don't close the Clip at the end of playing unless you are sure you are not going to ever play it again.
If you use clip.loop, you don't have to bother with listeners and count iterations.
Instead of:
//...
AudioInputStream inputStream;
File soundFile = new File("alerting.wav");
try {
inputStream = AudioSystem.getAudioInputStream(soundFile);
// ...
}
// ...
Try this:
//...
AudioInputStream inputStream;
File soundFile = new File("alerting.wav");
try {
byte[] bytes = Files.readAllBytes(soundFile.toPath());
inputStream = AudioSystem.getAudioInputStream(new ByteArrayInputStream(bytes));
// ...
}
// ...
Related
Problem; Only hearing one sound clip when executed. After one sound has played the other doesn't play & neither can play at the same time.
Result; To be able to play 2 sounds at the same time.
Code:
import java.io.*;
import javax.sound.sampled.*;
public class ThreadPlay extends Thread {
private String filename; // The name of the file to play
private boolean finished; // A flag showing that the thread has finished
private ThreadPlay(String fname) {
filename = fname;
finished = false;
}
public static void main(String[] args) {
ThreadPlay s1 = new ThreadPlay("soundClip1.wav");
ThreadPlay s2 = new ThreadPlay("soundClip2.wav");
s1.start();
s2.start();
while (!s1.finished || !s2.finished);
System.exit(0); // Java Sound bug fix...
}
public void run() {
try {
File file = new File(filename);
AudioInputStream stream = AudioSystem.getAudioInputStream(file);
AudioFormat format = stream.getFormat();
DataLine.Info info = new DataLine.Info(Clip.class, format);
Clip clip = (Clip)AudioSystem.getLine(info);
clip.open(stream);
clip.start();
Thread.sleep(100);
while (clip.isRunning()) { Thread.sleep(100); }
clip.close();
}
catch (Exception e) { }
finished = true;
}
}
Audio Lines:
AudioSystem.getMixerInfo() results in:
[Ljavax.sound.sampled.Mixer$Info;#52cc8049
Array length: 7
Contents:
PulseAudio Mixer, version 0.02
default [default], version 4.4.0-66-generic
PCH [plughw:0,0], version 4.4.0-66-generic
PCH [plughw:0,1], version 4.4.0-66-generic
PCH [plughw:0,3], version 4.4.0-66-generic
PCH [plughw:0,7], version 4.4.0-66-generic
Port PCH [hw:0], version 4.4.0-66-generic
For each mixer, AudioSystem.getMixer(AudioSystem.getMixerInfo()[x])
Results:
org.classpath.icedtea.pulseaudio.PulseAudioMixer#685f4c2e
com.sun.media.sound.DirectAudioDevice#7a07c5b4
com.sun.media.sound.DirectAudioDevice#5ce65a89
com.sun.media.sound.DirectAudioDevice#1de0aca6
com.sun.media.sound.DirectAudioDevice#443b7951
com.sun.media.sound.DirectAudioDevice#45283ce2
com.sun.media.sound.PortMixer#4d76f3f8
Imports:
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
Run method:
public void run() {
try {
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(this.getClass().getResource("NameOfFile.wav"));
Clip clip = AudioSystem.getClip();
clip.open(audioInputStream);
clip.start();
clip.loop(Clip.LOOP_CONTINUOUSLY); // There are several different amounts of time you can loop it, so you can change this if you want, or you can just use clip.stop() whenever you want.
} catch (Exception ex) {
ex.printStackTrace();
}
}
If you use this and piece of code over multiple threads, it should work. If I am correct in assuming that you are initiating this piece of code twice, once for each thread, then this should work. I hope that this helps.
Fixed:
public void run() {
try {
File file = new File(filename);
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(file);
DataLine.Info info = new DataLine.Info(Clip.class, audioInputStream.getFormat());
Clip clip = (Clip)AudioSystem.getLine(info);
clip.open(audioInputStream);
clip.start();
clip.loop(Clip.LOOP_CONTINUOUSLY); // There are several different amounts of time you can loop it, so you can change this if you want, or you can just use clip.stop() whenever you want.
} catch (Exception ex) {
ex.printStackTrace();
}
finished = true;
}
Also, if you're running Ubuntu like myself you need to remove OpenJDK/OpenJRE and set SunJDK/SunJRE as Default.
public static synchronized void playSound(final String url) {
new Thread(new Runnable() {
// The wrapper thread is unnecessary, unless it blocks on the
// Clip finishing; see comments.
public void run() {
try {
Clip clip = AudioSystem.getClip();
AudioInputStream inputStream = AudioSystem.getAudioInputStream(
Main.class.getResourceAsStream("Draco-s-Pong-master\\demo\\Sounds" + url));
clip.open(inputStream);
clip.start();
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
}).start();
playSound("Draco Background.wav");
}
I have looked dozens of codes from other threads and they all give me null and keep spamming it, until i close the program.I have that .wav file in the Sounds folder, i even placed it everywhere in the project and it still gives me null every time.I want it for simple background.
Move your audio files (in your case the sound folder) to your class folder and then simply call it with the updated path. As
Main.class.getResourceAsStream("*PUT YOUR CLASS PATHNAME*\\Sounds" + url))
EDIT:
Use only the absolute path to the audio file. Leave off the whole Main.class.getResourceAsStream and use
try {
String fileName = "..add the rest of the absolute path..\\Draco-s-Pong-master\\demo\\Sounds\\Draco Background.wav";
File file = new File(fileName);
if (file.exists()) {
AudioInputStream inputStream = AudioSystem.getAudioInputStream(file);
Clip clip = AudioSystem.getClip();
clip.open(inputStream);
clip.setFramePosition(0); // to start from the beginning
clip.start();
} else {
throw new RuntimeException("Sound: file not found: " + fileName);
}
} catch(Exception e){
stopPlay();
System.err.println(e.printStackTrace());
}
So now you will know if the problem is with the file giving the runtime exception. Otherwise it is somewhere else.
I have an Audioplayer that implements Runnable. It starts a sound and terminates afterwards. Is this a common practice or should I close it afterwards by myself, like in the last method, that isn't used currently. In my opinion it's a good idea to just let it terminate and force-close the rest automatically.
public class AudioPlayer implements Runnable {
AudioInputStream audioIn;
Clip clip;
public AudioPlayer (String res) {
try {
URL url = this.getClass().getResource(res);
audioIn = AudioSystem.getAudioInputStream(url);
clip = AudioSystem.getClip();
clip.open(audioIn);
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void run() {
clip.start();
}
public void close() throws IOException {
try {
clip.close();
audioIn.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Either open the streams in the run() method and close them in a finally clause, or implement AutoCloseable so that your class can be used as a resource.
To directly answer your question: no, that is not common practice, but bad practice!
In general it is bad practice to acquire resources and to not explicitly release them. Especially for streams - there might be file handles behind that, all kinds of stuff. Just opening them and throwing them away might work; but as said: simply bad practice. And note: for any kind of program that is intended to run longer periods of time ... it is not only "good" to release resources, it is an absolute must to do so.
Especially when one considers that Java 7 introduced try-with-resources years ago.
I would recommend to free memory/resources just after using it, for this, exists finally block:
public AudioPlayer (String res) {
try {
URL url = this.getClass().getResource(res);
audioIn = AudioSystem.getAudioInputStream(url);
clip = AudioSystem.getClip();
clip.open(audioIn);
} catch (Exception e) {
e.printStackTrace();
} finally {
close();
}
}
But, If your audio stream closes automatically when finished, you don't need to force close if not an error:
public AudioPlayer (String res) {
try {
URL url = this.getClass().getResource(res);
audioIn = AudioSystem.getAudioInputStream(url);
clip = AudioSystem.getClip();
clip.open(audioIn);
} catch (Exception e) {
e.printStackTrace();
close();
}
}
Just a note: To be extra sure to clean up everything, you may want to write it like:
public void close() throws IOException {
try {
clip.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
audioIn.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Okay so I'm get an error with this method that uses Files.toCopy it says that the file is already being used.
Files.copy( tempClip.toPath(), wavFile.toPath(), StandardCopyOption.REPLACE_EXISTING );
This is the method that opens the file. It copies fine when I don't use this method it gives the error when I do use this method. current.track below references the wavFile above. I thought I closed everything using the file with audioStream.close() and audioClip.close().
if ( e.getSource() instanceof QuoteButton) {
QuoteButton current = (QuoteButton)e.getSource();
try {
audioStream = AudioSystem.getAudioInputStream(current.track);
AudioFormat format = audioStream.getFormat();
DataLine.Info info = new DataLine.Info(Clip.class, format);
audioClip = (Clip) AudioSystem.getLine(info);
audioClip.open(audioStream);
audioClip.start();
audioClip.addLineListener(new LineListener() {
#Override
public void update(LineEvent event) {
if(event.getType() == LineEvent.Type.STOP){
audioClip.close();
}
}
});
audioStream.close();
} catch (UnsupportedAudioFileException | IOException | LineUnavailableException e1) {
e1.printStackTrace();
}
}
You close the audioclip in a listener so it's not actually called until you get LineEvent.Type.STOP
Never mind I had a privacy leak. Fixed it.
I use the following method to playback a recorded sound, it works fine, but why it only plays once, the second time I click the play button, it does nothing, how to reset the data ?
// Write data to the OutputChannel.
public class Playback implements Runnable
{
SourceDataLine line;
Thread thread;
public void start()
{
errStr=null;
thread=new Thread(this);
thread.setName("Playback");
thread.start();
}
public void stop() { thread=null; }
private void shutDown(String message)
{
if ((errStr=message)!=null)
{
System.err.println(errStr);
samplingGraph.repaint();
}
if (thread!=null)
{
thread=null;
samplingGraph.stop();
if (Java_Sound.Running_In_All_Permissions_Mode) captB.setEnabled(true);
pausB.setEnabled(false);
playB.setText("Play");
}
}
public void run()
{
AudioFormat format=formatControls.getFormat(); // get an AudioInputStream of the desired format for playback
AudioInputStream playbackInputStream=AudioSystem.getAudioInputStream(format,audioInputStream);
if (playbackInputStream==null)
{
shutDown("Unable to convert stream of format "+audioInputStream+" to format "+format);
return;
}
SourceDataLine.Info info=new DataLine.Info(SourceDataLine.class,format); // define the required attributes for our line,and make sure a compatible line is supported.
if (AudioSystem.isLineSupported(info))
{
try // get and open the source data line for playback.
{
line=(SourceDataLine)AudioSystem.getLine(info);
line.open(format,bufSize);
}
catch (LineUnavailableException ex)
{
shutDown("Unable to open the line: "+ex);
return;
}
int frameSizeInBytes=format.getFrameSize(); // play back the captured audio data
int bufferLengthInFrames=line.getBufferSize()/8;
int bufferLengthInBytes=bufferLengthInFrames*frameSizeInBytes;
byte[] data=new byte[bufferLengthInBytes];
int numBytesRead=0;
line.start(); // start the source data line
while (thread!=null)
{
try
{
if ((numBytesRead=playbackInputStream.read(data))==-1) break;
int numBytesRemaining=numBytesRead;
while (numBytesRemaining>0) { numBytesRemaining-=line.write(data,0,numBytesRemaining); }
}
catch (Exception e)
{
shutDown("Error during playback: "+e);
break;
}
}
}
if (thread!=null) line.drain(); // we reached the end of the stream. Let the data play out,then stop and close the line.
line.stop();
line.close();
line=null;
shutDown(null);
}
}
After my test, I found this line is causing the problem
"if ((numBytesRead=playbackInputStream.read(data))==-1) break;"
The first time I played back, there were data, it worked fine, but the second time, it broke. Why ? How to fix it ?
Never worked with java audio, but since you are using stream, you need to either reset the stream if that option is available, or need to create new stream every time you read it.
I figured it out, add the following line after "shutDown(null)"
if (file==null)
try { audioInputStream.reset(); }
catch (Exception e) { e.printStackTrace(); }
Now it works perfect.