Audio playback with FMOD Audio

Besides the simple and low-performance TGorillaAudioManager, Gorilla3D introduces an interface for usage of the professional audio lowlevel library FMOD.

Notice

If your development budget is under $500k or you’re not doing it for the money, go ahead and start creating. Simply register for a free license and include our logo in your game, before it is released to the world:

https://www.fmod.com/

FMOD allows many different formats on all platforms. It supports 3D positioning of sound sources. It guarantees high performance and high quality playback even with a large number of samples. By usage of sound- and channelgroups you can easily manage your audio files.

FMOD provides a few common sound effects, like: panning, volume, reverb, … By the geometry API you are able to implement even complex sound effects within a 3D world with obstacles, doppler-effect and reverb. FMOD allows DSP plugins to create your own native sound effects.

Supported file formats: AIFF, ASF, ASX, DLS, FLAC, FSB (FMOD's sample bank format), IT, M3U, MIDI, MOD, MP2, MP3, Ogg Vorbis, PLS, S3M, VAG (PS2/PSP format), WAV, WAX (Windows Media Audio Redirector), WMA, XM, XMA (only on the Xbox 360), as well as raw audio data.

Supported platforms: Microsoft Windows (x86 and x86-64), macOS, iOS, Linux (x86 and x86-64), Android, BlackBerry, Wii, Wii U, 3DS, Nintendo Switch, Xbox, Xbox 360, Xbox One, PlayStation 2, PlayStation 3, PlayStation 4, PlayStation Portable, PlayStation Vita, and Google Native Client.

Units

Gorilla3D provides the low level header declarations in Gorilla.Audio.FMOD.Lib.* units.

Gorilla.Audio.FMOD.Lib.API
Gorilla.Audio.FMOD.Lib.Codec
Gorilla.Audio.FMOD.Lib.Common
Gorilla.Audio.FMOD.Lib.DSP.Effects
Gorilla.Audio.FMOD.Lib.DSP
Gorilla.Audio.FMOD.Lib.Errors
Gorilla.Audio.FMOD.Lib.Output
Gorilla.Audio.FMOD.Lib.Types

To simplify usage of the massive amount of functions, Gorilla3D provides higher level structure declarations in Gorilla.Audio.FMOD.Intf.* and Gorilla.Audio.FMOD.* units.

Gorilla.Audio.FMOD.Intf.System
Gorilla.Audio.FMOD.Intf.Channel
Gorilla.Audio.FMOD.Intf.ChannelGroup
Gorilla.Audio.FMOD.Intf.DSP
Gorilla.Audio.FMOD.Intf.Sound
Gorilla.Audio.FMOD.Intf.SoundGroup
Gorilla.Audio.FMOD.Intf.Reverb3D
Gorilla.Audio.FMOD.Intf.Geometry
Gorilla.Audio.FMOD.System
Gorilla.Audio.FMOD.Custom
Gorilla.Audio.FMOD.Channel
Gorilla.Audio.FMOD.ChannelGroup
Gorilla.Audio.FMOD.DSP
Gorilla.Audio.FMOD.Sound
Gorilla.Audio.FMOD.SoundGroup
Gorilla.Audio.FMOD.Reverb3D
Gorilla.Audio.FMOD.Geometry

On the highest level Gorilla3D provides a component for quick usage by IDE. You can find the TGorillaFMODAudioManager in:

Gorilla.Audio.FMOD

Remarks: The higher level structures are based on a interface-class architecture, so you need to take care of reference counting. In your application you should only use the interfaces!

Examples

To load an audio file at runtime

Form1.pas
uses
  Gorilla.Audio.FMOD,
  Gorilla.Audio.FMOD.Sound;
 
var 
  FFMOD : TGorillaFMODAudioManager;
 
procedure TForm1.FormShow(Sender: TObject);
var LPath : String;
    LSound : IGorillaFMODSound;
begin
  FFMOD := TGorillaFMODAudioManager.Create(Self);
  FFMOD.Parent := Form1;
  LPath := IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0))) + 'demo.mp3';
  LSound := FFMOD.LoadSoundFromFile(LPath);
  FFMOD.PlaySound(LSound, nil, false, 0);
