STB Vorbis streaming - PointerBuffer usage

Started by xsupermetroidx, August 07, 2016, 13:23:35

Previous topic - Next topic

xsupermetroidx

I've been unable to figure out the intended usage of PointerBuffer in the following method:
stb_vorbis_decode_frame_pushdata(long f, ByteBuffer datablock, int[] channels, PointerBuffer output, int[] samples)


The documentation for this method isn't very understandable given how it's accessed through LWJGL.
Quote*output will contain an array of float* buffers, one per channel. In other words, (*output)[0][0] contains the first sample from the first channel, and (*output)[1][0] contains the first sample from the second channel.
Quoteoutput - place to write float ** array of float * buffers

I've tried using the getFloatBuffer method of PointerBuffer with the size specified by samples, but the resulting buffer does not contain any data, even when the number of samples produced is greater than zero. I have been unable to find any examples of this method being used through LWJGL. A little example of proper PointerBuffer usage in this context would be very helpful.

spasi

The native signature is: float ***output. This means that the output argument receives a pointer (a single value) that points to an array of pointers (float**) that each point to a float array (float*). The following code should help:

try ( MemoryStack stack = stackPush() ) {
	IntBuffer channels_p = stack.mallocInt(1); // int*
	IntBuffer samples_p = stack.mallocInt(1); // int*

	PointerBuffer output_ppp = stack.mallocPointer(1); // float***

	stb_vorbis_decode_frame_pushdata(f, datablock, channels_p, output_ppp, samples_p);

	int channels = channels_p.get(0);
	int samples = samples_p.get(0);

	PointerBuffer output_pp = output_ppp.getPointerBuffer(channels); // float**
	for ( int c = 0; c < channels; c++ ) {
		FloatBuffer channel = output_pp.getFloatBuffer(c, samples); // float*
		for ( int s = 0; s < samples; s++ ) {
			float sample = channel.get(s);
			// ...
		}
	}
}



xsupermetroidx

Thanks for the explanation- I was able to get it working, but the playback result is nothing but noise. I've gotten streaming OGG playback working with both JOrbis and J-Ogg, so I know that the streaming code itself isn't the issue. I've also gotten non-streaming playback working by using stb_vorbis to load the entire file at once.

What I don't understand is that OpenAL seems to expect input data in the form of shorts, yet the pushdata methods are only able to return float buffers. I assume that supplying floats where shorts are expected is the reason I'm getting noise. How can I incrementally decode and recieve short data from stb_vorbis? The get_frame_short functions seem to require the file to be fully loaded rather than streamed. Alternatively, how can I feed float data to OpenAL when it only seems to have constants defined for 8 and 16 bit formats?

Just a nudge in the right direction should be all I need... thanks for your time.

CoDi

Quote from: xsupermetroidx on August 07, 2016, 23:21:44
What I don't understand is that OpenAL seems to expect input data in the form of shorts, yet the pushdata methods are only able to return float buffers. I assume that supplying floats where shorts are expected is the reason I'm getting noise. How can I incrementally decode and recieve short data from stb_vorbis?

The stb_vorbis API seemingly does not do float->short conversions with its push API. Performance concerns aside, I'd try to convert sample data manually to verify your assumption, e.g. short_sample = (short)(float_sample * 32767.0f).

spasi

I have not used the pushdata API, but I'm guessing a float-to-short conversion should work indeed.

For a working stream decoding solution, see the Vorbis sample. It plays some music by decoding the file in 4kb batches (using stb_vorbis_get_samples_short_interleaved) and double buffering the audio playback with OpenAL.

xsupermetroidx

Float-to-short conversion did work. I encountered some popping noises during the playback until I noticed that the float output from decode_frame_pushdata isn't normalized to the range 0 to 1. For the sake of anyone trying to get this working I'll leave the simple clamped conversion code here:
short shortSample = clamp((short) (floatSample * Short.MAX_VALUE), Short.MIN_VALUE, Short.MAX_VALUE);


Thanks for the assistance spasi and CoDi.