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.
Channel Groups
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::Channel
s 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!
Coming Up…
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!
Here’s a suggestion. Use System::getMasterChannelGroup and store a reference to it, and then add your custom channel groups to it. This way you can also control all channel groups at the same time, which might be useful in a lot of situations.
Hi!
My name is Durval and I am from Portugal. Your article helped me quite a bit, but i would like to ask you two things:
Can you post an exame of a Channel Callback ? I am trying to make one but is not working, not many examples around..
Secondly, do you know how to remove a channel from a channelgroup? I thought that when a sound ended, or when i release them, the channel would be removed from the channel group, but when i make a call to getnumberChannels to the channel groups, their number does not decreases.
Sorry for my poor English.
Best regards,
Durval
Hi Durval, sorry for the slow reply, I’ve been moving house then got sick for a while.
What kind of channel callback do you want (which value of FMOD_CHANNEL_CALLBACKTYPE enumeration)? If your callback isn’t being triggered at all, make sure you are calling FMOD::System::update() frequently as the callbacks are only called from within this function.
Regarding removing a channel from a channel group, all channels which aren’t in a user-defined channel group use the so-called “system master group” instead. I haven’t tested it but looking at the documentation, I would suggest the way to remove a channel from your group would be to move it back to the master group, since channels can only be in one group at a time, and the documentation for ChannelGroup::release() states that all channels assigned to the group being released are re-assigned to the system master group. The code would go something like this:
FMOD::System *system;
FMOD::ChannelGroup *myChannelGroup;
FMOD::Channel *myChannel;
// initialization code
// ...
// when you want to remove myChannel from myChannelGroup:
FMOD::ChannelGroup *masterChannelGroup;
system->getMasterChannelGroup(&masterChannelGroup);
myChannel->setChannelGroup(masterChannelGroup);
The remarks for Channel::setChannelGroup in the FMOD API documentation state: “Setting a channel to a channel group removes it from any previous group, it does not allow sharing of channel groups.”
Hope that helps!
Hi! I posted a comment. Did you get it?
Hi! I have found your tutorials extremely helpful! However, it seems you have stopped updating your source code and libraries. I am using the latest version of Visual Studio 2017 (build 15.6.4) and this breaks a lot of the compiling and linking in both your Simple2D and FMOD libraries. For example, when I trying to compile and run the code in part 2 and using your FMOD libraries, I get a LINK2038 ERROR: “mismatch detected for ‘_MSC_VER’: Value ‘1700’ doesn’t match value ‘1900’ in SFMOD_ConsoleDemo2.obj. Have you stopped updating your code?