java - 播放整个音频剪辑时遇到问题

标签 java audio javasound game-development

我使用 javax.sound.sampled.Clip 在游戏中播放音效(WAV 文件)。不过,这2秒的效果并没有全部发挥出来,只是发挥了一部分。如果我添加 clip.loop(2) 那么声音会正确播放多次,即整个 2 秒。有什么问题以及如何修复它?

package com.zetcode;

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.JOptionPane;

public enum SoundEffect {

    EXPLODE("resources/explosion.wav"),
    HITGROUND("resources/impact.wav"),
    CANNON("resources/cannon.wav");

    private Clip clip;

    SoundEffect(String soundFileName) {
        try {
            URL url = this.getClass().getClassLoader().getResource(soundFileName);

            try (AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(url)) {
                clip = AudioSystem.getClip();
                clip.open(audioInputStream);
            }
        } catch (UnsupportedAudioFileException | IOException | LineUnavailableException e) {
            JOptionPane.showMessageDialog(null, "Cannot play sound", "Error",
                    JOptionPane.ERROR_MESSAGE);
        }
    }

    public void play() {

        if (clip.isRunning()) {
            clip.stop();
        }
        clip.setFramePosition(0);
        clip.start();
    }

    static void init() {
        values();
    }    
}

最佳答案

您是否意识到并考虑到以下事实:当播放剪辑时(通过 start() 方法),播放代码的线程是守护进程?与常规线程不同,守护线程不会阻止 Java 程序关闭。如果您的程序在声音完成执行之前结束,则守护程序状态线程即使尚未完成也会结束。

您是否尝试运行示例代码 SoundClipTest 或 SoundEffectDemo?在我看来,一旦程序执行 start() 方法,SoundClipTest 就会退出,而 SoundEffectDemo 将持续存在并允许声音完成播放。

<小时/>

** 编辑 1 **

<小时/>

我现在才注意到您已将 SoundEffect 设为枚举。我以前从未见过使用这种方法。

我使用的一种方法是创建一个名为 GameSound 的类,并让其构造函数单独预加载每个 Clip 并将它们保存在内存中,准备播放。然后,我创建一个或多个方法来播放它们。

gameSound.playExplosion();

GameSound 类将为每个声音效果提供实例变量。

private Clip explosion;
private Clip cannon;

GameSound 构造函数将具有:

URL url = this.getClass().getResource("resources/explosion.wav");
AudioInputStream ais = AudioSystem.getAudioInputStream(url); 
explosion = AudioSystem.getClip();
explosion.open(ais);

GameSound 会有方法:

public void playExplosion()
{
    if (explosion.isRunning()) {
        explosion.stop();
    }
    explosion.setFramePosition(0);
    explosion.start();
}

如果您希望让 play 方法接受一个参数并在 switch 结构上使用该参数来选择要播放的声音,那也很好,特别是因为它消除了一些代码重复。

这种方法可能效果更好(随着代码变得更加复杂,可以轻松添加到 GameSound),并且编码应该不会太困难。我很想知道更改此计划是否可以使问题消失。

<小时/>

** 编辑 2 **

<小时/>

对于上述代码中的错误,我们深表歉意。当我制作以下示例时,我修复了它们。不过,有一个想法,当我去听你链接的音效时,一些爆炸一开始就被截断了。您是否在其他应用程序(例如 Windows Groove Music,或从您获取它们的网站)中播放过这些声音,并验证了它们在您的代码中播放时是否不同?

这是一个快速的“GameSound”。我从您的引用文献中下载了 Explosion+2.wav 文件,因为它比其他文件更长并且结尾清晰,我正在引用该文件。

package stackoverflow.janbodnar;

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.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;

public class GameSound {

    private final Clip explosion;

    public GameSound() throws LineUnavailableException, 
    IOException, UnsupportedAudioFileException
    {
        URL url = this.getClass().getResource(
                "resources/Explosion+2.wav");
        AudioInputStream ais = AudioSystem.getAudioInputStream(url);
        explosion = AudioSystem.getClip();
        explosion.open(ais);
    }

    public void playExplosion()
    {
        if (explosion.isRunning()) {
            explosion.stop();
        }
        explosion.setFramePosition(0);
        explosion.start();
    }
}

这是一个调用 GameSound 的简单 Swing 按钮。

package stackoverflow.janbodnar;

import java.io.IOException;

import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class GameSoundTest extends JFrame
{
    private static final long serialVersionUID = 1L;

    private final GameSound gameSound;

    public GameSoundTest() throws LineUnavailableException,
    IOException, UnsupportedAudioFileException
    {
        gameSound = new GameSound();

        JButton button = new JButton("Play Explosion");
        button.addActionListener(e -> gameSound.playExplosion());
        getContentPane().add(button);
    }

    private static void createAndShowGUI()
    {
        JFrame frame;
        try 
        {
            frame = new GameSoundTest();
            frame.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
            frame.setBounds(0, 0, 200, 200);
            frame.setVisible(true);
        } 
        catch (LineUnavailableException | IOException 
                | UnsupportedAudioFileException e) 
        {
            e.printStackTrace();
        }
    }

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

当然,这一切都假设有一个名为“resources”的子文件夹,其中包含爆炸 SFX。

关于java - 播放整个音频剪辑时遇到问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50797938/

相关文章:

audio - 在 Fortran 90/95 中生成声音/蜂鸣声

android - 将值从 Activity 传递到服务android

Java AudioSystem 和 TargetDataLine

java - 声音将无法在Java中播放。.JFrame和Canvas不是Applet

java - 如何为Java JFrame添加声音?

java - 通过构造函数传递实例或使用静态访问它?

java - 将二叉树转换为排序数组

java - GridBagConstraints 在 Java 中如何工作

java - 是否有任何 IntelliJ 功能可以在两个相似对象之间映射值?

python - 改变音效 Pygame