自定义 AudioStream

前言

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中.

  1. /* audiostream_mytone.h */
  2. #include "core/reference.h"
  3. #include "core/resource.h"
  4. #include "servers/audio/audio_stream.h"
  5. class AudioStreamMyTone : public AudioStream {
  6. GDCLASS(AudioStreamMyTone, AudioStream)
  7. private:
  8. friend class AudioStreamPlaybackMyTone;
  9. uint64_t pos;
  10. int mix_rate;
  11. bool stereo;
  12. int hz;
  13. public:
  14. void reset();
  15. void set_position(uint64_t pos);
  16. virtual Ref<AudioStreamPlayback> instance_playback();
  17. virtual String get_stream_name() const;
  18. void gen_tone(int16_t *pcm_buf, int size);
  19. virtual float get_length() const { return 0; } // if supported, otherwise return 0
  20. AudioStreamMyTone();
  21. protected:
  22. static void _bind_methods();
  23. };
  1. /* audiostream_mytone.cpp */
  2. #include "audiostream_mytone.h"
  3. AudioStreamMyTone::AudioStreamMyTone()
  4. : mix_rate(44100), stereo(false), hz(639) {
  5. }
  6. Ref<AudioStreamPlayback> AudioStreamMyTone::instance_playback() {
  7. Ref<AudioStreamPlaybackMyTone> talking_tree;
  8. talking_tree.instance();
  9. talking_tree->base = Ref<AudioStreamMyTone>(this);
  10. return talking_tree;
  11. }
  12. String AudioStreamMyTone::get_stream_name() const {
  13. return "MyTone";
  14. }
  15. void AudioStreamMyTone::reset() {
  16. set_position(0);
  17. }
  18. void AudioStreamMyTone::set_position(uint64_t p) {
  19. pos = p;
  20. }
  21. void AudioStreamMyTone::gen_tone(int16_t *pcm_buf, int size) {
  22. for (int i = 0; i < size; i++) {
  23. pcm_buf[i] = 32767.0 * sin(2.0 * Math_PI * double(pos + i) / (double(mix_rate) / double(hz)));
  24. }
  25. pos += size;
  26. }
  27. void AudioStreamMyTone::_bind_methods() {
  28. ClassDB::bind_method(D_METHOD("reset"), &AudioStreamMyTone::reset);
  29. ClassDB::bind_method(D_METHOD("get_stream_name"), &AudioStreamMyTone::get_stream_name);
  30. }

参考:

创建 AudioStreamPlayback

AudioStreamPlayer使用 mix 回调来获取PCM数据. 回调必须与采样率匹配并填充缓冲区.

由于AudioStreamPlayback由音频线程控制, 因此禁止进行i/o和动态内存分配.

  1. /* audiostreamplayer_mytone.h */
  2. #include "core/reference.h"
  3. #include "core/resource.h"
  4. #include "servers/audio/audio_stream.h"
  5. class AudioStreamPlaybackMyTone : public AudioStreamPlayback {
  6. GDCLASS(AudioStreamPlaybackMyTone, AudioStreamPlayback)
  7. friend class AudioStreamMyTone;
  8. private:
  9. enum {
  10. PCM_BUFFER_SIZE = 4096
  11. };
  12. enum {
  13. MIX_FRAC_BITS = 13,
  14. MIX_FRAC_LEN = (1 << MIX_FRAC_BITS),
  15. MIX_FRAC_MASK = MIX_FRAC_LEN - 1,
  16. };
  17. void *pcm_buffer;
  18. Ref<AudioStreamMyTone> base;
  19. bool active;
  20. public:
  21. virtual void start(float p_from_pos = 0.0);
  22. virtual void stop();
  23. virtual bool is_playing() const;
  24. virtual int get_loop_count() const; // times it looped
  25. virtual float get_playback_position() const;
  26. virtual void seek(float p_time);
  27. virtual void mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames);
  28. virtual float get_length() const; // if supported, otherwise return 0
  29. AudioStreamPlaybackMyTone();
  30. ~AudioStreamPlaybackMyTone();
  31. };
  1. /* audiostreamplayer_mytone.cpp */
  2. #include "audiostreamplayer_mytone.h"
  3. #include "core/math/math_funcs.h"
  4. #include "core/print_string.h"
  5. AudioStreamPlaybackMyTone::AudioStreamPlaybackMyTone()
  6. : active(false) {
  7. AudioServer::get_singleton()->lock();
  8. pcm_buffer = AudioServer::get_singleton()->audio_data_alloc(PCM_BUFFER_SIZE);
  9. zeromem(pcm_buffer, PCM_BUFFER_SIZE);
  10. AudioServer::get_singleton()->unlock();
  11. }
  12. AudioStreamPlaybackMyTone::~AudioStreamPlaybackMyTone() {
  13. if(pcm_buffer) {
  14. AudioServer::get_singleton()->audio_data_free(pcm_buffer);
  15. pcm_buffer = NULL;
  16. }
  17. }
  18. void AudioStreamPlaybackMyTone::stop() {
  19. active = false;
  20. base->reset();
  21. }
  22. void AudioStreamPlaybackMyTone::start(float p_from_pos) {
  23. seek(p_from_pos);
  24. active = true;
  25. }
  26. void AudioStreamPlaybackMyTone::seek(float p_time) {
  27. float max = get_length();
  28. if (p_time < 0) {
  29. p_time = 0;
  30. }
  31. base->set_position(uint64_t(p_time * base->mix_rate) << MIX_FRAC_BITS);
  32. }
  33. void AudioStreamPlaybackMyTone::mix(AudioFrame *p_buffer, float p_rate, int p_frames) {
  34. ERR_FAIL_COND(!active);
  35. if (!active) {
  36. return;
  37. }
  38. zeromem(pcm_buffer, PCM_BUFFER_SIZE);
  39. int16_t *buf = (int16_t *)pcm_buffer;
  40. base->gen_tone(buf, p_frames);
  41. for(int i = 0; i < p_frames; i++) {
  42. float sample = float(buf[i]) / 32767.0;
  43. p_buffer[i] = AudioFrame(sample, sample);
  44. }
  45. }
  46. int AudioStreamPlaybackMyTone::get_loop_count() const {
  47. return 0;
  48. }
  49. float AudioStreamPlaybackMyTone::get_playback_position() const {
  50. return 0.0;
  51. }
  52. float AudioStreamPlaybackMyTone::get_length() const {
  53. return 0.0;
  54. }
  55. bool AudioStreamPlaybackMyTone::is_playing() const {
  56. return active;
  57. }