end;
 
procedure TForm1.Timer1Timer(Sender: TObject);
begin
  // Timer1.Interval := 33;
  FFMOD.Update();
end;
 

Output playback position

Form1.pas
uses
  Gorilla.Audio.FMOD,
  Gorilla.Audio.FMOD.Intf.Sound,
  Gorilla.Audio.FMOD.Intf.Channel;
 
procedure TForm1.Timer1Timer(Sender: TObject);
var LSound : IGorillaFMODSound;
    LMS, LLenMS : UInt32;
begin
  {...}
  LMS := FChannel.Position[FMOD_TIMEUNIT_MS];
  LSound := LChannel.GetCurrentSound() as IGorillaFMODSound;
  if Assigned(LSound) then
  begin
    LLenMS := LSound.Length[FMOD_TIMEUNIT_MS];
    Self.Caption := Format('sound #%s %d / %d', [IntToHex(NativeInt(LSound), 16), LMS, LLenMS]);
  end; 
end;

Add a FadePoint

Form1.pas
uses
  Gorilla.Audio.FMOD,
  Gorilla.Audio.FMOD.Sound,
  Gorilla.Audio.FMOD.Channel;
 
procedure TForm1.FormShow(Sender: TObject);
var LSound : IGorillaFMODSound;
    LChannel : IGorillaFMODChannel;
    LClock, LPClock : UInt64;
begin
  {...}
  LChannel := FFMOD.PlaySound(LSound, nil, false, 0);
  if Assigned(LChannel) then
  begin
    LChannel.GetDSPClock(LClock, LPClock);
    LChannel.AddFadePoint(LPClock, 0.0);
    LChannel.AddFadePoint(LPClock + 48000, 1.0);
  end;  
end;

Set 3D Listener Position

Form1.pas
uses
  Gorilla.Audio.FMOD,
  Gorilla.Audio.FMOD.Sound,
  Gorilla.Audio.FMOD.Channel,
  Gorilla.Audio.FMOD.System;
 
var
  FFMOD : TGorillaFMODAudioManager;
  FListenerPos : TFMOD_Vector;
  FListenerInc : Single;
 
procedure TForm1.FormShow(Sender: TObject);
begin
  {...}
  F3DListener := TGorillaFMOD3DListenerAttr.Create(FListenerPos); 
end;
 
procedure TForm1.Timer1Timer(Sender: TObject);
begin
  {...}
  // update 3d listener position : listener rotating around
  // soundsource = (0, 0, 0)
  FListenerInc := FListenerInc + 0.1;
  FListenerPos.X := Sin(FListenerInc) + 3;
  FListenerPos.Z := Cos(FListenerInc) + 3;
  F3DListener.Pos := FListenerPos;
  FFMOD.Set3DListener(F3DListener);  
end;

Loop sample

Form1.pas
uses
  Gorilla.Audio.FMOD,
  Gorilla.Audio.FMOD.Sound,
  Gorilla.Audio.FMOD.Lib.Common;
 
procedure TForm1.FormShow(Sender: TObject);
var LSound : IGorillaFMODSound;
begin
  {...}
  LSound := LFMOD.LoadSoundFromFile('drumloop.wav');
  LSound.Mode := FMOD_LOOP_NORMAL; 
end;

Apply a DSP effect to a channel

