我使用以下代码创建了一个音频剪辑 ( 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();
我反复触发 the clip 的播放与:
public void play() {
clip.stop();
clip.flush();
clip.setFramePosition(0);
clip.start();
}
我正在通过使用 JFrame
并在每次按键时调用 play()
来对此进行测试 ( full test class here )。大多数情况下,每次按键都会发出声音,无论按键速度有多快。但有时,当快速连续按下按键时,声音会跳过其中一个按键,根本不会播放。这是在游戏中实现的,因此一致的声音播放非常重要。
研究这个问题让我想到 this question ,建议每次播放结束时关闭该行,如下所示:
clip.addLineListener(e -> {
if (e.getType() == LineEvent.Type.STOP) e.getLine().close();
});
但是在第一次之后就完全停止了播放。
我尝试过的其他事情:
.stop()
的各种组合,.flush()
,和.drain()
在play()
中。- 使用
java.applet.AudioClip
而不是剪辑
- 在每次播放时重新创建 Clip 对象(这只会导致每次播放延迟)
我测试了几台电脑,这个问题在高端电脑上似乎不太突出,或者至少不太明显。如果是这样的话,有什么办法可以提高低端系统上的播放一致性吗?如果这里的问题是实现,那么实现这个问题的正确方法是什么?
最佳答案
Clip
可能无法提供您所需的粒度级别。我认为 Clip
在这方面没有任何特殊要求。如果缓冲区大小太大,SourceDataLine
将阻塞,因此 Clip
可能会发生类似的情况,但 Clip
不会。 t 让您指定内部缓冲区大小。
如果是我,我会使用 SourceDataLine
编写自己的 Clip
类对象,这样我就可以指定一个特定的缓冲区大小,可能是一个大约写入的缓冲区大小与游戏相同或部分帧速率。
int bytesPerSecond = (int) fmt.getSampleRate()
* fmt.getFrameSize();
int targetGameFPS = 30;
int bufferSize = bytesPerSecond / targetGameFPS;
请注意,使用太的缓冲区可能会导致在速度较慢的计算机上出现点击或撕裂等问题。
然后您一定要调用 sourceDataLine.open(audioFmt, bufferSize)
具有此缓冲区大小。
缺点是您需要自己编写start
和stop
控件,使用后台线程和同步。 (这并不难,但这确实意味着它不完全是一个简单的解决方案。)
我不能肯定地说这会解决问题,但这可能是我接下来会尝试的。 (从长远来看,编写自己的音频播放器确实具有更加灵活的好处。Clip
首先并没有令人印象深刻的功能集。)
关于java - 音频剪辑偶尔无法播放,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45179495/