Playing MP3 files in Half-Life

 
 
The idea of using mp3 in Half-Life come from a tutorial seen there : http://www.planethalflife.com/hlsdk2/
 
I thank Steve for giving us the idea and the first example of how to make it.
 
 
Normaly Half-Life cannot play MP3 files, this feature is not supported by the engine, fortunately there is a free software that you can use to do the job.
 
We are going to use a separate DLL that will handle all the playing job, this DLL can be found at : http://www.fmod.org/
 
The current version is 3.33, so you have to download the sdk ( fmodapi333.zip) to be able to use it in your apps.
There are some differences with the previous versions, so be carefull to use this one to get it working with this tutorial.
 
For the beginners : the sdk will contain the dll itself, the documentation about it and how to use it and of course the headers and library that you will need in the code you are going to build in Half-Life.
 
Now first thing to do, add the headers and library in the sources of Half-Life :
 
Once you have extracted the datas from fmodapi333.zip, you get a fmodapi333 folder that contains all you need.
 
What is important for us is what is in the api folder :
You will find the DLL : fmod.dll and other folders containing the sources you need.
The inc folder contains the headers that you will have to include in your own code.
The lib folder contains the library you need to include in you project settings so the compiler will find it.
 
To make it simple, I will just take what we really need to work with Visual C++.
 
So in the Half-Life source folder (SourceCode from the HL SDK2) we create a new folder that we can name apifmod for example, in that folder we put the 3 headers :
fmod.h the main header you absolutely need to include in your cpp source files
fmod_errors.h the error message header, not really needed, just in case you would be able to get the error messages
wincompat.h not needed to develop with Visual C++
And finaly the library you need for VisualC++ that is located in the lib folder:
fmodvc.lib the other libraries are used for other development apps.
 
Now that we have all we need to develop our code, we can start Visual C++ and open the HL.dsw workspace as usual.
 
Adding the library in the project settings :
 
Before doing anything we have to add the library in the project settings, to do this , in the Visual C++ menu choose Project, then Settings.
 
A new window opens, now choose the Link tab on the right part, there you will find the list of the libraries loaded under : Object/library modules
 
Be carefull not to delete one of them, and notice that you have to make it for all the configurations you are working on : Debug, Release or Profile
 
To add the library, just add the correct name following the existing ones, be carefull to let a space between.
Which name do you have to put there ?
Don't just give the library name fmodvc.lib, it wont work, you have to give the path also, as we have put the library in the apifmod folder and that the workspace file hl.dsw is in the dlls folder we have to make one step back and get in the apifmod folder:
Here is the correct path : ..\apifmod\fmodvc.lib
The two points to get one step back, and then we get in the right folder.
 
Now we are ready to build our code in the one of the source file of HL.
If we do not add this library the compiler will return an error message because it will not find the functions in the library.
 
 
Introducing the MP3 playing code in the Half-Life code :
 
We are going to make a real working example, so you will be able to put it in your own files, I will make a new trigger entity that will handle the playing of many MP3 files as a music background just as the trigger_cdaudio.
 
But first we are going to create some new functions in the util.cpp file, those functions will be usable in any part of the code that already has : #include util.h in the include list.
 
First thing to do, add the functions in the util.h header:
 
You can add this after the other functions of this type, for example at line : 296
 
// Play and stop a MP3 file with the use of the fmod dll
extern int UTIL_PlayMP3(const char *filename, int volume, int loop, int priority);
extern void UTIL_StopMP3(void);
 
The first function will start the playing of the MP3 sound file that has : filename as path, with the volume : volume(0-255), if it should loop or not : loop(TRUE-FALSE), and finaly if this MP3 will be played even if another one is still playing : priority (TRUE-FALSE)
The other function will just Stop any MP3 file that is playing and close the fmod process.
 
Now we set our functions in util.cpp source file :
 
First we have to include the fmod.h header , as for the library we have to give the full path :
 
#include "..\apifmod\fmod.h"
 
 
Then you can add the code at any place, for example before the save-restore code at line : 1707
 
//=============================================================
// Functions to handle the MP3 sound files
// Use the fmod.dll DLL
//=============================================================
 
FSOUND_STREAM *stream; //pointer to a structure needed by fmod
BOOL FsoundStarted;//booleean to check if we have to reset the fmod settings
 
