Issue with compiling custom audiostreams

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By wetbadger

I was following the tutorial here: Custom AudioStreams — Godot Engine (3.6) documentation in English, but the files don’t seem to compile.

scons -j8 platform=linuxbsd
scons: Reading SConscript files ...
Platform "linuxbsd" is still called "x11" in Godot 3.x. Building for platform "x11".
Warning: Speech Dispatcher development libraries not found. Disabling Text-to-Speech support.
Note: Building a debug binary (which will run slowly). Use `target=release_debug` to build an optimized release binary.
Warning: module 'x11' uses a deprecated `can_build` signature in its config.py file, it should be `can_build(env, platform)`.
Checking for C header file mntent.h... (cached) yes
scons: done reading SConscript files.
scons: Building targets ...
[ 30%] Compiling ==> modules/audiostream/audiostream.cpp
[ 30%] Compiling ==> modules/audiostream/audiostreamplayer.cpp
[ 46%] modules/audiostream/audiostream.cpp: In member function 'virtual Ref<AudioStreamPlayback> AudioStreamMyTone::instance_playback()':
modules/audiostream/audiostream.cpp:10:13: error: 'AudioStreamPlaybackMyTone' was not declared in this scope; did you mean 'AudioStreamPlaybackResampled'?
   10 |         Ref<AudioStreamPlaybackMyTone> talking_tree;
      |             ^~~~~~~~~~~~~~~~~~~~~~~~~~
      |             AudioStreamPlaybackResampled
modules/audiostream/audiostream.cpp:10:39: error: template argument 1 is invalid
   10 |         Ref<AudioStreamPlaybackMyTone> talking_tree;
      |                                       ^
modules/audiostream/audiostream.cpp:11:22: error: request for member 'instance' in 'talking_tree', which is of non-class type 'int'
   11 |         talking_tree.instance();
      |                      ^~~~~~~~
modules/audiostream/audiostream.cpp:12:21: error: base operand of '->' is not a pointer
   12 |         talking_tree->base = Ref<AudioStreamMyTone>(this);
      |                     ^~
modules/audiostream/audiostream.cpp:13:16: error: could not convert 'talking_tree' from 'int' to 'Ref<AudioStreamPlayback>'
   13 |         return talking_tree;
      |                ^~~~~~~~~~~~
      |                |
      |                int
[ 46%] In file included from modules/audiostream/audiostreamplayer.cpp:3:
modules/audiostream/audiostreamplayer.h:21:13: error: 'AudioStreamMyTone' was not declared in this scope
   21 |         Ref<AudioStreamMyTone> base;
      |             ^~~~~~~~~~~~~~~~~~
modules/audiostream/audiostreamplayer.h:21:31: error: template argument 1 is invalid
   21 |         Ref<AudioStreamMyTone> base;
      |                               ^
[ 46%] modules/audiostream/audiostreamplayer.cpp: In constructor 'AudioStreamPlaybackMyTone::AudioStreamPlaybackMyTone()':
modules/audiostream/audiostreamplayer.cpp:12:9: error: 'zeromem' was not declared in this scope
   12 |         zeromem(pcm_buffer, PCM_BUFFER_SIZE);
      |         ^~~~~~~
modules/audiostream/audiostreamplayer.cpp: In member function 'virtual void AudioStreamPlaybackMyTone::stop()':
modules/audiostream/audiostreamplayer.cpp:23:13: error: base operand of '->' is not a pointer
   23 |         base->reset();
      |             ^~
modules/audiostream/audiostreamplayer.cpp: In member function 'virtual void AudioStreamPlaybackMyTone::seek(float)':
modules/audiostream/audiostreamplayer.cpp:34:13: error: base operand of '->' is not a pointer
   34 |         base->set_position(uint64_t(p_time * base->mix_rate) << MIX_FRAC_BITS);
      |             ^~
modules/audiostream/audiostreamplayer.cpp:34:50: error: base operand of '->' is not a pointer
   34 |         base->set_position(uint64_t(p_time * base->mix_rate) << MIX_FRAC_BITS);
      |                                                  ^~
modules/audiostream/audiostreamplayer.cpp:30:15: warning: unused variable 'max' [-Wunused-variable]
   30 |         float max = get_length();
      |               ^~~
[ 46%] modules/audiostream/audiostreamplayer.cpp: In member function 'virtual void AudioStreamPlaybackMyTone::mix(AudioFrame*, float, int)':
modules/audiostream/audiostreamplayer.cpp:41:9: error: 'zeromem' was not declared in this scope
   41 |         zeromem(pcm_buffer, PCM_BUFFER_SIZE);
      |         ^~~~~~~
modules/audiostream/audiostreamplayer.cpp:43:13: error: base operand of '->' is not a pointer
   43 |         base->gen_tone(buf, p_frames);
      |             ^~
[ 60%] Compiling ==> platform/osx/export/export.cpp
[ 60%] Compiling ==> platform/osx/export/lipo.cpp
[ 60%] Compiling ==> platform/osx/export/macho.cpp
[ 60%] Compiling ==> platform/osx/export/plist.cpp
[ 60%] Compiling ==> platform/uwp/export/export.cpp
[ 60%] Compiling ==> platform/windows/export/export.cpp
scons: *** [modules/audiostream/audiostream.x11.tools.64.o] Error 1
scons: *** [modules/audiostream/audiostreamplayer.x11.tools.64.o] Error 1
scons: building terminated because of errors.
[Time elapsed: 00:00:05.713]

