自定义音频流
前言
AudioStream是所有音频发射对象的基类.AudioStreamPlayer绑定到AudioStream以将PCM数据发送到管理音频驱动程序的AudioServer.
所有音频资源都需要两个基于音频的类:AudioStream和AudioStreamPlayback. 作为数据容器,AudioStream包含资源并将其自身暴露给GDScript.AudioStream引用其自己的内部自定义AudioStreamPlayback, 该AudioStreamPlayback将AudioStream转换为PCM数据.
本指南假设读者知道如何创建C++模块. 如果没有, 请参考该指南 自定义 C++ 模块.
参考:
可以做什么?
绑定外部库(如Wwise, FMOD等).
添加自定义音频队列
添加对更多音频格式的支持
创建 AudioStream
AudioStream由三个组件组成: 数据容器, 流名称和AudioStreamPlayback朋友类生成器. 音频数据可以通过多种方式加载, 例如使用用于音调发生器的内部计数器, 内部/外部缓冲区, 或文件引用.
某些AudioStream需要是无状态的, 例如从ResourceLoader加载的对象.ResourceLoader加载一次并引用同一对象, 而不管在该特定资源上调用了多少次 load
. 因此, 播放状态必须被包含在AudioStreamPlayback中.
/* audiostream_mytone.h */
#include "core/reference.h"
#include "core/resource.h"
#include "servers/audio/audio_stream.h"
class AudioStreamMyTone : public AudioStream {
GDCLASS(AudioStreamMyTone, AudioStream)
private:
friend class AudioStreamPlaybackMyTone;
uint64_t pos;
int mix_rate;
bool stereo;
int hz;
public:
void reset();
void set_position(uint64_t pos);
virtual Ref<AudioStreamPlayback> instance_playback();
virtual String get_stream_name() const;
void gen_tone(int16_t *pcm_buf, int size);
virtual float get_length() const { return 0; } // if supported, otherwise return 0
AudioStreamMyTone();
protected:
static void _bind_methods();
};
/* audiostream_mytone.cpp */
#include "audiostream_mytone.h"
AudioStreamMyTone::AudioStreamMyTone()
: mix_rate(44100), stereo(false), hz(639) {
}
Ref<AudioStreamPlayback> AudioStreamMyTone::instance_playback() {
Ref<AudioStreamPlaybackMyTone> talking_tree;
talking_tree.instance();
talking_tree->base = Ref<AudioStreamMyTone>(this);
return talking_tree;
}
String AudioStreamMyTone::get_stream_name() const {
return "MyTone";
}
void AudioStreamMyTone::reset() {
set_position(0);
}
void AudioStreamMyTone::set_position(uint64_t p) {
pos = p;
}
void AudioStreamMyTone::gen_tone(int16_t *pcm_buf, int size) {
for (int i = 0; i < size; i++) {
pcm_buf[i] = 32767.0 * sin(2.0 * Math_PI * double(pos + i) / (double(mix_rate) / double(hz)));
}
pos += size;
}
void AudioStreamMyTone::_bind_methods() {
ClassDB::bind_method(D_METHOD("reset"), &AudioStreamMyTone::reset);
ClassDB::bind_method(D_METHOD("get_stream_name"), &AudioStreamMyTone::get_stream_name);
}
参考:
创建 AudioStreamPlayback
AudioStreamPlayer使用 mix
回调来获取PCM数据. 回调必须与采样率匹配并填充缓冲区.
由于AudioStreamPlayback由音频线程控制, 因此禁止进行i/o和动态内存分配.
/* audiostreamplayer_mytone.h */
#include "core/reference.h"
#include "core/resource.h"
#include "servers/audio/audio_stream.h"
class AudioStreamPlaybackMyTone : public AudioStreamPlayback {
GDCLASS(AudioStreamPlaybackMyTone, AudioStreamPlayback)
friend class AudioStreamMyTone;
private:
enum {
PCM_BUFFER_SIZE = 4096
};
enum {
MIX_FRAC_BITS = 13,
MIX_FRAC_LEN = (1 << MIX_FRAC_BITS),
MIX_FRAC_MASK = MIX_FRAC_LEN - 1,
};
void *pcm_buffer;
Ref<AudioStreamMyTone> base;
bool active;
public:
virtual void start(float p_from_pos = 0.0);
virtual void stop();
virtual bool is_playing() const;
virtual int get_loop_count() const; // times it looped
virtual float get_playback_position() const;
virtual void seek(float p_time);
virtual void mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames);
virtual float get_length() const; // if supported, otherwise return 0
AudioStreamPlaybackMyTone();
~AudioStreamPlaybackMyTone();
};
/* audiostreamplayer_mytone.cpp */
#include "audiostreamplayer_mytone.h"
#include "core/math/math_funcs.h"
#include "core/print_string.h"
AudioStreamPlaybackMyTone::AudioStreamPlaybackMyTone()
: active(false) {
AudioServer::get_singleton()->lock();
pcm_buffer = AudioServer::get_singleton()->audio_data_alloc(PCM_BUFFER_SIZE);
zeromem(pcm_buffer, PCM_BUFFER_SIZE);
AudioServer::get_singleton()->unlock();
}
AudioStreamPlaybackMyTone::~AudioStreamPlaybackMyTone() {
if(pcm_buffer) {
AudioServer::get_singleton()->audio_data_free(pcm_buffer);
pcm_buffer = NULL;
}
}
void AudioStreamPlaybackMyTone::stop() {
active = false;
base->reset();
}
void AudioStreamPlaybackMyTone::start(float p_from_pos) {
seek(p_from_pos);
active = true;
}
void AudioStreamPlaybackMyTone::seek(float p_time) {
float max = get_length();
if (p_time < 0) {
p_time = 0;
}
base->set_position(uint64_t(p_time * base->mix_rate) << MIX_FRAC_BITS);
}
void AudioStreamPlaybackMyTone::mix(AudioFrame *p_buffer, float p_rate, int p_frames) {
ERR_FAIL_COND(!active);
if (!active) {
return;
}
zeromem(pcm_buffer, PCM_BUFFER_SIZE);
int16_t *buf = (int16_t *)pcm_buffer;
base->gen_tone(buf, p_frames);
for(int i = 0; i < p_frames; i++) {
float sample = float(buf[i]) / 32767.0;
p_buffer[i] = AudioFrame(sample, sample);
}
}
int AudioStreamPlaybackMyTone::get_loop_count() const {
return 0;
}
float AudioStreamPlaybackMyTone::get_playback_position() const {
return 0.0;
}
float AudioStreamPlaybackMyTone::get_length() const {
return 0.0;
}
bool AudioStreamPlaybackMyTone::is_playing() const {
return active;
}
重采样
Godot的AudioServer目前使用44100Hz采样率. 当需要其他采样率如48000时, 要么提供一个, 要么使用AudioStreamPlaybackResampled.Godot为音频重采样提供立方插值.
AudioStreamPlaybackResampled不是重载 mix
, 而是使用 _mix_internal
来查询AudioFrames, 并使用 get_stream_sampling_rate
来查询当前的混合率.
#include "core/reference.h"
#include "core/resource.h"
#include "servers/audio/audio_stream.h"
class AudioStreamMyToneResampled;
class AudioStreamPlaybackResampledMyTone : public AudioStreamPlaybackResampled {
GDCLASS(AudioStreamPlaybackResampledMyTone, AudioStreamPlaybackResampled)
friend class AudioStreamMyToneResampled;
private:
enum {
PCM_BUFFER_SIZE = 4096
};
enum {
MIX_FRAC_BITS = 13,
MIX_FRAC_LEN = (1 << MIX_FRAC_BITS),
MIX_FRAC_MASK = MIX_FRAC_LEN - 1,
};
void *pcm_buffer;
Ref<AudioStreamMyToneResampled> base;
bool active;
protected:
virtual void _mix_internal(AudioFrame *p_buffer, int p_frames);
public:
virtual void start(float p_from_pos = 0.0);
virtual void stop();
virtual bool is_playing() const;
virtual int get_loop_count() const; // times it looped
virtual float get_playback_position() const;
virtual void seek(float p_time);
virtual float get_length() const; // if supported, otherwise return 0
virtual float get_stream_sampling_rate();
AudioStreamPlaybackResampledMyTone();
~AudioStreamPlaybackResampledMyTone();
};
#include "mytone_audiostream_resampled.h"
#include "core/math/math_funcs.h"
#include "core/print_string.h"
AudioStreamPlaybackResampledMyTone::AudioStreamPlaybackResampledMyTone()
: active(false) {
AudioServer::get_singleton()->lock();
pcm_buffer = AudioServer::get_singleton()->audio_data_alloc(PCM_BUFFER_SIZE);
zeromem(pcm_buffer, PCM_BUFFER_SIZE);
AudioServer::get_singleton()->unlock();
}
AudioStreamPlaybackResampledMyTone::~AudioStreamPlaybackResampledMyTone() {
if (pcm_buffer) {
AudioServer::get_singleton()->audio_data_free(pcm_buffer);
pcm_buffer = NULL;
}
}
void AudioStreamPlaybackResampledMyTone::stop() {
active = false;
base->reset();
}
void AudioStreamPlaybackResampledMyTone::start(float p_from_pos) {
seek(p_from_pos);
active = true;
}
void AudioStreamPlaybackResampledMyTone::seek(float p_time) {
float max = get_length();
if (p_time < 0) {
p_time = 0;
}
base->set_position(uint64_t(p_time * base->mix_rate) << MIX_FRAC_BITS);
}
void AudioStreamPlaybackResampledMyTone::_mix_internal(AudioFrame *p_buffer, int p_frames) {
ERR_FAIL_COND(!active);
if (!active) {
return;
}
zeromem(pcm_buffer, PCM_BUFFER_SIZE);
int16_t *buf = (int16_t *)pcm_buffer;
base->gen_tone(buf, p_frames);
for(int i = 0; i < p_frames; i++) {
float sample = float(buf[i]) / 32767.0;
p_buffer[i] = AudioFrame(sample, sample);
}
}
float AudioStreamPlaybackResampledMyTone::get_stream_sampling_rate() {
return float(base->mix_rate);
}
int AudioStreamPlaybackResampledMyTone::get_loop_count() const {
return 0;
}
float AudioStreamPlaybackResampledMyTone::get_playback_position() const {
return 0.0;
}
float AudioStreamPlaybackResampledMyTone::get_length() const {
return 0.0;
}
bool AudioStreamPlaybackResampledMyTone::is_playing() const {
return active;
}