Java音频在Linux下无法播放wav文件

问题描述 投票:0回答:4

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

这是结果:

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

do得到了匹配(使用

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

java linux audio pulseaudio
4个回答
1
投票

如果您能够在用例中使用

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)
以确保您选择默认的
Mixer
AudioSystem.getClip()
的语义(正如您所注意到的)不是特别可靠。将所有调用
Clip.open
的尝试包装在 try/catch 中。如果打开剪辑无法使用默认混音器,则循环遍历除默认混音器之外的可用
Mixer.Info
对象,并对每个对象调用
getClip(mixerInfo)
,直到有一个可以工作。

另一种方法是循环

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

这些都不是特别优雅。第一个是异常的错误使用。第二个有点复杂,尽管它看起来更正确。


1
投票

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

如何确保选择兼容的混音器?

另一种情况 - 默认情况下使用默认值,或者捕获异常并在故障转移时使用默认值。


0
投票

看起来这里涉及两个单独的问题。

首先,依赖

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

  • 按照@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); 
        [...]
    }
    
  • 或者,按照@egorlitvinenko的建议,使用

    AudioSystem.getLine(DataLine.Info)
    获得具有所需功能的线路。

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

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


0
投票

您启动剪辑,也关闭剪辑。否则它在 Linux 上只能播放一次。

clappingSoundButton.addActionListener(event -> {
     if (Settings.isSoundOn())
     {
        try
        {
           Clip clip = AudioSystem.getClip();
           clip.open(ApplicationSound.getClappingSound());
           FloatControl volume = (FloatControl) clip
                 .getControl(FloatControl.Type.MASTER_GAIN);
           volume.setValue(Settings.getVolume());
           clip.start();
           do {
              Thread.sleep(100);
          } while (clip.isRunning());
           clip.close();
        }
        catch (LineUnavailableException | IOException e)
        {
           // nothing
        }
        catch (InterruptedException e)
        {
           // nothing
        }
     }
  });

这对我在 Windows 和 Linux 上都有效。

© www.soinside.com 2019 - 2024. All rights reserved.