两次平行播放相同的声音

时间:2013-08-10 16:21:34

标签: java multithreading audio

我有以下代码在游戏中播放我的声音:

protected void playSound(final String sound, final int playTime){
        try {
            sounds.get(sound).open();
        } 
        catch(Exception e) {
            System.out.println(e);
        }
        new Thread(new Runnable() {
            public void run() {
                Clip actualClip = sounds.get(sound);
                actualClip.setFramePosition(0);
                if(playTime < 0){
                    actualClip.loop(Clip.LOOP_CONTINUOUSLY);
                }
                else{
                    actualClip.loop(playTime - 1);
                }
            }
        }).start();
    }

声音保存在散列图中:

private HashMap<String, Clip> sounds;

当我“在同一时间”播放两种不同的声音时(相差1 ms;)),它们彼此平行播放;所以我可以在同一时间听到两个声音。看起来像这样:

playSound("sound1", 1);
playSound("sound2", 1);

但是当我尝试两次播放相同的声音时,它不起作用:

playSound("sound1", 1);
//here its waiting in my programm
playSound("sound1", 1);

问题是,我想添加一个“死亡”声音 - 但是两个怪物也可以在同一时间死亡,或者只是一秒钟死亡。当发生这种情况时,要么没有任何反应,要么声音只播放一次。

为什么呢?我想我在自己的线程中创建一个相同文件的新AudioClip?那为什么它不工作呢?

3 个答案:

答案 0 :(得分:1)

首先,您可以调试方法以显示是否发出声音&#39;是为了确保它不重叠,所以你不能听到是否真的同时播放了相同的声音?

我认为可能在这里发生的是,在播放SAME剪辑时,其中一个线程将尝试访问另一个线程当前正在访问的相同数据,从而导致您正在描述的错误。您可能希望在Java中查看同步。

答案 1 :(得分:0)

当您演奏两次相同的声音时,音量会大一些吗?在完全相同的时间播放精确的声音与将单个声音的振幅加倍相同。我认为两个相同的声音在一起播放时听起来可能无法听到个别声音。

答案 2 :(得分:0)

原因很简单:阅读Clip.play()的Javadoc。它清楚地表明,无论何时播放,该方法都不会执行任何操作。 setFramePosition 的 Javadoc 说“当剪辑下次开始播放时,它将从在此位置播放帧开始。”因此,它不能保证帧指针在请求时立即移动;它只说指针将在您想要的位置,无论何时播放剪辑。

Clip 在内部使用 InputStream,它将在播放时指向当前的音频帧。如果向 Clip.play() 发出两个请求,该指针应该做什么?开发人员选择什么都不做,这是一个很好的设计选择,因为它比播放随机音频帧更受欢迎,因为它可以并行访问相同的输入流。

解决方案:对于每个播放音频的请求,构造一个新的 Clip 并为其提供一个新的 InputStream(包装成一个 AudioInputStream)。它还减少了额外的工作,例如重置帧指针,因为您使用 Clip 只播放一次。

为了提高效率,请读取音频文件一次并将其存储到 byte[] 中。然后,使用该 ByteArrayInputStream 构建一个 byte[] 并将其输入到新剪辑中。这样,您在直接从内存中读取音频数据时就不需要访问磁盘。

byte[] arr = ...;                 // audio data (e.g. read from disk)
Clip clip = AudioSystem.getClip();
ByteArrayInputStream bis = new ByteArrayInputStream(arr);
AudioInputStream ais = AudioSystem.getAudioInputStream(bis);
clip.open(ais);
clip.start();

注意资源也需要清理,这在示例中没有显示。