Windows 7上Java支持哪些音频格式?

8

我一直在尝试让Java播放一些简单的wav文件,但是一直没有成功。我尝试了以下代码:

Clip clip = AudioSystem.getClip();
AudioInputStream inputStream = AudioSystem.getAudioInputStream(new ByteArrayInputStream(soundBytes));
clip.open(inputStream);
clip.start();

这在“clip.open(...)”时失败,并出现异常:
javax.sound.sampled.LineUnavailableException: line with format PCM_SIGNED 44100.0 Hz, 16 bit, stereo, 4 bytes/frame, little-endian not supported.

我曾尝试过更复杂的(流媒体版本):

int BUFFER_SIZE = 128000;
AudioInputStream audioStream = null;
AudioFormat audioFormat;
SourceDataLine sourceLine = null;

try {
    audioStream = AudioSystem.getAudioInputStream(new ByteArrayInputStream(soundBytes));
} catch (Exception e){
    e.printStackTrace();
}

audioFormat = audioStream.getFormat();

DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
try {
    sourceLine = (SourceDataLine) AudioSystem.getLine(info);
    sourceLine.open(audioFormat);
} catch (LineUnavailableException e) {
    e.printStackTrace();
} catch (Exception e) {
    e.printStackTrace();
}

sourceLine.start();

int nBytesRead = 0;
byte[] abData = new byte[BUFFER_SIZE];
while (nBytesRead != -1) {
    try {
        nBytesRead = audioStream.read(abData, 0, abData.length);
    } catch (IOException e) {
        e.printStackTrace();
    }
    if (nBytesRead >= 0) {
       @SuppressWarnings("unused")
       int nBytesWritten = sourceLine.write(abData, 0, nBytesRead);
    }
}

sourceLine.drain();
sourceLine.close();

这也会在执行"sourceLine.open(...)"时失败并抛出异常:
javax.sound.sampled.LineUnavailableException: line with format PCM_SIGNED 44100.0 Hz, 16 bit, stereo, 4 bytes/frame, little-endian not supported.

我尝试了两个不同的wav文件,包括C:\Windows\Media中的著名的“tada.wav”。

我还使用GoldWave将其中一个文件更改为无符号8位单声道,但这只是将错误消息更改为:

javax.sound.sampled.LineUnavailableException: line with format PCM_UNSIGNED 44100.0 Hz, 8 bit, mono, 1 bytes/frame,  not supported.

您认为我可能哪里做错了吗?似乎播放一个简单的波形文件应该很简单,所以我猜我已经走错了路。

提前感谢。

更新

事情变得更加复杂了。如果我们将代码移动到独立的Java程序中,则代码可以正常工作。我们的应用程序中有些东西可能会影响Java播放声音的能力。

这是上述错误的堆栈跟踪:

javax.sound.sampled.LineUnavailableException: line with format PCM_UNSIGNED 44100.0 Hz, 8 bit, mono, 1 bytes/frame,  not supported.
at com.sun.media.sound.DirectAudioDevice$DirectDL.implOpen(DirectAudioDevice.java:492)
at com.sun.media.sound.AbstractDataLine.open(AbstractDataLine.java:107)
at com.sun.media.sound.AbstractDataLine.open(AbstractDataLine.java:139)
at com.hcs.orc.detail.SoundAddEdit.playButtonActionPerformed(SoundAddEdit.java:315)
at com.hcs.orc.detail.SoundAddEdit.access$100(SoundAddEdit.java:40)
at com.hcs.orc.detail.SoundAddEdit$2.actionPerformed(SoundAddEdit.java:225)

更新2:

发现更有趣的问题。看起来在加载DLL时存在冲突。我们有自己的DLL来帮助我们完成一些工作(例如查找可靠和可用的MAC地址)。如果在加载我们的DLL之前播放声音(会加载与声音相关的DLL),那么两者都可以正常工作。但是,如果先加载我们的DLL再尝试播放声音,则声音会报告以上错误。

有人了解为什么一个看似无关的DLL会导致另一个DLL后期加载不正确吗?

