java - 使用 Java 的 Clip 类的问题

标签 java audio javasound

我在开发的游戏中使用 Java 的 Clip 类时遇到问题。具体来说,我认为问题在于同时多次使用同一个剪辑。我做了一些谷歌搜索,有些人建议在自己的线程中运行该剪辑。我想复制剪辑对象,以便它在每个线程中使用不同的剪辑对象,以避免需要“重置”剪辑,但我仍然遇到问题。事实上,问题在于,当同一个剪辑同时播放多次时,游戏似乎会卡住。

Reddit 上有人建议这个问题可能是因为 Clip 类不是同步的,我每次想使用它时都必须打开它。显然,这是行不通的,因为声音的使用非常频繁。

如果有人能够指出该类的致命缺陷,代码如下:

/**
    This file is part of Generic Zombie Shooter.

    Generic Zombie Shooter is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Generic Zombie Shooter is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Generic Zombie Shooter.  If not, see <http://www.gnu.org/licenses/>.
 **/
package genericzombieshooter.misc;

import java.io.IOException;
import java.net.URL;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;

/**
 * Contains all pre-loaded sounds.
 * @author Darin Beaudreau
 */
public enum Sounds {
    // Weapon-Related
    POPGUN("shoot2.wav", false),
    RTPS("shoot1.wav", false),
    BOOMSTICK("shotgun1.wav", false),
    FLAMETHROWER("flamethrower.wav", true),
    THROW("throw2.wav", false),
    EXPLOSION("explosion2.wav", false),
    LANDMINE_ARMED("landmine_armed.wav", false),
    TELEPORT("teleport.wav", false),

    // Zombie-Related
    MOAN1("zombie_moan_01.wav", false),
    MOAN2("zombie_moan_02.wav", false),
    MOAN3("zombie_moan_03.wav", false),
    MOAN4("zombie_moan_04.wav", false),
    MOAN5("zombie_moan_05.wav", false),
    MOAN6("zombie_moan_06.wav", false),
    MOAN7("zombie_moan_07.wav", false),
    MOAN8("zombie_moan_08.wav", false),
    POISONCLOUD("poison_cloud.wav", false),

    // Game Sounds
    POWERUP("powerup.wav", false),
    PURCHASEWEAPON("purchase_weapon.wav", false),
    BUYAMMO("buy_ammo2.wav", false),
    PAUSE("pause.wav", false),
    UNPAUSE("unpause.wav", false);

    private Clip clip;
    private boolean looped;

    Sounds(String filename, boolean loop) {
        openClip(filename, loop);
    }

    private synchronized void openClip(String filename, boolean loop) {
        try {
            URL audioFile = Sounds.class.getResource("/resources/sounds/" + filename);

            AudioInputStream audio = AudioSystem.getAudioInputStream(audioFile);
            AudioFormat format = audio.getFormat();
            DataLine.Info info = new DataLine.Info(Clip.class, format);
            clip = (Clip) AudioSystem.getLine(info);

            clip.open(audio);
        } catch (UnsupportedAudioFileException uae) {
            System.out.println(uae);
        } catch (IOException ioe) {
            System.out.println(ioe);
        } catch (LineUnavailableException lue) {
            System.out.println(lue);
        }
        looped = loop;
    }

    public synchronized void play() {
        play(1.0);
    }

    public synchronized void play(final double gain) {
        Runnable soundPlay = new Runnable() {
            @Override
            public void run() {
                Clip clipCopy = (Clip)clip;
                FloatControl gainControl = (FloatControl)clipCopy.getControl(FloatControl.Type.MASTER_GAIN);
                float dB = (float)(Math.log(gain) / Math.log(10.0) * 20.0);
                gainControl.setValue(dB);
                if(!looped) reset(clipCopy);
                clipCopy.loop((looped)?Clip.LOOP_CONTINUOUSLY:0);

            }
        };
        new Thread(soundPlay).start();
    }

