Hello OggVorbis

How-to

The audio portion of a native game or app has traditionally been a land of “#ifdefs” and low-level platform specific code. This is especially true when targeting different devices supporting differing audio formats.

OpenAL, from Khronos, is designed to solve that exact problem. It is an open source, cross platform 3D Sound API for use in games and multimedia apps. Thankfully, OpenAL 1.1 is readily available on BlackBerry® 10. It is supported on multiple platforms and due to its API looking strikingly similar its 3D graphics cousin OpenGL API, it has proved to be very easy to learn and integrate. OpenAL, however, does not directly support compressed audio formats out-of-the-box. This could result in games having bigger audio files and hence a bigger memory footprint.

Enter OggVorbis.

There are several compressed audio formats and associated libraries available and I recommend OggVorbis for the following reasons:

  • It’s lightweight
  • It’s open source
  • It’s professional quality audio encoding
  • It’s streaming support

If you need further convincing, please visit their site.

This blog post uses a sample project to show how to set up and use OpenAL and OggVorbis with the BlackBerry 10 Native SDK. The sample is forked off the famous “cascades cowbell app” and minor modifications have been made to show how to load and play .ogg and .wav files. Assuming you are already familiar with the BlackBerry 10 Native SDK, here are the main steps to follow:

Convert your assets

Download a tool of your choice from http://www.vorbis.com/software to convert the heavy WAV PCM files to the slender OGG files.

Add the OggVorbis library to your project

OggVorbis is already ported and available at the BlackBerry Github community, so go ahead download the source code () and compile it using BlackBerry 10 Native SDK. Once compiled, add the library (example: libvorbis.a) and the headers into a folder inside the cowbell application project directory. Let’s call it “external-lib”. Now modify the cowbell.pro project to include the header and library files.

LIBS   += -lOpenAL -lalut -L../external-lib/oggvorbis/lib/qnx/arm/ -L../external-lib/oggvorbis/lib/qnx/x86/ -lvorbis

INCLUDEPATH += ../src ../external-lib/oggvorbis/include
SOURCES += ../src/*.cpp 
HEADERS += ../src/*.h

Now that we have the project setting done, we can focus on loading and playing the .ogg file.

Open the OGG file

The function that opens up the access to the OggVorbis file is ov_open. Declare an OggVorbis_File file type that is returned by the ov_open. This file will be used to decode the OggVorbis file.

OggVorbis_File ogg_file;
ALenum format;
int result;
unsigned int size = 0;

rewind(file);

// Open the ogg file
if (ov_open(file, &ogg_file, NULL, 0) < 0)
fclose(file);

Calculate the unpacked audio file size

In order to load the uncompressed data into an OpenAL buffer, we need to store the audio data in a temporary buffer. The size of the audio buffer can be found using a simple formula: (number of channels) * (number of samples) * (number of bytes per packet)

The number of channels info can be retrieved into vorbis_info using the ov_info function. The number of samples can be found using the ov_pcm_total function.

vorbis_info* info;
// Get the file info for creating the audio buffer
info = ov_info(&ogg_file, -1);

if (info->channels == 1)
	format = AL_FORMAT_MONO16;
else
	format = AL_FORMAT_STEREO16;

// buffer size = (no. of channels) * (no. of samples) * 2 (for 16 bit)
unsigned int data_size = ov_pcm_total(&ogg_file, -1) * info->channels * 2;

Decode the audio file

Once we have a temp buffer allocated, the ov_read function can be used to read and decode blocks of audio data.

char* data = new char[data_size];

// Read the Ogg file into audio buffer
while (size < data_size) {
	result = ov_read(&ogg_file, data + size, data_size - size, 0, 2, 1, &section);
	if (result > 0)
		size += result;
	else if (result < 0) {
		delete data;
		return false;
	}
	else {
		break;
	}
}

Load the data into an OpenAL buffer

Assuming we have already initialized OpenAL context and audio buffers to hold audio data, we can now upload the unpacked data into it.

// Load PCM data into OpenAL buffer.
alBufferData(buffer, format, data, data_size, info->rate);

ALenum error = alGetError();
if (error != AL_NO_ERROR)
	reportOpenALError(error);

Playing the audio is no different than playing regular PCM audio data.

The full sample is available under an Open Source license at here. Enjoy and consider participating in our Open Source community

About Ramprasad Madhavan

Ram was a Senior Gaming Consultant at BlackBerry's Developer Relations Team. With years of Advanced Graphics and Gaming Experience, he managed technical partnerships with Industry's Top notch Game Engines and Middleware partners. Indie Gaming has always been his passion and he strives to make available all the tools necessary for Indie Game developers be successful on new BlackBerry Platform. Prior to joining Developer Relations team, Ram was part of Gaming R&D team at BlackBerry where he helped design and develop the open-source gaming framework, Gameplay3D.

Join the conversation

Show comments Hide comments
+ -
blog comments powered by Disqus