I'm getting some weird behavior from JavaSound in Java 8 in which I have a Sound class and randomly (every 3-5 instances or so) when being created the thread halts for 0-2s
This is constructor method
public Sound(String fileName) {
// specify the sound to play
// (assuming the sound can be played by the audio system)
// from a wave File
try {
File file = new File(fileName);
if (file.exists()) {
AudioInputStream sound = AudioSystem.getAudioInputStream(file);
// load the sound into memory (a Clip)
clip = AudioSystem.getClip();
clip.open(sound);
length = clip.getMicrosecondLength();
System.out.println("length: " + length);
}
else {
throw new RuntimeException("Sound: file not found: " + fileName);
}
}
catch (MalformedURLException e) {
e.printStackTrace();
throw new RuntimeException("Sound: Malformed URL: " + e);
}
catch (UnsupportedAudioFileException e) {
e.printStackTrace();
throw new RuntimeException("Sound: Unsupported Audio File: " + e);
}
catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("Sound: Input/Output Error: " + e);
}
catch (LineUnavailableException e) {
e.printStackTrace();
throw new RuntimeException("Sound: Line Unavailable Exception Error: " + e);
}
}
My current thoughts are either it is having issues allocating memory(seeing as there are multiple of these instances in an ArrayList), the file is taking long to open from as the coding isn't ideal or it's having trouble removing the objects.
Here's the ArrayList code just in case
public static void playSound(String file){
sounds.add(new Sound("res/" + file));
Sound sound = sounds.get(sounds.size()-1);
sound.setVolume(Screen.music.getVolume());
sound.play();
}
I've tried running the above section of code in a new thread and the main thread continues but the sounds don't always play.
Edit: I do have some sort of garbage collection to clear up most of the memory for the Sound objects but I don't think the problem is related to that, however reusing the objects might be a valid idea?
public void checkStatus(){
if(clip.getMicrosecondPosition() >= length){
new Thread(){
public void run(){
clip.stop();
clip.close();
clip.flush();
clip.drain();
Screen.sounds.remove(this);
}
}.start();
}
}
Related
I wrote Snake code, and want to add a sound effect when snake eats an apple. I copyied a code from some guy on YT, but it doesn't work to me. Can somebody explain me how to do this?
Code:
import com.sun.tools.javac.Main;
import javax.sound.sampled.*;
import java.io.IOException;
import java.net.URL;
public class AppleEatSoundEffect {
public static Mixer mixer;
public static Clip clip;
public static void main(String[] args) {
Mixer.Info[] mixInfos = AudioSystem.getMixerInfo();
mixer = AudioSystem.getMixer(mixInfos[0]);
DataLine.Info dataInfo = new DataLine.Info(Clip.class, null);
try {
clip = (Clip) mixer.getLine(dataInfo);
} catch (LineUnavailableException lue) {
lue.printStackTrace();
}
try {
URL soundURL = Main.class.getResource("NotBad.wav");
AudioInputStream audioStream = AudioSystem.getAudioInputStream(soundURL);
clip.open(audioStream);
} catch (LineUnavailableException lue) {
lue.printStackTrace();
} catch (UnsupportedAudioFileException uafe) {
uafe.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
}
clip.start();
do {
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
} while (clip.isActive());
}
}
Compiler says thas something wrong with clip = (Clip) mixer.getLine(dataInfo);:
Exception in thread "main" java.lang.IllegalArgumentException: Line unsupported: interface Clip
at java.desktop/com.sun.media.sound.PortMixer.getLine(PortMixer.java:131)
Below is a method which allows you to play audio files, in particular the common WAV or MIDI files. Try it...if you get your desired audio then let me know.
public Thread playWav(final File wavFile, final boolean... loopContinuous) {
String ls = System.lineSeparator();
// Was a file object supplied?
if (wavFile == null) {
throw new IllegalArgumentException(ls + "playWav() Method Error! "
+ "Sound file object can not be null!" + ls);
}
// Does the file exist?
if (!wavFile.exists()) {
throw new IllegalArgumentException(ls + "playWav() Method Error! "
+ "The sound file specified below can not be found!" + ls
+ "(" + wavFile.getAbsolutePath() + ")" + ls);
}
// Play the Wav file from its own Thread.
Thread t = null;
try {
t = new Thread("Audio Thread") {
#Override
public void run() {
try {
Clip clip = (Clip) AudioSystem.getLine(new Line.Info(Clip.class));
audioClip = clip;
clip.addLineListener((LineEvent event) -> {
if (event.getType() == LineEvent.Type.STOP) {
clip.drain();
clip.flush();
clip.close();
}
});
clip.open(AudioSystem.getAudioInputStream(wavFile));
// Are we to loop the audio?
if (loopContinuous.length > 0 && loopContinuous[0]) {
clip.loop(Clip.LOOP_CONTINUOUSLY);
}
clip.start();
}
catch (LineUnavailableException | UnsupportedAudioFileException | IOException ex) {
ex.printStackTrace();
}
}
};
t.start();
Thread.sleep(100);
}
catch (InterruptedException e) {
e.printStackTrace();
}
return t;
}
To use this method simply call it wherever and whenever you want a particular sound effect to be played:
File file = new File("resources/NotBad.wav");
playWav(file);
Make sure the file object points to the correct file location. If you want to loop the audio file as for perhaps game background music then supply boolean true to the optional loopContinuous parameter:
File file = new File("resources/BackgroundMusic.mid");
playWav(file, true);
I try to load my sounds from my resource folder when trying out my Application in the IDE.
For images and other stuff that uses InputStreams I use this method:
#Override
public InputStream readAsset(String fileName) throws IOException {
ClassLoader classloader = Thread.currentThread().getContextClassLoader();
InputStream is = classloader.getResourceAsStream(fileName);
return is;
}
this lets me open an Inputstream of which I can pull Images.
As soon as I would try to cast this InputStream to an Audio InputStream I get errors. Also if I would try to make a new AudioInputStream passing the above InputStream as the parameter.
This is my current way to load sounds from external paths:
public class JavaSound implements Sound {
private Clip clip;
public JavaSound(String fileName){
try {
File file = new File(fileName);
if (file.exists()) {
//for external storage Path
AudioInputStream sound = AudioSystem.getAudioInputStream(file);
// load the sound into memory (a Clip)
clip = AudioSystem.getClip();
clip.open(sound);
}
else {
throw new RuntimeException("Sound: file not found: " + fileName);
}
}
catch (MalformedURLException e) {
e.printStackTrace();
throw new RuntimeException("Sound: Malformed URL: " + e);
}
catch (UnsupportedAudioFileException e) {
e.printStackTrace();
throw new RuntimeException("Sound: Unsupported Audio File: " + e);
}
catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("Sound: Input/Output Error: " + e);
}
catch (LineUnavailableException e) {
e.printStackTrace();
throw new RuntimeException("Sound: Line Unavailable Exception Error: " + e);
}
}
#Override
public void play(float volume) {
// Get the gain control from clip
FloatControl gainControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
// set the gain (between 0.0 and 1.0)
float gain = volume;
float dB = (float) (Math.log(gain) / Math.log(10.0) * 20.0);
gainControl.setValue(dB);
clip.setFramePosition(0); // Must always rewind!
clip.start();
}
#Override
public void dispose() {
clip.close();
}
}
how can i exchange the AudioInputStream part to work like the first code, pulling the files out of my resource directory?
EDIT :
this way of creating a new AudioInputStream by passing an InputStream
File file = new File(fileName);
if (file.exists()) {
ClassLoader classloader = Thread.currentThread().getContextClassLoader();
InputStream is = classloader.getResourceAsStream(fileName);
//for external storage Path
AudioInputStream sound = new AudioInputStream(is);
// load the sound into memory (a Clip)
clip = AudioSystem.getClip();
clip.open(sound);
}
also throws errors before even running it
this made it work in my above code:
public JavaSound(String fileName){
try {
ClassLoader classloader = Thread.currentThread().getContextClassLoader();
InputStream is = classloader.getResourceAsStream(fileName);
AudioInputStream sound = AudioSystem.getAudioInputStream(new BufferedInputStream(is));
// load the sound into memory (a Clip)
clip = AudioSystem.getClip();
clip.open(sound);
}
catch (MalformedURLException e) {
e.printStackTrace();
throw new RuntimeException("Sound: Malformed URL: " + e);
}
catch (UnsupportedAudioFileException e) {
e.printStackTrace();
throw new RuntimeException("Sound: Unsupported Audio File: " + e);
}
catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("Sound: Input/Output Error: " + e);
}
catch (LineUnavailableException e) {
e.printStackTrace();
throw new RuntimeException("Sound: Line Unavailable Exception Error: " + e);
}
}
just had to start a new bufferedInputStream with my inputStream to have the AudioInputStream... :D still thanks a lot ;)
You cannot cast InputStream to AudioInputStream (you could do the inverse). The Clip.open() wants an AudioInputStream.
An approach, suggested by this answer here is to use the URL from the .getResource() call, rather than attempting to open the InputStream and then pass that in.
Therefore, try:
URL soundURL = classloader.getResource(fileName);
AudioInputStream ais = AudioSystem.getAudioInputStream(soundURL);
I'm writing an new audio system for my game and i have come across this error and can not seem to find and solution anywhere,
java.lang.IllegalArgumentException: illegal call to open() in interface Clip
at com.sun.media.sound.DirectAudioDevice$DirectClip.implOpen(Unknown Source)
at com.sun.media.sound.AbstractDataLine.open(Unknown Source)
at com.sun.media.sound.AbstractDataLine.open(Unknown Source)
This is the code i use to load and play my audio.
private Clip load(String filename) {
try {
//Loads the file
InputStream in = new FileInputStream(new File("res/" + filename + FILE_EXT));
//Create the input buffer
InputStream bufferedIn = new BufferedInputStream(in);
//Convert into an audio stream
AudioInputStream audioStream = AudioSystem.getAudioInputStream(bufferedIn);
//Get the audio format
AudioFormat format = audioStream.getFormat();
//Get the data line info
DataLine.Info info = new DataLine.Info(Clip.class, format);
//Return the clip
Clip audioClip = (Clip) AudioSystem.getLine(info);
audioClip.addLineListener(this);
return this.clip = audioClip;
} catch (FileNotFoundException e) {
System.err.println("Failed to load audio! " + filename + " not found!");
throw new RuntimeException(e);
} catch (UnsupportedAudioFileException e) {
System.err.println("Failed to load audio! " + filename + " is unsupported!");
throw new RuntimeException(e);
} catch (IOException e) {
System.err.println("Failed to load audio! " + filename + " caused an IO Exception!");
throw new RuntimeException(e);
} catch (LineUnavailableException e) {
System.err.println("Failed to load audio! " + filename + " line is unavalible!");
e.printStackTrace();
}
throw new RuntimeException("Failed to load audio! input == null!");
}
private void startClip() {
if(this.clip != null) this.clip.start();
else throw new RuntimeException("Failed to start audio clip! The clip appears to be null.");
}
private void stopClip() {
if(this.clip != null) this.clip.close();
else throw new RuntimeException("Failed to close audio clip! The clip appears to be null.");
}
#Override
public void play() {
try {
if(isPlaying()) return;
else {
startClip();
this.clip.open();
this.playing = true;
}
} catch (LineUnavailableException e) {
e.printStackTrace();
}
}
The error occurs at this.clip.open();
Can anyone help me?
You don't pass anything to the Clip to play.
Line#open:
IllegalArgumentException - if this method is called on a Clip instance.
You need to call clip.open(audioStream) instead of clip.open(). Also, you need to do this before starting the Clip.
So, I have a function that reads file data, in this case image size. But after it's done it doesn't seem to properly release the files. I can't move those files afterwards. If I don't call this function everything works, but if I do I always get "file in use.. blah blah blah"
private void setMoveType() {
ImageInputStream in = null;
try {
in = ImageIO.createImageInputStream(new FileInputStream(file.toString()));
try {
final Iterator<ImageReader> readers = ImageIO.getImageReaders(in);
if(readers.hasNext()) {
ImageReader reader = readers.next();
try {
reader.setInput(in);
try {
moveType = Helper.getMoveType(new Dimension(reader.getWidth(0), reader.getHeight(0)));
} catch (IOException e) {
System.err.println("IOException: " + e.getMessage());
return;
}
} catch(Exception e) {
System.err.println("ReaderException: " + e.getMessage());
} finally {
reader.dispose();
}
}
} catch(Exception e) {
System.err.println("MoveTypeSetException: " + e.getMessage());
}
} catch (IOException e) {
System.err.print("IOException: failure while creating image input stream");
System.err.println(" -> createImageInputStream Error for file: " + file.getFileName());
return;
} finally {
if(in != null) {
try {
in.close();
} catch (IOException e) {
System.err.println("IOException: " + e.getMessage());
return;
}
}
}
}
EDIT: The ImageInputStream doesn't close properly
EDIT2: a FileInputStream wasn't closed
This stream should also be closed:
new FileInputStream(file.toString())
Closing the stream when you are done should work (in.close()). The operating system prevents the file from being changed, deleted or moved while it is in use. Otherwise, the stream would get messed up. Closing the stream tells the operating system you are no longer using the file.
The application I am working on has a terribly slow loading web page. If I run the following code it works fine but only because of the call to sleep. If I don't sleep then the InputStream is just a bunch of spaces, probably due to the application it is calling from. Is there any non-hack way around this?
public class PublishTool extends Thread {
private URL publishUrl;
private String filerLocation;
public PublishTool() {
}
public PublishTool(String publishUrl, String filerLocation) throws NibException {
try {
this.publishUrl = new URL(publishUrl);
} catch (MalformedURLException e) {
throw new NibException("Publish Url :" + publishUrl + " is not valid. ");
}
this.filerLocation = filerLocation;
}
public void run() {
File filerFile = new File(filerLocation);
BufferedWriter writer = null;
try {
URLConnection conn = publishUrl.openConnection();
BufferedReader reader = new BufferedReader(new InputStreamReader(new BufferedInputStream(conn.getInputStream())));
writer = new BufferedWriter(new FileWriter(filerLocation));
Thread.sleep(1000l);
while (reader.ready()) {
writer.write(reader.readLine() + "\n");
}
} catch (MalformedURLException e) {
throw new IllegalStateException("Malformed URL for : " + publishUrl + " " + filerLocation, e);
} catch (IOException e) {
throw new IllegalStateException("IO Exception for : " + publishUrl + " " + filerLocation, e);
} catch (InterruptedException e) {
throw new IllegalStateException("Thread was interrupted early... publishing might have failed.");
} catch (NibException e) {
throw new IllegalStateException("Publishing File Copy failed : " + filerLocation + ".bak" + " to " + filerLocation);
} finally {
try {
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Don't use reader.ready(). Just call readLine() and let readLine() block until the data's ready. The end of the data will generally be signalled with a null line.
In case its helpful, a couple of code examples on my web site: reading from a URL.
Firstly, it would be helpful if you posted your actual code.
My guess the problem is calling Reader.ready. Similar to InputStream.available, that returns true if there is already buffered input. If it needs to wait for, say, a socket the it will return false. Generally you don't need ready. Use readLine, and break out of the loop if it returns null (for end of stream).