重采样

Godot的AudioServer目前使用44100Hz采样率. 当需要其他采样率如48000时, 要么提供一个, 要么使用AudioStreamPlaybackResampled.Godot为音频重采样提供立方插值.

AudioStreamPlaybackResampled不是重载 mix , 而是使用 _mix_internal 来查询AudioFrames, 并使用 get_stream_sampling_rate 来查询当前的混合率.

  1. #include "core/reference.h"
  2. #include "core/resource.h"
  3. #include "servers/audio/audio_stream.h"
  4. class AudioStreamMyToneResampled;
  5. class AudioStreamPlaybackResampledMyTone : public AudioStreamPlaybackResampled {
  6. GDCLASS(AudioStreamPlaybackResampledMyTone, AudioStreamPlaybackResampled)
  7. friend class AudioStreamMyToneResampled;
  8. private:
  9. enum {
  10. PCM_BUFFER_SIZE = 4096
  11. };
  12. enum {
  13. MIX_FRAC_BITS = 13,
  14. MIX_FRAC_LEN = (1 << MIX_FRAC_BITS),
  15. MIX_FRAC_MASK = MIX_FRAC_LEN - 1,
  16. };
  17. void *pcm_buffer;
  18. Ref<AudioStreamMyToneResampled> base;
  19. bool active;
  20. protected:
  21. virtual void _mix_internal(AudioFrame *p_buffer, int p_frames);
  22. public:
  23. virtual void start(float p_from_pos = 0.0);
  24. virtual void stop();
  25. virtual bool is_playing() const;
  26. virtual int get_loop_count() const; // times it looped
  27. virtual float get_playback_position() const;
  28. virtual void seek(float p_time);
  29. virtual float get_length() const; // if supported, otherwise return 0
  30. virtual float get_stream_sampling_rate();
  31. AudioStreamPlaybackResampledMyTone();
  32. ~AudioStreamPlaybackResampledMyTone();
  33. };
  1. #include "mytone_audiostream_resampled.h"
  2. #include "core/math/math_funcs.h"
  3. #include "core/print_string.h"
  4. AudioStreamPlaybackResampledMyTone::AudioStreamPlaybackResampledMyTone()
  5. : active(false) {
  6. AudioServer::get_singleton()->lock();
  7. pcm_buffer = AudioServer::get_singleton()->audio_data_alloc(PCM_BUFFER_SIZE);
  8. zeromem(pcm_buffer, PCM_BUFFER_SIZE);
  9. AudioServer::get_singleton()->unlock();
  10. }
  11. AudioStreamPlaybackResampledMyTone::~AudioStreamPlaybackResampledMyTone() {
  12. if (pcm_buffer) {
  13. AudioServer::get_singleton()->audio_data_free(pcm_buffer);
  14. pcm_buffer = NULL;
  15. }
  16. }
  17. void AudioStreamPlaybackResampledMyTone::stop() {
  18. active = false;
  19. base->reset();
  20. }
  21. void AudioStreamPlaybackResampledMyTone::start(float p_from_pos) {
  22. seek(p_from_pos);
  23. active = true;
  24. }
  25. void AudioStreamPlaybackResampledMyTone::seek(float p_time) {
  26. float max = get_length();
  27. if (p_time < 0) {
  28. p_time = 0;
  29. }
  30. base->set_position(uint64_t(p_time * base->mix_rate) << MIX_FRAC_BITS);
  31. }
  32. void AudioStreamPlaybackResampledMyTone::_mix_internal(AudioFrame *p_buffer, int p_frames) {
  33. ERR_FAIL_COND(!active);
  34. if (!active) {
  35. return;
  36. }
  37. zeromem(pcm_buffer, PCM_BUFFER_SIZE);
  38. int16_t *buf = (int16_t *)pcm_buffer;
  39. base->gen_tone(buf, p_frames);
  40. for(int i = 0; i < p_frames; i++) {
  41. float sample = float(buf[i]) / 32767.0;
  42. p_buffer[i] = AudioFrame(sample, sample);
  43. }
  44. }
  45. float AudioStreamPlaybackResampledMyTone::get_stream_sampling_rate() {
  46. return float(base->mix_rate);
  47. }
  48. int AudioStreamPlaybackResampledMyTone::get_loop_count() const {
  49. return 0;
  50. }
  51. float AudioStreamPlaybackResampledMyTone::get_playback_position() const {
  52. return 0.0;
  53. }
  54. float AudioStreamPlaybackResampledMyTone::get_length() const {
  55. return 0.0;
  56. }
  57. bool AudioStreamPlaybackResampledMyTone::is_playing() const {
  58. return active;
  59. }

参考: