我在Java中看到了Clip
个实例的一些奇怪行为。
我正在处理的类的目的是保持包含相同声音样本的Clip
个实例的数量(由URI
索引。)当应用程序请求播放剪辑时已经有三个或更多来自同一来源的剪辑已经播放,执行以下步骤:
PAN
和framePosition
的加权总和对当前播放的片段进行排序。
void restart(Clip clip, float gain, float pan) {
clip.stop();
clip.flush();
pan = Math.max(-1f, Math.min(pan, 1f));
((FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN))
.setValue(gain);
((FloatControl) clip.getControl(FloatControl.Type.PAN))
.setValue(pan);
clip.setFramePosition(0);
clip.start();
}
如果快速连续多次调用此方法(例如1ms内20次),则会出现奇怪的行为:
START
个事件,表示已开始播放STOP
事件。stop
和start
的后续调用无效(但不会抛出异常。)0
,即使剪辑可听到(最后一次)。知道可能导致这种情况的原因吗?
我不认为这是一个线程问题(至少在我的代码中没有。)只有一个线程调用我的类的公共方法(并且它们都是synchronized
无论如何)
可能与this bug有关。
答案 0 :(得分:1)
在AbstractDataLine
内DataLine.start
的调音台上,DataLine.stop
和DataLine
的来电已经已经同步。
我强烈怀疑有一些调用堆栈(在implStart()
/ implStop()
之下,你所拥有的DataLine
化身,很可能在本地nStart
/ {{1 }})至少进行一次异步调用,从而导致您观察到的竞争条件。
使用nstop
或任何其他Java构造来解决此类问题是不可能的,而不必更深入地了解所调用的本机实现。
可行的,即时解决方法可能关闭旧剪辑并打开新实例而不是倒回旧实例。不是最佳的,但在进行更深入的调查之前,它可能会成功。
为了能够执行上述更深入的调查,您必须知道自己所处的平台,以及对synchronized
和{{1}的实际(实现)类名的确认实例。
<强>更新强>
同时,请使用内省设置Clip
(或在Mixer
中提供您自己的com.sun.media.sound.Printer
实施。)
基本上DirectClip.open()
spawns一个thread以非线程安全的方式访问多个volatile variables(特别感兴趣的是com.sun.media.sound.Printer.trace = true
),这可能会导致主回放循环挂。
您可以通过在明显挂起时强制thread dump并检查回放线程状态/堆栈跟踪来确认(或确认)此(与CLASSPATH
跟踪一起使用)(或使用调试器。)
如果doIO
等访问结果而不是成为问题,那么继续挖掘本机实现仍然是要做的事情;如果Printer
等访问 再次成为问题,那么就没有简单的解决方法(您可以尝试使用内省来抓取doIO
并定期发出信号因为doIO
而导致它失速 - 再次确认。)