Cutting Your Teeth on FMOD Part 2: Channel Groups
If you’ve been following Part 1 of this series you should now be able to play music and sound effects with FMOD in your applications, pause and unpause them and change their volumes. But what about when you have many sounds whose parameters – like volume and pause state – you want to be able to control universally via a single change without iterating over them all? This is where channel groups come in.
Note: The SimpleFMOD library contains all of the source code and pre-compiled executables for the examples in this series.
An obvious use case for channel groups which crops up often in video games is where we wish to give the player control of the overall volume of the music and the sound effects separately via two master volume settings. To accomplish this in FMOD, we can simply assign all of the music in the game to one channel group, all of the sound effects to a second channel group, then by setting the master volume or pause state of each group, all of the sounds belonging to that group have their volumes and pause states set automatically.
One of the great things about volume setting in channel groups is that it is done in a relative (multiplicative) rather than absolute way. Recalling that FMOD volumes go from 0.0 to 1.0, consider the case of loud gunfire playing at 1.0 volume and quiet footsteps playing at 0.3 volume simultaneously. If the player wanted to set the overall sound effects volume to 0.5, we would have to do the math by hand and set the gunfire to 0.5 and the footsteps to 0.15. Now consider the more complex case of a sound that is constantly varying in volume each frame, for example a helicopter approaching from the distance and then flying away again. Without channel groups, we would have to multiply the desired master volume by the desired relative helicopter volume every frame. But with channel groups, all of this mess is eliminated from our code: fades and other volume changes to individual sounds all take place relative to the channel group’s master volume. If the helicopter volume goes from 0.2 to 1.0 and down to 0.0 again over several seconds, we can still use these absolute values in our code, knowing that if the user has set the channel group master volume to 0.5, the helicopter volume will be automatically scaled from 0.1 to 0.5 and down to 0.0 without any changes to our code. We can therefore always assume that 1.0 is the maximum available volume without consideration to the actual master volume.
Using Channel Groups
After successfully initializing FMOD as shown in Part 1, and assuming
system is a pointer to
FMOD::System, create as many channel groups as you need, for example for music and sound effects as in the example above:
FMOD::ChannelGroup *channelMusic; FMOD::ChannelGroup *channelEffects; FMODErrorCheck(system->createChannelGroup(NULL, &channelMusic)); FMODErrorCheck(system->createChannelGroup(NULL, &channelEffects));
If you like you can assign a string name to each channel group in the first argument to
createChannelGroup, but this is not especially necessary.
We then load some sounds:
FMOD::Sound *song1, *song2, *effect; // Open music as a stream FMODErrorCheck(system->createStream("Song1.mp3", FMOD_DEFAULT, 0, &song1)); FMODErrorCheck(system->createStream("Song2.mp3", FMOD_DEFAULT, 0, &song2)); // Load sound effect into memory (not streaming) FMODErrorCheck(system->createSound("Effect.mp3", FMOD_DEFAULT, 0, &effect));
We will need to obtain pointers to the
FMOD::Channels of each sound so that we can assign them to a channel group. One way to do this is to start the sound paused so we can retrieve its channel:
// Assign each song to a channel and start them paused FMOD::Channel *songChannel1, *songChannel2, *effectChannel; FMODErrorCheck(system->playSound(FMOD_CHANNEL_FREE, song1, true, &songChannel1)); FMODErrorCheck(system->playSound(FMOD_CHANNEL_FREE, song2, true, &songChannel2)); FMODErrorCheck(system->playSound(FMOD_CHANNEL_FREE, effect, true, &effectChannel));
Note that the 3rd argument to
playSound ensures that each sound is paused on start, so no audio will be played. Note also that while non-looping effects are generally “fire and forget”, ie. we don’t need to retrieve their channel, that in order to assign them to a channel group, we do in fact need to do this. Streaming or looping music can generally be kept paused and on the same channels for the lifetime of the application, but non-looping sound effects will have their channel freed as soon as playback ends, so we will need to repeat the process of starting paused, retrieving the channel and assigning that channel to the appropriate channel group each time the effect is played (ie. the third playback line in the code above will generally not be executed in the initialization phase of your code, but each time the effect is played).
Assigning the sound channels to a channel group is now a piece of cake:
songChannel1->setChannelGroup(channelMusic); songChannel2->setChannelGroup(channelMusic); // Each time the effect is played effectChannel->setChannelGroup(channelEffects);
To start a song (or an effect later in your code), simply unpause it and it will play in the desired channel group:
songChannel1->setPaused(false); // replace songChannel1 with the channel of the desired sound
Changing the master volume
We can now easily get or set the volume of a channel group as follows:
// To get the master volume float vol; channelMusic->getVolume(&vol); // To set the master volume vol = max(min(vol, 1.0f), 0.0f); channelMusic->setVolume(vol);
As always, volumes should be between 0.0 and 1.0. And that’s all there is to it!
In Part 3 of our series on FMOD we shall look at how to load sounds from embedded resources in your application’s EXE file, and how to embed them using Visual Studio. Have fun!