The SCsub, config.py, and register_types files are set up according to this: Custom modules in C++ — Godot Engine (3.6) documentation in English

I’m no C++ expert. Why is AudioStreamPlaybackMyTone not declared in this scope? In audiostream_mytone.h it looks like it is declared as “friend class AudioStreamPlaybackMyTone”

:bust_in_silhouette: Reply From: Watchinofoye

I’ve just got exactly the same problem with Godot 3.5.

First of all, you need to protect the .h files against multiple inclusions like this :

#ifndef NAMEFILE_H
#define NAMEFILE_H

[*Content of your file*]

#endif // NAMEFILE_H

For this example, you need to replace NAMEFILE_H by AUDIOSTREAM_MYTONE_H for audiostream_mytone.h and by AUDIOSTREAMPLAYER_MYTONE_H for audiostreamplayer_mytone.h.

The second thing you need to do, is simply add :

#include "audiostream_mytone.h"
#include "audiostreamplayer_mytone.h"

at the beginning of audiostream_mytone.cpp, audiostreamplayer_mytone.cpp and register_types.cpp. For register_types.cpp, it would look like this :

/* register_types.cpp */

#include "register_types.h"

#include "core/class_db.h"
#include "audiostream_mytone.h"
#include "audiostreamplayer_mytone.h"


void register_audiostream_mytone_types() {
    ClassDB::register_class<AudioStreamMyTone>();
    ClassDB::register_class<AudioStreamPlaybackMyTone>();
}

void unregister_audiostream_mytone_types() {
   // Nothing to do here in this example.
}

Finally, the last thing to do is about zeromem. Apparently, it’s an old macro function that is not defined in recent versions of Godot anymore. It was defined like this :

#define zeromem(to, count) memset(to, 0, count)

You just need to add that line at the top of audiostreamplayer_mytone.cpp file.

Now it should compile without problem.

BUT, I now have problem when I’m trying to use the new type. It makes Godot crash… Seems it is a problem of memory allocation. I don’t see a solution for that, at the moment.

EDIT: The problem does not seem to occur with the Resampled version of the custom audio stream.

Anyway, I think this part of the documentation needs to be rewritten to take into account all the corrections listed above.

I hope it will help you.

Just starting to work on a project that involves this;

Found a lot of problems coming from the AudioStreamPlaybackMyTone::mix function.

I found that something from the docs was going past it’s own memory bounds; causing lots of vague malloc errors.

I also found that after I got past those issues I ran into AudioStreamMyTone::pos growing too large after some time.

Using a float based solution to generate the sine wave with a modulus wrap kept it within operational limits.

It is also not required to generate PCM-buffers; one could also generate a sinewave that is not bound to a buffer by math nature. I will probably put a youtube tutorial eventually when I finish the library I’m working on. But hopefully these snippets are enough to help passers-by


audiostream_mytone.cpp

This has been changed to use a float based solution with a cheap if-mod wrap.

I have also changed this function to a single float that increments the sine wave on call; I understand this to be bad practice but it works.

float AudioStreamMyTone::gen_tone(){
    float inc = 1.0 / (float(mix_rate)/float(hz));
    pos += inc;
    if (pos > 1.0){
            pos -= 1.0;
    }
    return sin(2.0 * Math_PI * pos);
}

audiostream_mytone.h

The header function must also be changed to match.

float pos;
// ...
float gen_tone();

audiostreamplayer_mytone.cpp

The mix function is causing a lot of issues; something has gone wrong with compatibility of this part of the docs and how memory gets allocated in the audio thread (OR something else I don’t understand)

I changed the function to the code below to generate the sine-wave

void AudioStreamPlaybackMyTone::mix(AudioFrame *p_buffer, 
                                    float p_rate, int p_frames) {
    ERR_FAIL_COND(!active);
    if (!active) {
                    return;
    }
    for(int i = 0; i < p_frames; i++) {
            float sample = base->gen_tone();

            p_buffer[i] = AudioFrame(sample, sample);
    }
}

After changing to this style of solution I see no harm in removing all buffer related code from the playback header and cpp files.


I would be interested in seeing if I’m writing terrible code as I am new to Rt-DSP but in theory I don’t see any real downsides to this solution especially if memory allocation in the Audio thread is properly handled


Edit: Nov 19 2022

I found that for some reason; the AudioStreamPlaybackMyTone::mix function was getting called with firstly around 2 Million frames in size, then it would repeatedly ask for 1024 frames. I’m not sure what is causing this overflow of steps on the first call to mix; but surely this isn’t intended. I can’t imagine asking for that many audio frames on playing the stream would do much of anything; I don’t know the answer yet but I’m on the case.

This would mean that the buffer implementation is likely correct but something handling the mix function is clearly calling it wrong. I will be back here with my notes.


There’s clearly some WHACK things going on with the calls to mix. on my laptop I was getting a call when requested to play for around 2 million frames. On my desktop now I get constant 10k frame requests until I hit play and then it settles to 1024. I really wonder what is causing these calls to mix to clearly exceed the buffer written into the tutorial. I’ve determined this to be the issue; and also that removing the buffer entirely is fine for getting the custom audio stream to work, but that it would be seriously beneficial for realtime audio processing to be able to work with buffers.

I think mostly this is solved with an if statement in mix to check if (p_frames > PCM_BUFFER_SIZE){return;} but I am still getting inconsistent frame counts coming into mix. When I find out who the dastardly dude who did this to us; I will correct his code and push it to the docs.

Jed Stevens | 2022-11-19 04:55