作为一项非常糟糕和劣质的解决方法,我们可以在启动时播放几分之一秒的静音,然后再查找MAC地址。这样做有很多弊端,包括很多客户根本不使用声音。

更新3:

深入研究我们的库,发现问题是由对RegisterClassEx(...)的调用引起的。我们这样做是为了显示一个嵌入式IE窗口,其中包含我们的HTML帮助文件。

2个回答

4
我之前遇到过类似的问题(虽然与加载DLL无关)。Javasound在底层使用一个或多个混音器,每个混音器都有一个或多个线路。这些线路中的每一个都有许多格式,可以支持播放,但这并不意味着当给定该格式播放时它就不会自己燃烧(实际上,没有任何阻止它吹嘘能够播放它无法处理的格式的东西)。
当您使用AudioSystem.getLine()时,它将遍历所有这些混音器中的所有这些线路,并基本上返回它遇到的第一个声称可以处理该格式的线路。如果该线路是一个大骗子,那么它就不会寻找其他线路——它只会选择它并产生您看到的错误。
现在要记住的重要事情是,它遍历这些线路的顺序是完全随机的。因此,任何事情都可能导致它改变,包括看似无关紧要的事情,例如加载DLL。我可以看到这里两种可能性,即DLL以某种方式提供了另一个音频线路,从而导致问题,或者加载DLL只是导致随机顺序发生变化,当您这样做时,由于某种原因首先遇到可疑的线路。
解决方法并不好,但比播放声音等待一段时间要好,您需要测试该线路是否在说实话:
SourceDataLine dataline = null;
for (Mixer.Info mixerInfo : AudioSystem.getMixerInfo()) {
    try {
        Mixer mixer = AudioSystem.getMixer(mixerInfo);
        dataline = (SourceDataLine)mixer.getLine(info);
        if(dataline==null) {
            continue; //Doesn't support this format
        }
        dataline.open(audioFormat);
        dataline.start();
    }
    catch (Exception ex) {
        //If we get here it's a buggered line, so loop round again
        continue;
    }
    try {
        dataline.close();
    }
    catch (Exception ex) {
        ex.printStackTrace(); //Shouldn't get here
    }
}


if(dataline==null) {
    //No dataline capable of *really* playing the stream
}
else {
    //We have a non-lying dataline!
}

这种方式需要花费一些时间,但本质上是进行了双重检查 - 我们遍历每个数据行,如果它“说”它可以播放该格式,我们会检查它是否真的可以 - 只有在这种情况下,我们才确定它是安全可用的。


谢谢你的想法。虽然我确实想将这样的检查纳入我的代码中,并且我相信你所说的关于任意排序和虚假混合器的问题,但这并不是我的问题。即使检查了所有不同的混合器及其源行,我仍然没有找到可以播放简单波形文件的东西。因此,我怀疑我们库中的某些内容与Java Sound库以一种非常奇怪的方式发生了冲突。 - Stephen M -on strike-
@StephenMcCants 在这种情况下,看起来确实是这样。如果可能的话,另一种替代方法是使用类似JavaFX声音库的东西 - 或许通过绕过Javasound来解决问题?(后者本身就非常不稳定和有缺陷。) - Michael Berry

0

在弄清楚这是我们JNI代码的问题之后,我们做了以下操作:

rc=CoInitialize(NULL);
rc=OleInitialize(NULL);
{
     WNDCLASSEX     wc; 
     // Register the class of our window to host the browser. 'WindowProc' is our message handler
 // and 'ClassName' is the class name. You can choose any class name you want.
 ZeroMemory(&wc, sizeof(WNDCLASSEX));
 wc.cbSize = sizeof(WNDCLASSEX);
 wc.hInstance = hinstance;
 wc.lpfnWndProc = WindowProc;
 wc.lpszClassName = &ClassName[0];
 rc=RegisterClassEx(&wc);
}

这是一个问题,因为我们在dllMain(...)中调用它的位置不对。相反,我们把它移到只调用一次的地方,在实际需要打开嵌入式浏览器之前。

这解决了我们的问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接