【问题标题】:Java audio fails to play wav file in LinuxJava 音频无法在 Linux 中播放 wav 文件
【发布时间】:2018-02-21 23:53:24
【问题描述】:

我在 Linux 上使用 Java 音频时遇到问题。这是 Ubuntu 14.04 上的 OpenJDK 8。以下示例因 .wav 文件 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();
    }
}

这是结果:

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)

显然问题是正在选择 PulseAudio 混音器,并且由于某种原因它无法播放 .wav 文件。

如果我将AudioSystem.getClip() 调用替换为AudioSystem.getClip(null),它选择了默认混音器,那么它可以工作。

如何确保选择了兼容的调音台?


更新:按照@Dave 的建议循环遍历可用的混音器,直到找到具有“兼容”格式的混音器,我看到以下内容:

目标格式(来自AudioInputStream.getFormat())是:

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

我循环遍历所有混音器、每个混音器的源行以及每个源行支持的格式,并得到以下匹配:

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

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

【问题讨论】:

  • 您需要Clip 接口,还是SourceDataLine 可以工作?我意识到你在这里的例子可能只是问题的最小再现,所以你需要寻找或循环吗?

标签: java linux audio pulseaudio


【解决方案1】:

看起来这里涉及两个不同的问题。

首先,依赖AudioSystem.getClip() 不是一个好主意,因为基本上不能保证剪辑能够处理特定格式的wav 文件。相反,应使用以下方法之一:

  • As suggested by @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); 
        [...]
    }
    
  • 或者,as suggested by @egorlitvinenko,使用 AudioSystem.getLine(DataLine.Info) 获得具有所需功能的线路。

上述两种方法都“应该”有效。

除此之外,PulseAudio 还存在一个额外问题,即存在一个可能导致“格式无效”异常的错误即使 PulseAudio 混音器实际上可以处理该格式 .这在以下错误报告中进行了描述(第二个包含解决方法):

【讨论】:

    【解决方案2】:

    我也在 Ubuntu 14.04 上,但有不同的混音器,它工作正常。 我认为您可以指定您的线路所需的具体参数:

    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();
        }
    }
    

    如何确保选择了兼容的调音台?

    其他情况 - 默认使用默认值,或者捕获异常并在故障转移时使用默认值。

    【讨论】:

    • 谢谢你和 +1。看起来这只是故事的一半——另一半实际上是 PulseAudio 中的一个错误:-/
    【解决方案3】:

    如果您能够在您的用例中使用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());
    

    (请注意,这在我这边尚未经过测试。)

    如果你打算寻找或循环,你只需要Clip

    如果您确实需要查找或循环的能力,一个(丑陋的)方法将首先调用AudioSystem.getClip(null),以确保您选择默认的MixerAudioSystem.getClip() 的语义(如您所见)不是特别可靠。将调用Clip.open 的所有尝试包装在try/catch 中。如果使用默认混音器无法打开剪辑,则循环遍历可用的Mixer.Info 对象(不包括默认值)并调用getClip(mixerInfo) 直到其中一个起作用。

    另一种方法是循环通过AudioSystem.getMixerInfo() 返回的Mixer.Info 对象。为每个调用 AudioSystem.getMixer(mixerInfo) 以获取 Mixer 实例。循环通过Mixer.getSourceLineInfo() 返回的Line.Info 对象。对于其中的每一个,如果它是DataLine.Info 的实例,则对其进行转换并调用DataLine.Info.getFormats() 以获取支持的AudioFormat 对象。将这些与 AudioInputStream.getFormat() 返回的内容(使用 matches)进行比较,直到找到兼容的。

    这些都不是特别优雅。首先是异常使用不当。第二个有点复杂,虽然看起来更正确。

    【讨论】:

    • 不幸的是我需要使用Clip。回覆。您的建议:第一个解决方法不实用,因为创建剪辑的位置 (getClip) 与我需要调用 Clip.open 的位置无关。第二个似乎很有希望,但即使我确实得到了匹配,我仍然会在以后得到一个例外。请参阅我的问题的更新。看起来匹配不足以保证兼容性。还有其他想法吗?
    • getClip 很可能在幕后做了类似于第二个建议的事情。我将不得不考虑一下,但我不知道有更好的方法。
    • 我尝试进行一些搜索,发现 this SO answer 表明不再支持 PulseAudio Java 绑定。我没有足够的知识来确认或否认,但也许您可以配置 Java 运行时以避免 PulseAudio。
    • 好的,看起来 PulseAudio 中存在错误(请参阅我的回答)。我赞成你的回答并奖励你赏金,因为你实际上让我走上了正确的轨道。但是,我将自己的答案标记为“已接受”,因为它描述了完整的故事,因此它更有可能帮助将来可能遇到相同问题的其他人。
    • Está muy generoso。即使我无法提供真正的解决方案,我也很高兴能帮助您找到令人信服的答案。感谢您与解决方法分享错误报告。很高兴知道这一点。
    猜你喜欢
    • 1970-01-01
    • 2021-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多