Windows 7上Java支持的任何声音格式?

时间:2014-01-06 21:44:43

标签: java audio javasound javax.sound.sampled

我们,我一直在试图让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(...)的调用引起的。我们这样做,因此我们可以使用HTML帮助文件显示嵌入式IE窗口。

2 个答案:

答案 0 :(得分:4)

我之前遇到过类似的问题(虽然与加载DLL无关。)Javasound使用1个或多个混音器在下面工作,每个混音器有1行或更多行。这些行中的每一行都有许多表示它支持的格式,但这并不意味着它在给定播放格式时不会自发燃烧(实质上,没有什么可以阻止它吹嘘它不能播放格式。)

当你使用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!
}

这种方式需要更长的时间,但它本质上是一个双重检查 - 我们遍历每个数据行,如果它它可以播放格式,我们检查它是否真的可以 - 并且只在那种情况下,我们确定它是否可以安全使用。

答案 1 :(得分: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(...)的错误位置调用它。相反,我们将它移动到仅调用一次的位置,就在实际需要打开嵌入式浏览器之前。

这解决了我们的问题。