Sound

1. The most important classes and their usage

3D game sound demo - TCastleSound
3D game sound demo - TCastleSoundSource

Both TCastleSoundSource and TCastleSound can be created, configured and linked in the CGE editor, e.g. when designing your state. You can hear the 3D sounds in the editor. You can also create and control them from code, as all CGE components.

2. Examples

Play sounds demo

3. Editor sound features

Sound editor options

4. Playing sound from Pascal code

Load a sound file as TCastleSound like this:

  1. Add CastleSoundEngine unit to your uses clause.

  2. Declare variable to hold it like MySound: TCastleSound;

  3. Initialize the variable and load sound file, e.g. in Application.OnInitialize:

    MySound := TCastleSound.Create(Application);
    MySound.Url := 'castle-data:/my-sound.wav';
  4. Play the sound like this:

    SoundEngine.Play(MySound);

See source code of examples/audio/simplest_play_sound/simplest_play_sound.dpr for a working simplest possible example of this.

Note
simplest_play_sound example is literally the simplest application that only plays a sound, without displaying anything. In a real situation, you want to use such code to play sound inside a larger CGE application, e.g. play sound when user presses some key, using our view events.

5. Using sounds collection (.castle-component file)

Sounds collection designed in editor

It is often comfortable to define a collection of sounds, which means that each sound file is assigned a simple name and configuration (e.g. priority, default volume), and all the sound files can be loaded easily from any place in code (regardless of the current view).

Do it by using a TCastleComponent as a design root and adding TCastleSound children. Save the resulting design to a file like sounds.castle-component.

See also examples/platformer for an example of this approach. In particular, important files in this example are:

6. Sound backends

FMOD

By default we use OpenAL to play sounds. It’s a great full-featured open-source audio library, perfect match for our open-source game engine.

You can alternatively switch to use the FMOD sound backend. This is just an option. FMOD is proprietary (not open-source) and commercial (though free in some cases, for indie developers).

  • Main advantage of FMOD in CGE for now is Nintendo Switch compatibility.

  • Big future advantage will be integration with the FMOD Studio. The goal of FMOD Studio is to make the work of sound designer easier. The sfx person can create sound effects in FMOD Studio, in a way that is agnostic to the game engine, and the code (like your game) simply sends "events" that may cause some sound effect (playing something, stopping something, fading in/out something…​).

  • See also plans about FMOD Studio and Wwise.

7. Various Advices

7.1. Stereo sounds are never spatialized

Stereo sounds are never spatialized. It means that their 3D position relative to the listener doesn’t matter. If you want the sound to be spatialized, convert it to mono e.g. using Audacity.

7.2. Trying to play too many sounds at once will result in some of them discarded

There is a limit how many sounds are actually played. That is because mixing a lot of sounds at once is computationally expensive. Moreover, the human ear cannot distinguish a lot of sounds at once anyway.

The engine hides this from you, by managing sound sources using their TCastleSound.Priority. The most important sounds are played, the rest (when it goes over the limit) may be stopped as necessary. By default, the limit is 16 (TSoundAllocator.DefaultMaxAllocatedSources).

To understand what actually happens, you can play with examples/audio/test_sound_source_allocator. The example has a limit of 6 (not 16), just to easier experience the problem. Load any sound (the longer the sound, the easier to be hit by the problem) and press "Play Sound" to see the slots being filled with playing sounds. When all the slots are filled, playing new sound necessarily means that some old sound has to be stopped.

The general advises to avoid stumbling on this problem:

  • Use TCastleSound.Priority to manage sound priority wisely.

  • Do not start many sounds, with longer duration, in a short amount of time. Make sounds shorter or make intervals between starting sounds longer or both.

E.g. let’s consider using TCastleTimer to start a sound. Let’s calculate whether this is going to cause us issues:

  • Assume the timer TCastleTimer.IntervalSeconds is 0.05, it means the timer fires 20 times per second.

  • Assume that each timer event plays new sound, by a code like this:

    procedure TViewMain.MyTimerEvent(Sender: TObject);
    begin
      SoundEngine.Play(Sound1);
    end;
  • Assume that the sound is long, e.g. 2 seconds.

This means that when 2 seconds end, the 1st sound is still playing, and 40 sounds are already started. You will likely experience issues, as the sound engine has actually rejected playing most of these sounds — only 16 are really mixed. Note that user probably couldn’t hear (at least, not clearly) 40 sounds at once anyway, even if we would mix them all.

The solution depends on the sound and use-case.

  • Maybe the sound has too long "tail" when it is almost-silent, playing echo, fading out etc. And maybe you can make it just shorter. E.g. cut the "tail" in Audacity. The screenshots below show a "brutal" cut done to make sound end shorter, to make it nicer you may want to fade out the sound at end.

    Sound in Audacity before cutting Sound in Audacity after cutting
  • Maybe you should just play the sound less often.

  • Maybe prepare a ready sound file that contains "premixed" sequence of shorter sounds. E.g. if you’re going to always play 10 sounds in succession, create one WAV file with all 10 sounds already mixed into one. In effect, you call SoundEngine.Play once, not 10 times. Both Castle Game Engine and the sound engine (like OpenAL) will have easier job to do.


To improve this documentation just edit this page and create a pull request to cge-www repository.