【问题标题】:Play the same sound twice parallel平行播放两次相同的声音
【发布时间】:2021-07-09 21:40:50
【问题描述】:

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

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 毫秒;))时,它们彼此平行播放;所以我可以同时听到两种声音。看起来像这样:

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

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

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

问题是,我想添加一个“死亡”声音 - 但两个小怪也可以同时死亡,或者只是在另一个之前一秒钟。当这种情况发生时,要么什么都没有发生,要么声音只播放一次。

为什么?我认为我在自己的线程中创建了同一文件的新 AudioClip?那么为什么它不起作用呢?

【问题讨论】:

  • 问题要简明扼要,内容不能有Good day!Thank you very much!等备注。这可能是有人编辑了您的问题的原因。您可以使用 cmets。
  • 小建议,你应该使用接口Map而不是使用实现!
  • @GGrec "他们的内容必须缺少诸如 Good day! 或Thank you very much! 之类的备注。"它是否写在 SO 规则的某处?如果您遇到过,请分享链接。
  • @rahulserver 我在之前的常见问题解答中以及 meta. Here's 一个。
  • @marcAndre 这有什么特别的原因吗?但是,好吧,我改变了这一点。知道为什么我有这个问题吗? ://

标签: java multithreading audio


【解决方案1】:

首先,您能否调试该方法以显示“声音”是否已播放,以确保它没有重叠,这样您就无法听到是否实际上同时播放了相同的声音?

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

【讨论】:

  • 同步....我对此一无所知,但我知道一种方法可以同步。是这个吗?
【解决方案2】:

当您播放相同的声音两次时,音量会变大吗?在完全相同的时间播放确切的声音与将单个声音的幅度加倍相同。我认为,当两个相同的声音靠在一起播放时,您在聆听时可能听不到单独的声音。

【讨论】:

  • 但这也发生在一个怪物死了,1秒后另一个怪物死了......我想我可以在这里区别。
【解决方案3】:

原因很简单:阅读Clip.play()的Javadoc。它明确指出,无论何时播放正在进行,该方法都不会做任何事情。 setFramePosition 的 Javadoc 说“当剪辑下一次开始播放时,它将从在此位置播放帧开始。”因此,它不能保证帧指针在请求时立即移动;它只是说,每当下次播放剪辑时,指针就会位于您想要的位置。

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

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

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

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();

请注意,资源也需要清理,示例中未显示。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-03-03
    • 2017-05-21
    • 1970-01-01
    • 1970-01-01
    • 2011-05-20
    • 2017-06-13
    相关资源
    最近更新 更多