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

问题描述:

我在Linux上使用Java音频时遇到问题。这是Ubuntu 14.04上的OpenJDK 8。下面的示例失败.wav文件from this linkJava音频无法在Linux中播放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),该呼叫将选择默认调音台,那么它将起作用。

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


更新:在从@戴夫遍历可用搅拌机的建议,直到我找到一个具有“兼容”的格式,我看到以下内容:

目标格式(从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?

+0

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

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

首先,依靠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其实可以处理的格式错误另外一个问题。这在下面的错误报告(第二个包含解决方法)描述:

如果你能在你的使用情况,而不是Clip使用SourceDataLine,那么你应该能够做这样的事情:

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()的语义是(如你所知)不是特别可靠。在try/catch中调用Clip.open的所有尝试。如果打开剪辑对于默认调音台不起作用,则循环播放可用的Mixer.Info对象(不包括默认设置),并调用getClip(mixerInfo),直到其中一个有效。

另一种方法是循环Mixer.InfoAudioSystem.getMixerInfo()返回的对象。请拨打AudioSystem.getMixer(mixerInfo)获取Mixer实例。循环访问由Mixer.getSourceLineInfo()返回的Line.Info对象。对于其中的每一个,如果它是DataLine.Info的实例,则将其投射并调用DataLine.Info.getFormats()以获得支持的AudioFormat对象。将它们与AudioInputStream.getFormat()返回(使用matches)进行比较,直到找到兼容的。

这些都不是特别优雅。首先是对异常的不良使用。第二个只是有点复杂,尽管看起来更正确。

+0

不幸的是我需要使用'Clip'。回覆。你的建议:第一个解决方法是不实际的,因为创建剪辑的地方('getClip')与我需要调用'Clip.open'的地方无关。其次看起来很有前途,但即使我**做到了比赛,我仍然会在以后得到例外。查看我的问题的更新。看起来这场比赛不足以保证兼容性。任何其他想法? – Grodriguez

+0

'getClip'很可能与幕后的第二个建议类似。我将不得不考虑这一点,但我不知道更好的方式。 – Dave

+0

我试图做一些搜索和碰到[这个SO回答](https://*.com/questions/45847635/java-audio-clip-cannot-be-closed-when-using-linux-pulseaudio? rq = 1),这表明PulseAudio Java绑定不再受支持。我无法确认或否认,但也许可以配置Java运行时以避免使用PulseAudio。 – Dave

我也在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

谢谢你和+1。看起来这只是故事的一半 - 另一半实际上是PulseAudio中的一个错误: - / – Grodriguez