Loading and playing audio causes access violation

Started by silent_ptr, August 03, 2020, 14:49:36

Previous topic - Next topic

silent_ptr

I'm trying to create a simple sound demo for my game engine using OpenAL. I don't need any special 3D sound effects at the moment, I'm just trying to play a normal music mp3. This is the code I use to initialise OpenAL and load the sound file:
device = ALC11.alcOpenDevice((ByteBuffer)null);

if (device == 0)
{
	return false;
}

capabilities = ALC.createCapabilities(device);

if (capabilities == null)
{
	ALC11.alcCloseDevice(device);
	return false;
}

context = ALC11.alcCreateContext(device, (IntBuffer)null);
ALC11.alcMakeContextCurrent(context);
AL.createCapabilities(capabilities);
buffer = AL11.alGenBuffers();

int sampleRate;

try (MemoryStack stack = MemoryStack.stackPush())
{
	IntBuffer channelsPtr = stack.mallocInt(1);
	IntBuffer sampleRatePtr = stack.mallocInt(1);
	PointerBuffer dataPtr = stack.mallocPointer(1);
	STBVorbis.stb_vorbis_decode_filename("test.mp3", channelsPtr, sampleRatePtr, dataPtr);
	sampleRate = sampleRatePtr.get(0);
	AL11.alBufferData(buffer, AL11.AL_FORMAT_STEREO16, dataPtr.getByteBuffer(dataPtr.sizeof()), sampleRate);
}

source = AL11.alGenSources();
AL11.alSourcei(source, AL11.AL_BUFFER, buffer);
return true;

When this code runs I get an access violation and my program shuts down. It does not give more specific information than this:
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00007ffbbf83dec8, pid=16576, tid=7492
#
# JRE version: Java(TM) SE Runtime Environment (14.0.1+7) (build 14.0.1+7)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (14.0.1+7, mixed mode, sharing, tiered, compressed oops, g1 gc, windows-amd64)
# Problematic frame:
# C  [OpenAL.dll+0x4dec8]
#
# No core dump will be written. Minidumps are not enabled by default on client versions of Windows
#
# An error report file with more information is saved as:
# C:\Users\logan\Documents\Phantasm Entertainment\Face Engine\Face Engine Eclipse Workspace\Face Engine Test\hs_err_pid16576.log
#
# If you would like to submit a bug report, please visit:
#   https://bugreport.java.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#

Am I doing something wrong with my pointers?

SomeJavaProgrammer

Hey silent_ptr, I'm just starting with OpenAL and I've been hitting a lot of the same problems as you so I took a look at your code.

Firstly, I notice from the filename you're passing in a .mp3 file to the ogg vorbis decoder, which probably won't work. Best test this out with an ogg encoded sound file.

I was able to recreate your EXCEPTION_ACCESS_VIOLATION crash locally, and I found a few things:

You're not checking the result of stb_vorbis_decode_filename, as it will return -1 on error.
We also need the number of samples for later...

int samplesDecoded = STBVorbis.stb_vorbis_decode_filename("C:\\Git\\test-project\\src\\main\\resources\\music.ogg", channelsPtr, sampleRatePtr, dataPtr);
if (samplesDecoded == -1) {
    throw new IllegalStateException("No samples were decoded");
}


The next problem is with alBufferData:

You're hardcoding the format which isn't ideal, you can replace that with
(channels == 1 ? AL11.AL_FORMAT_MONO16 : AL11.AL_FORMAT_STEREO16)


The data being passed in isn't right for a couple of reasons:

dataPtr.sizeof() just gets the size of the pointer itself in bytes (which is 4 bytes on a 32bit JVM and 8 bytes on a 64bit JVM) and not the size of the buffer which is what you were expecting.

Each sample is a short, so the size of the buffer that is referenced by the pointer will be the number of samples decoded multipled by the number of channels multiplied by the number of bytes in a short (samplesDecoded * channels * Short.BYTES).

We can get the data either by getting a ByteBuffer of the previously calulated size from the PointerBuffer:
dataPtr.getByteBuffer(samplesDecoded * channels * Short.BYTES)

or by just getting a ShortBuffer:
dataPtr.getShortBuffer(samplesDecoded * channels)


Putting it all together, here is an example that works (on my machine at least):

public static void main(String[] args) {
        int alError;
        long device = ALC11.alcOpenDevice((ByteBuffer)null);

        if (device == 0)
        {
            throw new IllegalStateException();
        }

        ALCCapabilities capabilities = ALC.createCapabilities(device);

        if (capabilities == null)
        {
            ALC11.alcCloseDevice(device);
            throw new IllegalStateException();
        }

        long context = ALC11.alcCreateContext(device, (IntBuffer)null);
        ALC11.alcMakeContextCurrent(context);
        AL.createCapabilities(capabilities);

        int buffer = AL11.alGenBuffers();
        int channels;
        int sampleRate;

        try (MemoryStack stack = MemoryStack.stackPush())
        {
            IntBuffer channelsPtr = stack.mallocInt(1);
            IntBuffer sampleRatePtr = stack.mallocInt(1);
            PointerBuffer dataPtr = stack.mallocPointer(1);
            int samplesDecoded = STBVorbis.stb_vorbis_decode_filename("C:\\Git\\test-project\\src\\main\\resources\\music.ogg", channelsPtr, sampleRatePtr, dataPtr);
            if (samplesDecoded == -1) {
                throw new IllegalStateException("No samples were decoded");
            }

            // Metadata
            channels = channelsPtr.get();
            sampleRate = sampleRatePtr.get(0);
            AL11.alBufferData(
                    buffer,
                    (channels == 1 ? AL11.AL_FORMAT_MONO16 : AL11.AL_FORMAT_STEREO16),
                    dataPtr.getShortBuffer(samplesDecoded * channels),
                    //dataPtr.getByteBuffer(samplesDecoded * channels * Short.BYTES),
                    sampleRate
            );
            alError = AL11.alGetError(); System.out.printf("alGetError=%s%n", AL10.alGetString(alError));
        }

        int source = AL11.alGenSources();
        AL11.alSourcei(source, AL11.AL_BUFFER, buffer);
        alError = AL11.alGetError(); System.out.printf("alGetError=%s%n", AL10.alGetString(alError));

        AL11.alSourcePlay(source);
        alError = AL11.alGetError(); System.out.printf("alGetError=%s%n", AL10.alGetString(alError));

        while (AL11.alGetSourcei(source, AL10.AL_SOURCE_STATE) == AL10.AL_PLAYING) {
            ;
        }
    }