I'm working in a Java project where I play music as a main menu theme. I have it setup where I can play it, and I set it to loop. However, I need a way to be able to stop it at any time, presumably when the user leaves the main menu. The three sections commented out at the bottom were my feable attempt to call on a 'global' audio clip that was defined at the top. This will play the music, but it throws an un-initialized error with the else statement. How can I stop my audio within this function so that I can call it from other functions?
void AudioHandler1(String name, boolean play)
{
Clip clip;
if(play)
{
try
{
clip = AudioSystem.getClip();
AudioInputStream inputStream = AudioSystem.getAudioInputStream(new File((name)));
File file = new File(name);
inputStream = AudioSystem.getAudioInputStream(file);
clip = AudioSystem.getClip();
clip.open(inputStream);
clip.loop(Clip.LOOP_CONTINUOUSLY);
clip.start();
}
catch(Exception e)
{
System.out.println("Error: Can't locate sound.");
e.printStackTrace();
}
}
else
{
//clip = AudioSystem.getClip();
// AudioInputStream inputStream = AudioSystem.getAudioInputStream(new File((name)));
//clip.stop();
}
}
I think here lies the problem:
else{
clip = AudioSystem.getClip(); //<-- getclip() initialises a new clip which is not started yet !
AudioInputStream inputStream = AudioSystem.getAudioInputStream(new File((name)));
clip.stop(); // <-- your clip hasnt started yet so you cant stop it.
}
what you want to do is to save your Clip into a local variable to allow the access from other methods within the class.
try
{
clip = AudioSystem.getClip();
this.clip = clip;
...
}
else
{
clip.stop();
}
Furthermore you could create two static methods instead of your if/else systematic.
Clip audioClip = null;
public static void startSoundLoop(String name){
try
{
clip = AudioSystem.getClip();
this.audioClip = clip
AudioInputStream inputStream = AudioSystem.getAudioInputStream(new File((name)));
File file = new File(name);
inputStream = AudioSystem.getAudioInputStream(file);
clip = AudioSystem.getClip();
clip.open(inputStream);
clip.loop(Clip.LOOP_CONTINUOUSLY);
clip.start();
}
catch(Exception e)
{
System.out.println("Error: Can't locate sound.");
e.printStackTrace();
}
}
public static void stopSoundLoop{
this.clip.stop();
}
Notice, that static method can be accessed through a static method call.
You are able to call the methods from anywhere in your programm by calling:
AudioHandler1.startSoundLoop("yourFileNameHere");
AudioHandler1.stopSoundLoop(); //<-- Throws exception if no clip has been started yet
I assume you try to pause by calling AudioHandler1("clip filename", false)?
Your clip variable is a local variable in the scope of the AuditHandler1() method. When you start playing by calling AudioHandler1("clip filename", true), the clip variable is discarded afterwards (though the AudioSystem class has a reference to the instance, method AudioHandler1 doesn't).
You need to move declaration of clip out of the method and add it as a class member.
Store the reference to clip you're currently playing outside the method as a class variable to be able to invoke something on it later, like:
public class SomeClass {
private Clip clip;
void AudioHandler1(String name, boolean play)
{
if(play)
{
try
{
clip = AudioSystem.getClip();
AudioInputStream inputStream = AudioSystem.getAudioInputStream(new File((name)));
File file = new File(name);
inputStream = AudioSystem.getAudioInputStream(file);
clip = AudioSystem.getClip();
clip.open(inputStream);
clip.loop(Clip.LOOP_CONTINUOUSLY);
clip.start();
}
catch(Exception e)
{
System.out.println("Error: Can't locate sound.");
e.printStackTrace();
}
}
else
{
clip.stop();
//clip = AudioSystem.getClip();
// AudioInputStream inputStream = AudioSystem.getAudioInputStream(new File((name)));
//clip.stop();
}
}
}
The problem is that 'try catch' is not a method, it seems that you have more general problem with undestanding java.
If you create and play a Clip, then the Clip instance is the handler, invoking method on other clip object obviously doesn't change the other object. Invoking 'clip = AudioSystem.getClip();' you obviusly create a new clip.
Related
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));
// ...
}
// ...
This question already has an answer here:
Java clip not working
(1 answer)
Closed 5 years ago.
public static void main(String[] args) {
try{
File file = new File("audiodata/test.wav");
AudioInputStream ain = AudioSystem.getAudioInputStream(file);
AudioFormat format = ain.getFormat();
DataLine.Info info = new DataLine.Info(Clip.class, format);
Clip clip = (Clip) AudioSystem.getLine(info);
clip.open(ain);
clip.start();
}catch(Exception e){
e.printStackTrace();
}
}
I have written this simple main method to play the sound file, but it does not work. There is no Exception thrown. The program ends and I cannot hear anything. I have no idea, what my mistake is. Also creating the Clip by AudioSystem.getClip(); does not change anything.
The correct way to wait for a sound to finish playing is with a concurrency class like CountDownLatch, or with lower-level concurrency operations like wait/notify or Locks:
Clip clip = (Clip) AudioSystem.getLine(info);
CountDownLatch doneLatch = new CountDownLatch(1);
clip.addLineListener(new LineListener() {
#Override
public void update(LineEvent event) {
if (event.getType().equals(LineEvent.Type.STOP)) {
doneLatch.countDown();
}
}
});
clip.open(ain);
clip.start();
doneLatch.await();
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 am rewriting my AudioManager class for my school project and I encountered a problem. My professor told me to load all my resources with the Try-with-resources block instead of using try/catch ( See code below ). I am using the Clip class from javax.sound.sampled.Clip and everything works perfectly with my PlaySound(String path) method that uses try/catch/ if I don't close() the Clip. I know that if I close() the Clip I can't use it anymore. I have read the Oracle Docs for Clip and Try-with-resources but I could not find a solution. So What I would like to know is:
Is it possible to use the Try-with-resource block to play/hear the sound from the clip before it closes?
// Uses Try- with resources. This does not work.
public static void playSound(String path) {
try {
URL url = AudioTestManager.class.getResource(path);
try (Clip clip = AudioSystem.getClip()){
AudioInputStream ais = AudioSystem.getAudioInputStream(url);
clip.open(ais);
clip.start();
}
} catch( LineUnavailableException | UnsupportedAudioFileException | IOException e) {
e.printStackTrace();}
}
// Does not use Try- with resources. This works.
public static void playSound2(String path) {
Clip clip = null;
try {
URL url = AudioTestManager.class.getResource(path);
clip = AudioSystem.getClip();
AudioInputStream ais = AudioSystem.getAudioInputStream(url);
clip.open(ais);
clip.start();
}
catch( LineUnavailableException | UnsupportedAudioFileException | IOException e) {
e.printStackTrace();}
finally {
// if (clip != null) clip.close();
}
}
Thanks in advance!
The problem is that try-with-resources block will automatically close the Clip created in it when the block finishes causing the playback to stop.
In your other example since you don't close it manually, the playback can continue.
If you want to close the Clip when it finished playing, you can add a LineListener to it with the addLineListener() and close it when you receive a STOP event like this:
final Clip clip = AudioSystem.getClip();
// Configure clip: clip.open();
clip.start();
clip.addLineListener(new LineListener() {
#Override
public void update(LineEvent event) {
if (event.getType() == LineEvent.Type.STOP)
clip.close();
}
});