java合成wav在linux出错,Java audio fails to play wav file in Linux

问题

I am having trouble using Java audio on Linux. This is OpenJDK 8 on Ubuntu 14.04. The following sample fails with the .wav file from this link:

import java.net.URL;

import javax.sound.sampled.*;

public class PlaySound {

public void play() throws Exception

{

// List all mixers and default mixer

System.out.println("All mixers:");

for (Mixer.Info m : AudioSystem.getMixerInfo())

{

System.out.println(" " + m);

}

System.out.println("Default mixer: " + AudioSystem.getMixer(null).getMixerInfo());

URL url = getClass().getResource("drop.wav");

Clip clip;

AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(url);

clip = AudioSystem.getClip();

System.out.println("Clip format: " + clip.getFormat());

clip.open(audioInputStream);

clip.start();

do { Thread.sleep(100); } while (clip.isRunning());

}

public static void main(String [] args) throws Exception {

(new PlaySound()).play();

}

}

This is the result:

All mixers:

PulseAudio Mixer, version 0.02

default [default], version 4.4.0-31-generic

Intel [plughw:0,0], version 4.4.0-31-generic

Intel [plughw:0,2], version 4.4.0-31-generic

NVidia [plughw:1,3], version 4.4.0-31-generic

NVidia [plughw:1,7], version 4.4.0-31-generic

NVidia [plughw:1,8], version 4.4.0-31-generic

NVidia [plughw:1,9], version 4.4.0-31-generic

Port Intel [hw:0], version 4.4.0-31-generic

Port NVidia [hw:1], version 4.4.0-31-generic

Default mixer: default [default], version 4.4.0-31-generic

Clip format: PCM_SIGNED unknown sample rate, 16 bit, stereo, 4 bytes/frame, big-endian

Exception in thread "main" java.lang.IllegalArgumentException: Invalid format

at org.classpath.icedtea.pulseaudio.PulseAudioDataLine.createStream(PulseAudioDataLine.java:142)

at org.classpath.icedtea.pulseaudio.PulseAudioDataLine.open(PulseAudioDataLine.java:99)

at org.classpath.icedtea.pulseaudio.PulseAudioDataLine.open(PulseAudioDataLine.java:283)

at org.classpath.icedtea.pulseaudio.PulseAudioClip.open(PulseAudioClip.java:402)

at org.classpath.icedtea.pulseaudio.PulseAudioClip.open(PulseAudioClip.java:453)

at PlaySound.play(PlaySound.java:22)

at PlaySound.main(PlaySound.java:29)

Apparently the problem is that the PulseAudio mixer is being selected, and for some reason it cannot play the .wav file.

If I replace the AudioSystem.getClip() call with AudioSystem.getClip(null), which selects the default mixer, then it works.

How can I make sure that a compatible mixer is selected ?

Update: Following the suggestion from @Dave to loop through the available mixers until I find one that has a "compatible" format, I see the following:

Target format (from AudioInputStream.getFormat()) is:

PCM_SIGNED 44100.0 Hz, 16 bit, stereo, 4 bytes/frame, little-endian

I loop through all mixers, source lines for each mixer, and supported formats for each source line, and get the following match:

Mixer: PulseAudio Mixer, version 0.02

Source line: interface SourceDataLine supporting 42 audio formats, and buffers of 0 to 1000000 bytes

Format matches: PCM_SIGNED unknown sample rate, 16 bit, stereo, 4 bytes/frame, little-endian

I do get a match (using format.matches()) yet I still get the "Invalid format" exception. Perhaps because the format that matched says "Unknown sample rate" and then when I try to open the clip, it finds that it does not actually support 44100 Hz ?

回答1:

If you are able to use SourceDataLine in your use case instead of Clip, then you should be able to do something like this:

URL url = getClass().getResource("drop.wav");

SourceDataLine sourceLine;

AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(url);

sourceLine = AudioSystem.getSourceDataLine(audioInputStream.getFormat());

System.out.println("Source format: " + sourceLine.getFormat());

sourceLine.open(audioInputStream);

sourceLine.start();

do { Thread.sleep(100); } while (sourceLine.isRunning());

(Note this is as yet untested on my side.)

You only need Clip if you plan on seeking or looping.