//filename : path from the Half-Life folder where is fmod
//volume between 0 and 255, loop 1 or 0
//priority : set if the current mp3 must be stopped and immédiately replaced
int UTIL_PlayMP3(const char *filename, int volume, int loop, int priority)
{
//check the current playing position in the mp3 file
if ( stream )//if there is an existing stream
{
int sizemp3 = FSOUND_Stream_GetLength( stream );//MP3 file size in bytes
int positionmp3 = FSOUND_Stream_GetPosition( stream );//current playing position in bytes
//ALERT(at_console, "Size MP3 %d\nPosition MP3 %d\n", sizemp3, positionmp3);
if ( positionmp3 >= sizemp3 - 256 || positionmp3 == 0 || priority ==1)//we are close or at the end, or the test failed, or there is a priority
{
FSOUND_Stream_Close(stream);//we close the current stream
stream = NULL;
}
else
{
return 2;// stop and return the information that the mp3 is still playing and can't be stopped and replaced
}
}
//now if there is no existing stream we have to collect info about the mp3 file
int length = 0;//length of the mp3 file
FILE * mp3;
mp3 = fopen(filename, "rb");//we open the mp3 file in read mode binary
if (!mp3)//if there is no existing file at this path
{
return 0;//stop and return the info that there was nothing there
}
fseek(mp3, 0, SEEK_END);//we go to end of file
length = ftell(mp3);//length of mp3
//ALERT(at_console, "MP3 LENGTH %d\n", length);
fclose(mp3);//we close the mp3 file
 
//do we have to play in loop?
int LoopType = 0;
if( loop )
{
LoopType = FSOUND_LOOP_NORMAL;//fsound flag
}
else
{
LoopType = FSOUND_LOOP_OFF;
}
 
if ( stream )//now if there is a stream, we close it and reset the stream structure
{
FSOUND_Stream_Close(stream);
}
stream = NULL;
 
if ( !FsoundStarted )//if we did not already initialise fsound
{
FSOUND_SetOutput(FSOUND_OUTPUT_DSOUND); // Output by DirectSound
FSOUND_SetBufferSize(200);//buffer to 200 miliseconds
FSOUND_SetDriver(0); // Select DirectSound by défaut
FSOUND_Init(44100, 16, 0); // Initialise playback of stream at 44khz 16channels
FsoundStarted = 1;
}
stream = FSOUND_Stream_OpenFile(filename, FSOUND_NORMAL | LoopType, longueur); // Opens the mp3 file
FSOUND_Stream_Play(FSOUND_FREE, stream); // Play the mp3
 
FSOUND_SetSFXMasterVolume(volume);// set the master volume of all streams
 
return 1;//stop and return the info that the mp3 as been successfully started
}
 
//=============================================================
//Now the function that will stop the fmod process :
//=============================================================
 
void UTIL_StopMP3(void)
{
if ( stream )//if there is a stream
{
FSOUND_Stream_Close(stream);//we close the current stream
}
stream = NULL;
FSOUND_Close();//we close the fmod process
FsoundStarted = 0;//we set that the fmod process has been stopped
}
 
 
 
Now our code is ready for playing mp3, we now have to see where to put the fmod.dll and mp3 files and how to set the path
 
First we have to place the dll of fmod in the right place, you have to put it directly in the Half-Life folder with the other game dlls as hw.dll or mp.dll, dont put it in your mod folder.
 
Then where to put mp3 files, you can put them in your mod folder in a subfolder or in another folder of your mod, but remember one thing, as fmod.dll is located in the Hal-Life folder, you will have to set the path from there, so always include your mod folder name in the path, then the name of the mp3 folder.
Don't do as for wav sounds called from the mod dll where you only have only to set the sound folder in the path
For example if your mod is in the MyMod folder and mp3 files in the mp3 folder, you will have to write the path as : MyMod/mp3/track0.mp3  
 
 
Last important thing to do before testing :
 
Now if you go to testing, you may experience an error message when exiting from Half-Life, the reason is that you have to be sure that all the stream that have been started are closed and that the fmod process is ended.
Of course you can call the UTIL_StopMP3( ) function when the mp3 is not needed anymore, but if you quit Half-Life before the mp3 is at the end you will not be able to close it.
So how can we be sure that fmod will be closed before we quit Half-Life?
I found a sure way to close it by calling the FSOUND_Close( ) function from the client dll in a code that is called when Half_life exits.
 
To do this you have to open the cl_dll.dsw and add the library fmodvc.lib in the project settings just as for the hl.dsw
 
Then I placed the call in input.cpp right at the end, where is the HUD_Shutdown function
Don't forget to place the include in input.cpp : #include "..\apifmod\fmod.h"
 
void DLLEXPORT HUD_Shutdown( void )
{

FSOUND_Close();//to close all the streams and the fmod process
ShutdownInput();
}
 
Now the mp3 will continue playing while you are in the menus of the game, but it will be stopped before the game closes and avoid the error that can maybe lead to a crash, i did not get any crash but it's better to avoid those problems.
 
 
Remark:
This tutorial was about MP3, but fmod can do much more, it can also play module files as mod/s3m/xm/it files or CD audio tracks.
For example Unreal use module files for the background music.
Just have a look at the docs coming with the fmod sdk and you will certainly be able to use those files in Half-Life too.
 
Now we can see an example that use these functions to make a trigger_mp3 entity to play mp3 as background music: Trigger_mp3