Getting error stopping an audio player in JMF
I recently asked a question about controlling the volume of a Java audio clip and it was suggested that I try JMF. As I'm using an old JVM (1.4) and only interested in playing WAV files, JMF was a perfect solution and has greatly simplified the code to load, play, and control the volume of audio clips in my application.
But I'm having trouble stopping the clips on demand. It would seem as simple as calling player.stop()
, but stop()
seems to block until the player has finished playing. Here is a simple code example:
Player p = Manager.createRealizedPlayer( f.toURI().toURL() );
p.start();
System.out.println( "Player playing" );
Thread.sleep( 1000 );
System.out.println( " Clip is at time: " + p.getMediaTime().getSeconds() );
p.stop();
System.out.println( " Clip is at time: " + p.getMediaTime().getSeconds() );
This produces output like:
Player playing
Clip is at time: 0.8591836730000001
Clip is at time: 3.227619047
Where I would have expected the second time to read equal to or only briefly later than the first. (Or perhaps to have been reset to 0 or something.) I have also tried p.setStopTime( p.getMediaTime() )
. This stops the clip on time, but plays a s开发者_Python百科plit second of distortion before stopping, which is undesirable.
Googling around makes me think that others aren't having this issue, and it's been kind of hard to find options. Could this be a buffering issue or am I missing something else? Any insight you can provide is greatly appreciated.
Details: I am using (and cannot change) JVM 1.4.2 with JMF 2.1.1. All of my WAV files are only 1-10 seconds long.
And indeed it was a buffering issue. It took me a while to find this because when I attempted to display the available Controls with the following code snippet, there were no BufferControls listed:
Player p = ...
Control cs[] = p.getControls();
for ( int j = 0; j < cs.length; j++ )
System.out.println( " Found control: " + cs[j].getClass().getCanonicalName() );
Eventually I just made a blind stab at getting a BufferControl with this snippet and it worked:
BufferControl bc = (BufferControl) p.getControl( "javax.media.control.BufferControl" );
After probing and fiddling around some more it turned out that the default buffer length was 2 seconds. Reducing the buffer length to 1 second allowed me to correctly stop WAV files which were 2.5 seconds long or longer. Further reducing it to .5 seconds ended up giving me the behavior I was originally seeking on all of my input files.
Without looking at the JMF source, I'm assuming that I was just running into an issue where the audio was being read and played in 2 second chunks (2 sec being the length of the buffer.) and that those chunks had to finish playing -- for whatever reason -- before they could be stopped. Reducing it to .5 seconds might not actually be fixing the problem (if the sounds keep playing after they're told to stop) but it will at least reduce the issue to the point of not being noticeable. Furthermore, all of my WAV files are small and local, so latency or skips in playback shouldn't be a problem.
In the end, here is the revision of the code snippet from the original question that results the desired behavior:
Player p = Manager.createRealizedPlayer( f.toURI().toURL() );
BufferControl bc = (BufferControl) p.getControl( "javax.media.control.BufferControl" );
if ( bc != null )
bc.setBufferLength( 500 ); // buffer length specified in milliseconds
p.start();
System.out.println( "Player playing" );
Thread.sleep( 1000 );
System.out.println( " Clip is at time: " + p.getMediaTime().getSeconds() );
p.stop();
System.out.println( " Clip is at time: " + p.getMediaTime().getSeconds() );
I was having this problem with short audio files only, too. Setting the buffer didn't help. I ended up adding silence to the audio in audacity.
I hate JMF :)
Edit: Well, JMF also blocks GainControl.setLevel() calls when you have a short audio file. I guess the root of the issue is the same, JMF just blocks your request (set volume, stop) till the short file is complete.
精彩评论