libGDX 提供了播放音效和声音片段的方法。 它还提供了对音频硬件的读写访问接口。所有音频设备的访问都是通过音频模块完成的:
Audio audio = Gdx.audio;
当应用程序暂停并恢复时,libGDX 的音频模块将会自动暂停并恢复所有音频播放。
音频模块可以为您提供直接访问音频硬件,以便向其写入 PCM 样本。音频硬件是通过 AudioDevice (source) 接口提取的。一下为创建一个新的 AudioDevice 实例:
AudioDevice device = Gdx.audio.newAudioDevice(44100, true);
这将创建一个采样频率为44.1 khz 并输出 mono 的新 AudioDevice。如果无法创建设备,则将抛出一个 GdxRuntimeException。
我们可以将16位有符号 PCM 或32位浮动 PCM 数据写入设备:
float[] floatPCM = ... generated from a sine for example ...
device.writeSamples(floatPCM, 0, floatPCM.length);
short[] shortPCM = ... generated from a decoder ...
device.writeSamples(shortPCM, 0, shortPCM.length);
如果使用立体声,左右通道样本通常交错(第一个浮点/短 -> 左,第二个浮点/短-> 右)
以毫秒为单位的延迟可以这样查询:
int latencyInSamples = device.getLatency();
这将返回样本中音频缓冲区的大小,从而为您提供关于延迟的良好指示器。返回值越大,音频到达收件人所需的时间就越长。请注意,几乎所有安卓手机的延迟都高得离谱。实时音频应用程序很难达到10-30毫秒的实用范围。通常你可以达到100毫秒的延迟,许多手机的延迟高达400毫秒。遗憾的是,这是一个与驱动程序/操作系统相关的问题,无法解决。
AudioDevice 是本地资源,不再使用时需要处理:
device.dispose();
JavaScript/WebGL 不支持直接 PCM 输出。
您可以通过 AudioRecorder 接口从 PC 或 Android 手机上的麦克风访问 PCM 数据:
AudioRecorder recorder = Gdx.audio.newAudioRecorder(22050, true);
这将创建一个在单声道模式下采样率为22.05千赫的 AudioRecorder。如果无法创建记录器,将抛出一个 GdxRuntimeException
样本可以读作16位有符号 PCM:
short[] shortPCM = new short[1024]; // 1024 samples
recorder.readSamples(shortPCM, 0, shortPCM.length);
立体声样本通常交错存储(第一个样本 > 左通道,第二个样本 > 右通道)。
AudioRecorder 是一种本地资源,如果不再使用需要处理:
recorder.dispose();
JavaScript/WebGL 不支持音频录制。
音效是一些小的音频样本,通常不超过几秒钟,在特定的游戏事件中播放,比如角色跳跃或者开枪。
音效可以以各种格式存储,包括 MP3、 OGG 和 WAV。您应该使用哪种格式,取决于您的具体需要,因为每种格式都有自己的优点和缺点。例如,WAV 文件与其他格式相比相当大,OGG 文件在 RoboVM (iOS)和 Safari (GWT)上都不能工作,而且 mp3文件存在不适当的循环问题。
注意: 在 Android 上,Sound 实例的大小不能超过1mb (未压缩的原始 PCM 大小,而不是文件大小)。如果你有一个更大的文件,得使用 Music 代替。
音效由 Sound 接口表示。加载音效的代码如下:
Sound sound = Gdx.audio.newSound(Gdx.files.internal("data/mysound.mp3"));
这将从内部目录数据中加载一个名为“ mysound.mp3”的音频文件。一旦我们加载了声音,我们就可以播放它:
sound.play(1.0f);
这将播放音效一次,在全音量。单个 Sound 实例上的 play 方法可以连续调用多次,例如对于一个游戏中的一系列镜头,它将被相应地覆盖。可以使用更细粒度的控制。每次调用 Sound.play ()都返回一个 long,用于标识音效实例。使用这个句柄我们可以修改特定的回放实例:
long id = sound.play(1.0f); // play new sound and keep handle for further manipulation
sound.stop(id); // stops the sound instance immediately
sound.setPitch(id, 2); // increases the pitch to 2x the original pitch
id = sound.play(1.0f); // plays the sound a second time, this is treated as a different instance
sound.setPan(id, -1, 1); // sets the pan of the sound to the left side at full volume
sound.setLooping(id, true); // keeps the sound looping
sound.stop(id); // stops the looping sound
目前,这些方法在 JavaScript/WebGL 中的功能有限。从1.9.6开始,setPan ()只有在支持 flash 并启用 gwtapplicationconfiguration.preferveflash = true 的情况下才能工作
setPan ()方法不适用于立体声
一旦你不再需要一个 Sound,确保你处理掉它:
sound.dispose();
在释放声音后访问它将导致未定义的错误。
正如在一个 Audio 上所说的,Android 在音频方面有很多问题。其中之一是,等待合理的 ID 可能需要很长时间。Libgdx 的音乐默认是同步播放的。如果你同时播放很多声音,它会导致主循环被冻结很长时间。特别是这个问题在 Android 10上很明显。解决方案是让它们异步播放。但是,在需要 ID 的地方,它将导致无法使用 sounds 方法。
对于任何超过几秒钟的声音,最好是从磁盘流传出来,而不是将其完全加载到 RAM 中。libGDX 提供了一个音乐接口:
Music music = Gdx.audio.newMusic(Gdx.files.internal("data/mymusic.mp3"));
这将从内部目录数据中加载一个名为“ mymusic.MP3”的 mp3文件。
播放代码如下:
music.play();
当然,您可以设置 Music 实例的各种播放属性:
music.setVolume(0.5f); // sets the volume to half the maximum volume
music.setLooping(true); // will repeat playback until music.stop() is called
music.stop(); // stops the playback
music.pause(); // pauses the playback
music.play(); // resumes the playback
boolean isPlaying = music.isPlaying(); // obvious :)
boolean isLooping = music.isLooping(); // obvious as well :)
float position = music.getPosition(); // returns the playback position in seconds
Music 实例是重量级的,使用时得注意加载的数量。
如果不再需要 Music 实例,则需要将其释放,以释放资源。
music.dispose();
安卓后端使用 SoundPool API 播放声音,MediaPlayer 播放音乐。这些 API 在某些情况下有一些限制和已知的问题:
@Override
public AndroidAudio createAudio(Context context, AndroidApplicationConfiguration config) {
return new AsynchronousAndroidAudio(context, config);
}
一般来说,Android 上的音频是有问题的,可能还有其他的场景或设备特殊的问题。
为了解决这些问题,Google 创建了 Oboe,这个 Oboe 可以用于 libGDX 项目,这要感谢 libGDX Oboe。