How can a Java application play a sound clip? - java

The following is a link to the most likely explanation I've seen, but I still have questions.
How can I play sound in Java?
I'll quote the code here:
public static synchronized void playSound(final String url) {
new Thread(new Runnable() {
public void run() {
try {
Clip clip = AudioSystem.getClip();
AudioInputStream inputStream = AudioSystem.getAudioInputStream(Main.class.getResourceAsStream("/path/to/sounds/" + url));
clip.open(inputStream);
clip.start();
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
}).start();
}
Does this work in an application, as opposed to an Applet?
The method Main.class.getResourceAsStream() seems to require import com.sun.tools.apt.Main; but I cannot find documentation for that, and I don't know what it does. For instance, is "/path/to/sounds/" absolute, or relative, and if the latter, relative to where?
I've spent many hours now trying to play a simple sound effect. It's unbelievable how difficult it is. I hope that the above code can be made to work. Thanks for any help.
Chap

That should work in an application.
That line of code is most likely referencing the class that method is in. So that method was originally in class Main, if you put the method in class FooBar, you should change it to FooBar.class.getResourceAsStream().
It is a relative path. It will look for the resource outside of every package. Example: Let's say the class that's running this piece of code is located at C:\Users\Jeffrey\bin\foo\bar\SoundPlayer.class and the class is in package foo.bar. This means that the ClassLoader will look for the resources inside the C:\Users\Jeffrey\bin\ folder. (In your case, it will look for the resource at C:\Users\Jeffrey\bin\path\to\sounds\ + url)
I always loaded sounds like this:
Clip sound = (Clip) AudioSystem.getLine(new Line.Info(Clip.class));
sound.open(AudioSystem.getAudioInputStream(file));
but your method should also work.

Does this work in an application, as opposed to an Applet?
It works in either.
The method Main.class.getResourceAsStream() seems to require import com.sun.tools.apt.Main;
Where did you get that idea? I've made plenty of sound examples, and never heard of that class that you should not be using.
..but I cannot find documentation for that,..
No, the com.sun classes are not only undocumented, but might change in the next micro-version.
..and I don't know what it does. For instance, is "/path/to/sounds/" absolute, or relative, and if the latter, relative to where?
It is relative to the root of the class-path.
..It's unbelievable how difficult it is.
Media handling in general, is tricky.
BTW - I'm not much impressed with the code on the linked thread. The Thread wrapper is unnecessary, as mentioned in several of the comments, even for playing multiple Clip instances simultaneously.
Instead see this code that I (wrote &) personally recommend.

Although I drew heavily from #Andrew's code, I did have to make some tweaks here and there. The following is a demo of my solution, complete except for a sample .wav file.
// Developed in Eclipse, YMMV regarding resource location.
import java.net.URL;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
class ClipPlayer {
public static void main(String[] args) {
// First, instantiate ourselves so we can call demoSam which
// needs to be able to do a wait().
ClipPlayer cp = new ClipPlayer();
// Now run the actual demo
cp.demoSam();
}
private void demoSam() {
/**
* Construct a Sam, capable of playing the "Chook.wav", a 0.1 sec sound.
* NOTE: it's very tricky debugging an incorrectly-located
* resource file, and I'm unable to give a general rule
* here. But in this example, Chook.wav is expected to be in the same
* directory as the .class file, and there is no surrounding
* package (i.e. we're taking the default package name). If you
* are using a package, you may have to write "myPackage/Chook.wav"
* instead.
*/
Sam sam;
try {
sam = new Sam("Chook.wav"); // or whatever, but it has to be .wav
}
catch (Exception e) {
say("Exception thrown by Sam: " + e.getMessage());
System.exit(1); // scoot
return; // get rid of warning about sam possib not init'd
}
int countDown = 20;
do {
say("Doing something requiring accompanying sound effect...");
try {
sam.playIt();
}
catch (Exception e) {
say("Caught exception from playIt: " + e.getMessage());
System.exit(1);
}
// Now wait a human-scale duration, like 1/8 second. In
// practice we may be processing, since the sound is playing
// asynchronously.
synchronized (this) {
try {
wait(125); // wait 1/8 sec
}
catch (Exception e2) {
say("huh?");
}
}
} while (--countDown > 0);
}
/**
* 'Sam' is a class that implements one method, playIt(), that simply
* plays the .wav file clip it was instantiated with. Just using an
* inner class here for simplicity of demo.
*/
final class Sam {
AudioInputStream ais;
Clip clip;
/**
* Constructor: prepare clip to be played. Do as much here as
* possible, to minimize the overhead of playing the clip,
* since I want to call the play() method 5-10 times a second.
*/
Sam(String clipName) throws Exception {
// Resource is in same directory as this source code.
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
URL url = classLoader.getResource(clipName);
ais = AudioSystem.getAudioInputStream(url);
clip = AudioSystem.getClip();
clip.open(ais);
}
/**
* playIt(): Start the clip playing once, asynchronously, and exit.
*/
public void playIt() throws Exception {
clip.setFramePosition(0); // Must always rewind!
clip.loop(0);
clip.start();
}
}
private static void say(String s) {
System.out.println(s);
}
}

Related

How to reliably detect that an atomic move of a directory failed because the target already exists

In a server, I am initializing the working directory for a resource on first access to the resource. There may be parallel requests for the resource handled by multiple processes of the server, which means that I need to take care that none of the processes sees a partially initialized working directory. The solution for this is initialize the working directories in a temporary, sibling directory and then move it to its final location with Files.move and the StandardCopyOption.ATOMIC_MOVE.
In case two processes initialize a working directory at the same time, the second atomic move fails. This is not really a problem because then the working directory is initialized correctly, so the process that came in second only needs to discard the temporary directory it created and continue.
I tried to do this with the following code:
private void initalizeWorkDirectory(final Resource resource) throws IOException {
File workDir = resource.getWorkDirectory();
if (!workDir.exists()) {
File tempDir = createTemporarySibligDirectory(workDir);
try {
fillWorkDirectory(tempDir, resource);
Files.move(tempDir.toPath(), workDir.toPath(), StandardCopyOption.ATOMIC_MOVE);
} catch (FileAlreadyExistsException e) {
// do some logging
} finally {
FileUtils.deleteQuietly(tempDir);
}
}
}
However I noticed, that only catching the FileAlreadyExistsException doesn't seem enough. In case of a move collision, there are also other exceptions thrown. I don't just want to catch all exceptions because this could be hiding real problems.
So isn't there a way to reliably detect from the exception thrown by Files.move that an atomic move of a directory failed because the target directory already existed?
From observing the exceptions and by looking at the FileSystemProvider implementations for Windows and Linux, we found out the following:
On Windows, an AccessDeniedException is thrown when trying to move a directory to an existing directory.
On Unix (and its relatives), a generic FileSystemException is thrown with a message that corresponds to the errno.h constant ENOTEMPTY.
When programming against these implementation details, one can reasonably well detect directory move collisions without too much danger of hiding other problems.
The following code implements an atomic move which always throws a FileAlreadyExistsException in case of a move collision:
import java.io.File;
import java.io.IOException;
import java.nio.file.AccessDeniedException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.FileSystemException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
public class AtomicMove {
private static final String ENOTEMPTY = "Directory not empty";
public static void move(final File source, final File target) throws FileAlreadyExistsException, IOException {
try {
Files.move(source.toPath(), target.toPath(), StandardCopyOption.ATOMIC_MOVE);
} catch (AccessDeniedException e) {
// directory move collision on Windows
throw new FileAlreadyExistsException(source.toString(), target.toString(), e.getMessage());
} catch (FileSystemException e) {
if (ENOTEMPTY.equals(e.getReason())) {
// directory move collision on Unix
throw new FileAlreadyExistsException(source.toString(), target.toString(), e.getMessage());
} else {
// other problem
throw e;
}
}
}
}

Trying to play sound in Java: NullPointerException

I'm trying to play sound in Java but it doesn't work and I got error message.
Here is my Code
public class PlaySoundClip extends JFrame {
public PlaySoundClip() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setTitle("Test Sound Clip");
this.setSize(300, 200);
this.setVisible(true);
try {
URL url = this.getClass().getClassLoader().getResource("click.wav");
AudioInputStream audioIn = AudioSystem.getAudioInputStream(url);//Line 27
// Get a sound clip resource.
Clip clip = AudioSystem.getClip();
// Open audio clip and load samples from the audio input stream.
clip.open(audioIn);
clip.start();
clip.loop(Clip.LOOP_CONTINUOUSLY); // repeat forever
} catch (UnsupportedAudioFileException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (LineUnavailableException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new PlaySoundClip();//Line 44
}
}
And I get this error message then I'm not here any sound! How to fix this problem?
Exception in thread "main" java.lang.NullPointerException
at com.sun.media.sound.StandardMidiFileReader.getSequence(StandardMidiFileReader.java:205)
at javax.sound.midi.MidiSystem.getSequence(MidiSystem.java:836)
at com.sun.media.sound.SoftMidiAudioFileReader.getAudioInputStream(SoftMidiAudioFileReader.java:174)
at javax.sound.sampled.AudioSystem.getAudioInputStream(AudioSystem.java:1145)
at playsoundclip.PlaySoundClip.<init>(PlaySoundClip.java:27)
at playsoundclip.PlaySoundClip.main(PlaySoundClip.java:44)
Reference from Playing Sound.
Something is wrong with click.wav, it was not found on the classpath, so url became null, hence the NullPointerException.
You should put the click.wav on your classpath, so the program will find it. The most straightforward way is putting into the root folder of the jar file.
Most likely in your build phase there is a list of "resources" that are copied from source tree to the output compiled classes. Usually it is a string of regular expressions like ".txt;.json;…" In your case you need to add ".wav" to it or copy file by hand to the compiler output location.
Most IDEs (Eclipse|IteliJ IDEA) and some ant build scripts have provision for resource copying.

How do I load resources when using a classloader?

I'm using JarFile and JarURLConnection to load files out of a jar file. I'm then taking the classes, and loading them via BCEL (ByteCode Engineering Library, apache library). I cant just directly use a class loader because im modifying some classes slightly with the BCEL. I need to load the classes by their bytes into my bcel loader. However, one of the classes I'm loading references a resource. This resource is inside of the jar, so I can get the file (When iterating over the entries in the JarFile, I ignore the regular files, and take the class files for loading later). But just having the file won't do me any good, as the class loads it as a resource. Is there any way I can take that resource from the jar (well I can take it and load it into a byte[], the next part is the issue) and dynamically add it as a resource for my program, so that the classes that I load wont be missing their resources?
Got a lot of stuff here, if anythings confusing, ask in comments, I might've said something wrong, or missed something altogether :) Thanks
I'll show a little of my class loader here (extends ClassLoader):
#Override
public URL getResource(String name) {
System.out.println("LOADING RESOURCE: " + name);
try {
return new URL(null, name, new Handler(files));
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
Now, it is printing out "LOADING RESOURCE: filename", but its then giving me a MalformedURLException (I have no protocol atm, just a file path, that's not a true valid path, but it's just an attempt to give it to my Handler class below).
class Handler extends URLStreamHandler {
#Override
protected URLConnection openConnection(URL u) throws IOException {
return new URLConnection(u) {
#Override
public void connect() throws IOException {
}
#Override
public InputStream getInputStream() throws IOException {
System.out.println("IS: " + url);
return /*method to get input steam*/;
}
};
}
}
The /*method to get input steam*/ is set in my real code, but that's not relevant here. So any further ideas with this?

Music in Java (start/stop)

Ok, so I am making a game and the music changes when you are in different regions or if there is an interruption, like with an AI.
So I have JUST learned how to make music showup in my program, and now I am trying to make it stop, but I am unsure how to, below is a snippet of code where the music plays and then I try to overwite it with new music when an action occurs.
public static void songs(String word) {
String temp = word;
if (temp.equals("start")) {
try {
try {
blah = new FileInputStream("C:/Users/Austin/Desktop/Storage/programimages/game/battle.wav");
} catch (Throwable e) {
e.printStackTrace();
}
AudioStream as = new AudioStream(blah);
AudioPlayer.player.start(as);
System.out.println("going");
} catch (IOException e) {
System.err.println(e);
}
}
if (temp.equals("stop")) {
try {
try {
blah = new FileInputStream("C:/Users/Austin/Desktop/Storage/programimages/game/silence.wav");
} catch (Throwable e) {
e.printStackTrace();
}
AudioStream as = new AudioStream(blah);
AudioPlayer.player.stop(as);
System.out.println("stopping");
} catch (IOException e) {
System.err.println(e);
}
}
}
This is the only method I have been able to find that has the music play, but if you guys have any other suggestions please let me know.
Again, I want to have sound affects and music going, and right now all that happens is one song will play, and it will not stop under any circumstance until it hits the very end of its length. I want to be able to stop songs whenever a new one should come on, and also allow sound affects to pop up.
Thanks!
(since I am stuck on this and need an answer now I will probably repost on one or two more java sites so I can get a response ASAP, thank you though!!!!)
EDITED CODE: (still does not stop the current stream, any more suggestions appreciated)
public static void songs(String word) throws IOException {
String temp = word;
if (temp.equals("go")) {
try {
blah = new FileInputStream("C:/Users/Austin/Desktop/Storage/programimages/game/battle.wav");
} catch (Throwable e) {
e.printStackTrace();
}
AudioStream as = new AudioStream(blah);
AudioPlayer.player.start(as);
System.out.println("going");
}
if (temp.equals("stop")) {
//don't try and do things with a null object!
if (as != null) {
AudioPlayer.player.stop(as);
System.out.println("stopping1");
}
System.out.println("stopping2");
AudioPlayer.player.stop(as);
}
}
Currently you're creating a new AudioStream in your stop branch and calling the stop method using this. This is a different object to the one that is currently playing. Try making the AudioStream a class variable, and calling stop on that instead.
EDIT: at the top of the class containing your code...
class YourClass {
//the class member variable
private AudioStream as;
//[etc...]
In your start branch:
// 'as' has already been defined above
as = new AudioStream(blah);
AudioPlayer.player.start(as);
System.out.println("going");
In your stop branch:
try
{
//don't try and do things with a null object!
if (as != null)
{
AudioPlayer.player.stop(as);
}
System.out.println("stopping");
}
catch (IOException e)
{
System.err.println(e);
}
You may have trouble with the static identifier on your method - if you're calling this from within an instantiated class you don't need this.
I can't even access these sun.audio Objects on my Eclipse IDE--I know they are in rt.jar, but there is header info about them being proprietary and such.
Can the Java Sound library (javax.sound.sampled) handle what you want to do? Both Clip and SourceDataLine allow one to stop playback. That is a more usual way of playing sound, if you want to use native Java.
Playback into is here:
http://docs.oracle.com/javase/tutorial/sound/playing.html
But the documentation, overall, is not exactly rich with examples. There's example code at this site
http://www.jsresources.org/
and plenty of people here who could help if you run into problems with the native Java approach.

Detect whether Java class is loadable

I have two programs: one CLI program, and one GUI. The GUI is a frontend for the CLI, but also a GUI for another program as well.
I am importing the CLI's classes and extending them in the GUI to add GUI elements to the classes, and all is great.
But now I want to split the CLI that I currently have embedded in the GUI (as an included JAR). The JAR is in a fixed location (/opt/program/prog.jar), and the application will only be used on Linux, so I realize that this breaks traditional Java thought.
I've edited the ClassPath in the Manifest file to reflect this change, and it works fine. However, when I remove the file, the GUI fails to load, citing not being able to load the class.
Is there a way to try to load a class and if it does not work, then do something else? In essence, I'm trying to catch the ClassNotFound exception, but have not had any luck yet.
One common way to check for class existence is to just do a Class.forName("my.Class"). You can wrap that with a try/catch that catches ClassNotFoundException and decide what to do. If you want, you could do that in a wrapper class that has a main(). You could try to load the class and if it succeeds, then call main() on the loaded class and if not, do something else.
public static void main(String arg[]) {
try {
Class.forName("my.OtherMain");
// worked, call it
OtherMain.main();
} catch(ClassNotFoundException e) {
// fallback to some other behavior
doOtherThing();
}
}
Is there a way to try to load a class and if it does not work, then do something else?
Assuming you had a class file in C:\ called Foo.class
public static void main(String[] args) {
File f = new File("c:\\");
if (f.exists()) {
URLClassLoader CLoader;
try {
CLoader = new URLClassLoader(new URL[]{f.toURL()});
Class loadedClass = CLoader.loadClass("Foo");
} catch (ClassNotFoundException ex) {
} catch (MalformedURLException ex) {
}
} else {
//do something else...
}
}

Categories

Resources