Java中可靠的声音API,用于简单的数字样本播放

Posted

技术标签:

【中文标题】Java中可靠的声音API,用于简单的数字样本播放【英文标题】:Reliable sound API in Java for simple digital samples playback 【发布时间】:2012-12-10 01:57:01 【问题描述】:

有没有一个好的方法可以在 Java 中获得体面、可靠的数字采样声音播放?

我的请求列表很短:

从 .wav 文件之类的文件加载内存中的数字化样本(例如,从 jar 中捆绑的资源) 以非阻塞方式播放它们 当我同时播放多个样本并且它们在时间上相交时,它们应该得到适当的混合

如果有以下内容会很好,但事实上我可以没有它:

从 .ogg 或类似的压缩格式播放(显然没有在 Java 中实现占用大量 CPU 的解码器) 在播放同一样本时再次播放它不应停止给定样本的先前播放,但应开始第二个副本并与第一个正确混合

我尝试了臭名昭著的Java Sound API,但发现它完全不可靠,似乎无法满足我最小的愿望清单。我遇到的问题:

在带有 ALSA dmix (OpenJDK 6) 的 Linux 上,让任何其他应用程序在初始化 Java Sound API 时使用音频只会使 Java 应用程序中的所有声音消失,而不会出现任何错误/警告。

在 Linux (OpenJDK 6) 上,列出 MixerInfos 并尝试使用其中任何一个获取 Clip 对象会在尝试加载 wav 文件时引发以下异常:

java.lang.IllegalArgumentException: Line unsupported: interface Clip supporting format PCM_SIGNED unknown sample rate, 16 bit, stereo, 4 bytes/frame, big-endian

因此,Audiosystem.getClip(anySortOfMixer) 似乎根本不起作用。只有AudioSystem.getClip() 有效。

使用Clip 加载具有不同采样率/位/格式的文件失败并显示LineUnavailableException。似乎第一次调用clip.open 将声音系统设置为特定的声音选项,然后调用以加载采样率略有不同的文件(例如,第一个是 44100,第二个是 48000)

李>

在 Linux (OpenJDK 6) 上初始化几个不同的 Clips 并尝试播放它们只会使最后加载的 Clip 可听 - 没有给出错误/警告,但仅在最后加载的 Clip 上使用 play任何声音都没有 - 所有其他人都是沉默的:

Clip loadSound(String name) 
    URL url = this.getClass().getResource("/" + name + ".wav");
    Clip clip = AudioSystem.getClip();
    AudioInputStream ais = AudioSystem.getAudioInputStream(url);
    clip.open(ais);
    return clip;


void playSound(Clip) 
    if (clip.isRunning())
        clip.stop();
    clip.setFramePosition(0);
    clip.start();

...
Clip c1 = loadSound("foo");
Clip c2 = loadSound("bar");
...
playSound(c1); // silence
...
playSound(c2); // audible

使用此代码在 Windows 上一切正常 - 所有剪辑都可以正常播放、播放和混音。没有在 Mac 上测试过。

支持的文件格式(使用AudioSystem.getAudioFileTypes 分析)在 Linux/OpenJDK6 和 Windows/Oracle JDK 7 上都返回 wav / au / aif,因此没有 ogg 甚至 mp3 :(

如果不将第二个副本加载为不同的 Clip,似乎没有简单的方法可以同时制作相同 Clip 声音的两个副本。

所以,问题是 - 是否有一个好的解决方案/解决方法来补救所有这些问题并使其更可靠?切换到其他声音系统(例如LWJGL OpenAL 或paulscode.com sound system)会有帮助吗?或者是否可以将 Java Sound API 包装在一些安全防护中并且它会正常工作?

我已经制作了一个小应用程序来测试以上所有内容,但它有点长,所以我想将它发布为gist,但不幸的是,GitHub 现在遇到了一些网络问题。所以,我想,我会稍后发布它。

【问题讨论】:

“简单的数字样本” “.ogg 或类似的压缩格式” 压缩的声音格式并不简单。 -- 也许JavaFX 提供了合适的控件或API。好问题顺便说一句。 +1 这取决于 simple 的定义 ;) 一般来说,如果我理解正确,即使在 Java Sound API 中,也可以为 mp3 或ogg,但我什至没有探索过这个机会,因为这对我的案子来说太麻烦了。我肯定会对只使用 wavs 感到满意。 “我肯定只对 wav 感到满意。”(耸耸肩)如果你能找到一个听起来可靠的 API,我怀疑它会支持压缩格式。我之前添加了 JMF 的 mp3plugin.jar 以在 Java 声音应用程序中支持 MP3。我建议使用 JavaFX,希望它使用一些完全替代的 API 来替代 Java Sound(正如您所注意到的,它并不完全可靠)。但是说到 Linux 和声音,Linux 缺乏声音支持是导致Jamie Zawinski 为 Mac 折腾 Linux(为此他写了很多代码)的原因。 ;) 我个人喜欢通过 LWJGL 使用 OpenAL 您可以尝试使用 gstreamer for Java。我过去使用过它,理解如何设置音频管道需要一些努力,但是一旦你让它运行它应该能够处理你扔给它的几乎任何东西。 code.google.com/p/gstreamer-java 【参考方案1】:

