Let's start with the quickest and easiest way of allowing our games to stop being the strong, silent type.
The s3eAudio API allows us to play compressed music formats such as MP3 and AAC. Some devices may also allow us to play other formats, such as MIDI files. Marmalade makes use of whichever audio codecs a particular device may have built-in rather than decoding the audio itself, so be sure to check that your chosen audio format is supported by all the devices you wish to target.
Let's now take a look at how we can get an audio track playing and what other functionality the s3eAudio API provides for us. There is nothing we need to add to our MKB file in order to allow us to use s3eAudio, as it is one of the low level APIs of Marmalade that is always available for use. All we need to do is include the header file s3eAudio.h
in any source file that needs access to s3eAudio functions.
There are two ways of starting the playback of an audio track. The first allows us to specify the filename of the audio track we want to play and the number of times we would like the track to repeat, and looks like this:
s3eAudioPlay("music.mp3", aRepeatCount);
The filename is just a standard C, null-terminated string and is relative to the data
directory when run from Windows or the application install directory on the device. Specifying a number for the repeat count will cause the audio track to play that many times, while setting it to zero will cause the track to loop continuously.
The other method is to play the audio track from an area of memory as follows:
s3eAudioPlayFromBuffer(apBuffer, aBufferLength, aRepeatCount);
The parameters apBuffer
and aBufferLength
provide the memory location where the audio track resides and the length of audio data in bytes. The repeat count is specified in the same manner as with s3eAudioPlay
.
In most cases we will find that the first method is good enough since it is easy to use and doesn't require us to allocate blocks of memory and fill it with data. You may find that the buffer method provides slightly faster initial playback if you have preloaded the audio data, but on most recent devices the difference is negligible.
If you make a call to either of these functions while an audio track is currently playing, that track will be stopped and the new track will begin playing.
Once an audio track is playing, we can pause playback by calling the s3eAudioPause
function. The audio can be started again from the point at which it was paused by calling s3eAudioResume
. Finally, to stop playback completely just call s3eAudioStop
.
All three of these functions take no parameters and will return S3E_RESULT_SUCCESS
when no errors occur. An error is raised if any of these functions are called when it makes no sense to do so, for example calling s3eAudioPause
when there is no audio playing.
Like most of the low level APIs in Marmalade, s3eAudio features a pair of functions called s3eAudioGetInt
and s3eAudioSetInt
that are used to change attributes related to that API. In s3eAudio, one of the things we use these functions for is to change the volume of audio playback.
To set the playback volume we can make the following call:
s3eAudioSetInt(S3E_AUDIO_VOLUME, S3E_AUDIO_MAX_VOLUME / 2);
In the aforementioned example we set the volume to half of S3E_AUDIO_MAX_VOLUME
, which is the maximum allowed volume.
To determine the current volume we use this code:
int32 lVolume = s3eAudioGetInt(S3E_AUDIO_VOLUME);
We can also request the default volume for audio by passing in the value S3E_AUDIO_VOLUME_DEFAULT
. This is the default volume level for playing audio and has been chosen by the Marmalade SDK so as to provide a fairly consistent volume level across all devices.
The s3eAudioGetInt
function allows us to make several other queries regarding audio playback. The following table shows which properties can be specified:
Property |
Description |
---|---|
Returns current audio status—one of | |
Returns the current position in the audio track in milliseconds, or | |
Returns the currently selected audio channel. This property can also be used in | |
Returns the number of audio channels available. On most platforms this will return | |
Returns | |
Returns the length, in milliseconds, of the track currently playing. | |
Returns |
There are two methods we can use to determine when an audio track has finished. One is to use a polled approach, the other is to make use of a callback.
To poll whether an audio track has completed or not, we can do the following:
if (s3eAudioIsPlaying() == S3E_FALSE) { // Audio is not playing! }
This function returns S3E_TRUE
if the audio is currently playing, or S3E_FALSE
if it is stopped or paused. This function is actually just a shortcut for calling s3eAudioGetInt
with the property S3E_AUDIO_STATUS
.
The callback approach is also very simple to use, as the following code snippet shows:
int32 AudioFinished(s3eAudioCallbackData* apAudioData, void* apUserData) { // apAudioData->m_ChannelID identifies the audio channel that // has completed. // s3eCallback functions must return a value, but in case of // audio callback the value returned does not matter. return 0; } // Use the following line to set up the audio callback s3eAudioRegister(S3E_AUDIO_STOP, (s3eCallback) AudioFinished, NULL); // And this line to remove the callback function s3eAudioUnRegister(S3E_AUDIO_STOP, (s3eCallback) AudioFinished);
The callback function will be called whenever an audio track finishes and will pass the pointer to user data supplied as the last parameter in the s3eAudioRegister
call by using the apUserData
argument. It will not be called if we have asked the audio track to be looped unless it is the last repetition. The function will also be called if the audio is stopped due to an error, such as a corrupted track. We can determine whether completion was caused due to error by calling the s3eAudioGetError
function, which returns an error code of the enumerated type s3eAudioError
. A complete list of error codes can be found in s3eAudio.h
.
The decision of whether to use the polling or callback-based approach depends on your application, and indeed quite often in games we don't even really care that much about when an audio track has finished as we often just want the same track to loop forever until a new piece of audio is required. If you are just waiting for a jingle to finish during a splash screen, the polled method is probably adequate, but if you want to join several tracks together one after the other, the callback approach would probably lead to a clean solution.
3.138.37.191