简体   繁体   English

Java音频无法在Linux中播放WAV文件

[英]Java audio fails to play wav file in Linux

I am having trouble using Java audio on Linux. 我在Linux上使用Java音频时遇到问题。 This is OpenJDK 8 on Ubuntu 14.04. 这是Ubuntu 14.04上的OpenJDK 8。 The following sample fails with the .wav file from this link : 以下示例因此链接中的.wav文件而失败:

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. 显然问题在于选择了PulseAudio混音器,并且由于某种原因它无法播放.wav文件。

If I replace the AudioSystem.getClip() call with AudioSystem.getClip(null) , which selects the default mixer, then it works. 如果我将AudioSystem.getClip()调用替换为AudioSystem.getClip() AudioSystem.getClip(null) ,它将选择默认的混音器,那么它将起作用。

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: 更新:遵循@Dave的建议,循环遍历可用的混音器,直到找到具有“兼容”格式的混音器,然后看到以下内容:

Target format (from AudioInputStream.getFormat() ) is: 目标格式(来自AudioInputStream.getFormat() )为:

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. 确实得到了一个匹配项(使用format.matches() ),但仍然收到“无效格式”异常。 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 ? 也许是因为匹配的格式显示为“未知采样率”,然后当我尝试打开剪辑时,它发现它实际上并不支持44100 Hz?

If you are able to use SourceDataLine in your use case instead of Clip , then you should be able to do something like this: 如果您可以在用例中使用SourceDataLine而不是Clip ,则应该可以执行以下操作:

    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. 如果您打算搜索或循环播放,则仅需要Clip

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 . 如果确实需要查找或循环的功能,则一个(很丑陋的)方法将首先调用AudioSystem.getClip(null)以确保您选择了默认的Mixer The semantics of AudioSystem.getClip() are (as you have noticed) not particularly reliable. 正如您所注意到的, AudioSystem.getClip()的语义不是特别可靠。 Wrap all attempts at calling Clip.open in a try/catch. 在try / catch中包装所有调用Clip.open的尝试。 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. 如果打开剪辑不适用于默认的混音器,请遍历除默认值之外的可用Mixer.Info对象, getClip(mixerInfo)每个对象分别调用getClip(mixerInfo)直到一个getClip(mixerInfo)

Another method would be to loop through Mixer.Info objects returned by AudioSystem.getMixerInfo() . 另一种方法是遍历AudioSystem.getMixerInfo()返回的Mixer.Info对象。 Call AudioSystem.getMixer(mixerInfo) for each to get the Mixer instance. AudioSystem.getMixer(mixerInfo)为每个调用AudioSystem.getMixer(mixerInfo)以获取Mixer实例。 Loop through the Line.Info objects returned by Mixer.getSourceLineInfo() . 循环浏览由Mixer.getSourceLineInfo()返回的Line.Info对象。 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. 对于每一个,如果它是DataLine.Info的实例,则将其DataLine.Info并调用DataLine.Info.getFormats()以获取受支持的AudioFormat对象。 Compare these against what AudioInputStream.getFormat() returns (using matches ) until you find a compatible one. 比较这些反对什么AudioInputStream.getFormat()返回(使用matches ),直到你找到一个兼容的一个。

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. 第二个似乎有点令人费解,尽管看起来更正确。

I'm also on Ubuntu 14.04, but have different mixer, it works fine. 我也在Ubuntu 14.04上,但是有不同的混音器,它工作正常。 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. 另一种情况-默认情况下使用默认值,或者捕获异常并在故障转移时使用默认值。

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. 首先,依赖AudioSystem.getClip()并不是一个好主意,因为基本上不能保证该剪辑将能够处理wav文件的特定格式。 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: 正如@Dave所建议的那样 :循环浏览可用的混音器并查询是否支持目标格式:

     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. 或者, 按照@egorlitvinenko的建议 ,使用AudioSystem.getLine(DataLine.Info)获得具有所需功能的线路。

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 . 最重要的是,PulseAudio还存在另一个问题,即存在一个错误, 即使PulseAudio混合器可以实际处理格式 ,该错误也可能导致“无效格式”异常。 This is described in the following bug reports (the second one contains workarounds): 以下错误报告对此进行了描述(第二个报告包含解决方法):

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM