自定义音频流
前言
AudioStream是所有音频发射对象的基类.AudioStreamPlayer绑定到AudioStream以将PCM数据发送到管理音频驱动程序的AudioServer. 所有音频资源都需要两个基于音频的类:AudioStream和AudioStreamPlayback. 作为数据容器,AudioStream包含资源并将其自身暴露给GDScript.AudioStream引用其自己的内部自定义AudioStreamPlayback, 该AudioStreamPlayback将AudioStream转换为PCM数据. 自定义 C++ 模块.
参考:
- servers/audio/audio_stream.h
- scene/audio/audioplayer.cpp
可以做什么?
- 绑定外部库(如Wwise, FMOD等).
- 添加自定义音频队列
- 添加对更多音频格式的支持
创建 AudioStream
AudioStream由三个组件组成: 数据容器, 流名称和AudioStreamPlayback朋友类生成器. 音频数据可以通过多种方式加载, 例如使用用于音调发生器的内部计数器, 内部/外部缓冲区, 或文件引用.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);}
参考:
- servers/audio/audio_stream.h
创建 AudioStreamPlayback
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为音频重采样提供立方插值.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;}
参考:
- core/math/audio_frame.h
- servers/audio/audio_stream.h
- scene/audio/audioplayer.cpp