FMOD offers a large number of sound effects, f.e. echo, oscillator, distortion, …

  FMOD_DSP_TYPE = (
    FMOD_DSP_TYPE_UNKNOWN,            { This unit was created via a non FMOD plugin so has an unknown purpose. }
    FMOD_DSP_TYPE_MIXER,              { This unit does nothing but take inputs and mix them together then feed the result to the soundcard unit. }
    FMOD_DSP_TYPE_OSCILLATOR,         { This unit generates sine/square/saw/triangle or noise tones. }
    FMOD_DSP_TYPE_LOWPASS,            { This unit filters sound using a high quality, resonant lowpass filter algorithm but consumes more CPU time. Deprecated and will be removed in a future release (see FMOD_DSP_LOWPASS remarks for alternatives). }
    FMOD_DSP_TYPE_ITLOWPASS,          { This unit filters sound using a resonant lowpass filter algorithm that is used in Impulse Tracker, but with limited cutoff range (0 to 8060hz). }
    FMOD_DSP_TYPE_HIGHPASS,           { This unit filters sound using a resonant highpass filter algorithm. Deprecated and will be removed in a future release (see FMOD_DSP_HIGHPASS remarks for alternatives). }
    FMOD_DSP_TYPE_ECHO,               { This unit produces an echo on the sound and fades out at the desired rate. }
    FMOD_DSP_TYPE_FADER,              { This unit pans and scales the volume of a unit. }
    FMOD_DSP_TYPE_FLANGE,             { This unit produces a flange effect on the sound. }
    FMOD_DSP_TYPE_DISTORTION,         { This unit distorts the sound. }
    FMOD_DSP_TYPE_NORMALIZE,          { This unit normalizes or amplifies the sound to a certain level. }
    FMOD_DSP_TYPE_LIMITER,            { This unit limits the sound to a certain level. }
    FMOD_DSP_TYPE_PARAMEQ,            { This unit attenuates or amplifies a selected frequency range. Deprecated and will be removed in a future release (see FMOD_DSP_PARAMEQ remarks for alternatives). }
    FMOD_DSP_TYPE_PITCHSHIFT,         { This unit bends the pitch of a sound without changing the speed of playback. }
    FMOD_DSP_TYPE_CHORUS,             { This unit produces a chorus effect on the sound. }
    FMOD_DSP_TYPE_VSTPLUGIN,          { This unit allows the use of Steinberg VST plugins }
    FMOD_DSP_TYPE_WINAMPPLUGIN,       { This unit allows the use of Nullsoft Winamp plugins }
    FMOD_DSP_TYPE_ITECHO,             { This unit produces an echo on the sound and fades out at the desired rate as is used in Impulse Tracker. }
    FMOD_DSP_TYPE_COMPRESSOR,         { This unit implements dynamic compression (linked/unlinked multichannel, wideband) }
    FMOD_DSP_TYPE_SFXREVERB,          { This unit implements SFX reverb }
    FMOD_DSP_TYPE_LOWPASS_SIMPLE,     { This unit filters sound using a simple lowpass with no resonance, but has flexible cutoff and is fast. Deprecated and will be removed in a future release (see FMOD_DSP_LOWPASS_SIMPLE remarks for alternatives). }
    FMOD_DSP_TYPE_DELAY,              { This unit produces different delays on individual channels of the sound. }
    FMOD_DSP_TYPE_TREMOLO,            { This unit produces a tremolo / chopper effect on the sound. }
    FMOD_DSP_TYPE_LADSPAPLUGIN,       { Unsupported / Deprecated. }
    FMOD_DSP_TYPE_SEND,               { This unit sends a copy of the signal to a return DSP anywhere in the DSP tree. }
    FMOD_DSP_TYPE_RETURN,             { This unit receives signals from a number of send DSPs. }
    FMOD_DSP_TYPE_HIGHPASS_SIMPLE,    { This unit filters sound using a simple highpass with no resonance, but has flexible cutoff and is fast. Deprecated and will be removed in a future release (see FMOD_DSP_HIGHPASS_SIMPLE remarks for alternatives). }
    FMOD_DSP_TYPE_PAN,                { This unit pans the signal, possibly upmixing or downmixing as well. }
    FMOD_DSP_TYPE_THREE_EQ,           { This unit is a three-band equalizer. }
    FMOD_DSP_TYPE_FFT,                { This unit simply analyzes the signal and provides spectrum information back through getParameter. }
    FMOD_DSP_TYPE_LOUDNESS_METER,     { This unit analyzes the loudness and true peak of the signal. }
    FMOD_DSP_TYPE_ENVELOPEFOLLOWER,   { This unit tracks the envelope of the input/sidechain signal. Deprecated and will be removed in a future release. }
    FMOD_DSP_TYPE_CONVOLUTIONREVERB,  { This unit implements convolution reverb. }
    FMOD_DSP_TYPE_CHANNELMIX,         { This unit provides per signal channel gain, and output channel mapping to allow 1 multichannel signal made up of many groups of signals to map to a single output signal. }
    FMOD_DSP_TYPE_TRANSCEIVER,        { This unit 'sends' and 'receives' from a selection of up to 32 different slots.  It is like a send/return but it uses global slots rather than returns as the destination.  It also has other features.  Multiple transceivers can receive from a single channel, or multiple transceivers can send to a single channel, or a combination of both. }
    FMOD_DSP_TYPE_OBJECTPAN,          { This unit sends the signal to a 3d object encoder like Dolby Atmos.   Supports a subset of the FMOD_DSP_TYPE_PAN parameters. }
    FMOD_DSP_TYPE_MULTIBAND_EQ,       { This unit is a flexible five band parametric equalizer. }

    FMOD_DSP_TYPE_MAX,                { Maximum number of pre-defined DSP types. }
    FMOD_DSP_TYPE_FORCEINT = 65536    { Makes sure this enum is signed 32bit. }
  );

