Skip to content

Commit 8d13d96

Browse files
committed
Added an inital guide on how to create an audiostream.
change creating to create
1 parent 3ef045a commit 8d13d96

File tree

2 files changed

+346
-0
lines changed

2 files changed

+346
-0
lines changed
+345
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,345 @@
1+
.. _custom_audiostreams:
2+
3+
Custom AudioStreams
4+
===================
5+
6+
Introduction
7+
------------
8+
9+
AudioStream is the base class of all audio emitting objects.
10+
AudioStreamPlayer binds onto an AudioStream to emit pcm data
11+
into an AudioServer which manages audio drivers.
12+
13+
All audio resources requires two audio based classes; AudioStream
14+
and AudioStreamPlayback. As a data container, AudioStream contains
15+
the resource and exposes itself to GDScript. AudioStream references
16+
its own internal custom AudioStreamPlayback which translate
17+
AudioStream into PCM data.
18+
19+
This doc assumes the reader knows how to create C++ Modules. If not, refer to this guide
20+
:ref:`doc_custom_modules_in_c++`.
21+
22+
References:
23+
~~~~~~~~~~~
24+
25+
- `servers/audio/audio_stream.h <https://github.com/godotengine/godot/blob/master/servers/audio/audio_stream.h>`__
26+
- `scene/audio/audioplayer.cpp <https://github.com/godotengine/godot/blob/master/scene/audio/audio_player.cpp>`__
27+
28+
29+
30+
What for?
31+
---------
32+
33+
- Binding external libraries (like Wwise, FMOD, etc).
34+
- Adding custom audio queues
35+
- Adding support for more audio formats
36+
37+
Create an AudioStream
38+
---------------------
39+
40+
41+
An AudioStream consists of three components; data container, stream name,
42+
and an AudioStreamPlayback friend class generator. Audio data can be
43+
loaded a number of ways such as an internal counter for a tone generator,
44+
internal/external buffer, or a file reference.
45+
46+
47+
Some AudioStreams need to be stateless such as objects loaded from
48+
ResourceLoader. ResourceLoader loads once and references the same
49+
object regardless how many times ``load`` is called on a specific resource.
50+
Therefore, playback state must be self contained in AudioStreamPlayback.
51+
52+
53+
.. code:: cpp
54+
55+
/* audiostream_mytone.h */
56+
57+
#include "reference.h"
58+
#include "resource.h"
59+
#include "servers/audio/audio_stream.h
60+
61+
class AudioStreamMyTone : public AudioStream {
62+
GDCLASS(AudioStreamMyTone, AudioStream)
63+
private:
64+
friend class AudioStreamPlaybackMyTone;
65+
uint64_t pos;
66+
int mix_rate;
67+
bool stereo;
68+
int hz;
69+
public:
70+
void reset();
71+
void set_position(uint64_t pos);
72+
virtual Ref<AudioStreamPlayback> instance_playback();
73+
virtual String get_stream_name() const;
74+
void gen_tone(int16_t *, int frames);
75+
AudioStreamMyTone();
76+
77+
protected:
78+
static void _bind_methods();
79+
};
80+
81+
82+
.. code:: cpp
83+
84+
/* audiostream_mytone.cpp */
85+
AudioStreamMyTone::AudioStreamMyTone()
86+
: mix_rate(44100), stereo(false), hz(639) {
87+
}
88+
89+
Ref<AudioStreamPlayback> AudioStreamMyTone::instance_playback(){
90+
Ref<AudioStreamPlaybackMyTone> talking_tree;
91+
talking_tree.instance();
92+
talking_tree->base = Ref<AudioStreamMyTone>(this);
93+
return talking_tree;
94+
}
95+
96+
String AudioStreamMyTone::get_stream_name() const {
97+
return "MyTone";
98+
}
99+
void AudioStreamMyTone::reset() {
100+
set_position(0);
101+
}
102+
void AudioStreamMyTone::set_position(uint64_t p) {
103+
pos = p;
104+
}
105+
void AudioStreamMyTone::gen_tone(int16_t * pcm_buf, int size){
106+
for( int i = 0; i < size; i++){
107+
pcm_buf[i] = 32767.0 * sin(2.0*Math_PI*double(pos+i)/(double(mix_rate)/double(hz)));
108+
}
109+
pos += size;
110+
}
111+
void AudioStreamMyTone::_bind_methods(){
112+
ClassDB::bind_method(D_METHOD("reset"), &AudioStreamMyTone::reset);
113+
ClassDB::bind_method(D_METHOD("get_stream_name"), &AudioStreamMyTone::get_stream_name);
114+
115+
}
116+
117+
References:
118+
~~~~~~~~~~~
119+
120+
- `servers/audio/audio_stream.h <https://github.com/godotengine/godot/blob/master/servers/audio/audio_stream.h>`__
121+
122+
123+
Create an AudioStreamPlayback
124+
-----------------------------
125+
126+
AudioStreamPlayer uses ``mix`` callback to obtain pcm data. The callback must match sample rate and fill the buffer.
127+
128+
129+
.. code:: cpp
130+
131+
/* audiostreamplayer_mytone.h */
132+
#include "reference.h"
133+
#include "resource.h"
134+
#include "servers/audio/audio_stream.h"
135+
136+
137+
class AudioStreamPlaybackMyTone : public AudioStreamPlayback {
138+
GDCLASS(AudioStreamPlaybackMyTone, AudioStreamPlayback)
139+
friend class AudioStreamMyTone;
140+
private:
141+
enum{
142+
PCM_BUFFER_SIZE = 4096
143+
};
144+
enum {
145+
MIX_FRAC_BITS = 13,
146+
MIX_FRAC_LEN = (1 << MIX_FRAC_BITS),
147+
MIX_FRAC_MASK = MIX_FRAC_LEN - 1,
148+
};
149+
void * pcm_buffer;
150+
Ref<AudioStreamMyTone> base;
151+
bool active;
152+
public:
153+
virtual void start(float p_from_pos = 0.0);
154+
virtual void stop();
155+
virtual bool is_playing() const;
156+
virtual int get_loop_count() const; //times it looped
157+
virtual float get_playback_position() const;
158+
virtual void seek(float p_time);
159+
virtual void mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames);
160+
virtual float get_length() const; //if supported, otherwise return 0
161+
AudioStreamPlaybackMyTone();
162+
163+
};
164+
165+
166+
167+
.. code:: cpp
168+
169+
/* audiostreamplayer_mytone.cpp */
170+
#include "audiostreamplayer_mytone.h"
171+
#include "math/math_funcs.h"
172+
#include "print_string.h"
173+
174+
AudioStreamPlaybackMyTone::AudioStreamPlaybackMyTone()
175+
: active(false){
176+
AudioServer::get_singleton()->lock();
177+
pcm_buffer = AudioServer::get_singleton()->audio_data_alloc(PCM_BUFFER_SIZE);
178+
zeromem(pcm_buffer, PCM_BUFFER_SIZE);
179+
AudioServer::get_singleton()->unlock();
180+
}
181+
void AudioStreamPlaybackMyTone::stop(){
182+
active = false;
183+
base->reset();
184+
}
185+
186+
void AudioStreamPlaybackMyTone::start(float p_from_pos){
187+
seek(p_from_pos);
188+
active = true;
189+
}
190+
void AudioStreamPlaybackMyTone::seek(float p_time){
191+
float max = get_length();
192+
if (p_time < 0) {
193+
p_time = 0;
194+
}
195+
base->set_position(uint64_t(p_time * base->mix_rate) << MIX_FRAC_BITS);
196+
}
197+
void AudioStreamPlaybackMyTone::mix(AudioFrame *p_buffer, float p_rate, int p_frames){
198+
ERR_FAIL_COND(!active);
199+
if (!active) {
200+
return;
201+
}
202+
zeromem(pcm_buffer, PCM_BUFFER_SIZE);
203+
int16_t * buf = (int16_t * )pcm_buffer;
204+
base->gen_tone(buf, p_frames);
205+
206+
for(int i = 0; i < p_frames; i++){
207+
float sample = float(buf[i])/32767.0;
208+
p_buffer[i] = AudioFrame(sample, sample);
209+
}
210+
}
211+
int AudioStreamPlaybackMyTone::get_loop_count() const {
212+
return 0;
213+
}
214+
float AudioStreamPlaybackMyTone::get_playback_position() const {
215+
return 0.0;
216+
}
217+
float AudioStreamPlaybackMyTone::get_length() const {
218+
return 0.0;
219+
}
220+
bool AudioStreamPlaybackMyTone::is_playing() const {
221+
return active;
222+
}
223+
224+
225+
Resampling
226+
~~~~~~~~~~
227+
228+
229+
Godot’s AudioServer currently uses 44100 Hz sample rate. When other sample rates are
230+
needed such as 48000, either provide one or use AudioStreamPlaybackResampled.
231+
Godot provides cubic interpolation for audio resampling.
232+
233+
Instead of overloading ``mix``, AudioStreamPlaybackResampled uses ``_mix_internal`` to
234+
query AudioFrames and ``get_stream_sampling_rate`` to query current mix rate.
235+
236+
.. code:: cpp
237+
238+
#include "reference.h"
239+
#include "resource.h"
240+
241+
#include "servers/audio/audio_stream.h"
242+
243+
class AudioStreamMyToneResampled;
244+
245+
class AudioStreamPlaybackResampledMyTone : public AudioStreamPlaybackResampled {
246+
GDCLASS(AudioStreamPlaybackResampledMyTone, AudioStreamPlaybackResampled)
247+
friend class AudioStreamMyToneResampled;
248+
private:
249+
enum{
250+
PCM_BUFFER_SIZE = 4096
251+
};
252+
enum {
253+
MIX_FRAC_BITS = 13,
254+
MIX_FRAC_LEN = (1 << MIX_FRAC_BITS),
255+
MIX_FRAC_MASK = MIX_FRAC_LEN - 1,
256+
};
257+
void * pcm_buffer;
258+
Ref<AudioStreamMyToneResampled> base;
259+
bool active;
260+
protected:
261+
virtual void _mix_internal(AudioFrame *p_buffer, int p_frames);
262+
public:
263+
virtual void start(float p_from_pos = 0.0);
264+
virtual void stop();
265+
virtual bool is_playing() const;
266+
virtual int get_loop_count() const; //times it looped
267+
virtual float get_playback_position() const;
268+
virtual void seek(float p_time);
269+
virtual float get_length() const; //if supported, otherwise return 0
270+
virtual float get_stream_sampling_rate();
271+
AudioStreamPlaybackResampledMyTone();
272+
273+
};
274+
275+
276+
277+
.. code:: cpp
278+
279+
#include "mytone_audiostream_resampled.h"
280+
#include "math/math_funcs.h"
281+
#include "print_string.h"
282+
283+
AudioStreamPlaybackResampledMyTone::AudioStreamPlaybackResampledMyTone()
284+
: active(false){
285+
AudioServer::get_singleton()->lock();
286+
pcm_buffer = AudioServer::get_singleton()->audio_data_alloc(PCM_BUFFER_SIZE);
287+
zeromem(pcm_buffer, PCM_BUFFER_SIZE);
288+
AudioServer::get_singleton()->unlock();
289+
}
290+
void AudioStreamPlaybackResampledMyTone::stop(){
291+
active = false;
292+
base->reset();
293+
}
294+
295+
void AudioStreamPlaybackResampledMyTone::start(float p_from_pos){
296+
seek(p_from_pos);
297+
active = true;
298+
}
299+
void AudioStreamPlaybackResampledMyTone::seek(float p_time){
300+
float max = get_length();
301+
if (p_time < 0) {
302+
p_time = 0;
303+
}
304+
base->set_position(uint64_t(p_time * base->mix_rate) << MIX_FRAC_BITS);
305+
}
306+
void AudioStreamPlaybackResampledMyTone::_mix_internal(AudioFrame *p_buffer, int p_frames){
307+
ERR_FAIL_COND(!active);
308+
if (!active) {
309+
return;
310+
}
311+
zeromem(pcm_buffer, PCM_BUFFER_SIZE);
312+
int16_t * buf = (int16_t * )pcm_buffer;
313+
base->gen_tone(buf, p_frames);
314+
315+
for(int i = 0; i < p_frames; i++){
316+
float sample = float(buf[i])/32767.0;
317+
p_buffer[i] = AudioFrame(sample, sample);
318+
}
319+
}
320+
float AudioStreamPlaybackResampledMyTone::get_stream_sampling_rate(){
321+
return float(base->mix_rate);
322+
}
323+
int AudioStreamPlaybackResampledMyTone::get_loop_count() const {
324+
return 0;
325+
}
326+
float AudioStreamPlaybackResampledMyTone::get_playback_position() const {
327+
return 0.0;
328+
}
329+
float AudioStreamPlaybackResampledMyTone::get_length() const {
330+
return 0.0;
331+
}
332+
bool AudioStreamPlaybackResampledMyTone::is_playing() const {
333+
return active;
334+
}
335+
336+
337+
338+
339+
References:
340+
~~~~~~~~~~~
341+
- `core/math/audio_frame.h <https://github.com/godotengine/godot/blob/master/core/math/audio_frame.h>`__
342+
- `servers/audio/audio_stream.h <https://github.com/godotengine/godot/blob/master/servers/audio/audio_stream.h>`__
343+
- `scene/audio/audioplayer.cpp <https://github.com/godotengine/godot/blob/master/scene/audio/audio_player.cpp>`__
344+
345+

development/cpp/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ Engine development
1212
object_class
1313
inheritance_class_tree
1414
custom_modules_in_cpp
15+
custom_audiostreams
1516
creating_android_modules

0 commit comments

Comments
 (0)