我使用以下代码创建了一个音频剪辑(javax.sound.sampled.Clip):
public Clip getClip() throws Exception {
AudioInputStream in = AudioSystem.getAudioInputStream(getClass().getResource("test.wav"));
Clip clip = AudioSystem.getClip();
clip.open(in);
return clip;
}
...
this.clip = getClip();我正在多次触发夹子的回放:
public void play() {
clip.stop();
clip.flush();
clip.setFramePosition(0);
clip.start();
}我正在通过使用JFrame并在每个按键(这里有完整的测试课程)上调用play()来测试这一点。在大多数情况下,声音播放与每一个按键,无论按键被按多快。但有时,当键被快速按下,声音跳过其中一个键,根本不播放。这是在游戏中实现的,所以一致的声音回放是相当重要的。
通过研究这个问题,我想到了这个问题,它建议每次回放结束时都关闭这条线路,如下所示:
clip.addLineListener(e -> {
if (e.getType() == LineEvent.Type.STOP) e.getLine().close();
});但在这第一次之后,这一切都停止了。
我尝试过的其他事情:
.stop()、.flush()和.drain()在play()中的各种组合。java.applet.AudioClip而不是Clip我测试了一些个人电脑,这个问题在高端个人电脑上似乎不那么突出,或者至少不那么引人注目。如果是这样的话,有什么可以做的来提高低端系统的播放一致性吗?如果这里的问题是实现,那么实现这个问题的正确方法是什么?
发布于 2017-07-19 01:53:00
这可能是因为Clip没有提供您需要的粒度级别。我不认为在这方面对Clip有任何特殊的要求。如果缓冲区大小太大,SourceDataLine会阻塞,所以Clip可能有类似的情况,但是Clip不允许您指定内部缓冲区大小。
如果是我,我会使用一个Clip-like对象来编写自己的SourceDataLine对象,这样我就可以指定一个特定的缓冲区大小,可能是以与游戏相同的帧速率编写的,或者它的一些部分。
int bytesPerSecond = (int) fmt.getSampleRate()
* fmt.getFrameSize();
int targetGameFPS = 30;
int bufferSize = bytesPerSecond / targetGameFPS;请注意,使用太小的缓冲区可能会导致诸如单击或撕毁速度较慢的计算机之类的工件。
然后,确保使用此缓冲区大小调用sourceDataLine.open(audioFmt, bufferSize)。
缺点是您需要自己编写start和stop控件,使用后台线程和同步。(这并不难,但确实意味着这不是一个简单的解决方案。)
我不能肯定这会解决问题,但这可能是我下一步要尝试的。(从长远来看,编写自己的音频播放器也有更大的灵活性。首先,Clip没有一个非常令人印象深刻的特性。)
发布于 2017-07-19 16:53:54
你写的代码在我看来很好。我不确定您是否需要flush方法,但是当我注释掉它时,它无助于性能。
如果我理解Radiodef的理论,为Clip执行内部缓冲区的时间可能比击键之间的时间更长,如果它们非常接近的话。除了缓冲区边界之外,Clips和SourceDataLines都因不允许更改而臭名昭著。(例如,如果尝试执行卷淡出,可能会很烦人。)
有一种方法可以指定Clip的缓冲区大小。API是这里。
open(AudioFormat format, byte[] data, int offset, int bufferSize)在您的情况下,很有可能指定一个低缓冲区大小,尽管辍学的风险会增加,特别是如果有其他声音播放的话。老实说,我还没有尝试过这种加载和播放Clips的方式。请注意,为了使用此方法,必须将PCM数据保存在字节数组中。我在StackOverflow这里见过关于如何做到这一点的问题。
另一种解决方案是使用击键捕获方法加载数组(可能包括时间戳),并让另一个进程从该数组执行音频播放。然后,你可以修补所需的最短时间之间的游戏,以确保每一笔有一个相关的发挥。
作为一种选择,我想提供的是使用AudioCue。我知道你是作为一个学习项目来做这件事的,在这个基础上,我可能不愿意使用预先写好的课程。但是,在这种情况下,源代码是可用的,因此您可以自由地检查和编辑/修改代码。其中涉及三个文件,主类以及用于实现侦听器的Interface和helper类。
AudioCue完全具备Radiodef推荐的能力。该类将媒体数据加载到数组中,并通过SourceDataLine播放。当您open AudioCue时,有一个允许您选择指定缓冲区大小的方法。AudioCue可以像Clip一样播放,可以停止、重置和启动单个实例,但它也支持并发回放,并且还具有一些附加功能(例如,实时卷、摇摄、回放速度限制器,它们基于每帧响应,而不仅仅是在缓冲区边界上)。
如果没有其他问题,您可以查看代码,并查看如何按照Radiodef的建议通过Clip-like实现SourceDataLine的示例。
https://stackoverflow.com/questions/45179495
复制相似问题