I'm having some issues playing mp3 files using AudioClip with JavaFX.
my code is like this:
// define String path to mp3 file
myFilePath = "path/to/file.mp3";
// Create an AudioClip Object from path
audioToPlay = new AudioClip(Objects.requireNonNull(getClass().getResource(audioToPlay)).toExternalForm());
// play the AudioClip
audioToPlay.play();
The audio files I am playing are all mp3 files and they are all fairly short (< 5 seconds). About 80-90% of the files are playing without any issue but the rest aren't playing at all, or are only playing for a very short time (less than a second so it sounds like a click).
I have no idea why the 10-20% aren't working; they play perfectly fine using my media player and from my browser.
Is the issue because I'm using AudioClip? Should I be using something else to play my short audio files?
Edit: myFilePath is defined as a String and audioToPlay is defined as an AudioClip object.
AudioClip is appropriate for short files that can be held in memory. This issue might be related to how or when you are calling the play() method. Does this occur when calling the play method via a button click, where the GUI stays open? Some folks don't realize playback is a daemon thread. Act of playback won't prevent a program from closing if the program triggers the sound and then completes and exits before the sound finishes playing. Also FWIW, once loaded into memory, there's no need to keep reloading AudioClip over and over.
Thought I'd add this: it seems to me that sometimes Java doesn't gracefully handle the first audio call in a session, that there is often a stutter. Due to this, I often play a "silent" sound at the start of a program, in order to get things going. Just mentioning, in case the problem occurs when the program is first getting going. If you use a GUI (with button to make sound) then you can test if an additional call repeats the problem or not, and thus help verify whether it's the sound file or the circumstance.
Related
How can you ensure the transition between two pieces of audio is seamless?
In a JavaFX application, I am using the javafx.scene.media.MediaPlayer to play an intro-piece, which is proceeded by the main/looping-piece. The media is played just fine, but the issue is the transition and loop.
Here is what I am currently doing:
private static void foo(final Media intro, final Media loop) {
final MediaPlayer introPlayer = new MediaPlayer(intro);
introPlayer.play();
final MediaPlayer loopPlayer = new MediaPlayer(loop);
loopPlayer.pause(); // An attempt to load the media so it will be ready to be played.
introPlayer.setOnEndOfMedia(loopPlayer::play());
loopPlayer.setOnEndOfMedia(() -> loopPlayer.seek(Duration.ZERO));
//loopPlayer.setCycleCount(Integer.MAX_VALUE); // Similar to the above line, but there is still a delay between loops.
}
The MediaPlayer::pause does help some, but there is a very noticeable delay between the end of the intro media and the start of the looping media. Furthermore, there is another noticeable delay between the end of the looping media and the repeat.
I additionally tried using javafx.scene.media.AudioClip, since it supposedly has less overhead than a javafx.scene.media.MediaPlayer. I wrote my own listener to tell when the track ended (and to immediately thereafter start the looping piece), but I still saw a similar delay.
Here were some similar posts I found, but did not provide a solution to the problem:
JavaFX MediaPlayer playing background music loop with small intro music
This one is definitely relevant (coincidentally, it is almost the
anniversary of that post), but I am already using a .wav formatted
media file and still experience a delay.
JavaFX AudioClip.play()
Which is similar to what I tried with the Audioclip, exept I used a
scheduled executor to time when to replay the audio. (Where I still
experienced a delay).
And, as a final note, I have tested my audio in Audacity, where they transitioned and looped seamlessly.
What are some recommended solutions for these types of problems?
Edit:
Added an addendum to the code-block, mentioning MediaPlayer::setCycleCount(Integer)
I realize it's been a while since you posted. Have you found an answer? I am wondering if you loaded loopPlayer before playing introPlayer, if that would help.
If MediaPlayer's "listener" is just kind of sluggish, maybe switching to using Java's SourceDataLine with a LineListener used to trigger the looping cue would work more seamlessly? (I would use a Clip for the looping playback.)
Last suggestion, I have an audio library AudioCue that could work for this. The library includes an AudioCueListener that can trigger an event (such as starting another AudioCue playing) when a cue ends. But limitations of the library would require that you hold your music in memory, and that the source files be .wav's.
The AudioClip Javadocs state that an AudioClip represents a segment of audio that can be played with minimal latency and are usable immediately. However, it also states
Media objects are however better suited for long-playing sounds. This is primarily because AudioClip stores in memory the raw, uncompressed audio data for the entire sound, which can be quite large for long audio clips. A MediaPlayer will only have enough decompressed audio data pre-rolled in memory to play for a short amount of time so it is much more memory efficient for long clips, especially if they are compressed.
Depending on the length of the looping media, an AudioClip might be better suited for you. Instead of needing a ScheduledExecutorService to replay the audio, you can use AudioClip.setCycleCount(AudioClip.INDEFINITE) to loop forever.
Using this info, I believe your best bet is to use a MediaPlayer for the intro and then utilize MediaPlayer#setOnEndOfMedia to call the looping AudioClip; possibly having a small delay between the intro and looping transition but seamless after that.
I'm trying to program a loopstation program in JSyn inspired by something like this. The idea is, that I can record to Wav-files and play them from the program. That worked well until I attempted to do this for multiple files simultaneously.
How do I approach this?
I cannot create multiple synthesizer engines otherwise I get an error, so I have created a class with my line out and my synthesizer. But if the audio only plays while the synthesizer sleeps, how can I play from multiple files at once?
System.out.println("queue the sample");
samplePlayer.dataQueue.queue(sample);
System.out.println("queued the sample");
// Wait until the sample has finished playing.
do {
mySynth.sleepFor(1); //synth has to sleep while sample is being played
} while (samplePlayer.dataQueue.hasMore()); //this seems to always return true -> synth never wakes up & the program crashes
This is adapted from the examples included in the JSyn library. I have based most of my own coding on the JSyn Programmer's Guide
This is what the GUI looks like (programmed in Java Swing). It responds to the mouse and the numpad. This works.
The constructor of my output class. This contains the synthesizer and line out.
public OutputMix() {
filePath = sampleMachineFrame.filePath; //string containing path to location for sample files
mySynth = JSyn.createSynthesizer();
myLineOut = new LineOut();
mySynth.add(myLineOut);
recorder = new RecordMic[10]; //one recorder for each button (my own class)
player = new PlayFromWav[10]; //one player for each button (my own class)
}
The recording works absolutely fine. I can even start overlapping recordings (i.e. record to two files at once) and play them with an external program.
But when I try to play them back the synthesizer never wakes up and I am also struggling to imagine how I would play multiple files at once.
Thanks for your help :)
In general, you do not want to sleep in your program unless you are sequencing events. Just queue samples in response to Button events and let them run.
If the files are small enough then you can just load them into memory. Then you can queue multiple files any time you want. You might need to increase the heap size of your Java virtual machine.
Here is an example: https://github.com/philburk/jsyn/blob/master/tests/com/jsyn/examples/PlaySample.java
If the samples are too big then you will have to stream them off the disk using multiple threads, which is more difficult.
You can make all the samples the same size. Then they will stay in phase. Or you can trigger them at specific beats using timestamps.
I'm looking to write a simple MIDI-driven audio sequencer using Javasound.
I have multiple samples (one for each MIDI pitch) that are loaded into memory as a (globally accessible) Map<MidiPitch,AudioInputStream>.
A custom subclass of javax.sound.midi.Receiver responds to incoming MIDI events as follows:
If the event is a note-on, a Clip is obtained and played as follows:
Clip clip = AudioSystem.getClip();
clip.open(lookupAIS(pitch));
clip.start();
The clip is then added to a globally accessible Map<MidiPitch,List<Clip>>, representing started clips, i.e. clips for which start() has been called as above, but for which a note-off event has not yet been received.
If the event is a note-off, the corresponding list of started clips is obtained from the above map. The clip at the head of the list is removed, and stop() and close() are called on it.
The above Receiveris connected to MidiSystem.getSequencer() in the usual way, then the following called:
sequencer.setSequence(MidiSystem.getSequence(new File(myMidFile)))
sequencer.open()
sequencer.start()
Thread.sleep(aLongTime())
sequencer.stop()
sequencer.close()
The above works when the driving MIDI sequence is at a slow tempo, but at higher tempos, notes simply hang (even for sequences containing a very small number of notes).
My understanding is that clip.start() is run within a separate thread behind the scenes by the Javasound API.
Can anyone suggest why this might be happening? Is it perhaps a synchronization issue?
EDIT: By 'hang', I mean that some notes are stuck, despite the fact that log output reports that the corresponding 'stop' method has been called.
EDIT2: It looks as if the hanging first happens when a given note is played for the second time. This happens even if the MIDI sequence is monophonic, i.e. the previous note has stopped.
Your method of loading the Clip for each play is going to be a considerable source of variable latency. Every time you call this, the file is read anew and will not start playing until it the entire file has finished loading.
I recommend pre-loading all the clips and holding them in memory. When the note-on is called, set the clip cursor to zero and then play:
clip[mapIndex].setFramePosition(0);
clip[mapIndex].start();
These clips should have already been opened. I'm putting them in an array and using "mapIndex" as a plausible way of selecting the correct clip that might work with the mapping you've already set up.
You probably won't need to "stop" or "close" the clips until the entire sequence has finished, unless the clips are rather long and are designed to be stopped while in progress, or if they are being played as loops.
This should improve things considerably. I can't say if it will fix everything. The cpu is probably doing some thread multiplexing, and it is plausible that occasionally, in your current code, the clip.close is being called on one thread before the clip has finished loading on the other.
I was wondering I'm working on an Android app where MediaPlayer/TextToSpeech is used to tell the user something and then a Speech Recognition object opens up after the MediaPlayer/TextToSpeech finishes and another is played after they answer the question with the recognition. Problem is I'm not 100% sure of a way, or good way, to delay the Speech Recognition from opening right after MediaPlayer is starts, or waiting till mediaplayer is done before speech recognition opens up. I see a few options based off my own knowledge/Google -
use the methods isPlaying(MediaPlayer)/isSpeaking(TextToSpeech) and putting them in some kind of loop to check until they aren't true anymore, then opening the speech recognition.
Call Thread.sleep(). The problem I see with this is I'm not sure exactly how long user is going to speak so it could still get interrupted unless I put it at a high time which just slows the app down.
I've research and played around with the Handler class a bit, but haven't fully comprehended how to do this...anyone help?
If you want to do something when a MediaPlayer finishes, you can set a OnCompletionListener with setOnCompletionListener() to receive a callback when the media playback is complete.
I have a Sound class that contains a method that, when called, plays a sound using a Clip object (in this case, clip).
public static void play() {
clip.stop(); // The purpose of the first three lines
clip.flush(); // is to restart the Clip object so it
clip.setFramePosition(0); // can be played multiple times.
clip.start();
}
The instantiation of the Clip object occurs in a separate static method which is called prior to this method, which is why the above method can be declared static.
Another class that implements KeyListener contains the following code:
public void keyPressed(KeyEvent e) {
Sound.play(); // Sound is the class that implements the previous method.
}
Therefore, my code should be playing the sound associated with clip everytime a key is pressed. However, if I press a key quickly and repeatedly, the sound will sometimes not play. This is especially noticeable after a while (It seems as though the problem gets worse after each key press).
Why does this happen, and how can I circumvent this problem?
I have had the same kind of problems in the past, and something which worked for me is adding a line listener for whenever a line has finished, and closing it.
The code underneath is a stripped down version of what I use:
music = AudioSystem.getClip();
AudioInputStream ais = AudioSystem.getAudioInputStream(Sound.class.getResource("/sounds" + filename));
music.open(ais);
music.addLineListener(new LineListener(){
public void update(LineEvent e){
if(e.getType() == LineEvent.Type.STOP){
e.getLine().close();
}
}
});
music.start();
When you create the clip, just add in the line listener. When you reset the clip using your play function, it should play properly. I hope this works for you!
I came across this post via a reference in a similar question.
Yes, the number of Clips playing at once can contribute to the lag, but I don't know how to predict how much of an effect it will be, as this depends on how the JVM interacts with the OS.
You might be able to aid the situation by making the buffer size of the Clip smaller. SourceDataLine and Clip both seem to only allow interactions with incoming requests at buffer boundaries (I'm not sure this is a 100% accurate statement). Specifying the Clip's buffer size is a bit circuitous, as it requires obtaining the data as a PCM array. But if you wish to give it a try, the API is here.
A good way to reduce the number of lines is to use a sound library like TinySound. Many of the programmers at Java-gaming.org have used this library with success.
Another alternative that I'm promoting is AudioCue, a class I recently wrote for concurrent Clip playback. The license is BCD, source is provided, so feel free to examine, tinker with, and make use of the code. AudioCue doesn't reduce the number of output lines (yet). But if you are doing the common thing of managing multiple copies of the same cue, it could help in this regard as all concurrent instances are mixed down to a single output. Basic principle: the file is loaded into an array and played back via cursors that iterate over the array and merge their output into a SourceDataLine. This setup also allows implementation of smooth, real-time volume, panning and pitch fading.