If you do need the ability to seek or loop, one (ugly) method would be calling AudioSystem.getClip(null) first to ensure you are selecting the default Mixer. The semantics of AudioSystem.getClip() are (as you have noticed) not particularly reliable. Wrap all attempts at calling Clip.open in a try/catch. If opening a clip does not work with the default mixer, then loop through available Mixer.Info objects excluding the default and call getClip(mixerInfo) with each until one works.

Another method would be to loop through Mixer.Info objects returned by AudioSystem.getMixerInfo(). Call AudioSystem.getMixer(mixerInfo) for each to get the Mixer instance. Loop through the Line.Info objects returned by Mixer.getSourceLineInfo(). For each of these, if it is an instance of DataLine.Info, cast it and call DataLine.Info.getFormats() to get the supported AudioFormat objects. Compare these against what AudioInputStream.getFormat() returns (using matches) until you find a compatible one.

Neither of these are particularly elegant. The first is a bad use of exceptions. The second is just a bit convoluted, although it seems more correct.

回答2:

I'm also on Ubuntu 14.04, but have different mixer, it works fine.

I think you could specify concrete parameters, which are needed for your line:

import javax.sound.sampled.*;

import java.net.URL;

public class PlaySound {

public void play() throws Exception {

// List all mixers and default mixer

System.out.println("All mixers:");

for (Mixer.Info m : AudioSystem.getMixerInfo()) {

System.out.println(" " + m);

}

System.out.println("Default mixer: " + AudioSystem.getMixer(null).getMixerInfo());

URL url = getClass().getResource("drop.wav");

Clip clip;

AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(url);

// clip = AudioSystem.getClip();

try {

AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,

44100,

16, 2, 4,

AudioSystem.NOT_SPECIFIED, true);

DataLine.Info info = new DataLine.Info(Clip.class, format);

clip = (Clip) AudioSystem.getLine(info);

} catch (LineUnavailableException e) {

System.out.println("matching line is not available due to resource restrictions");

return;

} catch (SecurityException ee) {

System.out.println("if a matching line is not available due to security restrictions");

return;

} catch (IllegalArgumentException eee) {

System.out.println("if the system does not support at least one line matching the specified Line.Info object " +

"through any installed mixer");

return;

}

System.out.println("Clip format: " + clip.getFormat());

clip.open(audioInputStream);

clip.start();

do {

Thread.sleep(100);

} while (clip.isRunning());

}

public static void main(String[] args) throws Exception {

(new PlaySound()).play();

}

}

How can I make sure that a compatible mixer is selected ?

Another cases - use default by default, or catch exception and use default on failover.

回答3:

Looks like there are two separate issues involved here.

First, relying on AudioSystem.getClip() is not a good idea as basically there's no guarantee that the clip will be able to handle the specific format of the wav file. Instead, one of the following approaches should be used:

As suggested by @Dave: Loop through the available mixers and query if the target format is supported:

URL url = getClass().getResource("drop.wav");

AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(url);

AudioFormat format = audioInputStream.getFormat();

DataLine.Info lineInfo = new DataLine.Info(Clip.class, format);

Mixer.Info selectedMixer = null;

for (Mixer.Info mixerInfo : AudioSystem.getMixerInfo()) {

Mixer mixer = AudioSystem.getMixer(mixerInfo);

if (mixer.isLineSupported(lineInfo)) {

selectedMixer = mixerInfo;

break;

}

}

if (selectedMixer != null) {

Clip clip = AudioSystem.getClip(selectedMixer);

[...]

}

Or, as suggested by @egorlitvinenko, use AudioSystem.getLine(DataLine.Info) to get a line with the desired capabilities.

Both of the above approaches "should" work.

On top of that, there is an additional problem with PulseAudio, which is that there is a bug which may result in an "invalid format" exception even though the PulseAudio mixer can actually handle the format. This is described in the following bug reports (the second one contains workarounds):

https://icedtea.classpath.org/bugzilla/show_bug.cgi?id=3452 (my own)

https://icedtea.classpath.org/bugzilla/show_bug.cgi?id=2915

来源:https://stackoverflow.com/questions/46194666/java-audio-fails-to-play-wav-file-in-linux

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值