3

I'm trying to play some sound on Android using OpenAL and C/C++. I'm using SDL framework.

I keep getting mysterious crashes with alBufferData() even when my code is as simple as:

ALuint buf, src;
alGenBuffers(1, &buf);
alGenSources(1, &src);
uint8_t data[8000]{};
alBufferData(buf, AL_FORMAT_MONO8, data, 8000, 8000);
alSourcei(src, AL_BUFFER, buf);
alSourcePlay(src);

Every time the program reaches alBufferData() it crashes or hangs. I'm not getting any kind of error message.

Surprisingly enough, if I pass 0 instead of actual pointer to alBufferData(), then the program runs fine and plays some random noise.

I have absolutely no idea what's wrong.

MCVE:

#include <cstdlib>
#include <cmath>
#include <string>

#include <SDL2/SDL.h>
#include <AL/al.h>
#include <AL/alc.h>

#if !defined(ANDROID) && !defined(__ANDROID__)
#define GLEW_STATIC
#include <GL/glew.h>
#else
#include <GLES2/gl2.h>
#endif

void Msg(const char *txt)
{
    SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "test app", txt, 0);
}
void Err(const char *txt)
{
    Msg(txt);
    std::exit(0);
}

int SDL_main(int, char **)
{
    Msg("Running build compiled at " __DATE__ " " __TIME__);

    if (SDL_Init(SDL_INIT_EVENTS | SDL_INIT_VIDEO))
        Err("SDL init failed");
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
    #if !defined(ANDROID) && !defined(__ANDROID__)
    SDL_Window *win = SDL_CreateWindow("test app", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
    #else
    SDL_Window *win = SDL_CreateWindow("test app", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_FULLSCREEN);
    #endif
    if (!win)
        Err("Window creation failed");
    SDL_GLContext con = SDL_GL_CreateContext(win);
    if (!con)
        Err("OpenGL context creation failed");
    #if !defined(ANDROID) && !defined(__ANDROID__)
    glewExperimental = 1;
    if (glewInit() != GLEW_OK)
        Msg("GLEW init failed");
    while (glGetError()) {}
    #endif
    SDL_GL_SetSwapInterval(1);

    ALCdevice *device = alcOpenDevice(0);
    if (!device)
        Err("OpenAL init failed");
    const ALCint config_array[] = {ALC_FREQUENCY, 44100, ALC_MONO_SOURCES, 4, ALC_STEREO_SOURCES, 4, 0};
    ALCcontext *context = alcCreateContext(device, config_array);
    if (!context)
        Err("OpenAL context creation failed");
    if (!alcMakeContextCurrent(context))
        Err("OpenAL context switching failed");


    uint8_t sound_wave[8000];
    for (int i = 0; i < 8000; i++)
    {
        float pi = std::atan(1)*4;
        sound_wave[i] = int(std::pow(std::sin(i * pi / 180 * 4),0.5) * 127) + 127;

        auto smoothstep = [](float x){return 2*x*x*x-3*x*x;};
        if (i < 1000)
            sound_wave[i] *= smoothstep(i / 1000.f);
        else if (i >= 7000)
            sound_wave[i] *= smoothstep((8000 - i) / 1000.f);
    }

    ALuint buf, src;
    int frames = 0;

    while (1)
    {
        SDL_Event event;
        bool click = 0;
        while (SDL_PollEvent(&event))
        {
            switch (event.type)
            {
              case SDL_QUIT:
                std::exit(0);
                break;
              case SDL_KEYDOWN:
                click = 1;
                break;
            }
        }

        switch (frames)
        {
          case 3:  Msg("Creating a buffer");   break;
          case 4:  alGenBuffers(1, &buf); if (!buf) Err("No buffer"); break;
          case 5:  Msg("Creating a source");   break;
          case 6:  alGenSources(1, &src); if (!src) Err("No source"); break;
          case 7:  Msg("Setting buffer data"); break;
          case 8:
            alBufferData(buf, AL_FORMAT_MONO8, sound_wave, 8000, 8000);
            if (int err = alGetError()) // This code is not reached
                Msg((std::string("Error code: ") + std::to_string(err)).c_str());
            break;
          case 9:  Msg("Attaching buffer");    break;
          case 10: alSourcei(src, AL_BUFFER, buf);  break;
          case 11: Msg("Done");                break;
        }
        if (frames < 12)
            frames++;

        if (frames == 12 && click)
            alSourcePlay(src);

        SDL_GL_SwapWindow(win);
    }
}

Here is my Application.mk:

APP_ABI := armeabi armeabi-v7a x86 mips

APP_STL := c++_shared

LOCAL_SHARED_LIBRARIES := c++_shared
APP_CFLAGS += -w
APP_CPPFLAGS += -fexceptions -frtti -I../lib/include
APP_CPPFLAGS += -std=c++14 -O3 -s
NDK_TOOLCHAIN_VERSION := clang

I'm using OpenAL which was prebuilt using standalone toolchains. I don't know if it matters, but here is my build command (this one is for armv7-a, for other ABIs there are different commands):

cmake -D CMAKE_SYSTEM_NAME:string=android -D CMAKE_C_COMPILER:filepath="Y:/clang_3.8_android_api12_androideabi/bin/arm-linux-androideabi-clang.cmd" ^
                                          -D CMAKE_CXX_COMPILER:filepath="Y:/clang_3.8_android_api12_androideabi/bin/arm-linux-androideabi-clang++.cmd" ^
                                          -D CMAKE_C_FLAGS:string="-w -O3 -mthumb -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16" ^
                                          -D CMAKE_CXX_FLAGS:string="-w -O3 -mthumb -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16" ^
                                          -D CMAKE_SHARED_LINKER_FLAGS:string="-lc++_shared" ^
                                          -D CMAKE_MODULE_LINKER_FLAGS:string="-lc++_shared" ^
                                          -D CMAKE_BUILD_TYPE:string="Release" ^
                                          -G "MinGW Makefiles" ^
                                          ../../..
mingw32-make.exe

EDIT:

Now it's getting more interesting. I've managed to make it work once, but after a tiny unrelated change to the source code it broke again.

There MUST be some kind undefined behaviour somewhere, but I'm sure it's not on my side.

Is it a bug in OpenAL? The exact version I'm using is openal-soft-1.17.1.

EDIT:

Updated to OpenAL 1.17.2. No luck, same error.

EDIT:

Here is logcat output for the mcve from the emulator.
It's hosted externally because it's 2000+ lines long.

If I understood correctly, the log says that there is a heap corruption and that that application had sigsegv'ed in dlmalloc(). But I have no idea how to fix it.

I've tried to add SDL_INIT_AUDIO flag to SDL_Init() - same error.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
  • Related: Someone got same issue on Mac OS: http://stackoverflow.com/questions/37889119/openal-soft-crashes-in-release-mode-debug-works-fine – HolyBlackCat Jul 04 '16 at 23:12

2 Answers2

0

It seems like a bug in OpenAL, which also affects MacOS. It should be already fixed.

Also it crashes only when the library is built in "Release" or "RelWithDebInfo" modes. Switching to any random string or "Debug" fixes the issue.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
0

Hello :) for me the line

alBufferData(buf, AL_FORMAT_MONO8, sound_wave, 8000, 8000);

should be part of the issue. From documentation :

void alBufferData(
       ALuint      buffer,
       ALenum      format,
       const ALvoid *data,
       ALsizei      size,
       ALsizei      freq
      );

size : the size of the audio data in bytes freq : the frequency of the audio data

My first point your freq (8000) is perhaps unsupported by implementation, try 44100 as the context frequency. You'll need to enlarge your sound_wave array. If it's work you'll can get the '8k' frequency effect by having the same sample value for 5/6 samples ;)

My second point is for the size, it may be better to use the type ALubyte from al.h and for the size something like sizeof(sound_wave) or samples_count * sizeof(ALubyte) to be sure and always match code changes.

But for documentation giving NULL in place of data will set AL_INVALID_VALUE error state (as alGetError() returns)

Hope it'll help

  • AL is definitely able to convert sounds between different sample rates (at least between 44100 and 48000, not sure why 8000 wouldn't work). I doubt `ALubyte` is anything but `unsigned char`, otherwise I would get an error on the pointer conversion. Most probably it was an OpenAL bug, see the bug report I linked. – HolyBlackCat Jan 25 '21 at 07:29