    public synchronized void reset() {
        reset(clip);
    }

    public synchronized void reset(Clip clipCopy) {
        synchronized(clipCopy) { clipCopy.stop(); }
        clipCopy.setFramePosition(0);
    }

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

那么我尝试使用枚举和静态 play() 方法做的事情是否可以实现我想要完成的目标?或者我必须寻找其他方法吗?我可以使用不同的类,甚至在必要时使用库,但我宁愿留在核心 Java 中。

有人能想出一种方法来完成我想做的事情吗?

最佳答案

剪辑不支持并发。您可以随时重新启动剪辑并多次播放。但在任何给定时间,同一时间只能播放一个给定剪辑的一个实例。

TinySound 是一个很好的库,但我不记得他的 Clips 是否允许并发播放。您可以在全力以赴之前检查一下。Java-gaming.org 上有关于这个库的大量帖子。

我不了解 Net Beans,但要在 Eclipse 中使用 Git Hub 源,我按照 Git Hub 提供的有关如何设置帐户和进行克隆的说明进行操作,然后将克隆下载到我的 table ​​面上(使用 Bash)。这些说明并不简单,但这是该产品 (GitHub) 的一个非常基本的使用,您应该能够在其中找到支持。

项目位于桌面上后,我会在 Eclipse 中创建一个新项目,并将提供的桌面克隆的根目录作为位置。 Eclipse 有用于将外部库链接到项目的附加设置。如果 Net Beans 没有类似的东西,我会感到非常惊讶。

Eclipse 还可以从 zip 文件创建项目或链接库。 Zip 与 Jar 文件的格式完全相同。 Net Beans 应该有一种方法来链接到项目的 zip 或 jar。在 Eclipse 中,他们使用术语“归档”来表示存储在 zip 中的项目。但我不知道您拥有的 zip 是否正是作为外部项目加载所需的一切。我对 GitHub 的使用经验很少。

如果您想重复使用枪声或其他任何内容,您可能必须为您希望支持的每个并发副本制作一个剪辑。

我最终编写了自己的剪辑对象,并向其中提供了多个游标,以及一个用于混合来自并发游标的数据的接口(interface)。效果非常好!我愿意向您发送一份副本来试用。它被设置为使用连续运行的音频混合器(我认为与 TinySound 相同)。但如果我向您发送代码,您也许可以重写 Clip 部分以使其独立。 “PFClip”还允许以不同的速度进行平移、音量设置和播放,因此您可以改变枪声的速度。这样做,听起来像是不同的枪(或者如果你让它们非常慢的话会爆炸)。[在这里插入双关语,以获得最大的性价比。]

该库有一些很酷的附加功能:用于循环剪辑的 PFLoop(允许以平滑的方式重叠末端),我刚刚制作了镶边、立体声合唱和音高可变回声。与 TinySound 不同,除了 44100fps 和 16 位的立体声 wav 格式外,我不支持任何其他格式。我为我正在开发的游戏编写了此内容,可通过浏览器玩:http://hexara.com/game.html风铃效果是使用并发剪辑的效果。

忘记了!我之前有另一个混音器演示,它同时以多种方式使用一次枪声: enter link description here JGO 中列出的代码已经过时,但线程下方有一页包含可用于制作您自己的并发剪辑的基本思想。

第二个链接网站上提供了联系信息。

关于java - 使用 Java 的 Clip 类的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19168231/

相关文章:

java - 使用 Java 播放奇异的 WAV 文件

java - 如何使用 java 声音 API 从麦克风捕获声音?

java - 如何实例化抽象类以删除 NullPointerException?

java - 让 Smackx PubSub 工作

c - PortAudio 麦克风捕获,单独的 channel 值

c - 生成错误的 WAV 文件

java - GWT - 我的 flowpanel 的 html 文件的结构

java - 将 .json 文件转换为 JSONArray

javascript - 播放新音频时暂停先前的音频

java - Java麦克风TargetDataLine灵敏度/最大输入振幅