Playing Ogg/Vorbis Files using Alogg with Threaded Support
Playing Ogg/Vorbis Files using Alogg with Threaded Support
My tutorial on streaming an Ogg/Vorbis file with Allegro showed you how to use Alogg to play an audio file. In that example, we had to update the audio stream periodically (how often depends on the block size) or else the audio would become choppy. An alternative method, which I will discuss here, involves playing the audio in its own thread. Basically, you tell it to start playing, then it will take care of the rest. The advantage is that if your program enters a long loop, the audio should still play just fine. This method uses pthreads, and, as such, requires the operating system have it installed (you will want to include the pthreads.dll with Windows applications.)
Requirements
-
Allegro (see the Allegro with Dev-C++ tutorial)
-
Ogg library NOTE: It appears the Vorbis SDK is no longer linked on the site. Here is a direct link to the Vorbis SDK.
If you haven't done so, you might want to look over my tutorial on streaming an Ogg/Vorbis file. I'll cover all of the steps here, but it might help to understand what's going on.
Assuming you have Dev-C++ and Allegro installed, it is now time to install Alogg. Download and decompress the files to a temporary folder. Before going any further, lets build the documentation.
Build Alogg Documentation
You will need the makedoc.exe utility that came with Allegro (it will be in the \Allegro\docs\ folder,) so copy that to the alogg source directory. Open a DOS prompt and go to the directory containing the alogg files. type make docs type=html or make docs type=texi and the documentation will be created. You can refer to this for more assistance in setting up alogg in your environment.
Setup the Ogg/Vorbis Library
In order to build (and use) Alogg, we are going to need the Ogg/Vorbis library. The simplest thing to do is to download the Win32 SDK from http://www.vorbis.com. Extract the contents of this file, and move the contents of the include folder to your Dev-C++ include path and copy the contents of the lib file to your Dev-C++ library path. Maintain the directory structure with the header files (i.e. \include\ogg\ogg.h.) Also, copy the binary files (in the bin folder) to your \windows\system32 directory.
Setup PThreads
After you've downloaded the latest pre-built version of PThreads, extract the files to a temporary location. Find the three header files (pthread.h, sched.h, and semaphore.h) and copy them to your Dev-C++ include path. These cannot be in a sub-folder, or Alogg will not build (without modification.) PThreads will also have a lib folder. This will contain the library files and DLLs for gcc and Visual Studio. Since this tutorial is all about using Dev-C++ and gcc, we will focus on those. Basically, the *GC.* and *.GCE.* files are for gcc (see the readme file.) The E means it contains exception handling. Install the appropriate DLLs (pthreadsGC.dll and pthreadsGCE.dll) in the \windows\system32\ path, and copy the library files (pthreadsGC.a and pthreadsGCE.a) to your Dev-C++ library path.
Modify Files for Alogg
Unfortunately, Alogg will not compile directly for threaded support, and I don't think that there is an included fix for it. However, its easy enough that we can do it ourselves here. Threaded support requires a select function, which seems to be missing in the PThreads headers, so we will use the Windows version (it does the job.) You might want to get fancy and make sure this is being compiled under windows with a #ifdef _WIN32. Anyway, open the file aloggpth.c and go where the headers are being included. Add the following lines to your code:<\P>
#ifdef _WIN32 #include <winsock2.h> #endif
Save the file and close it. We will also need to modify the make file to include Windows' appropriate library file. Open the Makefile and scroll to line 158. You will see the line LIBS+=-lpthread. We want to add the winsock library file (ws2_32.a,) but we also need to change the name of the PThread file, unless you renamed the file. Replace the text in line 158 with the following:
LIBS+=-lpthreadGC -lws2_32Notice the use of the PThreadGC.a file. Save the make file and close.
Build Alogg
Open a DOS prompt and go to the directory containing the Alogg files. Type make PTHREAD=1 to generate the library. Then, type make install PTHREAD=1 to install the library files.
Your First Project
Now for an example of playing an Ogg/Vorbis file with threaded streaming. We will create a window with Allegro, then play a hard-coded audio file. The name of the audio file will be displayed on the screen.
Create a Project
Create an Allegro file as described in the Dev-C++ and Allegro tutorial (you can even use my template to generate the project.
Get an Ogg File
Kinda basic, but necessary. I'll pretty much leave this up to you, but you can check out http://www.vorbis.com for some samples. I'll be using Mists of Time for this example. Once you have the file, copy it to your project directory.
Include the Threaded Alogg Library
At the top of your code, where you are placing your includes, add the following line:
#include <alogg\aloggpth.h>Define the Size of an Audio Block
The block size will determine how much of the audio is read into memory at a time. A small block size will use less memory, and load fast, but could sound choppy. A large block size will sound good (usually,) but requires more memory and more time to load. You may need to play with this (or make it user definable) in order to get the right value.
#define BLOCK_SIZE 4096Add a Method to Create the Audio Block
We will create a function to open the audio file, begin streaming it, and then return a pointer to the thread streaming the file.
///////////////////////////////////////////////////////////////////// // Method to open an Ogg file and start playing it in its own thread // // [in] pszFileName Name of the audio file // // [return] Pointer to the thread playing the audio file ///////////////////////////////////////////////////////////////////// static struct alogg_thread *PlayOggFile( char *pszFileName ) {
We need to declare a pointer to an Alogg stream:
// Stream pointer: struct alogg_stream *asStream;Open the audio file and begin streaming it, using the block size:
// Load the file and start streaming:
asStream = alogg_start_streaming( pszFileName, AUDIO_BLOCK_SIZE );
// Verify that the stream is valid:
if ( !asStream )
{
// Error opening the file:
fprintf( stderr, "Error opening %s ", pszFileName );
alogg_exit();
exit( 1 ); }Finally, we create a thread, using the audio stream. We want to return a pointer to this thread so it can be properly removed when necessary.
// Create a thread for this stream, and return a pointer to the thread:
return alogg_create_thread( asStream );
}We are now ready to modify the main procedure.
Declare a Thread Pointer
At the start of the 'main' procedure, add the following variables:
// Threaded audio variables:
struct alogg_thread **pThreads;
char pszAudioFile[ 256 ];Specify the Name of an Audio File
We need to fill in the audio file name variable with the name we are using. This will be the full name of the file you are using.
// Specify the name of the audio file:
sprintf( pszAudioFile, "Mists_of_Time-4T.ogg" );Initialize the Alogg Library
Just after initializing Allegro, initialize Alogg:
// Initialize Alogg:
alogg_init();Set the Per-Voice Volume
As more voices are reserved for the digital sound driver, Allegro reduces the volume of each in order to (hopefully) prevent avoid distortion. Check out the Allegro documentation for more, but by using a value of 0, we can play all samples at the maximum value without distortion. Place the following code just before we set the graphics mode (set_gfx_mode.)
// Set the per-voice volume:
set_volume_per_voice( 0 );Initialize the Sound Module
The following parameters are generally sufficient for initializing the audio. If the return value is less than 0, then the sound initialization failed.
// Initialize the sound module:
if ( install_sound( DIGI_AUTODETECT, MIDI_AUTODETECT, NULL ) < 0 )
{
fprintf(stderr,"Failed to initialize sound module\n");
alogg_exit();
exit(1);
}Create the Audio Thread
We need to allocate the memory for the thread, then we can start playing the audio. We have declare the thread pointer so it could contain a number of threads, but we will only use one. Once the pointer is allocated, we set it using our function to play the audio file.
// Create a thread to play the audio file:
pThreads = new struct alogg_thread* [ 1 ];
pThreads[ 0 ] = PlayOggFile( pszAudioFile );Display the Name of the Audio File (optional)
Just for fun, go to the method that displays the welcome message (if you are using the template) and change it to display the name of the audio file:
textout_centre( screen, font, pszAudioFile, SCREEN_W / 2.0, SCREEN_H / 2.0, makecol( 255, 255, 255 ) );Stop the Threads
After the user has pressed a key to close the application, we need to shut down any running threads:
// Shut down any audio threads:
if ( pThreads[ 0 ] )
{We will use a shortcut pointer to the desired thread. This isn't entirely necessary, but it could make for cleaner, easier to read code.
// Get a temporary pointer to the thread:
struct alogg_thread *pTempThread = pThreads[ 0 ];Tell the thread to stop:
// Stop the thread:
alogg_stop_thread( pTempThread );Because the thread might not finish right away (and because its possible for us to leave this function before the thread ends) we need to wait right here as long as the thread is alive:
// Wait for the thread to end: while ( alogg_is_thread_alive( pTempThread ) );
}Cleanup
Now all we have left to do is exit Alogg and Allegro:
// Stop the audio streaming:
alogg_stop_streaming( pStream );
// Exit the alogg library:
alogg_exit();Set Linker Parameters
The following linker parameters must be added under project options:
- lalogg - lalleg - lvorbisfile - lvorbis - logg - lpthreadGC - lws2_32
That wasn't so bad now, was it? You will need to make sure PThreadGC.dll is on the computer running this application (i.e. in the \windows\system32\ folder,) but that's all there is to streaming an Ogg/Vorbis file in its own thread. Whether you use threading or not is up to you.
Downloads
| MyFirstAloggPTH.zip | The above application, ready for Dev-C++. |
| DevCpp_AloggStreaming_template.zip | A project template for Dev-C++ to generate a simple threaded Alogg application, like the one above. |
