123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352 |
- .. _doc_custom_audiostreams:
- Custom AudioStreams
- ===================
- Introduction
- ------------
- AudioStream is the base class of all audio emitting objects.
- AudioStreamPlayer binds onto an AudioStream to emit PCM data
- into an AudioServer which manages audio drivers.
- All audio resources require two audio based classes: AudioStream
- and AudioStreamPlayback. As a data container, AudioStream contains
- the resource and exposes itself to GDScript. AudioStream references
- its own internal custom AudioStreamPlayback which translates
- AudioStream into PCM data.
- This guide assumes the reader knows how to create C++ modules. If not, refer to this guide
- :ref:`doc_custom_modules_in_cpp`.
- References:
- ~~~~~~~~~~~
- - `servers/audio/audio_stream.h <https://github.com/godotengine/godot/blob/master/servers/audio/audio_stream.h>`__
- - `scene/audio/audio_stream_player.cpp <https://github.com/godotengine/godot/blob/master/scene/audio/audio_stream_player.cpp>`__
- What for?
- ---------
- - Binding external libraries (like Wwise, FMOD, etc).
- - Adding custom audio queues
- - Adding support for more audio formats
- Create an AudioStream
- ---------------------
- An AudioStream consists of three components: data container, stream name,
- and an AudioStreamPlayback friend class generator. Audio data can be
- loaded in a number of ways such as with an internal counter for a tone generator,
- internal/external buffer, or a file reference.
- Some AudioStreams need to be stateless such as objects loaded from
- ResourceLoader. ResourceLoader loads once and references the same
- object regardless how many times ``load`` is called on a specific resource.
- Therefore, playback state must be self-contained in AudioStreamPlayback.
- .. code-block:: cpp
- /* 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();
- };
- .. code-block:: cpp
- /* 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.instantiate();
- 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);
- }
- References:
- ~~~~~~~~~~~
- - `servers/audio/audio_stream.h <https://github.com/godotengine/godot/blob/master/servers/audio/audio_stream.h>`__
- Create an AudioStreamPlayback
- -----------------------------
- AudioStreamPlayer uses ``mix`` callback to obtain PCM data. The callback must match sample rate and fill the buffer.
- Since AudioStreamPlayback is controlled by the audio thread, i/o and dynamic memory allocation are forbidden.
- .. code-block:: cpp
- /* 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();
- };
- .. code-block:: cpp
- /* 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;
- }
- Resampling
- ~~~~~~~~~~
- Godot's AudioServer currently uses 44100 Hz sample rate. When other sample rates are
- needed such as 48000, either provide one or use AudioStreamPlaybackResampled.
- Godot provides cubic interpolation for audio resampling.
- Instead of overloading ``mix``, AudioStreamPlaybackResampled uses ``_mix_internal`` to
- query AudioFrames and ``get_stream_sampling_rate`` to query current mix rate.
- .. code-block:: cpp
- #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();
- };
- .. code-block:: cpp
- #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;
- }
- References:
- ~~~~~~~~~~~
- - `core/math/audio_frame.h <https://github.com/godotengine/godot/blob/master/core/math/audio_frame.h>`__
- - `servers/audio/audio_stream.h <https://github.com/godotengine/godot/blob/master/servers/audio/audio_stream.h>`__
- - `scene/audio/audio_stream_player.cpp <https://github.com/godotengine/godot/blob/master/scene/audio/audio_stream_player.cpp>`__
|