gpt4 book ai didi

java - 使用 Swing 播放音频 - 我的代码线程安全吗?

转载 作者:行者123 更新时间:2023-12-02 11:19:17 25 4
gpt4 key购买 nike

我实际正在做的事情的简化版本(计时器到期时触发蜂鸣器声音),但这足够好地展示了我的设计。

一旦开始播放,Swing 就不再需要触摸音频剪辑。我已经能够确认这段代码确实会播放声音并且不会阻塞事件调度线程,但我想确保不存在我在不知不觉中违反的其他线程安全问题。谢谢!

import java.io.IOException;
import java.net.URL;

import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class TriggeringSoundTest extends JFrame
{
private static final long serialVersionUID = -4573437469199690850L;

private boolean soundPlaying = false;

public TriggeringSoundTest()
{
JButton button = new JButton("Play Sound");
button.addActionListener(e -> new Thread(() -> playBuzzer()).start());
getContentPane().add(button);
}

private void playBuzzer()
{
URL url = getClass().getResource("resources/buzzer.wav");
try (AudioInputStream stream = AudioSystem.getAudioInputStream(url);
Clip clip = AudioSystem.getClip())
{
clip.open(stream);
clip.addLineListener(e -> {
if (e.getType() == LineEvent.Type.STOP)
soundPlaying = false;
});
soundPlaying = true;
clip.start();
while (soundPlaying)
{
try
{
Thread.sleep(1000);
}
catch (InterruptedException exp)
{
// TODO Auto-generated catch block
exp.printStackTrace();
}
}
}
catch (UnsupportedAudioFileException exp)
{
// TODO Auto-generated catch block
exp.printStackTrace();
}
catch (IOException exp)
{
// TODO Auto-generated catch block
exp.printStackTrace();
}
catch (LineUnavailableException exp)
{
// TODO Auto-generated catch block
exp.printStackTrace();
}
}

private static void createAndShowGUI()
{
JFrame frame = new TriggeringSoundTest();
frame.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}

public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> createAndShowGUI());
}
}

最佳答案

我已经摆脱了使用 Clips 的所有麻烦,因为它们有很多烦人的方面。

但是,正如评论中提到的,您应该在程序开始时初始化(打开)每个 Clip 一次且仅一次。然后,每次需要时重新启动Clip。 API 将向您显示将 Clip 重置为其起始帧并重播的确切命令。

管理线程很烦人。如果我理解正确的话,Clip 会启动一个用于实际音频输出的守护线程。当父线程终止时,守护线程就会终止。因此,您的解决方案是放入 Thread.sleep() 命令和 LineListener 以使线程在 SFX 期间保持 Activity 状态,这样它的守护进程就不会被 chomped当父线程终止时。

但我担心,如果您使用这种方法,调用线程(按下按钮启动的代码)将无法在您的“ sleep ”期间执行任何其他操作。如果按钮代码仅执行播放,那么应该没问题。但是,如果您稍后决定通过按下相同的按钮来触发其他内容(例如蜂鸣器动画)怎么办?可能有必要添加另一层复杂性,例如将剪辑包装在另一个线程中,该线程唯一的职责是剪辑播放。然后,按下按钮就可以启动这个包装器并执行其他操作,而不会受到 Thread.sleep() 解决方案的影响。

现在,应该可以避免这一切了!假设您制作了一个名为 buzzerClip 并在程序的早期打开它,将其保存在实例变量中(按照 Andrew 的建议)。然后,假设您从无限期保持 Activity 状态的线程(例如经典的游戏循环)中调用 buzzer.start()。我认为在此情况下,Clip start() 将启动它的守护线程,并只要该游戏循环线程继续存在就可以播放。这样,你就可以省去 sleep 和听歌了。但如果没有实际编写示例并亲自尝试,我并不能 100% 确定。

(按钮线程如何告诉游戏循环线程播放声音?也许按钮线程设置一个标志,游戏循环线程检查该标志作为其正常“更新”周期的一部分。嗯。也许在提供建议之前,我确实需要尝试对其进行编码。)

作为替代方案,请随意检查代码,并可能使用 AudioCue 。它在加载和播放方面类似于 Clip,但其底层使用 SourceDataLine 而不是 Clip 进行输出。如果您查看代码,您将看到 SourceDataLine 被维护在它自己的线程中(它作为 AudioCue 被打开的一部分保持 Activity 状态)。这消除了很多复杂性。

这种方法的一个很好的优点是它可以提供并发播放——例如,如果您想在第一个蜂鸣器仍在播放时启动第二个蜂鸣器实例。 AudioCue 还允许您执行诸如以不同速度播放蜂鸣器之类的操作,这对于利用 SFX 听起来像多个提示可能很有用。我尽力提供文档和示例。该许可证允许您剪切和粘贴代码,以最适合您的特定需求。

关于java - 使用 Swing 播放音频 - 我的代码线程安全吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50049245/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com