You can attach an effect easily onto a channel, but not directly to a sound instance.

uses
  Gorilla.Audio.FMOD,
  Gorilla.Audio.FMOD.Custom,
  Gorilla.Audio.FMOD.Lib.DSP.Effects,
  Gorilla.Audio.FMOD.Intf.Channel,
  Gorilla.Audio.FMOD.Intf.DSP;
 
[..]
 
/// CAUTION: For applying multiple effects, the AIndex argument has to start at zero and needs
/// to increase continously without gaps!
procedure TForm1.AttachEffect(AChannel : IGorillaFMODChannel; AIndex : Integer; AEffect : TFMOD_DSPType);
var LDSPEffect : IGorillaFMODDSP;
    LWetDryMix : TGorillaFMODWetDryMix;
begin
  // Access the system object of FMOD API
  LDSPEffect := GorillaFMODAudioManager1.SystemObject.CreateDSPByType(AEffect);
 
  // Activate the effect
  LDSPEffect.Active := true;
 
  // Setup the wet-dry mix record
  LWetDryMix.PreWet := 0.75;
  LWetDryMix.PostWet := 0.75;
  LWetDryMix.Dry := 0.25;
 
  // Apply it to the dsp effect
  LDSPEffect.WetDryMix := LWetDryMix;
 
  // Finally attach the dsp effect to our channel, where we play a sound
  AChannel.AddDSP(AIndex, LDSPEffect);
end;
 
[...]
 
// Let's add some kind of echo
AttachEffect(FChannel, 0, FMOD_DSP_TYPE_ECHO);

Android

Using FMOD in Android needs some additional configuration. Because on Android the FMOD engine uses a Java library (fmod.jar), we need to attach those to our project.

To allow correct loading of this FMOD Java library, we created an extra Java library (Gorilla.jar). This library loads the FMOD library in the right context.

Go to your project tree and select Android as destination platform. Then enter the library section and add these 2 files: fmod.jar, Gorilla.jar to your project. You can find them in the “lib” folder of your package download.

Next step: AssetsManager

Remarks

It is not allowed to release FMOD interfaces when the TGorillaFMODAudioManager component, especially the IGorillaFMODSystem interface inside of it, is already destroyed. In this case you will receive an AccessViolation, because FMOD can not destroy the internal handle anymore.

WRONG:

var LFMOD : TGorillaFMODAudioManager;
    LSound : IGorillaFMODSound;
 
[...]
 
LFMOD := TGorillaFMODAudioManager.Create(nil);
try
  LSound := LFMOD.LoadSoundFromFile(Common_MediaPathRaw('drumloop.wav'));
finally
  FreeAndNil(LFMOD);
  LSound := nil; // <<< WRONG!
end;

CORRECT:

var LFMOD : TGorillaFMODAudioManager;
    LSound : IGorillaFMODSound;
 
[...]
 
LFMOD := TGorillaFMODAudioManager.Create(nil);
try
  LSound := LFMOD.LoadSoundFromFile(Common_MediaPathRaw('drumloop.wav'));
finally
  LSound := nil; // !!! CORRECT!
  FreeAndNil(LFMOD);
end;

Next step: Assets Manager