我在 Java-gaming.org 上发布了一个相当简单、有限的混音器,欢迎您通过以下网址查看: http://www.java-gaming.org/topics/simple-audio-mixer-2nd-pass/27943/view.html

第一篇文章中列出的 jar 有源代码和示例用法,我投入了一些精力来制作 javadoc cmets。 (98% 的下载是我包含的单个样本 wav。)此外,线程上有很多 api 信息。

它仍然存在 Linux 问题。但是您的分析给我留下了深刻的印象,我想分享一下尝试解决问题并解决此问题的努力!

关于你的观点:

我记得听说在某些 Linux 系统中,单一输出是可能的,并且某些应用程序无法公平播放,并在发生争用时将音频释放到 Java。如果这是准确的,那么很难将其称为 Java 问题,而可能是 Linux 操作系统问题?

第二点:我还没有尝试从 Linux 中的 Mixer 加载,但我知道有些人已经能够通过我的 Web 应用 Java Theremin 做到这一点。在那个应用程序(链接在上面的线程中)中,我包含一个允许用户选择混音器的下拉菜单。至少有一些 Linux 用户在这方面取得了成功。

我没有使用过 Big-Endian wavs -- 但只使用了 little-endian wavs。您必须翻转 Audacity 中的字节或类似的东西才能使用我目前的混音器。

我的系统可以处理并发。您将 wav 加载到 PFClipData 对象中。然后,您可以通过 PFClipShooter(可以处理并发播放——20 或 30,以及不同的音高)或 PFClipLooper(将使用结尾的可选重叠模式循环剪辑以帮助平滑循环点)。所有输出都在后台汇集到单个 SourceDataLine。

我还没有实现 ogg 或 mp3,只有 16 位、44100fps 立体声 little-endian wav 文件。

如果有其他人愿意分享,我很乐意考虑将其作为开源 git 项目。

--我最近成功地在我的 PC 上的双启动分区中安装了 Linux(Ubuntu 桌面),并且正准备安装声卡,看看我是否重新创建并希望能解决所描述的一些问题。 Ubuntu 同时具有 OpenJDK 和 Oracle 的 JDK,所以我希望看看 Java 实现是否可能是问题的一部分。正在进行中...

【讨论】:

谢谢,菲尔!我一定会查看您的解决方案,稍后会报告。 我看了一下,基本上,我看到您自己实现了一个软件混音器。您在PFAudioMixer 的循环中总结来自PFMixerTracks 的数据 - 这很好,但在我看来它遗漏了一些重要的点:1)良好的信号混合比仅仅总结所有内容要复杂得多(并且得到奇怪的剪辑伪影),2)信号混合主要在现代声卡上的硬件中完成,3)即使它不是硬件混合,在纯 Java 中执行这种 CPU 密集型工作也不是最佳选择。 我明白你的意思:你正在做所有的事情,没有额外的依赖和复杂性,所以它会在任何地方工作的解决方案。但是,我不确定回归到纯 Java 中的软件混合是否值得。我会记住您的解决方案,如果我找不到一组解决方法来提高 JavaSound 自身的可靠性,我会使用它。 如果您遇到答案,请在此处回复!我认为在您的第二条评论中,您会了解我的实现的偏差:我不担心剪辑,因为可以/应该由程序员在发布应用程序之前对级别进行基本质量控制来处理。我同意硬件混合会更好。最后一点,我认为 CPU 的成本比通常想象的要低,很多事情可以用它的容量的 2% 来完成。感谢您查看并评论代码。 从技术上讲,正确的音频信号混合比仅仅添加单个样本要困难得多。有十几种方法,其中大多数都非常消耗 CPU(例如进行高分辨率 FFT,将频谱相加,然后对结果进行逆 FFT)。大多数方法都可以在没有削波的情况下达到不错的声级,甚至不需要采取额外的削波去除方法,例如限制。例如,您可以使用res = A + B - A * B 技术改进求和,如vttoth.com/CMS/index.php/technical-notes/68 中所述

以上是关于Java中可靠的声音API,用于简单的数字样本播放的主要内容,如果未能解决你的问题,请参考以下文章

防止 Java 7/8 采样声音在播放时点击(适用于 6)

在 iOS (Novocaine) 上播放和停止声音样本

如何简单地预加载声音并在 Java 中播放?

游戏会话声音播放/录音

麦克风采集与播放 (源码)

使用网络音频 api 播放简单的声音